基于简单天气查询agent的知识点串联理解

0 阅读5分钟

一次提问,图上到底跑了几步

flowchart TB
    A[START<br/>messages 0 条] --> B[超步 1 model<br/>决定调用 get_current_time]
    B --> C[超步 2 tools<br/>执行 拿到时间]
    C --> D[超步 3 model<br/>决定调用 web_search]
    D --> E[超步 4 tools<br/>执行 拿到搜索结果]
    E --> F[超步 5 model<br/>写出最终回答]
    F --> G[END<br/>messages 6 条]

模型不是「问一句、答一句」,而是「想一步 → 调个工具 → 拿着结果接着想」,直到不用再调了才开口答。而且每跑完一个超步,都会落一个检查点。

就拿「今天上海的天气如何?」这一句,实测下来:

项目实测
超步5 个
model 跑了几次3 次,不是 1 次
tools 跑了几次2 次,一次查时间、一次搜天气
messages从 0 条长到 6 条

下面把这一趟按顺序走一遍。


整体:图长什么样

先看这张图,就是之前学过的那个循环:

1.png START 进 model,model 出来分两条路:上一轮还带着工具调用就去 tools,没有工具调用了就直接 END。tools 干完再回到 model —— 就是这条回边,让模型能「想一步、看一眼、接着想」。

真跑起来之后,trace 里能一行一行数出节点执行的顺序:

2.png

__start__ → model → tools → model → tools → model,model 和 tools 交替出现。这就是上面那 5 个超步的本体。

最后在 chat 里看到的回答:

3.png


messages 是怎么一条条长出来的

每跑完一个超步就落一个检查点,检查点里存的是那一刻完整的 state。 所以把一串检查点按顺序看下来,messages 是怎么长出来的就一目了然 —— 这也是检查点最实用的地方:过程可以倒着回看。

起点:检查点里什么都没有

4.png

messages 是空的 []。这是这一趟开始之前的模样。

输入进来:reducer 把 HumanMessage 接上去

5.png

START 收到用户的问题,检查点里就多了一条 Human。注意节点只负责「产生这条新消息」,是 reducer 把它接到数组末尾的,不是把整个数组重写一遍。

第一次调用工具:model 要查时间

6.png

model 这一轮没有直接回答,而是产出了一条带工具调用的 AI 消息 —— 它要调 get_current_time。(右边那个 ID 是这次工具调用的编号,展开才看得到。)

这一轮跑完,检查点里 AI 那条也进去了:

7.png

现在是 [Human, AI] 两条,只增不减。

第一次执行:工具节点跑工具

8.png

tools 按这条调用去执行 get_current_time,返回一个时间字符串。跑完照样落检查点:

9.png

现在是 [Human, AI, Tool] 三条。

第二次调用工具:model 拿到时间,又决定要搜天气

10.png

这一步最值得看:模型先拿到了时间,然后才判断「光有时间不够,还得搜一下天气」,于是又一次发起工具调用,参数也自己写好了 —— query = 上海今天天气 2026年9月29日。(下半张是这个超步之后的检查点,[Human, AI, Tool, AI] 四条。)

第二次执行:搜索

11.png

tools 执行 web_search,检查点里又多一条 Tool,变成 5 条。

收尾:这次不用调工具了,直接回答

12.png

数据齐了,model 这一轮没有再产出工具调用,而是直接写回答。

13.png

最后这条 AI 消息进 messages,凑齐 6 条。model 发现自己不用再调工具了,这一次就走 END,一趟结束。


第二轮:上下文只会越来越大

第二轮我又问了一句「那今天杭州的天气如何?」,先看这一轮开始之前的 messages:

14.png

上一轮的 6 条原封不动还在 —— 因为 state 存在检查点里,第二轮接着往下用,不用重新来。

新的一句 Human 追加到末尾:

15.png

第 7 条就是新问题。这一轮的机制和第一轮一模一样(查时间、搜天气、回答),就不重复走了。

要留意的是:messages 只进不出,聊得越久上下文越长。 这不是 bug —— 好处是模型记得住前面聊过什么,代价是每一轮都得把全部历史重新发给模型。


之前学的概念,都落在这条 trace 的哪里

之前学的在这条 trace 里对应什么
图(graph)那张 START → model ↔ tools 的循环图
消息(message)检查点里 messages 数组的每一项
reducer每个超步跑完,messages 比上一步多一条
超步(super step)trace 里 model、tools 各占一行,一行就是一个超步
检查点(checkpointer)每个超步之后新落的那一条,点开就能看到当时的 state
工具节点(ToolNode)trace 里的 tools 那一行

容易踩的坑

  1. 以为「问一句 = 模型答一次」。 实测这一趟 model 跑了 3 次 —— 每拿到一批工具结果,它都要重新想一遍。
  2. 以为工具是模型一口气调完的。 实测是分两批:先查时间,拿到时间之后才决定要搜天气。模型是看着上一步的结果决定下一步的,不是开局就把两步都计划好。
  3. 以为 messages 会被新消息覆盖。 实测只增不减,第二轮跑的时候第一轮那 6 条还在,新问题接在第 7 条。

小结

  • 模型是「想一步、调一次、再想」,不是一问一答。 这一趟 5 个超步:model 3 次、tools 2 次。
  • 节点只负责产生新消息,追加是 reducer 干的。 所以 messages 只增不减。
  • 一个超步落一个检查点,检查点存的是当时完整的 state。 整个过程可以倒着回看。
  • 上下文只进不出。 第二轮直接用第一轮的 messages,聊得越久每轮发给模型的东西越多。