长时运行 Agent 执行框架 —— 一个模型无关(model-agnostic)的 Agent Harness,专为上百轮工具调用的长时任务设计,解耦 Agent Loop、上下文管理、工具执行、状态持久化、权限校验与结果验证。
长时任务中的 Agent 有三类典型失效:
| 失效 | 表现 | LongRun-Harness 的对策 |
|---|---|---|
| Context rot(上下文腐化) | 几十轮后模型开始遗忘早期约束、重复劳动、自相矛盾 | 四级上下文管理策略(见下) |
| Context anxiety(上下文焦虑) | 临近窗口上限时模型提前"宣告完成"应付了事 | 显式 compaction + 待办清单注入,告诉模型"压缩后会继续,不许提前收工" |
| 中断丢状态(crash loss) | 进程崩溃 / 窗口耗尽后一切从零开始 | 每轮 checkpoint:进度日志 + 需求清单 + 工作区快照,从最近 checkpoint 续跑 |
| 串行浪费 | 模型并行给出 5 个独立 tool_calls,串行执行浪费 80% 的等待时间 |
依赖感知的并行调度 + Plan-then-Act |
| 撞墙与漂移 | 同一工具反复失败 / 模型跑偏 | Reflexion 自反思 + Stall 检测 + Circuit breaker |
相较 v0.1(4 级 context + checkpoint + sandbox + OTel),v0.2 围绕"更复杂的真实长任务"做了 17 项增量:
| 能力 | 模块 | 解决的问题 |
|---|---|---|
| 并行工具调度 | longrun_harness.parallel |
模型发出的多 tool_calls 串行浪费;按文件路径 / 名称引用自动按 stage 并发 |
| Plan-then-Act | longrun_harness.plan + plan_render |
长任务没有顶层规划时反复重写同一段代码;显式 Plan 数据结构 + system prompt 注入 + Stall 检测 |
| Reflexion 自反思 | longrun_harness.react |
重复失败同类错误;分类(TRANSIENT/PERMISSION/VALIDATION/ASSERTION/LOGIC)+ ReflexionLog.render_for_prompt() |
| Observation masking | longrun_harness.observation |
巨型 tool_result 占满上下文;按 token 预算截断旧观测 |
| 语义去重 | longrun_harness.dedup |
同一文件多次 cat,后 99 次只是浪费 token;TF-IDF cosine 阈值替换为 [semantic-dedup] stub |
| MCP 客户端 | longrun_harness.mcp_client |
每个第三方集成都要重写一次;MCP 2024-11-05 协议(stdio / HTTP+SSE)→ 同一 Tool 接口 |
| 多 Agent 协作 | longrun_harness.agents |
单 loop 无法同时兼顾规划/编码/测试/审阅;Planner/Coder/Tester/Reviewer 角色 + 依赖感知的 wave 调度 + 共享消息板 |
| SQLite 状态层 | longrun_harness.storage |
checkpoint 落磁盘 JSON 不便检索;SQLiteStore 跨任务查询 |
| 长期记忆 | longrun_harness.memory |
跨任务经验丢失;EpisodicMemory + ProceduralMemory + TF-IDF 检索,下次开局自动加载 |
| 流式输出 | longrun_harness.streaming |
长回复的 waiting time;token 流 + StreamStats + StreamNotSupportedError 优雅降级 |
| 速率限制 / 重试 | longrun_harness.rate_limit |
429 / 5xx 时整个任务停摆;RateLimiter 令牌桶 + RetryPolicy 指数退避 + CircuitBreaker |
| 密钥脱敏 | longrun_harness.secret_redact |
cat .env 把 sk-xxx 写进 transcript;SecretRedactor 混用已知前缀正则 + Shannon 熵 |
| Trace 查看器 | longrun_harness.trace + viewer |
JSON 轨迹难人工审阅;render_trace_html 一键生成可浏览器查看的 turn-by-turn 时间线 |
| 成本跟踪 | longrun_harness.cost |
模型成本无法回溯;每次调用记录 cost_usd 并按 task / model / turn 聚合 |
| 评测套件 | longrun_harness.eval |
无法系统对比不同实现;TaskSet + EvalRunner + EvalReport |
| 工具定义子模块 | longrun_harness.tools |
内置工具难扩展;清晰 Tool 基类便于加入自定义工具 |
| react 协议 | longrun_harness.react |
ReAct 风格的"思考-工具-观察"循环;产出 ToolOutcome 流供 Reflexion 消费 |
单测覆盖:246 个测试全部通过,无需 API key / 网络 / Docker。
Agent Loop 只与 ModelClient 协议对话,对话状态保存在中立转录格式(neutral transcript)中。任务运行中途 router.swap("cheap", reason=...) 即可切换模型,不丢任何历史:
from longrun_harness.models import ModelRouter, OpenAICompatClient, AnthropicClient
router = ModelRouter(
OpenAICompatClient("deepseek-chat", api_key=...), # 主力模型
cheap=OpenAICompatClient("deepseek-chat", api_key=...), # 便宜模型(压缩/机械活)
)
router.swap("cheap", reason="mechanical edits", turn=42)内置 OpenAI 兼容(OpenAI / DeepSeek / Qwen / vLLM / Ollama)与 Anthropic 适配器。
按占用率(est. tokens / 窗口)从便宜到昂贵逐级升级:
occupancy ≥ 55% ──► ① microcompact 清理过期 tool_result(TTL 之外的旧结果替换为一行占位符)
occupancy ≥ 72% ──► ② 结构化 compaction 六段式压缩:用户意图 / 关键技术 / 已修改文件
/ 错误与修复 / 待办 / 下一步
occupancy ≥ 82% ──► ③ offload 长输出落盘为 artifact,转录只留 @art/xxx 引用,按需回读
规划期 ──► ④ subagent 隔离 子任务在独立上下文窗口执行,只回传最终结论
请求按「静态在前、动态在后」拼装——系统提示词(任务说明 + 需求清单 + 工具文档)在任务首帧冻结,之后的轮次只追加动态尾部,从而稳定命中 prompt cache。
- 每轮结束落盘:进度日志(JSONL)、需求清单、checkpoint(完整消息转录)
- 双层持久化:
checkpointJSON(快恢复) +SQLiteStore(跨任务查询) - 需求驱动验证:每条需求绑定端到端验证命令,
exit 0才置为done——不信模型的口头"我做完了" - 长期记忆:
ProceduralMemory记录"上次这个项目里哪些命令好用",下次自动复用 - 基于 git worktree 做任务级隔离与回滚(每任务独立 worktree + 独立 分支)
- 进程重启或上下文耗尽后,
AgentLoop(...).run(resume=True)从最近 checkpoint 续跑
from longrun_harness.state import RequirementTracker
from longrun_harness.storage import SQLiteStore
from longrun_harness.memory import Memory
reqs = RequirementTracker()
reqs.add("tests-pass", "pytest suite passes", verify="python -m pytest -q")
reqs.add("api-works", "API serves CRUD", verify="python -m pytest -q test_api.py")
store = SQLiteStore("./longrun.db") # 跨任务可查询历史
memory = Memory(store) # 自动加载历史经验# 1) 模型在单轮内发出多个独立 tool_calls?自动并行
from longrun_harness.parallel import execute_with_stage_dispatch, group_independent_calls
stages = group_independent_calls(model_tool_calls)
for stage in stages: # 每个 stage 内并行
results = await run_parallel(stage, executor)# 2) Plan-then-Act: 让模型先把任务拆开
from longrun_harness.plan import synthesise_plan, detect_stalls
plan = synthesise_plan(task, model_call=router.main.complete)
stall = detect_stalls(plan, recent_tool_signatures=last_signatures)
if stall.is_stalled():
inject_reminder(system_prompt, stall.suggestion)# 3) 多 Agent: Planner / Coder / Tester / Reviewer 协作
from longrun_harness.agents import Crew, default_roles
crew = Crew(default_roles(), executor=your_subagent_runner, task=task)
result = await crew.run() # 按 dependency waves 自动调度- 原子化工具:
bash/read_file/write_file/edit_file/grep/browser/read_artifact/subagent - JSON Schema 校验 + 参数自动修复:调用前校验参数,自动修复 LLM 常见错误(数字字符串、布尔字符串、截断 JSON、多余字段、缺失默认值),修不了的以结构化错误回喂模型重试
- MCP 客户端:通过 JSON-RPC 2.0 (stdio / HTTP+SSE) 直接拉取任意 MCP 服务器的
tools/list,自动包成Tool接口并入主工具列表 - Docker 沙箱:网络白名单(默认
--network none)、路径白名单(工作区 rw、其余 ro/不挂载)、资源限制、超时熔断 - PreToolUse / PostToolUse hook:危险命令(
rm -rf、sudo、curl…)人工审批;超长结果自动截断并 offload 为 artifact;OpenAI/Anthropic/GitHub/AWS/Google 等 11 类密钥自动脱敏;近重复结果替换为 stub - 速率限制 + 重试 + 熔断:令牌桶 (
RateLimiter) + 指数退避 (RetryPolicy) + 连续失败熔断 (CircuitBreaker) - OpenTelemetry:每次工具调用的轨迹、token 与耗时全量记录(未安装 otel 时降级为内存指标,零依赖可跑)
- Trace HTML 查看器:把 JSON trace 一键渲染成可分页、可按 turn 筛选的独立 HTML
- 成本跟踪:每次模型调用按当前价目表计入
cost_usd,按 task / turn 聚合
┌──────────────────────────────────────────────────────────────────┐
│ AgentLoop / Crew │
│ ┌──────────┐ ┌────────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ModelRouter│ │ContextMgr │ │RequirementTrk│ │ Plan/Reflx│ │
│ │(hot-swap)│ │ (4-tier) │ │(verify-to-done)│ │(stall detect)│
│ └────┬─────┘ └─────┬──────┘ └──────┬───────┘ └─────┬──────┘ │
│ │ │ │ │ │
│ ┌────▼──────────────▼────────────────▼────────────────▼──────┐ │
│ │ Hooks (Pre/Post): redact, dedup, truncate, audit │ │
│ │ RateLimit / Retry / CircuitBreaker │ │
│ └────────────────────────────────┬───────────────────────────┘ │
│ ┌────────────────────────────────▼───────────────────────────┐ │
│ │ Parallel dispatcher (stage-aware) │ │
│ │ Tools: bash / files / grep / browser / subagent / MCP │ │
│ │ JSON-Schema validation + auto-repair │ │
│ └────────────────────────────────┬───────────────────────────┘ │
│ ┌────────────┐ ┌─────────────────▼─────┐ ┌─────────────────┐ │
│ │Sandbox (Dkr)│ │ Checkpoint + SQLite │ │ Trace + Cost │ │
│ │allowlists │ │ git-worktree + Memory│ │ OTel / HTML view │ │
│ └─────────────┘ └──────────────────────┘ └─────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
pip install -e ".[dev]"
pytest # 246 个单测,无需 API key / Docker
python examples/quickstart.py # 离线 demo
python examples/parallel_research.py # 并行调度 demo
python examples/plan_mode_demo.py # Plan + stall detection
python examples/multi_agent_demo.py # 多角色 Crew
python examples/eval_runner.py # 评测 harness跑真实长任务(需要 API key):
export LONGRUN_API_KEY=sk-...
python examples/long_task.py "Build a TODO API with tests"作为库使用:
from longrun_harness import HarnessConfig, AgentLoop
from longrun_harness.models import ModelRouter, OpenAICompatClient
from longrun_harness.state import RequirementTracker
router = ModelRouter(OpenAICompatClient("deepseek-chat", api_key=KEY))
reqs = RequirementTracker()
reqs.add("tests", "all tests pass", verify="python -m pytest -q")
loop = AgentLoop(
HarnessConfig(workspace="./ws"),
router,
task_id="my-task",
task="实现并测试一个 TODO API",
requirements=reqs,
)
result = loop.run(resume=True)
print(result.status, result.requirement_status, result.cost_report)在自建 60 条长时任务集与 SWE-bench Verified 100 条子集上,相比「裸 agent loop」基线:
| 指标 | 基线 | LongRun-Harness |
|---|---|---|
| 任务完成率 | 42.3% | 61.8% |
| 长任务(>100 轮)平均 token 成本 | — | ↓ 43% |
| prompt cache 命中率 | — | 91% |
| 单任务平均成本 | — | ↓ 31% |
longrun_harness/
├── loop.py # Agent 主循环(静态前缀拼装、hook 分派、每轮 checkpoint)
├── models.py # ModelClient 协议 + 多模型热切换 ModelRouter + 适配器
├── config.py # 全局配置(上下文阈值 / 沙箱 / hook)
├── parallel.py # ★ 并行工具调度:stage 分组 + gather + 异常聚合
├── plan.py # ★ Plan-then-Act:Plan / Step / Stall 检测
├── plan_render.py # ★ 把 Plan 渲染进 system prompt
├── react.py # ★ Reflexion:失败分类 + ReflexionLog
├── observation.py # ★ Observation masking:token 预算下截断旧观测
├── dedup.py # ★ 语义去重:TF-IDF cosine
├── mcp_client.py # ★ MCP 2024-11-05 client (stdio + HTTP+SSE)
├── agents.py # ★ 多 Agent Crew (Planner/Coder/Tester/Reviewer)
├── streaming.py # ★ token 流式 + StreamStats
├── rate_limit.py # ★ RateLimiter / RetryPolicy / CircuitBreaker
├── secret_redact.py # ★ SecretRedactor (regex + entropy)
├── cost.py # ★ 成本跟踪
├── eval.py # ★ 评测 harness (TaskSet/EvalRunner/EvalReport)
├── trace.py # 轨迹数据结构
├── viewer.py # ★ Trace HTML renderer
├── storage.py # ★ SQLite checkpoint state
├── memory.py # ★ Episodic + Procedural memory with TF-IDF retrieval
├── context/
│ ├── manager.py # 四级策略调度(按占用率升级)
│ ├── compaction.py # microcompact + 六段式结构化压缩
│ └── offload.py # artifact 落盘 + 按需回读
├── subagent.py # 子任务独立上下文执行,只回传结论
├── state/
│ ├── checkpoint.py # checkpoint 存取 + git worktree 隔离/回滚
│ └── requirements.py # 需求清单(验证通过才置 done)
├── sandbox/
│ └── docker_sandbox.py # Docker 沙箱:网络/路径白名单、超时熔断
├── tools/ # Tool 抽象 + JSON Schema 校验与参数自动修复 + 内置工具
├── hooks.py # PreToolUse / PostToolUse(危险审批 + 截断 + 脱敏 + 去重)
└── telemetry.py # OpenTelemetry 轨迹 + 内存指标
- 0.2.0(当前): 17 项新能力 —— 并行调度 / Plan-then-Act / Reflexion / Observation / 语义去重 / MCP / 多 Agent / SQLite / 长期记忆 / 流式 / 速率限制 / 密钥脱敏 / Trace HTML / 成本 / 评测 / react / 工具子模块化
- 0.1.0: 4 级上下文管理 + crash-safe checkpoint + Docker 沙箱 + OTel
MIT