一、本章内容
本章介绍了四种主流智能体框架:AutoGen(多智能体对话协作)、AgentScope(消息驱动的多智能体)、CAMEL(角色扮演协作)、LangGraph(图结构工作流)。教程中 LangGraph 的案例是"三步问答助手",但依赖 Tavily 搜索 API,我因为无法注册 Tavily 账号,所以没有照抄教程案例,而是用 LangGraph 做了一个不需要任何外部搜索 API 的原创 Demo:探店文案助手。
实践环境:
- Python 3.x +
langgraph、langchain-openai、python-dotenv - 大模型:ModelScope 免费 API-Inference 接口(OpenAI 兼容协议),沿用前几章的
.env配置,无需新增任何密钥
二、原创 Demo 设计:探店文案助手
2.1 功能与设计思路
输入餐厅名称和招牌菜,自动生成一段探店文案;内置"评审员"对文案打分,不满意就打回重写(最多重写 2 次),满意才输出终稿。
这个设计相当于把第四章 Reflection(执行-反思-优化)的思想用框架重新实现了一遍,同时完整演示了 LangGraph 的三个核心概念:
- State(状态):文案草稿、评审意见、重写次数在节点之间流转;
- Node(节点):
writer写作节点、reviewer评审节点,各是一个普通 Python 函数; - 条件边 + 循环(Conditional Edge):评审不通过且未超重写上限 → 回到写作节点,形成"写作→评审→重写"的循环;否则走向 END 结束。
流程结构:
START → writer(写作)→ reviewer(评审)→ 条件边判断 ├─ 不通过且未超次数 → 回到 writer └─ 通过或超限 → END2.2 关键代码
状态定义:用 TypedDict 声明在图中流转的数据结构。
classCopyState(TypedDict):messages:Annotated[list,add_messages]# 过程消息记录restaurant:str# 餐厅名称dish:str# 招牌菜draft:str# 当前文案草稿review:str# 评审员的意见approved:bool# 评审是否通过revision_count:int# 已重写次数条件边:整个循环控制的核心,只有这一个函数。
MAX_REVISIONS=2# 最多重写次数,防止无限循环defshould_revise(state:CopyState)->str:"""评审不通过且没超过重写上限 → 回到写作节点;否则结束。"""ifnotstate["approved"]andstate["revision_count"]<=MAX_REVISIONS:return"writer"returnEND构建图:添加节点、连接边、编译运行,十几行代码就把流程固化下来。
workflow=StateGraph(CopyState)workflow.add_node("writer",writer_node)workflow.add_node("reviewer",reviewer_node)workflow.add_edge(START,"writer")workflow.add_edge("writer","reviewer")workflow.add_conditional_edges("reviewer",should_revise,["writer",END])app=workflow.compile()2.3 调试中解决的问题
这个 Demo 看着简单,实际调试时踩了几个和前几章一脉相承的坑,记录如下:
- 模型无推理服务:最初把模型换成
Qwen/Qwen2.5-72B-Instruct,报 400 错误has no provider supported——魔搭上开源了权重不等于提供 API 推理服务。解决:换回已验证可用的模型,选择模型时认准模型页的 API-Inference 标识。 - 推理型模型的思考过程泄漏进正文:模型的内心独白(甚至模拟与用户的对话)混在输出里,污染了文案和评审结论。解决:加
clean_text()清洗<think>标签内容,并在展示评审结论时只保留最后一个正式结论行之后的内容。 - 结论误判:最初用
"结论:通过" in review_text判断,结果评审文本里引用的格式示例"结论:通过 或 结论:重写"造成误命中,实际打回却显示通过。解决:改用正则^\s*结论\s*[::]\s*(通过|重写)\s*$只匹配独占一行的正式结论,并取最后一个匹配。 - 空响应与截断:保留空响应自动重试 3 次的机制,并新增
finish_reason == "length"的截断检测提示。
三、最终运行结果
以"大米先生(南京建邺万达店)+ 小炒黄牛肉"为例,运行过程原样如下:
🍜 探店文案助手启动! 请输入餐厅名称(直接回车使用示例):大米先生(南京建邺万达店) 请输入招牌菜(直接回车使用示例):小炒黄牛肉 ✍️ 写作节点:第 1 稿已生成 🔍 评审节点:❌ 打回重写 ✍️ 写作节点:第 2 稿已生成 🔍 评审节点:✅ 通过可以看到条件边发挥了作用:第 1 稿被评审员打回,流程自动回到写作节点,第 2 稿通过后正常结束。最终输出原样如下:
================================================== 📋 最终文案: 大米先生(南京建邺万达店),饭点烟火气足。招牌小炒黄牛肉,牛肉薄嫩, 大火爆炒边缘微焦,裹青红椒芹菜,油亮诱人。入口鲜辣嫩滑带嚼劲, 配热米饭停不下筷。现炒现出锅,锅气满满,人均三十元,吃出家的满足。 下饭神器,实至名归。 🔍 评审结论: 结论:通过 理由:语言生动且感官描写丰富,明确突出招牌小炒黄牛肉,以现炒锅气、 人均三十元、下饭神器等给出清晰推荐理由,百字篇幅精炼合适。 意见:无 ==================================================四、心得体会
- 框架把"流程控制"变成了显式的图。第四章手写 Reflection 时,循环、退出条件、状态传递都散落在 if/else 和循环体里;用 LangGraph 后,节点是函数、流转是边、循环靠条件边,流程结构一目了然,也更容易向别人讲清楚智能体在做什么。
- 框架省的是骨架,省不掉细节。状态怎么设计、提示词怎么写、模型输出怎么解析,这些决定成败的细节和手写代码时一模一样。这次三个坑(思考泄漏、结论误判、空响应)全都出在模型输出处理上,和框架无关。
- 条件边是 LangGraph 的灵魂。线性流程(START→A→B→END)用普通函数调用也能写,LangGraph 真正的价值在于条件边带来的循环和动态路由能力——"不满意就重来"这种自我修正机制,一行
add_conditional_edges就实现了。 - 任务简单就别用推理型模型。这次调试中一大半问题都来自推理模型的思考泄漏和 token 消耗。几百字的生成/评审任务,用普通指令模型反而又快又干净,选模型要匹配任务复杂度。
参考文献
- Datawhale Hello-Agents 教程:第六章 框架开发实践. https://github.com/datawhalechina/hello-agents
- LangGraph 官方文档. https://langchain-ai.github.io/langgraph/
- LangChain 官方文档. https://python.langchain.com/