现在把前几节串起来。
我们已经有:
Main Loop
State
Provider
Tool Executor
接下来真正的 Agent Loop 应该长这样:
while True:
response = provider.chat(
state.messages,
tools=tool_schemas
)
if response.tool_calls:
result = execute_tool(
response.tool_calls[0]
)
state.messages.append(
tool_result_to_message(result)
)
continue
return response.text
这里真正发生的是:
模型:
我想读 app.py
↓
Harness:
可以,执行 read_file
↓
Observation:
文件不存在
↓
模型:
那我先 list_files
↓
Harness:
执行
↓
Observation:
发现 src/main.py
↓
模型:
读取 src/main.py
这就是“长出手脚”。
一个完整可运行的 Tool Calling Agent
我们继续不用真实云模型。
写一个 Fake Provider。
这个程序已经把前面四节真正接在了一起。
发生了什么?
第一轮模型:
read_file("app.py")
Harness:
执行
结果:
FileNotFoundError
第二轮模型看到 Observation:
list_files()
Harness:
执行
结果:
["src/main.py", "requirements.txt"]
第三轮模型:
read_file("src/main.py")
最终:
COMPLETED
这就是 Agent 真正开始和环境互动的那一刻。
Tool Result 为什么必须带回模型?
因为 Tool Calling 的完整价值不在:
调用一次工具
而在:
调用
↓
观察
↓
根据结果继续调用
如果 Tool Result 不进入下一轮 Context,Agent 仍然只是“盲调用”。
所以,Tool Calling + Observation 回流,才真正组成多步 Agent。
如果把这一节压缩成一张图,核心就是下面这个闭环:Agent 先基于当前 State 决策;如果需要工具,就交给 Harness 执行;执行结果再作为 Observation 回到 messages 中,驱动下一轮动作。
![图片[1]-小节三:把 Tool Calling 接回 Main Loop——Agent 为什么终于可以连续“做事”了?-大赛人网](https://www.dsrw.com/wp-content/uploads/2026/11/image-1456.png)
也正因为有了这条回流链,Agent 才不是“一次性输出一条 JSON”就结束,而是真的能连续根据环境反馈往下做事。
模型一次响应也可能提出两个甚至多个 Tool Call。执行层要逐个记录工具名、参数、结果和耗时,但这一轮的 input token、output token 和模型耗时,只属于一次 模型请求。如果把同一份 Token 数据复制到每条 Tool Trace,成本报表就会被放大两倍、三倍。
配套 Engine 把它们拆成两类记录:先记一条 LLM 事件,保存本轮完整 tool_calls 与 Token;再为每个实际执行的工具记一条 Tool 事件,Token 记为 0,只保存工具耗时。核对时可以抓住一个规则:同一份 ProviderResponse 无论包含几个工具调用,对应的模型用量只能累计一次。
















请登录后查看评论内容