前言
单智能体(Single Agent)在处理复杂任务时存在明显瓶颈:上下文窗口有限、工具集过大导致决策混乱、无法并行处理子任务。
LangGraph 通过图结构来编排多个智能体的协作流程,支持循环、条件分支和人工介入,是当前 Multi-Agent 系统最成熟的工程化方案之一。
本文将通过完整代码示例,带你从零搭建一个可运行的 Multi-Agent 协作系统。
一、核心概念
1.1 为什么需要多智能体?
假设你要做一个市场调研报告任务,单 Agent 的处理方式是:
用户 → 单 Agent → 搜索 → 分析 → 写报告 → 输出
↑ 所有能力堆在一个 Agent 里
↑ 上下文越长越容易出错
多智能体方式:
用户
↓
Planner Agent(分解任务)
├── Researcher Agent(信息搜集) → 并行
├── Analyst Agent(数据分析) → 并行
└── Writer Agent(报告撰写)
↓
最终输出
多智能体的三个核心优势:
- 专业化:每个 Agent 只做一件事,工具集小,决策更精准
- 并行化:独立子任务可以同时执行,缩短总耗时
- 可控性:每个环节可单独调试、审计和干预
1.2 LangGraph 的核心抽象
LangGraph = StateGraph(状态图) + Nodes(节点) + Edges(边)
| 概念 | 含义 | 类比 |
|---|---|---|
| State | 在整个图中传递的共享状态 | 流水线上的物料 |
| Node | 一个处理步骤(通常是调用 LLM 或一个函数) | 流水线上的工位 |
| Edge | 节点之间的流转路径 | 传送带 |
| Conditional Edge | 根据条件动态决定下一步走向 | 分流器 |
二、环境准备
2.1 安装依赖
bash
pip install langgraph langchain-openai langchain python-dotenv
2.2 配置 API Key
python
# .env 文件
OPENAI_API_KEY=sk-your-key-here
# 或使用兼容 OpenAI 格式的其他模型
# OPENAI_BASE_URL=https://your-proxy.com/v1
三、实战一:基础多智能体流水线
3.1 需求说明
构建一个博客文章生成系统,由三个专业 Agent 协作完成:
输入: 文章主题
↓
[Researcher] → 生成关键词 + 模拟搜索结果
↓
[Writer] → 根据研究资料撰写文章
↓
[Editor] → 审查并修改文章
↓
输出: 最终文章
3.2 定义共享状态
python
# state.py
from typing import TypedDict, List, Optional
from langgraph.graph import START, StateGraph, END
class BlogState(TypedDict):
"""博客文章生成流程的共享状态"""
# 输入
topic: str # 文章主题
# Researcher 产出
keywords: List[str] # 研究关键词
research_notes: str # 研究笔记
# Writer 产出
draft: str # 文章草稿
# Editor 产出
edited_article: str # 编辑后的文章
review_comments: List[str] # 审稿意见
# 流程控制
needs_revision: bool # 是否需要重新修改
revision_count: int # 已修改次数
3.3 实现三个 Agent 节点
python
# agents.py
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
import os
# 初始化模型(可替换为任意兼容的 LLM)
llm = ChatOpenAI(
model="gpt-4o-mini", # 根据需求选择模型
temperature=0.7,
max_retries=2
)
# ─── Researcher Agent ────────────────────────────
RESEARCHER_SYSTEM = """你是一位专业的技术研究员。
你的任务是根据文章主题,生成相关的研究关键词,并撰写研究笔记。
研究笔记应包含:核心概念解释、技术要点、常见问题和最佳实践。
输出格式:
关键词:xxx, yyy, zzz
---
研究笔记:
(详细内容)
"""
def researcher_node(state: BlogState) -> BlogState:
"""研究节点:根据主题生成关键词和研究笔记"""
messages = [
SystemMessage(content=RESEARCHER_SYSTEM),
HumanMessage(content=f"请研究以下主题,生成关键词和研究笔记:\n\n{state['topic']}")
]
response = llm.invoke(messages)
content = response.content
# 解析返回内容(简单解析,生产环境建议用结构化输出)
lines = content.split('\n')
keywords = []
notes_lines = []
in_notes = False
for line in lines:
if line.startswith('关键词') or line.startswith('Keywords'):
kw_part = line.split(':')[-1] if ':' in line else line.split(':')[-1]
keywords = [k.strip() for k in kw_part.split(',') if k.strip()]
keywords += [k.strip() for k in kw_part.split(',') if k.strip()]
elif '研究笔记' in line or '笔记' in line:
in_notes = True
elif in_notes:
notes_lines.append(line)
return {
**state,
"keywords": keywords[:5], # 最多保留5个关键词
"research_notes": '\n'.join(notes_lines).strip()
}
# ─── Writer Agent ────────────────────────────────
WRITER_SYSTEM = """你是一位资深的技术博客作家。
根据提供的研究笔记和关键词,撰写一篇结构清晰、深入浅出的技术博客。
要求:
1. 字数 800-1500 字
2. 包含代码示例(如适用)
3. 语言通俗易懂,面向有一定基础的开发者
4. 结构:引言 → 核心概念 → 实战示例 → 总结
"""
def writer_node(state: BlogState) -> BlogState:
"""写作节点:根据研究笔记撰写文章草稿"""
context = f"""
主题:{state['topic']}
关键词:{', '.join(state['keywords'])}
研究笔记:
{state['research_notes']}
"""
messages = [
SystemMessage(content=WRITER_SYSTEM),
HumanMessage(content=context)
]
response = llm.invoke(messages)
return {
**state,
"draft": response.content
}
# ─── Editor Agent ────────────────────────────────
EDITOR_SYSTEM = """你是一位严格的技术内容编辑。
审查文章草稿,检查以下方面:
1. 技术准确性
2. 逻辑连贯性
3. 代码示例正确性
4. 语言表达清晰度
如果文章质量合格,直接输出文章内容。
如果需要修改,先输出审稿意见(以"审稿意见:"开头),然后输出修改后的文章(以"修改后文章:"开头)。
最多允许修改2次,如果超过仍然不合格,直接输出当前版本。
"""
def editor_node(state: BlogState) -> BlogState:
"""编辑节点:审查并修改文章"""
revision_count = state.get("revision_count", 0)
# 超过2次修改直接通过
if revision_count >= 2:
return {
**state,
"edited_article": state["draft"],
"needs_revision": False,
"revision_count": revision_count
}
context = f"""
主题:{state['topic']}
文章草稿:
{state['draft']}
"""
messages = [
SystemMessage(content=EDITOR_SYSTEM),
HumanMessage(content=context)
]
response = llm.invoke(messages)
content = response.content
# 判断是否需要通过审稿
needs_revision = "审稿意见" in content and "修改后文章" in content
review_comments = []
edited = state["draft"]
if needs_revision:
# 解析审稿意见
if "审稿意见:" in content:
comments_part = content.split("审稿意见:")[-1].split("修改后文章:")[0]
review_comments = [c.strip() for c in comments_part.split('\n') if c.strip()]
# 解析修改后的文章
if "修改后文章:" in content:
edited = content.split("修改后文章:")[-1].strip()
else:
edited = content # 直接通过,内容即为最终文章
return {
**state,
"edited_article": edited,
"review_comments": review_comments,
"needs_revision": needs_revision and revision_count < 2,
"revision_count": revision_count + (1 if needs_revision else 0)
}
3.4 组装流程图
python
# graph.py
from graph import BlogState
from agents import researcher_node, writer_node, editor_node
def build_blog_graph():
"""构建博客文章生成的 StateGraph"""
graph = StateGraph(BlogState)
# 添加节点
graph.add_node("researcher", researcher_node)
graph.add_node("writer", writer_node)
graph.add_node("editor", editor_node)
# 添加边(定义流转顺序)
graph.add_edge(START, "researcher") # 起点 → researcher
graph.add_edge("researcher", "writer") # researcher → writer
graph.add_edge("writer", "editor") # writer → editor
# 条件边:编辑后是否需要修改?
def should_revise(state: BlogState) -> str:
if state.get("needs_revision", False):
return "writer" # 回到 writer 修改
return END # 结束
graph.add_conditional_edges("editor", should_revise)
return graph.compile()
# 运行示例
if __name__ == "__main__":
app = build_blog_graph()
result = app.invoke({
"topic": "LangGraph 多智能体系统实战",
"keywords": [],
"research_notes": "",
"draft": "",
"edited_article": "",
"review_comments": [],
"needs_revision": False,
"revision_count": 0
})
print("=" * 60)
print("最终文章:")
print("=" * 60)
print(result["edited_article"])
print("=" * 60)
print(f"修改次数: {result['revision_count']}")
if result["review_comments"]:
print(f"审稿意见: {result['review_comments']}")
四、实战二:带并行处理的多智能体系统
4.1 需求说明
构建一个代码审查系统,同时启动多个专业审查 Agent 并行工作:
输入: 代码文件内容
↓
[调度器] 分发代码给多个审查 Agent(并行)
├── Security Reviewer → 安全检查
├── Performance Reviewer → 性能分析
├── Style Reviewer → 代码规范
└── Logic Reviewer → 逻辑正确性
↓
[Aggregator] 汇总所有审查意见
↓
输出: 综合审查报告
4.2 实现并行处理
LangGraph 的 send API 支持将一个状态同时发送给多个节点:
python
# code_review_graph.py
from typing import TypedDict, List
from langgraph.graph import StateGraph, START, END
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.3)
class ReviewState(TypedDict):
code: str # 待审查的代码
filename: str # 文件名
security_review: str # 安全审查结果
performance_review: str # 性能审查结果
style_review: str # 规范审查结果
logic_review: str # 逻辑审查结果
final_report: str # 最终汇总报告
# ─── 各审查 Agent ────────────────────────────────
def security_reviewer(state: ReviewState) -> dict:
prompt = f"""你是安全专家。审查以下代码的安全问题:
- SQL 注入、XSS、命令注入
- 敏感信息泄露
- 不安全的依赖或配置
- 权限校验缺失
代码文件:{state['filename']}
代码内容:
{state['code']}
只输出审查意见,格式:
[安全] 问题等级(High/Medium/Low): 具体描述"""
response = llm.invoke(prompt)
return {"security_review": response.content}
def performance_reviewer(state: ReviewState) -> dict:
prompt = f"""你是性能优化专家。审查以下代码的性能问题:
- 时间复杂度/空间复杂度
- 数据库查询优化
- 内存泄漏风险
- 并发安全问题
代码内容:
{state['code']}
只输出审查意见,格式:
[性能] 问题等级(High/Medium/Low): 具体描述"""
response = llm.invoke(prompt)
return {"performance_review": response.content}
def style_reviewer(state: ReviewState) -> dict:
prompt = f"""你是代码规范专家。审查以下代码的规范问题:
- 命名规范(变量、函数、类名)
- 代码格式(缩进、空行、注释)
- 最佳实践遵循情况
- 可读性和可维护性
代码内容:
{state['code']}
只输出审查意见,格式:
[规范] 问题等级(High/Medium/Low): 具体描述"""
response = llm.invoke(prompt)
return {"style_review": response.content}
def logic_reviewer(state: ReviewState) -> dict:
prompt = f"""你是逻辑审查专家。审查以下代码的逻辑正确性:
- 边界条件处理
- 空值/异常处理
- 业务逻辑正确性
- 算法实现是否正确
代码内容:
{state['code']}
只输出审查意见,格式:
[逻辑] 问题等级(High/Medium/Low): 具体描述"""
response = llm.invoke(prompt)
return {"logic_review": response.content}
# ─── 汇总 Agent ──────────────────────────────────
def aggregator_node(state: ReviewState) -> dict:
prompt = f"""你是代码审查汇总专家。
根据以下四位审查专家的意见,生成一份综合审查报告。
## 安全审查
{state.get('security_review', '未提供')}
## 性能审查
{state.get('performance_review', '未提供')}
## 规范审查
{state.get('style_review', '未提供')}
## 逻辑审查
{state.get('logic_review', '未提供')}
生成格式:
# 代码审查报告 - {state['filename']}
## 总体评估
(给出 Overall 评级:通过/需要修改/重大问题)
## 详细意见
(按 High → Medium → Low 排序汇总所有问题)
## 修改建议
(给出优先级排序的修改建议列表)
"""
response = llm.invoke(prompt)
return {"final_report": response.content}
# ─── 构建并行图 ──────────────────────────────────
def build_review_graph():
graph = StateGraph(ReviewState)
# 添加所有节点
graph.add_node("security", security_reviewer)
graph.add_node("performance", performance_reviewer)
graph.add_node("style", style_reviewer)
graph.add_node("logic", logic_reviewer)
graph.add_node("aggregator", aggregator_node)
# START 同时发送到四个审查节点(并行!)
graph.add_edge(START, "security")
graph.add_edge(START, "performance")
graph.add_edge(START, "style")
graph.add_edge(START, "logic")
# 所有审查节点完成后,进入汇总节点
graph.add_edge("security", "aggregator")
graph.add_edge("performance", "aggregator")
graph.add_edge("style", "aggregator")
graph.add_edge("logic", "aggregator")
# 汇总完成后结束
graph.add_edge("aggregator", END)
return graph.compile()
# 运行示例
if __name__ == "__main__":
# 读取示例代码
with open("example_code.py", "r", encoding="utf-8") as f:
code_content = f.read()
app = build_review_graph()
result = app.invoke({
"code": code_content,
"filename": "example_code.py",
"security_review": "",
"performance_review": "",
"style_review": "",
"logic_review": "",
"final_report": ""
})
print(result["final_report"])
4.3 关键技巧:用 Send API 实现动态并行
上面的实现中四个审查节点是固定的。如果审查维度是动态生成 的(比如根据代码类型决定需要哪些审查),可以用 Send API:
python
from langgraph.types import Send
def dispatch_reviewers(state: ReviewState) -> List[Send]:
"""动态决定需要启动哪些审查 Agent"""
# 根据代码内容动态决定审查维度
dispatches = []
# 总是做安全和逻辑审查
dispatches.append(Send("security", state))
dispatches.append(Send("logic", state))
# 如果是 Python 代码,加性能和规范审查
if state["filename"].endswith(".py"):
dispatches.append(Send("performance", state))
dispatches.append(Send("style", state))
# 如果是前端代码,加可访问性审查
if any(state["filename"].endswith(ext) for ext in [".js", ".jsx", ".tsx", ".vue"]):
dispatches.append(Send("accessibility", state)) # 需提前定义此节点
return dispatches
# 在图构建中使用:
# graph.add_conditional_edges(START, dispatch_reviewers)
五、生产实践要点
5.1 状态持久化(Checkpointer)
生产环境中,流程可能因为各种原因中断(API 限流、服务重启等)。LangGraph 内置了 Checkpointer 机制,支持从中断点恢复:
python
from langgraph.checkpoint.sqlite import SqliteSaver
# 使用 SQLite 持久化状态
with SqliteSaver.from_conn_string("checkpoints.db") as saver:
app = build_blog_graph()
# 每次执行都会自动保存状态到数据库
result = app.invoke(initial_state, config={"configurable": {"thread_id": "session-001"}})
# 中断后恢复:
# 只需要用相同的 thread_id 重新 invoke,会自动从断点继续
5.2 人工介入(Human-in-the-Loop)
某些关键决策需要人工确认,LangGraph 支持在流程中暂停等待人工输入:
python
from langgraph.types import interrupt
def human_approval_node(state: BlogState) -> dict:
"""需要人工审批是否发布文章"""
approval = interrupt({
"question": "是否发布这篇文章?",
"article": state["edited_article"]
})
# approval 是人工通过 UI 或其他方式返回的结果
if approval.get("approved"):
return {**state, "published": True}
else:
return {**state, "published": False, "revision_instructions": approval.get("feedback", "")}
# 在图中添加此节点,并在需要人工介入的位置插入
# graph.add_node("human_approval", human_approval_node)
5.3 错误处理与重试
python
import time
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=30)
)
def safe_llm_call(messages):
"""带重试的 LLM 调用"""
try:
return llm.invoke(messages)
except Exception as e:
print(f"LLM 调用失败: {e},正在重试...")
raise
def researcher_node_with_retry(state: BlogState) -> BlogState:
"""带错误处理的 Researcher 节点"""
try:
response = safe_llm_call([
SystemMessage(content=RESEARCHER_SYSTEM),
HumanMessage(content=f"研究主题:{state['topic']}")
])
# ... 解析 response ...
return {**state, "research_notes": "..."}
except Exception as e:
# 出错时返回错误状态,让图有机会处理
return {**state, "error": str(e), "research_notes": "研究失败,请检查配置。"}
5.4 流式输出(提升用户体验)
长流程的任务,用户等待体验很差。LangGraph 支持流式返回每个节点的结果:
python
app = build_blog_graph()
# 流式执行
for chunk in app.stream(initial_state, stream_mode="updates"):
for node_name, node_output in chunk.items():
print(f"[{node_name}] 完成")
if node_name == "writer":
print(f"草稿预览: {node_output['draft'][:200]}...")
elif node_name == "editor":
print(f"审稿完成,修改次数: {node_output['revision_count']}")
# 或者只流式返回最终输出(token 级别)
for token in app.stream(initial_state, stream_mode="messages"):
print(token.content, end="", flush=True)
六、与其他 Multi-Agent 框架对比
| 特性 | LangGraph | AutoGen | CrewAI | 原生 Function Calling |
|---|---|---|---|---|
| 图结构编排 | ✅ 原生支持 | ❌ 基于对话 | ✅ 基于任务序列 | ❌ 无编排 |
| 循环/条件分支 | ✅ 完整支持 | ⚠️ 有限支持 | ⚠️ 有限支持 | ❌ 不支持 |
| 状态管理 | ✅ TypedDict 强类型 | ⚠️ 隐式传递 | ⚠️ 隐式传递 | ❌ 无状态 |
| 人工介入 | ✅ interrupt API | ❌ 不支持 | ❌ 不支持 | ❌ 不支持 |
| 持久化 | ✅ Checkpointer | ⚠️ 需自行实现 | ⚠️ 需自行实现 | ❌ 不支持 |
| 学习曲线 | 中等 | 低 | 低 | 最低 |
| 适用场景 | 复杂流程编排 | 对话式协作 | 角色扮演任务 | 简单工具调用 |
选择建议:
- 需要精细控制流程 (循环、条件、人工介入)→ LangGraph
- 快速原型、对话式多 Agent → AutoGen
- 角色扮演式任务分配 → CrewAI
- 简单工具调用 → Function Calling
七、常见问题 FAQ
Q: LangGraph 和 LangChain 是什么关系?
LangGraph 是 LangChain 生态的一部分,但可以独立使用。LangChain 提供模型调用和工具封装,LangGraph 负责流程编排。两者配合最好,但 LangGraph 也支持直接使用任意 LLM 调用库。
Q: 图中节点之间如何共享数据?
通过共享的 State 对象。每个节点接收 State,返回需要更新的字段。LangGraph 会自动合并返回值到 State 中。
Q: 如何处理节点执行失败?
推荐做法:① 节点内部捕获异常,返回错误状态字段;② 在图中增加错误处理的专用节点;③ 用
try/except包裹整个app.invoke()调用。
Q: 支持异步执行吗?
支持。所有节点函数都可以是
async def,调用时用await app.ainvoke()。并行节点会自动并发执行。
Q: 如何调试 LangGraph 流程?
推荐使用 LangSmith(LangChain 官方可观测性平台)进行追踪。或者设置
verbose=True查看每个节点的输入输出。也可以把中间状态打印出来:
python
for event in app.stream(state, stream_mode="debug"):
print(event) # 打印每个节点的详细执行信息
总结
LangGraph 的核心价值在于把 Multi-Agent 协作从"黑盒"变成了"白盒"------你可以精确控制每个 Agent 的行为、它们之间的数据流转、以及整个流程的走向。
三个关键点:
- State 设计是核心 ------ 合理设计共享状态,让数据流转清晰可追踪
- 节点要单一职责 ------ 每个 Agent 只做一件事,工具集越小越精准
- 生产环境必做三件事 ------ 状态持久化、错误处理、关键节点人工介入
Multi-Agent 不是银弹,对于简单任务,单 Agent + Function Calling 更高效。但当任务复杂度达到需要分解、需要专业化、需要可控性这个阈值时,LangGraph 是目前最成熟的工程化选择。
如有问题欢迎评论区讨论。