首页 / 文章

专业代理、关键词路由器与中断函数:LangGraph教练

设计一个具备两名专家角色的LangGraph助手,该助手拥有确定性路由器、可在任务交接时保持状态不变的共享状态,以及基于中断和命令机制的人机检查点功能。

发布者:REACTAPP.TOP阅读约 11 分钟2462 词

来源: Building Multi-Agent AI Systems with LangGraph: A Fitness & Nutrition Coach Example

一个试图同时涵盖两个无关领域的助手提示词往往无法很好地处理这两个领域。本文则构建了一个小型LangGraph系统来充当健身与营养教练:两个专业智能体、一个决定由谁回答的确定性路由器、一个在任务交接时传递约束条件的共享状态,以及一个在每次回复后暂停系统运行的人工检查点。读完本文后,你将了解每个组件的功能、该系统为何呈现当前结构,以及如何将这种结构复用于任意一对专业智能体。

场景与测试对话

目标产品是健身应用中的助手,负责提供训练和饮食方面的建议。其设计通过三轮对话进行验证:首先请求一份锻炼计划,接着询问饮食相关问题,最后提出必须改变餐食建议的饮食限制条件:

Scenario: A fitness app wants an assistant that helps with workouts and nutrition.

Test conversation flow (3 turns):
"Build muscle, lose fat — suggest a workout plan"
"What should I eat to support this workout plan?"
"I'm vegetarian, adjust the meal suggestion"

以下的所有设计决策都是为了确保这三次对话能够正确且可预测地进行。

为何通用聊天机器人力不从心

