本文关注任务状态、预算、循环检测、错误分类、重试、幂等与恢复。
多轮工具调用如何始终记住核心任务
模型没有永久运行的内在记忆。每次推理时,Harness 都要重新构造有效上下文。
建议分四层维护。
固定任务契约
每轮保留:
核心目标
验收标准
用户约束
禁止事项
审批边界
近期工作记忆
保留最近的:
- Tool Call;
- Tool Result;
- 错误信息;
- 当前计划步骤;
- 最新假设和观察。
模型外结构化状态
{
"goal": "修复登录接口偶发 500",
"status": "testing",
"completed": [
"定位 AuthService",
"确认空指针根因",
"完成代码修改"
],
"pending": [
"修复测试夹具",
"运行完整测试",
"检查 git diff"
],
"changed_files": [
"src/AuthService.java"
],
"last_test": {
"command": "mvn test -Dtest=AuthServiceTest",
"result": "failed"
}
}
环境状态作为外部记忆
文件系统、Git Diff、测试报告和日志比模型总结更接近事实来源。需要时重新读取,而不是把所有历史永久塞进 Prompt。
上下文过长时,压缩旧历史,但必须保留:
- 核心目标和约束;
- 已完成和待完成步骤;
- 重要设计决策;
- 当前错误和未解决问题;
- 修改文件和测试状态。
长任务不能只依赖对话历史,还要通过 Task State、Checkpoint、进度记录和 Git 恢复真实状态。Anthropic 的长时运行 Agent Harness 实践也采用结构化功能列表、进度文件和 Git 历史帮助新上下文恢复任务。
如何防止无限循环和重复调用
不能只靠 Prompt 告诉模型“不要重复”,必须由 Harness 硬性兜底。
硬性预算
limits = {
"max_turns": 30,
"max_tool_calls": 50,
"max_same_tool_errors": 3,
"max_runtime_seconds": 600,
"max_tokens": 100_000,
"max_cost": 2.0
}
这些数字只是示例,应根据任务复杂度和真实评测确定。
重复调用指纹
不能只按工具名判断重复,而要加入参数和环境版本:
signature = stable_hash({
"tool": call.name,
"arguments": canonicalize(call.arguments),
"environment_version": git_diff_hash,
"task_state": current_step
})
这里的 stable_hash 与 canonicalize 是伪代码,需要使用稳定序列化后的摘要实现;Python 的字典不能直接传给内置 hash()。
合理重复:
修改文件前 read_file
→ apply_patch
→ 修改后再次 read_file
异常重复:
环境没有变化
→ 相同参数调用相同工具
→ 获得相同结果
→ 连续重复多次
无进展检测
即使工具名不同,也可能在兜圈子。因此还要检测:
- 是否获得新证据;
- 是否改变文件或任务状态;
- 是否完成计划步骤;
- 测试失败数量是否减少;
- 是否排除一个假设;
- 是否只是重复相同错误。
干预可以逐级升级:
第一次重复 → 记录
第二次重复 → 返回 LOOP_WARNING
第三次重复 → 要求重新规划或暂时禁用工具
仍无进展 → 终止并报告阻塞原因
推荐的终态:
success
failed
budget_exceeded
loop_detected
paused_for_approval
当前 OpenAI 模型指南也建议明确并发、重试和停止限制,并要求不要重复已完成调用。OpenAI Docs:Model guidance
工具调用出错如何处理
不能把所有错误统一成“再试一次”,应先分类。
| 错误类型 | 示例 | 处理策略 |
|---|---|---|
| 参数错误 | 缺字段、类型错误 | 不执行,返回结构化错误让模型修正 |
| 瞬时错误 | 429、5xx、短暂网络超时 | Harness 有限退避重试 |
| 业务错误 | 文件不存在、搜索无结果 | 返回模型,换路径、参数或工具 |
| 状态冲突 | Patch 失效、文件已变化 | 重新观察最新状态,再生成操作 |
| 权限错误 | 沙箱拒绝、无权写入 | 不重试,审批或报告 |
| 副作用不确定 | 请求超时但可能已退款 | 查询状态或使用幂等键,禁止盲重试 |
| 不可恢复错误 | 凭证缺失、环境不可用 | 保存状态并明确终止 |
结构化错误示例:
{
"ok": false,
"error": {
"code": "PATCH_CONFLICT",
"message": "目标文件内容已经变化,Patch 无法应用。",
"retryable": false,
"suggested_action": "重新读取文件,再根据最新内容生成 Patch。"
}
}
参数错误
使用严格 JSON Schema,在执行前拒绝非法参数,并把可修正错误回填给模型。OpenAI Function Calling 文档建议启用 strict: true,使函数参数严格符合 Schema。OpenAI Docs:Strict mode
瞬时错误
由 Harness 内部采用有限指数退避和随机抖动:
1s → 2s → 4s → 失败并返回模型
模型无法修复短暂网络波动,因此通常不必每次重试都重新推理。
业务错误
例如文件不存在:
{
"code": "FILE_NOT_FOUND",
"retryable": false,
"suggested_action": "先使用 search_code 或 list_files 查找真实路径。"
}
模型根据 Observation 更换策略。
有副作用的工具
read_file、search_code 等只读工具通常可以安全重试;send_email、create_refund、deploy_production 等可能产生副作用,必须:
- 使用
idempotency_key; - 查询上次操作状态;
- 记录唯一业务请求 ID;
- 在危险操作前 Checkpoint;
- 必要时人工审批。
对于敏感工具,模型可以提出调用,但 Harness 应暂停执行并等待审批。官方 Agent 文档将 Guardrail 用于自动校验,将 Human Review 用于敏感工具调用的批准或拒绝。OpenAI Docs:Guardrails and human review