TT Lab
开始
学习 学习路径 课程

AI 智能体 — 是图不是模型

停不下来的智能体,坏的不是循环而是条件

在 TT Lab 中继续学习

一句话总结

智能体停不下来的事故,大多不是因为构成了循环,而是因为存在达不到结束循环的条件的输入。

为什么需要它

上线了一个把回答草稿改写到符合规则为止的智能体。头几天运行得很好。然后有一天,有一条一直结束不了、不停地转,日志里 review 和 revise 交替打印了几百行。

看代码,结束条件好端端地在那里。“分数达到 70 分以上就放行。”问题在于那一条的草稿永远达不到 70 分。扣分的原因有两个——违禁词和长度——而修订节点只清除了违禁词。因长度被扣掉的 40 分,转多少圈都原封不动。

循环没有错,错的是“修改就会变好”这个假设。

画出一个永远不会结束的循环。review 和 revise 来回往复,只有分数达到 70 以上才会流向 ship。但是 100 分中因长度被扣掉的 40 分,修订节点并不处理,所以分数最多停在 60,够不到放行线 70

工作原理

在 LangGraph 中,分支用 add_conditional_edges 创建。有三个参数。

graph.add_conditional_edges("review", after_review,
                            {"ship": "ship", "revise": "revise", "escalate": "escalate"})

中间的函数返回的是分支名称,而不是下一个节点的名称。哪个分支通向哪个节点,由第三个参数(路径映射)决定。为什么要多这一层——是为了把判断的代码和接线分开。如果把节点名称写在函数里,每次改图的形状都得修改判断代码,而且负责绘图的一方(get_graph())也无从知道能通向哪里。

循环只是往回走的边。一行 graph.add_edge("revise", "review"),经过 revise 之后就会回到 review。没有特别的语法,这既是这个模型的好处,也是它可怕的地方。

递归上限数的是什么——亲自测得的数字

没有结束条件就会无限转下去吗?不会。LangGraph 在触及 recursion_limit 时会抛出 GraphRecursionError。消息是这样的。

Recursion limit of 25 reached without hitting a stop condition.
You can increase the limit by setting the `recursion_limit` config key.

计数的单位不是节点执行次数,而是超级步(superstep)。一个超级步里可以运行多个节点。在本实验镜像的 langgraph 0.2.60 中亲自测量,结果如下。

图 运行到结束所需的最小 recursion_limit
串联 3 个节点 4
并排放置 3 个节点 2
(默认值) 25

串联时为 노드 수 + 1(韩文,意为“节点数 + 1”),是因为起始通道要占一个超级步;并排时无论节点数是多少都是 2,是因为全部在同一个超级步里运行。不了解这个差别,就会把“增加节点后撞上了上限”误诊——并排增加的节点并不消耗上限。

默认值 25 不是一个宽裕的数。如果修订循环每转一圈用掉两个超级步(review + revise),连十二圈都转不到。

触及上限与完成了任务是两回事

实际工作中常在这里出错。如果捕获 GraphRecursionError 只记成“失败”,以后就无法知道那一条为什么失败。因为两件完全不同的事被放进了同一格。

所以捕获到的异常要像 {"status": "limit"} 这样换一个名称记录。而且当触及上限的条目积累起来时,不是去调高上限,而是先看结束条件是否够得到。

设置两层上限

即使结束条件正常,上限也是必要的。实际工作中设置两层。

没有内层上限时,会由外层上限来拦,那时到目前为止做好的草稿也会一起消失。内层上限留下结果,外层上限丢弃结果。这个差别在生产环境中很大。

在现场相遇的样子

第一,靠调高上限来蒙混过关。把 recursion_limit 从 25 调到 200,那一条就过去了。然后下个月再调到 400。真正的问题是存在够不到结束条件的输入,这与上限无关。

第二,判断分散在多个地方。如果在节点里看一次是否结束,在条件函数里又看一次,总有一天两处会不一致。判断集中在条件函数一处,节点只干活。

第三,分支名称和节点名称写成一样的。这样方便,但会省略路径映射,结果绘图时就看不到有哪些分支。即使名称相同,也最好写上映射。

第四,把并排增加的节点归咎于上限。上面的表纠正了这种误解。并排的节点属于同一个超级步。

实际工作中真正重要的事

下一项实验要做什么

一步步扩展 /root/work/agroute/route.py。先做出带路径映射的分支,接上把修订后的草稿再送去评分的循环。然后做一个故意去掉结束条件的版本,亲眼看到 GraphRecursionError,再亲手测量串联图和并排图的最小上限,确认什么是超级步。最后把触及上限的与已完成的记为不同的结果,并做出筛选“修了也不会变好的输入”的诊断。