工具结果为什么必须回到下一轮
用户要求:“读取 task.md,告诉我里面写了什么。”
模型发出 read 调用,handler 读到“把 Loop 跑通”。程序把结果打印到日志,随后继续请求模型。第二次请求没有带上这段结果,模型就会再次读取,或者直接猜答案。
工具没有坏,断掉的是 handler 之后的消息链。
一次读取至少要两轮
Section titled “一次读取至少要两轮”第一轮模型调用产生动作请求:
user 读取 task.md 后回答
assistant toolCall(id="read-1", name="read", path="task.md")Harness 执行工具后,要追加一条与 read-1 对应的结果:
tool toolResult(id="read-1", content="把 Loop 跑通。")第二轮模型看到的顺序必须是:
user → assistant(toolCall) → tool(toolResult)模型这时才有材料生成最终答案,或继续提出下一项动作。
调用 ID 很关键。同一条 assistant message 可以同时要求读取两个文件,也可能出现一个成功、一个失败。如果只按工具名称匹配结果,第二轮很容易把 a.txt 的内容接到 b.txt 上。
Pi 在哪一步执行工具
Section titled “Pi 在哪一步执行工具”Loop 的主流程会收集 assistant message 中的工具调用,执行完成后把 tool result 放进上下文,再决定是否开启下一轮。查看 runAgentLoop() 的循环段。
一条调用还要经历更细的步骤:
查找工具 ↓prepareArguments ↓JSON Schema 校验 ↓beforeToolCall ↓execute ↓afterToolCall ↓ToolResultMessage这段实现集中在 agent-loop.ts 的工具执行部分。
失败结果也必须回填。假如 read 返回“文件不存在”,模型需要看到这个观察,才能换路径或说明失败。如果异常只写进服务端日志,模型会继续沿着一次并未发生的成功执行推理。
参数不完整时不要猜
Section titled “参数不完整时不要猜”模型可能在输出 JSON 参数时达到长度上限。此时工具名看起来完整,最后一个字符串或括号却可能缺失。Pi 会拒绝执行这一批可能不完整的 tool call,而不是替模型补齐参数。查看长度截断保护。
对只读工具而言,猜错一次也许只是报错;对写文件、发消息或运行命令而言,猜参数会变成真实副作用。
把第二次输入打印出来
Section titled “把第二次输入打印出来”运行实验:
cd agent-harness-labnpm run lab -- 03输出中有三项关键观察:
{ "terminal": "completed", "turns": 2, "toolCalls": 1, "secondModelInputRoles": ["user", "assistant", "tool"]}turns 为 2,说明工具请求与最终回答分属两次模型调用;toolCalls 为 1,说明文件只读取了一次;第二次输入中出现 tool,直接证明结果已经回填。
如果第二个数组缺少 tool,即使模型偶尔猜对答案,也不能算 Loop 已经接通。
现在这条链路能读文件,但所有动作都被直接放行。下一章换成一条 schema 合法的写入请求,再把参数校验、权限判断和人工批准拆开。