很多开发者第一次接触 LangChain 多智能体时,容易把“多 Agent”想成让一堆大模型角色互相聊天,最后有一条主线把结论串起来。这个思路没有错,但落到真实业务里往往不稳定:消息来回传、上下文互相污染、工具调用混乱,方案生成出来像“开盲盒”。婚礼策划就是一个非常典型的场景——预算、场地、餐饮、摄影、时间线,每个环节都有独立的专业判断标准,如果全部压进一个超长 Prompt,模型很容易前后矛盾:预算只有几万,却推荐五星级酒店;200 人婚礼,却安排进只能容纳 50 人的宴会厅。
本文的核心判断是:LangChain 做多智能体项目,真正的价值不在“让多个 Agent 自由聊天”,而在“把复杂任务拆成有边界的专业角色,再用图编排把它们串成一条可控的流水线”。一套能落地、可维护、运行成本可控的“中配”方案,比看起来炫酷但无法复现的高配架构更有意义。下面我会用“婚礼策划师”这个极易理解的场景,带你从零实现一套基于 LangChain + LangGraph + Streamlit 的多智能体应用。
这套方案定位为“中配”:不需要本地 GPU,不需要自建向量数据库,不需要微服务,只需要一个支持工具调用的模型 API,以及一台能跑 Python 的普通开发机。读完本文,你可以得到一份完整可运行的代码,学会如何设计 Agent、如何给 Agent 配工具、如何用 LangGraph 控制多智能体协作,以及如何用 Streamlit 在浏览器中做出交互界面。这套思路可以直接迁移到旅游规划、活动执行、装修报价、企业顾问等任何“多角色协同出方案”的业务场景。
1. 为什么婚礼策划需要“多智能体”
1.1 婚礼策划是典型的多角色协作场景
婚礼策划的内容看起来很统一,就是“出一份婚礼方案”,但实际上它至少包含五个专业模块:预算分配、场地选择、餐饮菜单、摄影摄像、时间线执行。每一个模块的决策依据都来自不同的专业经验。预算规划师关注的是“总预算如何在十几个项目之间分配”,场地顾问关心的是“城市、人数、风格与场地类型的匹配”,餐饮顾问要考虑“人均餐标和宴席形式”,摄影顾问则要判断“机位数量与拍摄风格”。
这些模块之间还有明显的依赖关系:只有先确定总预算,才能推算餐饮和摄影的资金比例;只有确定了场地类型,才能判断菜单适合圆桌宴席还是自助冷餐;只有前面所有模块都落定,时间线才能排得出来。这种“有先后、有依赖、有专业分工”的任务结构,恰恰就是多智能体最擅长处理的场景。如果全部依赖单个模型,把 5 个角色的职责塞进同一个上下文里,不仅 Prompt 会变得非常长,而且模型在生成过程中很难始终如一地扮演所有角色。
1.2 单智能体与多智能体的边界
单智能体方案其实也能生成婚礼方案,它适合需求简单、输出篇幅短、不需要反复调用外部工具的场景。但一旦需求复杂,它的劣势就会放大:工具堆积过多时,模型可能调用错误工具;上下文过长时,模型会遗忘前面的约束;修改某个模块的需求,往往会牵连其他模块的生成结果。
多智能体方案把任务按专业角色切分,每个 Agent 拥有独立的系统提示词、独立的工具集合、独立的上下文。预算 Agent 只看到预算相关的工具,场地 Agent 只看到场地相关的工具,互相之间通过共享状态传递关键结果,而不是把所有原始信息全部灌给同一个模型。这样做的好处是:任务边界清晰,Prompt 不会互相污染;新增一个模块时,只需增加一个节点;某个 Agent 出错时,可以单独调试,不影响其他节点。
下面是单智能体与多智能体在婚礼策划场景下的对比:
| 对比维度 | 单智能体方案 | 多智能体方案 |
|---|---|---|
| 任务边界 | 所有职责混在一个上下文 | 每个角色拥有独立上下文 |
| 工具管理 | 全部工具堆在一起,容易误用 | 按角色分配工具,误用率低 |
| 扩展性 | 增加新模块会让 Prompt 越来越长 | 增加新节点即可,影响面小 |
| 可维护性 | 改一个需求容易牵动全局 | 各节点独立修改、独立测试 |
| 生成一致性 | 长上下文中容易前后矛盾 | 节点间只传递关键结果,一致性更可控 |
1.3 中配方案适合谁
我之所以强调“中配”,是想把方案定位在大多数开发者都能实际跑通的范围内。它适合用来自学多智能体原理、做毕业设计、做作品集,也适合中小型团队在没有专门基建的情况下快速搭建内部效率工具。如果你想做一个高并发的 C 端产品、有严格的实时性要求,或者业务流程中必须有人工审批环节,那本文方案还需要在工程层继续加固,比如增加队列、审核节点、限流和监控。
换句话说,中配方案的价值在于“以最小的成本把多智能体跑通”,而不是一步到位实现生产级系统。先跑通,再迭代,这是最务实的路径。
2. LangChain 多智能体的核心概念与使用边界
2.1 Agent:以一个 LLM 为中心的执行单元
Agent 是 LangChain 生态里的核心抽象。一个 Agent 最少包含三部分:LLM 模型、系统提示词、可用工具列表。模型根据用户输入和系统提示词判断下一步动作:直接回答,还是调用某个工具,然后再基于工具返回的结果继续推理。
在多智能体项目中,每个 Agent 通常只负责一个专业角色。以婚礼策划为例,预算 Agent 的系统提示词会写“你是一位严谨的婚礼预算规划师”,它的工具列表里只有一个预算计算工具;场地 Agent 的系统提示词会写“你是一位熟悉婚礼场地的选址顾问”,它的工具列表里只有一个场地推荐工具。这种“一个角色一套上下文和一套工具”的设计,会明显降低模型误用工具的概率。
2.2 Tool:把外部能力暴露给模型
Tool 的本质是一个带有描述信息的函数。LangChain 通过@tool装饰器把普通 Python 函数包装成 Agent 可以识别的工具,函数名和 docstring 会被作为“说明书”交给模型。模型在看到用户问题后,会判断是否需要调用函数,并自动填充参数。
这里有一个容易忽略的点:工具描述写得越具体,模型调用越准确。比如recommend_venue(city: str, guest_count: int, style: str, budget: float)这个函数,docstring 必须写清楚每个参数的含义,否则模型就可能把预算的单位搞错,或者把宾客人数和预算位置传反。
2.3 LangGraph:用图来编排多智能体
LangGraph 是 LangChain 生态中的图编排框架,专门用于管理 Agent 之间的调用关系。它基于StateGraph构建流程:State 是全局共享的数据结构,Node 是具体的执行单元,Edge 定义了 Node 之间的流转方向。
在本文的婚礼策划项目中,State 里既包含用户输入的原始信息,也包含每个 Agent 产出的中间结果。每个 Node 从 State 读取自己所需的字段,执行完成后再把结果写回 State。最终由一个汇总 Node 读取所有中间结果,生成完整方案。这样做的好处是:流程结构可视化、节点职责单一、状态流转清晰。
2.4 与 LangChain、CrewAI、MCP 的关系
初学者经常会混淆这几个概念。LangChain 是一个组件库,提供 LLM 封装、Prompt 模板、Tool 定义、Memory 等基础能力;LangGraph 是 LangChain 生态中的编排框架,专注流程控制。两者不是替代关系,而是组合关系:用 LangChain 的模型和工具组件构造节点能力,用 LangGraph 把节点编排成图。
CrewAI 是另一套流行的多智能体框架,它更偏“角色协作”,强调用声明式配置定义角色、任务和流程,上手快,但对复杂控制流的掌控能力不如 LangGraph。MCP(Model Context Protocol)则是模型接入外部工具和数据的标准化协议,本文使用的@tool是本地函数绑定,MCP 把工具作为独立服务暴露,更适合跨项目复用。理解了这层关系,你就知道什么时候该选 LangGraph、什么时候该选 CrewAI、什么时候该引入 MCP。
3. 环境准备与项目初始化
3.1 前置环境要求
本文方案需要的运行环境并不高,推荐配置如下:
- 操作系统:Windows / macOS / Linux 均可。
- Python 版本:建议 3.10 或 3.11,3.12 在部分依赖组合下可能有兼容问题。
- 模型 API:任意支持工具调用(function calling)的 OpenAI 兼容接口,本文示例默认使用 OpenAI 模型名,如果你接入其他模型服务商,只需修改
ChatOpenAI的配置。 - 开发工具:VS Code 或其他 Python IDE,能用命令行执行 Python 脚本即可。
建议在项目目录下创建 Python 虚拟环境,避免依赖污染系统环境。
3.2 安装依赖
在项目根目录创建requirements.txt,写入以下依赖:
langchain>=0.2.0 langchain-openai>=0.1.0 langgraph>=0.2.0 streamlit>=1.39.0 python-dotenv>=1.0.0然后在终端执行安装:
pip install -r requirements.txt如果你后续想用其他模型服务商,还需要额外安装对应的 LangChain 集成包,例如langchain-anthropic、langchain-google-genai等。具体以你的模型服务商文档为准。
3.3 配置模型 API
在项目根目录创建.env文件,写入你的 API Key 和模型名:
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx OPENAI_MODEL=gpt-4o-mini注意两点:第一,OPENAI_API_KEY不要硬编码在代码里,也不要提交到 Git 仓库,建议把.env加入.gitignore;第二,如果你接入的是其他兼容 OpenAI 协议的模型服务商,可以在代码里通过base_url参数指定接入地址,具体配置以服务商文档为准。
4. 项目整体设计
4.1 架构总览
整个项目包含 6 个 Agent,分别是预算 Agent、场地 Agent、餐饮 Agent、摄影 Agent、时间线 Agent 和汇总 Agent。用户请求先进入预算 Agent,再依次经过场地、餐饮、摄影、时间线节点,最后由汇总 Agent 输出完整方案。这个流程是线性的,每一步都依赖前一步的结果:
用户输入(城市、预算、人数、风格、特殊要求)→ 预算方案 → 场地建议 → 餐饮建议 → 摄影建议 → 时间线 → 汇总方案
这种顺序单链结构是最容易理解和调试的多智能体结构。你可以在任意两个节点之间增加条件路由,比如预算较低时跳过某些高级模块,或者在某一步结果不合格时退回上一步重新生成。
4.2 状态数据结构
LangGraph 里 State 的设计直接决定了多智能体协作的灵活性。本文定义的状态字段如下:
| 字段名 | 类型 | 含义 |
|---|---|---|
messages | list | 全局消息记录,用于保留上下文 |
city | str | 举办城市 |
budget | float | 总预算,单位万元 |
guest_count | int | 宾客人数 |
style | str | 婚礼风格 |
special_requirement | str | 用户特殊要求 |
budget_plan | str | 预算 Agent 输出 |
venue_plan | str | 场地 Agent 输出 |
menu_plan | str | 餐饮 Agent 输出 |
photo_plan | str | 摄影 Agent 输出 |
timeline_plan | str | 时间线 Agent 输出 |
final_plan | str | 汇总后的完整方案 |
其中messages使用add_messages注解,LangGraph 会自动把多个节点写入的消息合并,而不是简单覆盖。
4.3 多智能体协作流程
每个 Agent 在图中是一个 Node。Node 函数从 State 中读取需要的字段,拼成新消息,调用对应的 Agent,最后把返回结果写入 State。通过add_edge把节点按顺序连接起来,就构成了一个多智能体流水线。
这种设计的关键在于“节点之间只传递必要信息”。预算 Agent 输出的完整文本会进入budget_plan字段,场地 Agent 不需要重新理解用户的原始需求,只需要读取预算方案和自己的输入信息即可。信息隔离做得越好,模型在长流程里的表现就越稳定。
5. 核心代码实现
5.1 工具层实现
创建tools.py,定义 4 个工具函数。这些工具使用模拟规则生成结果,你可以把它们替换成真实 API 调用或数据库查询。
# tools.py from langchain_core.tools import tool @tool def compute_budget_plan(total_budget: float, guest_count: int) -> str: """根据婚礼总预算(万元)和预计宾客人数,输出各项目的预算分配区间。""" if guest_count <= 0: guest_count = 1 per_guest = total_budget * 10000 / guest_count plan = { "场地与搭建": total_budget * 0.35, "餐饮酒水": total_budget * 0.25, "摄影摄像": total_budget * 0.12, "婚纱礼服与妆造": total_budget * 0.10, "策划与执行": total_budget * 0.08, "甜品伴手礼": total_budget * 0.06, "应急预留": total_budget * 0.04, } lines = [f"- {name}: {amount:.2f} 万元" for name, amount in plan.items()] lines.append(f"按 {guest_count} 人估算,人均餐饮预算约 {per_guest * 0.25:.0f} 元/人") return "\n".join(lines) @tool def recommend_venue(city: str, guest_count: int, style: str, budget: float) -> str: """根据城市、宾客人数、婚礼风格和总预算(万元),推荐合适的场地类型与筛选建议。""" if guest_count > 300: venue_type = "大型宴会厅或会展中心" elif guest_count >= 100: venue_type = "中大型酒店宴会厅或户外草坪场地" elif guest_count >= 30: venue_type = "精品酒店或私密餐厅" else: venue_type = "私人会所或露台餐厅" if budget < 5: budget_note = "预算偏低,建议压缩桌数和布置规模,优先保证餐饮体验。" elif budget < 20: budget_note = "预算适中,建议选择本地口碑较好的酒店,利用当季花材降低布置成本。" else: budget_note = "预算充足,可考虑拥有独立草坪、宴会厅和婚房的综合场地。" return ( f"推荐类型:{venue_type}\n" f"建议城市:{city}\n" f"匹配风格:{style}\n" f"预算提示:{budget_note}" ) @tool def recommend_menu(style: str, per_guest_budget: float) -> str: """根据婚礼风格和人均餐标预算(元/人),给出菜单搭配建议。""" if style in ["中式", "复古"]: base = "中式圆桌宴席,冷菜八道、热菜十道、主食汤品各一" elif style in ["森系", "户外草坪"]: base = "自助冷餐或西式分餐,搭配海鲜台、甜品台和低度酒水" elif style == "海边": base = "海鲜自助为主,辅以烧烤档和鲜榨饮品" else: base = "混搭自助餐,中西菜式各半,设置饮品台" if per_guest_budget >= 500: quality = "可加入龙虾、鲍鱼等高阶食材,并配备位上菜服务。" elif per_guest_budget >= 300: quality = "以当季食材为主,保持出品稳定,可设置一至两道招牌菜。" else: quality = "建议精选家常菜,重点保证分量和出餐速度。" return f"菜单形式:{base}\n品质建议:{quality}" @tool def recommend_photo_package(style: str, budget_ratio: float) -> str: """根据婚礼风格和摄影预算占比(小数),推荐摄影摄像服务方案。""" if budget_ratio > 0.15: level = "双机位摄影 + 双机位摄像 + 婚前微电影" elif budget_ratio > 0.08: level = "双机位摄影 + 单机位摄像,保留精修原片和短视频快剪" else: level = "单机位摄影 + 重要环节拍摄,选择性价比工作室" if style in ["森系", "户外草坪", "海边"]: recommend = "适合胶片风格或自然光线,建议与摄影师提前踩点。" else: recommend = "适合大气宴会厅拍摄,注意现场灯光与摇臂机位的协调。" return f"套餐建议:{level}\n拍摄建议:{recommend}"这些工具函数本身不依赖真实外部接口,因此你可以直接运行。在真实的工程项目里,建议把recommend_venue替换为酒店场所库查询,把recommend_menu替换为菜品数据库或 AI 影像库,工具层和智能体层可以完全解耦。
5.2 智能体层实现
创建agents.py,定义角色 Agent。这里使用langgraph.prebuilt.create_agent来创建“模型 + 系统提示词 + 工具”的执行体,它比手写 Agent 循环更简洁。
# agents.py import os from typing import TypedDict, Annotated from dotenv import load_dotenv from langchain_core.messages import HumanMessage, SystemMessage from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph, START, END from langgraph.graph.message import add_messages from langgraph.prebuilt import create_agent from tools import ( compute_budget_plan, recommend_menu, recommend_photo_package, recommend_venue, ) load_dotenv() MODEL_NAME = os.getenv("OPENAI_MODEL", "gpt-4o-mini") llm = ChatOpenAI(model=MODEL_NAME, temperature=0.3) budget_agent = create_agent( model=llm, tools=[compute_budget_plan], system_prompt=( "你是一位严谨的婚礼预算规划师。" "你会收到城市、总预算、宾客人数、婚礼风格等信息," "请优先调用 compute_budget_plan 计算各项目预算区间," "然后结合经验给出 3 条预算优化建议。" ), ) venue_agent = create_agent( model=llm, tools=[recommend_venue], system_prompt=( "你是一位熟悉全国婚礼场地的选址顾问。" "你会收到城市、宾客人数、婚礼风格和整体预算等信息," "请调用 recommend_venue 获取场地建议," "再补充与场地相关的注意事项,例如签约、档期和停车。" ), ) menu_agent = create_agent( model=llm, tools=[recommend_menu], system_prompt=( "你是一位婚宴餐饮顾问。" "你会收到婚礼风格和人均餐标预算," "请调用 recommend_menu 获得菜单框架,再补充酒水搭配和试菜建议。" ), ) photo_agent = create_agent( model=llm, tools=[recommend_photo_package], system_prompt=( "你是一位婚礼摄影摄像顾问。" "你会收到婚礼风格和摄影预算占比," "请调用 recommend_photo_package 获得套餐建议,再补充拍摄流程安排。" ), ) timeline_agent = create_agent( model=llm, tools=[], system_prompt=( "你是一位婚礼执行导演。" "你会收到预算、场地、餐饮、摄影等模块结果," "请按时间顺序输出婚礼当天从早到晚的执行流程表," "包含每个环节的时间、负责人、注意事项。" ), ) merge_agent = create_agent( model=llm, tools=[], system_prompt=( "你是一位经验丰富的婚礼策划总监。" "你会收到预算、场地、餐饮、摄影、时间线等模块方案," "请将它们整合成一份结构清晰、语言自然、可执行的完整婚礼策划书。" "最终输出需要包含:方案概览、预算分配、场地建议、餐饮安排、" "摄影摄像建议、执行时间线、重点提醒。" ), )每个 Agent 的system_prompt就是它的“岗位说明书”。你可以根据实际需要调整语气和职责描述。工具列表为空的时间线 Agent,意味着它只能依靠模型自身能力生成内容,这也是多智能体中常见的“纯推理型角色”。
5.3 编排层:用 LangGraph 构建流程
在agents.py中继续定义状态结构和节点函数。
# agents.py 追加内容 class WeddingState(TypedDict): messages: Annotated[list, add_messages] city: str budget: float guest_count: int style: str special_requirement: str budget_plan: str venue_plan: str menu_plan: str photo_plan: str timeline_plan: str final_plan: str def budget_node(state: WeddingState): result = budget_agent.invoke({ "messages": [HumanMessage( content=( f"用户基本信息:\n" f"城市:{state['city']}\n" f"总预算:{state['budget']} 万元\n" f"宾客人数:{state['guest_count']} 人\n" f"婚礼风格:{state['style']}\n" f"特殊要求:{state['special_requirement']}\n\n" "请制定预算方案。" ) )] }) return {"budget_plan": result["messages"][-1].content} def venue_node(state: WeddingState): result = venue_agent.invoke({ "messages": [HumanMessage( content=( f"城市:{state['city']}\n" f"宾客人数:{state['guest_count']} 人\n" f"婚礼风格:{state['style']}\n" f"总预算:{state['budget']} 万元\n" f"预算方案:\n{state['budget_plan']}\n\n" "请给出场地建议。" ) )] }) return {"venue_plan": result["messages"][-1].content} def menu_node(state: WeddingState): per_guest_budget = state["budget"] * 10000 * 0.25 / state["guest_count"] result = menu_agent.invoke({ "messages": [HumanMessage( content=( f"婚礼风格:{state['style']}\n" f"人均餐标预算:{per_guest_budget:.0f} 元/人\n" f"场地建议:\n{state['venue_plan']}\n\n" "请给出餐饮建议。" ) )] }) return {"menu_plan": result["messages"][-1].content} def photo_node(state: WeddingState): result = photo_agent.invoke({ "messages": [HumanMessage( content=( f"婚礼风格:{state['style']}\n" f"摄影预算占比参考:0.12\n" f"餐饮建议:\n{state['menu_plan']}\n\n" "请给出摄影摄像服务建议。" ) )] }) return {"photo_plan": result["messages"][-1].content} def timeline_node(state: WeddingState): result = timeline_agent.invoke({ "messages": [HumanMessage( content=( f"预算方案:\n{state['budget_plan']}\n\n" f"场地建议:\n{state['venue_plan']}\n\n" f"餐饮建议:\n{state['menu_plan']}\n\n" f"摄影建议:\n{state['photo_plan']}\n\n" "请输出婚礼当天的执行时间线。" ) )] }) return {"timeline_plan": result["messages"][-1].content} def merge_node(state: WeddingState): content = ( f"预算方案:\n{state['budget_plan']}\n\n" f"场地建议:\n{state['venue_plan']}\n\n" f"餐饮建议:\n{state['menu_plan']}\n\n" f"摄影建议:\n{state['photo_plan']}\n\n" f"时间线:\n{state['timeline_plan']}\n\n" "请整合成一份完整的婚礼策划书。" ) result = merge_agent.invoke({ "messages": [HumanMessage(content=content)] }) return {"final_plan": result["messages"][-1].content}接下来把这些节点编译成图:
# agents.py 追加内容 graph = StateGraph(WeddingState) graph.add_node("budget", budget_node) graph.add_node("venue", venue_node) graph.add_node("menu", menu_node) graph.add_node("photo", photo_node) graph.add_node("timeline", timeline_node) graph.add_node("merge", merge_node) graph.add_edge(START, "budget") graph.add_edge("budget", "venue") graph.add_edge("venue", "menu") graph.add_edge("menu", "photo") graph.add_edge("photo", "timeline") graph.add_edge("timeline", "merge") graph.add_edge("merge", END) wedding_graph = graph.compile() def run_wedding_planner( city: str, budget: float, guest_count: int, style: str, special_requirement: str = "暂无", ) -> str: initial_state = { "messages": [SystemMessage(content="这是一个多智能体婚礼策划项目。")], "city": city, "budget": budget, "guest_count": guest_count, "style": style, "special_requirement": special_requirement, "budget_plan": "", "venue_plan": "", "menu_plan": "", "photo_plan": "", "timeline_plan": "", "final_plan": "", } final_state = wedding_graph.invoke(initial_state) return final_state["final_plan"]这段代码的关键在于:每个 Node 都只读取自己需要的 State 字段,并返回自己负责的字段。LangGraph 会把返回值合并到全局 State 中,最终merge节点拿到所有子方案,生成一份完整策划书。
5.4 Streamlit 交互层实现
创建app.py,用 Streamlit 搭建一个可视化交互界面。
# app.py import streamlit as st from agents import run_wedding_planner st.set_page_config(page_title="LangChain多智能体婚礼策划师", layout="wide") st.title("LangChain 多智能体婚礼策划师") st.markdown("输入婚礼基本信息,点击按钮生成完整策划方案。") with st.sidebar: st.header("婚礼基本信息") city = st.text_input("举办城市", value="杭州") budget = st.number_input( "总预算(万元)", min_value=1.0, max_value=500.0, value=20.0, step=1.0, ) guest_count = st.number_input( "宾客人数", min_value=10, max_value=2000, value=200, step=10, ) style = st.selectbox( "婚礼风格", ["中式", "西式", "户外草坪", "海边", "森系", "复古"], ) special_requirement = st.text_area( "特殊要求", placeholder="例如:希望有户外宣誓环节、需要宠物友好场地", ) if st.button("生成婚礼方案", type="primary"): if not special_requirement: special_requirement = "暂无" with st.spinner("多智能体正在协作生成方案,请稍候..."): try: plan = run_wedding_planner( city=city, budget=float(budget), guest_count=int(guest_count), style=style, special_requirement=special_requirement, ) st.success("方案生成完成") st.markdown(plan) except Exception as e: st.error(f"生成失败:{e}")如果你希望每次生成前清空历史消息,可以在按钮逻辑中直接调用一个新的run_wedding_planner,它每次都会从空状态开始,天然避免了会话累积导致的上下文污染。
6. 运行效果与验证
在终端执行以下命令启动 Streamlit 应用:
streamlit run app.py正常情况下,终端会输出本地访问地址,浏览器自动打开http://localhost:8501。在侧边栏填写城市、预算、人数、风格和特殊要求,点击“生成婚礼方案”按钮后,页面会显示加载提示,等待多个 Agent 依次执行。
判断运行成功的标准有两个:第一,浏览器页面最终出现完整方案,而不是报错或空白;第二,终端没有任何 Python 异常堆栈。如果方案缺失某个模块,比如只有预算没有场地,优先检查对应 Node 是否有返回、invoke结果中messages是否为空。
这里需要提醒一个常见误区:由于每个 Agent 都会调用模型,整体耗时会比单次调用更长,通常在十几秒到几十秒之间。这是多智能体的正常代价,不是代码卡死。你可以在每个 Node 函数里加一行print日志,观察执行进度:
print("预算节点完成") print("场地节点完成")这种方式在调试阶段比任何可视化工具都直接。
7. 常见问题与排查
多智能体项目第一次跑不起来,大概率是环境问题或 API 配置问题。下面按出现频率整理:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
模块导入报错:ModuleNotFoundError: langgraph | langgraph 未安装或版本过低 | 执行pip show langgraph查看版本 | 执行pip install -U langgraph |
create_agent无法导入 | langgraph 版本过旧 | 查看报错中的导入路径 | 升级到支持langgraph.prebuilt.create_agent的版本 |
| 模型调用超时 | 网络不通或模型服务商不可达 | 检查服务商状态页,用 curl 测试接口 | 更换接入地址,或改用其他兼容模型 |
| 生成结果缺字段 | 某个 Node 返回了空字符串 | 在各 Node 函数内打印返回值 | 检查对应 Agent 的 invoke 结果结构 |
| 页面转圈后无输出 | 异常被吞或 API 返回格式异常 | 查看终端完整报错 | 在except中打印 traceback |
如果你使用的是非 OpenAI 官方的兼容接口,还要重点确认两点:第一,模型名是否真的支持该平台的 function calling;第二,base_url是否正确,不同的服务商路径前缀差异很大。很多“多智能体不工作”的问题,本质上是模型根本不会调用工具,而不是框架出问题。
8. 工程化改进与上线建议
8.1 提示词管理
系统提示词是多智能体质量的第一个杠杆。建议把每个 Agent 的system_prompt抽到独立的配置文件或数据库里,而不是硬编码在 Python 文件中。这样调整角色语气、增加规则、做 A/B 测试都更灵活。
8.2 状态与上下文清理
长会话场景下,历史消息会一直累积,既增加 token 成本,也可能干扰模型判断。Streamlit 每次点击按钮都重新调用run_wedding_planner,所以暂时不存在这个问题。但如果以后要扩展成真正的对话系统,就需要设计消息窗口或定期清理机制。
8.3 稳定性与成本控制
多智能体流程串行调用多次模型,成本会成倍增加。控成本的思路有三个:第一,选择便宜且支持工具调用的模型;第二,在节点入口增加缓存,相同输入直接返回历史结果;第三,对于不需要模型生成的模块,用规则逻辑替代 LLM。例如本文的预算计算其实完全可以用函数完成,保留 Agent 只是为了演示多智能体结构。
8.4 安全与权限
如果未来接入真实的场地库、供应商 API,必须增加权限校验、敏感数据脱敏和操作审计。工具层是模型与外部系统交互的边界,建议对传入参数做白名单校验,避免模型构造恶意参数。
8.5 可观测性
多智能体的调试难度比单个模型调用高得多,建议在正式项目中接入 LangSmith 或至少做好结构化日志。日志至少包含:每个节点的开始时间、结束时间、输入摘要、输出摘要、token 消耗。这样即使某个节点出错,也能快速定位。
9. 总结与进阶方向
这篇文章通过一个婚礼策划场景,完整实现了“中配”多智能体应用:用 LangChain 构造模型与工具,用 LangGraph 编排 6 个专业 Agent,用 Streamlit 提供交互界面。整套代码不依赖 GPU,普通开发机就能跑通,适合拿来学习、做毕设,也可以作为内部工具的原型。
下一步你可以做的改进有三个方向。第一,把模拟工具替换成真实 API,例如接场地库存接口、餐饮供应商接口,让方案真正可预订。第二,在 LangGraph 中加入条件路由,比如当预算小于 5 万时跳过摄影 Agent,走精简方案,这样可以进一步控制成本。第三,对比一下 CrewAI 的声明式多智能体写法,你会更理解 LangGraph 的图编排在复杂流程中的优势。
多智能体不是“把多个 AI 堆在一起”,而是通过合理的任务切分、状态流转和工具隔离,让每个模型的注意力集中在一件有边界的事情上。先把这套婚礼策划师跑通,再换一个业务领域,你会发现架构完全不用改,只是角色和工具变了而已。建议收藏备用,动手跑一遍代码,才能真正理解多智能体的价值边界在哪里。