如果让一个通用聊天机器人同时提供锻炼计划和相应的饮食建议,其给出的答案往往不够深入。该模型需要在同一个系统提示中处理各种不同的角色设定,因此训练建议会失去针对性,饮食建议也会变得千篇一律。这种缺陷源于三个不同的需求同时作用于同一个提示:

  • 区分不同领域。 锻炼计划与营养规划属于不同的专业范畴,某一领域的建议不应渗透到另一领域或与之矛盾。
  • 保持对话上下文连贯性。 之前提到的限制条件,比如三条消息前提及的膝盖伤势,仍应影响之后制定的饮食或锻炼计划。
  • 保持人类掌控。该系统不应完全自主运行;必须在生成任何内容之前设有一个由人操作的节点,以便其进行确认、补充细节或调整方向。
  • 该架构并非让一个提示词承担三项任务,而是为每项需求配备独立的机制:用于领域分离的专用模块、用于维护上下文的共享状态,以及用于监督的人为节点。

    五个核心组成部分

    该系统由五个部分构成,每个部分仅负责一项功能:

    • 锻炼建议器:负责制定锻炼计划、训练安排以及针对伤病的调整方案。
    • 营养建议器:负责制定饮食计划、规划宏量营养素摄入,并处理素食、纯素食或因过敏而需遵循的饮食限制等需求。
    • 路由器:一个轻量级的决策环节,用于检查最新的用户消息并选择下一个由哪位顾问回复。
    • 人工节点:一种有意的暂停机制,此时系统会等待人类操作而非继续生成输出。
    • 共享状态:记录当前对话的权威信息,以及最后回复的是哪位顾问。

    这是一种分层或协调模式。协调者会将任务分配给专门的子代理,而非让一个单一的代理同时承担所有角色。

    确保任务交接可预测的规则

    有一条结构规则让整个设计具备可信性:顾问永远不会直接将控制权交给另一位顾问。每个智能体的输出都会先经过人类节点,然后再由路由器处理。因此,两个智能体绝不可能悄无声息地互相传递控制权,且每一次状态转换都是可见的、可中断的。

    消息如何在图中传递

    遵循单一用户消息的处理流程。该消息首先送达人类节点,然后传递给路由器,路由器会读取消息文本;如果仅凭文本无法做出判断,它会查询共享状态中的last_active_agent字段,从而在运动建议师和营养建议师之间进行选择。被选中的建议师会调用其所属领域的工具,之后再将控制权交还给人类节点,人类节点则会再次暂停并等待下一条消息。因此,整个流程始终是:人类节点、路由器、建议师、可选工具、人类节点。

    模型:Groq上的开源大型语言模型

    这两类建议师都使用由Groq提供的开源模型openai/gpt-oss-120b,并配置了temperature=0参数:

    • 选择此模型的原因:它具备完善的原生工具调用功能,而这正是以工具驱动的工作流程所依赖的。
  • 为何选择温度值为0:这样能使输出尽可能具有重复性,从而使图表遵循既定规则,不会因每次运行而变化。当有人反复测试或评估同一对话时,这一点非常重要。实际上,即便温度值为0,在托管模型中也无法完全保证确定性,因此测试应侧重于路由和结构而非确切的措辞。
  • 为何选择Groq:其LPU推理硬件能够保持较低的延迟,这在每次用户输入后需要经过多个环节(人类节点、路由器、顾问、工具、人类节点)才能生成回复的情况下尤为重要。
  • 该图表中的内容并不特定依赖于 Groq。任何具备工具调用功能的聊天模型都可以使用,例如 Google Gemini 或 Anthropic Claude,只需提供相应的 API 密钥(如 GEMINI_API_KEY 或 ANTHROPIC_API_KEY),并将模型封装指向该服务提供商即可。托管平台上的模型可用性会随时间变化,因此请根据服务提供商当前的目录确认模型标识符。

    共享状态:内存的实际存储位置

    每个节点都会读取和写入一个名为 MultiAgentState 的类型化状态对象,该对象包含两个字段:

    • messages:完整的对话记录,继承自 LangGraph 的 MessagesState,该字段还提供了用于追加新消息而非覆盖列表的还原函数。
  • last_active_agent:设置为"workout_advisor"或"nutrition_advisor";每当路由器无法判断诸如“告诉我更多”之类的追问属于哪个域名时,就会使用该值作为参考。
  • 正是由于这种状态机制,即便回复是由不同的节点生成的,上一轮中设定的约束依然会在后续轮次中继续生效。模型不会对任何内容进行总结或重新解释。每次运行时,每个顾问都会收到相同的累积messages列表,而检查点机制则负责在同一对话线程的各轮次之间保持状态。

    核心设计思路是内存为集中式管理,而非按每个顾问独立存储。如果每个顾问都保留自己的私有历史记录,那么营养顾问就永远无法知晓用户向运动顾问提出的目标。

    每位顾问仅可使用一个专用工具

    每个智能体只能调用一个工具,且该工具必须属于其负责领域。在当前版本中,这些工具基于规则运作并通过关键词进行匹配,从而使行为具有可预测性且易于演示。在实际应用中,可以用真正的健身或营养API替代它们,或者让模型完全自行生成答案。由于图结构的其他部分仅依赖于工具的接口,因此更换实现方式无需其他调整。

    确保每位顾问各司其职

    仅有专用工具并不足以让智能体始终围绕主题展开工作;模型还必须知道哪些问题不应回答。因此,每个系统提示都会明确设定界限:

    • 健身顾问被要求不得提供饮食或营养建议,因为相关请求会由图结构转交给营养顾问处理。
  • 营养顾问被要求不得设计锻炼计划,因为那属于锻炼顾问的职责范围。
  • 其背后的原则是模型不会自行决定流程。通过边界提示,每个智能体都会遵循既定规则,而非在其专业领域之外即兴发挥,而且所有的流程决策都是由图表明确指定的,而非大语言模型。

    基于关键词的路由器,而非再次调用大语言模型

    在各种顾问都受到限制的情况下,路由器只需选择下一个发言者,而它是通过简单的关键词匹配来实现的:

    • 诸如健身房、重复次数、有氧运动或肌肉之类的词汇会路由到锻炼顾问。
    • 诸如饮食、食物、进食、蛋白质或素食之类的词汇会路由到营养顾问。
  • 既不符合第一个列表也不符合第二个列表的内容,将会被交给存储在 last_active_agent 中的相应顾问处理。
  • 确定性路由具有可预测性且易于测试:相同的消息总会产生相同的处理路径,而且无需调用模型即可对路由器进行单元测试。但其缺点是脆弱性较高。关键词列表无法涵盖同义词,而那些同时涉及多个领域的消息(比如关于健身前是否可以进食的问题)则需要明确的决策规则。当表述方式过于多样而无法用关键词处理时,进行简单的分类判断是一种合理的升级方案,但这样会失去部分可预测性。

    带中断与命令功能的人机交互模式

    最重要的设计决策并非路由器,而是人类节点。在每位顾问轮到发言后,图结构会刻意停止运行。它不会猜测用户的下一个问题,也不会持续生成内容。有两个LangGraph原语用于实现暂停功能:

    • interrupt(...)会在被调用处立即停止运行,并将控制权连同传递给它的值一起交还给调用者。
    • Command(resume=...)会继续运行被暂停的图结构,将人类的输入作为同一节点内interrupt调用返回的值。

    中断功能依赖于检查点机制:图结构必须在暂停时保存其状态,以便日后能够继续运行,这就是为什么编译后的图结构需要检查点器和线程ID的原因。

    对于健身与营养助手而言,这一暂停并非仅仅是用户界面的便利功能。它让用户能够在对话继续之前添加与安全相关的限制条件,比如伤病、过敏或饮食禁忌,而不是等到系统已经给出忽略这些条件的建议之后才处理。

    逐步解析三次对话轮次

    第一轮:开启对话

    用户提出了两个目标——增肌和减脂,并请求获得锻炼计划。新的对话始终从“锻炼建议器”开始。它识别出“增肌”和“减脂”这两个需求,调用相应工具,最终返回一份结构化的锻炼计划,例如为期四天的上下肢分开训练方案,每组8到12次,共3到4组,同时包含三天力量训练和两天有氧运动。

    第二轮:转交营养规划

    接下来,用户想知道哪些食物能够支持该计划。人类节点会将消息转发,路由器匹配到“进食”选项,随后执行任务交给营养顾问。营养顾问会返回一份每日约2,500千卡的饮食计划,重点为瘦蛋白和复合碳水化合物,并规定每三到四小时进食一次。

    第三阶段:应用共享状态中的约束条件

    最后,用户提到自己是素食者,并要求调整餐食建议,另外再加一份零食。这条消息再次被发送给营养顾问,此时有两个机制起作用:“素食者”本身就是一个营养相关关键词,而且last_active_agent仍然指向营养领域,因此即便只是像“调整一下,并加份零食”这样较为模糊的请求,也会被发送到同一个处理环节。顾问重新阅读了全部历史记录,依然遵循第一轮设定的目标,在此基础上加入素食限制,最终制定出一套以豆腐、白奶酪和扁豆为主的无肉增肌计划,并搭配毛豆和鹰嘴豆泥等零食。由于系统状态中已包含该目标,因此无需再重复说明。

    这一轮正是集中式状态机制的体现。生成答案的节点并非最初接收目标的那个节点,但它拥有完整的上下文信息。

    构建状态机

    从结构上看,其结果是一个包含三个节点的小型状态图(两个建议器节点和人类节点),再加上作为它们之间条件逻辑的路由器,以及一个固定的入口点:

    • 每个新线程都从锻炼建议器开始。这一默认设置反映了产品的使用习惯:人们打开健身应用时,通常会先询问训练相关问题,而非饮食问题。
    • 在首次回复之后,人类节点和路由器会决定后续的流程走向。
  • 该图使用MemorySaver检查点机制进行构建,每段对话都通过thread_id来标识。每当调用者重复使用相同的thread_id时,该图会自动恢复相关消息及last_active_agent信息;调用者无需再手动传入历史记录。
  • MemorySaver将检查点存储在进程内存中,这种方式非常适合笔记本环境,但程序重启后会丢失所有数据。而部署后的版本应使用由数据库支持的持久化检查点机制。

    完整的实现代码可作为配套笔记本获取。关于路由与审批步骤的其他构建方式,可参阅涵盖路由、分发、评审与审批的五种LangGraph模式。

    将该模式用于健身之外的场景

    这里的内容并非仅限于锻炼或饮食领域。同样的三个要素——专业代理、由共享状态驱动的路由器,以及通过中断与恢复机制实现人工审核——适用于任何具备以下特征的系统:

    • 对话可能需要多个不同的专业领域知识,
  • 出于合规、安全或个性化考虑,需要在流程进行到一半时暂停,以便相关人员参与评估或确认。
  • 在对话的某个环节中建立的上下文,必须能够正确影响后续的其他专家。
  • 将这两名顾问替换为另一对领域专家,图表结构依然保持不变,只有工具、提示词和路由关键词会发生变化。

    设计原则

    • 采用协调机制而非单一系统。利用图表来划分不同的专业处理路径,而不是依赖单个代理完成所有工作。
    • 集中管理状态。由检查点机制持久化的单个MultiAgentState是实现无缝交接和保持约束条件有效的关键。
  • 控制至关重要。在生产环境中避免完全自动化的循环;在关键节点使用 interrupt() 确保有人类介入。
  • 核心要点

    • 按领域划分任务,将路由决策交给确定的图结构逻辑而非模型来完成。
    • 保留一个共享的 messages 历史记录以及 last_active_agent,这样早期设定的约束能够影响后续由其他代理生成的答案。
    • 将 interrupt() 和 Command(resume=...) 视为让人类在下次回复前添加安全约束的机制,并记住它们需要检查点与线程标识。
    • 通过稳定的接口限制工具的功能范围,这样基于规则的逻辑日后便可发展为真正的 API 或完整的模型推理系统,而无需改动图结构。
    • 将该模式内化的最快方法是为自己挑选两名能力明确的专家,构建一个完整的可运行图结构;拓扑结构会保持不变,而提示词和工具则是需要根据需求定制的部分。