AI in Harness(二)

书接上文 juejin.cn/post/765961...

Error Recovery - 从错误中恢复

让 agent loop 在面对 LLM 三类常见故障(输出截断 / context 太长 / 后端过载)时自我恢复,而不是把异常一路抛回出去。

对于一个 Java 程序员处理异常那就太熟悉不过了。给 LLM 的调用方法加上 try catch。

目前最常见的错误,输出太长,输入太长,调用太快,模型压力太大。

输出太长:

  1. 第一次 :把 max_tokens 从默认 8000 → 升级到 64000(escalated),丢掉截断的输出,重新跑这一轮。模型重生成,通常这次能说完
  2. 第二次还截断 :已经 64k 还说不完 → 真的需要"分两次说"。append 截断输出 + 注入 Continue. user message,让模型续写
  3. 超过 maxRecoveryRetries=3 次 continuation:放弃,Fatal

输入太长:

  1. 可以直接调用 reactive_compact 压缩模式。使用比较激进的压缩模式,只保留最近的若干条消息。

调用太快、模型压力过大:

  1. 调用 LLM 的时候才用指数退避的方式重试。若干次后依然报错,则抛出异常。

Task System - 更强的规划能力

让 LLM 自己用文件级任务图把"大目标"拆成"有 DAG 依赖、可被 claim/complete 跟踪、跨 agent loop 持久化"的小任务,而不是把所有计划塞 todo_write 在内存里。

已经有 todo_write 了 ------ 内存里一个三态 todo 列表,LLM 用它做计划。但 todo_write 有 4 个致命缺陷,只要任务复杂一点就暴露:

todo_write task system
只活在当前 session,重启 → 全没 文件级持久化 (.tasks/{id}.json)
只有"线性列表",没法表达"B 必须等 A 完成才能做" blockedBy DAG 依赖 , canStart 计算
只有 owner 隐式("当前 agent"),没 claim 概念 claim_task 显式 owner + 状态机
整体替换语义(每次都重写整个列表) 单条 update(完成 A 不动 B/C)

更重要的是 task system 让子 agent 也能参与------ 父 agent 派子 agent 干活时,子可以读到 task 列表知道全局计划,而不是"接到 description 就盲跑"。

核心 API:

实际上 harness 只暴露了 4 个原语 ,claim/complete/block 都是 TaskUpdate 的不同用法 ------ 靠状态字段区分,而不是独立接口。

API 作用 关键参数 返回
TaskCreate 建节点 subject, description, activeForm 新任务 id(初始 status=pending, owner="")
TaskList 看队列 所有任务的摘要(id/subject/status/owner/blockedBy)
TaskGet 读详情 taskId 单个任务的完整字段(含 description、blocks/blockedBy 双向边)
TaskUpdate 改一切 taskId + 变更字段 更新后的任务

为什么没有独立的 claim / complete / block ? 因为它们都能用 TaskUpdate 的字段组合表达,合并成一个接口反而简化了状态机:

语义动作 对应的 TaskUpdate 调用
claim(认领) {taskId, owner: "self", status: "in_progress"}
complete(完成) {taskId, status: "completed"}
建依赖(我等 X) {taskId, addBlockedBy: [X]}
建依赖(我挡 Y) {taskId, addBlocks: [Y]}
让出(放弃 owner) {taskId, owner: ""}
删除 {taskId, status: "deleted"}

设计要点:

  • TaskCreate 只建节点,不连边 ------ 依赖必须先拿到 ID 再用 TaskUpdate 补
  • TaskList 是决策入口 ------ agent 每轮 tick 先 List 找可 claim 的(owner="" && blockedBy=\[\])
  • TaskGet 是执行前的最后一步 ------ claim 后进入 in_progress,用 TaskGet 拿完整 description 再干活(因为 List 只返回摘要,为省 token)
  • TaskUpdate 幂等 ------ 重复 mark completed 不会出错,方便 error recovery 场景重放

核心工作流程:

1、需要将一个复杂任务拆解。并确认 DAG (有向无环图)依赖。使用 DAG 意味着任务可并行, 可拓扑排序,无死锁。

为什么 DAG 而不是树/线性队列?真实开发依赖不是树形的:任务 D 可能同时依赖 B 和 C(菱形),线性队列表达不了并行,树形结构表达不了合并。DAG 是最小的能表达"部分序"的结构。

2、任务拆解以后,将任务持久化,方便 agent 认领提交。

