小节三:把 Tool Calling 接回 Main Loop——Agent 为什么终于可以连续“做事”了?

现在把前几节串起来。

我们已经有:

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 为什么终于可以连续“做事”了?-大赛人网

也正因为有了这条回流链,Agent 才不是“一次性输出一条 JSON”就结束,而是真的能连续根据环境反馈往下做事。

模型一次响应也可能提出两个甚至多个 Tool Call。执行层要逐个记录工具名、参数、结果和耗时,但这一轮的 input token、output token 和模型耗时,只属于一次 模型请求。如果把同一份 Token 数据复制到每条 Tool Trace,成本报表就会被放大两倍、三倍。

配套 Engine 把它们拆成两类记录:先记一条 LLM 事件,保存本轮完整 tool_calls 与 Token;再为每个实际执行的工具记一条 Tool 事件,Token 记为 0,只保存工具耗时。核对时可以抓住一个规则:同一份 ProviderResponse 无论包含几个工具调用,对应的模型用量只能累计一次。

© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
评论 抢沙发
头像-大赛人网
欢迎您留下宝贵的见解!
提交
头像-大赛人网

昵称

取消
昵称

    请登录后查看评论内容