3、Agent 会通过 tools,调用TaskList()获取 pending 和 无 blockedBy 的任务。

4、LLM 挑选一个 task 执行。

5、调用 TaskUpdate 更新 task 的状态。

scss 复制代码
[Agent tick N]
  TaskList()  → 拿到 pending / owner="" / blockedBy=[] 的任务
  ↓
  LLM 挑一个(通常最小 ID)
  ↓
  TaskUpdate({taskId, owner: "self", status: "in_progress"})
  ↓
  (可选)同一 tick 内立即开始第一步执行 ------ 发起 Read/Bash 等工具调用
  ↓
[后续多个 tick] 反复执行工具调用推进任务
  ↓
[某个 tick] TaskUpdate({taskId, status: "completed"})
  ↓
  回到 TaskList 找下一个可 claim 的任务

注意:claim 和"开始执行"通常发生在同一个 tick 里,harness 不会在 TaskUpdate 之后强制切换 ------ LLM 一轮可以发多个 tool call。

Background Task - 后台执行耗时任务

**让 LLM 调慢工具(./mvnw testdocker buildpip install)时,Agent 立即返回 placeholder 让 LLM 继续工作,后台 daemon 线程跑完后通过 <task_notification> 文本块注入下一轮 --- 取消同步等待

为什么需要 Background Task

如果 agent loop 是完全同步的:

arduino 复制代码
LLM tool_use(bash, "./mvnw test")
  ↓
agent loop 进程同步等子进程(直到默认 2 分钟超时)
  ↓
返回 ToolResult,继续 loop

LLM 这段时间里完全失去主动权 ------ 不能并行去做别的小任务,不能主动决定"要不要 abort 重新调",甚至连"我此刻在等什么"都看不到。当单线程 RPC 客户端用。

我们需要:

arduino 复制代码
LLM tool_use(bash, "./mvnw test", run_in_background=true)
  ↓
agent 派 daemon thread → 立即返回 "[Background task bg_0001 started]"
  ↓
LLM 看到 placeholder,可以:
  - 想"我先去看下其他文件"
  - 调别的工具(读文件、跑快命令)
  - 安排接下来的步骤
  ↓
后台 thread 完成,harness 在下一轮 tool_results 时
  追加 <task_notification id="bg_0001">...</task_notification>
  ↓
LLM 看到通知,自然消费结果("噢 test 跑完了,结果是 X")

LLM 重新拿回主导权:它决定什么时候等、等多久、要不要先做别的事。

相关 API

API 作用
Bash(run_in_background=true) 派后台任务,立即返回 task_id
TaskOutput(task_id, block=true) 阻塞等结果(等价于同步)
TaskOutput(task_id, block=false) 立即返回当前状态(异步 peek)
TaskStop(task_id) 主动取消跑飞的后台任务

<task_notification> 不是特殊的 IPC 中断机制,而是 harness 在下一次 API 请求时拼在 user 消息里的一段文本。所以 LLM 是"在下一次思考中读到"通知,不是"被中断"------ 这就是它保持主导权的技术根源。

github 开源地址 github.com/diaozxin007...

相关推荐
IT_陈寒1 小时前
Python装饰器竟然偷偷改了函数名?这个坑我爬了三天
前端·人工智能·后端
阿里云大数据AI技术1 小时前
DataWorks Data Agent “又双叒叕”升级:更低成本、更强智能、更多连接
人工智能·agent
Elastic 中国社区官方博客1 小时前
Elastic 在 Everest Group 企业搜索产品 PEAK Matrix® 评估 2026 中被评为领导者
大数据·运维·人工智能·elasticsearch·搜索引擎·ai·全文检索
bkl_92132 小时前
GPT-Image-2 文生图:如何稳定生成完整的人物全身照?
后端·gpt·restful
牧艺2 小时前
Cursor 多 Agent 与 Worktree:大重构不再赌一把
人工智能·agent·cursor
一键生成网站2 小时前
开源AI政务大屏制作工具对比:国产化安全合规选型深度分析
人工智能·开源·政务·
jy2 小时前
支付后端研发的 AI 工作流实践:巡检、排查、开发全流程落地
后端·ai编程
智食分子2 小时前
大模型开发框架LangChain
人工智能
YHL2 小时前
🤖 Agent 智能体开发实战 · 第一课:Tool Use —— 让大模型自动干活
前端·javascript·人工智能