第九篇 Agent 架构设计
本章摘要
引用吴恩达教授观点:Agentic Workflow 的未来在于 协作 (Collaboration)。本章将深入探讨 Multi-Agent Systems (MAS),学习如何构建 去中心化 (Swarm)、分布式 (Distributed) 和 开放连接 (MCP) 的智能系统。
我们将采用 LangGraph 官方标准,废弃手写的轮子,聚焦于工业级的架构模式和最佳实践。
目录导航
第1章:协作模式演进 (Patterns)
1.1 吴恩达的四种 Agentic 模式
在 2024 年的演讲中,Andrew Ng 总结了四种核心的 Agentic Workflow 模式:
#mermaid-svg-ScwynOftCphFgT41{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ScwynOftCphFgT41 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ScwynOftCphFgT41 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ScwynOftCphFgT41 .error-icon{fill:#552222;}#mermaid-svg-ScwynOftCphFgT41 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ScwynOftCphFgT41 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ScwynOftCphFgT41 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ScwynOftCphFgT41 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ScwynOftCphFgT41 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ScwynOftCphFgT41 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ScwynOftCphFgT41 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ScwynOftCphFgT41 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ScwynOftCphFgT41 .marker.cross{stroke:#333333;}#mermaid-svg-ScwynOftCphFgT41 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ScwynOftCphFgT41 p{margin:0;}#mermaid-svg-ScwynOftCphFgT41 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ScwynOftCphFgT41 .cluster-label text{fill:#333;}#mermaid-svg-ScwynOftCphFgT41 .cluster-label span{color:#333;}#mermaid-svg-ScwynOftCphFgT41 .cluster-label span p{background-color:transparent;}#mermaid-svg-ScwynOftCphFgT41 .label text,#mermaid-svg-ScwynOftCphFgT41 span{fill:#333;color:#333;}#mermaid-svg-ScwynOftCphFgT41 .node rect,#mermaid-svg-ScwynOftCphFgT41 .node circle,#mermaid-svg-ScwynOftCphFgT41 .node ellipse,#mermaid-svg-ScwynOftCphFgT41 .node polygon,#mermaid-svg-ScwynOftCphFgT41 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ScwynOftCphFgT41 .rough-node .label text,#mermaid-svg-ScwynOftCphFgT41 .node .label text,#mermaid-svg-ScwynOftCphFgT41 .image-shape .label,#mermaid-svg-ScwynOftCphFgT41 .icon-shape .label{text-anchor:middle;}#mermaid-svg-ScwynOftCphFgT41 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ScwynOftCphFgT41 .rough-node .label,#mermaid-svg-ScwynOftCphFgT41 .node .label,#mermaid-svg-ScwynOftCphFgT41 .image-shape .label,#mermaid-svg-ScwynOftCphFgT41 .icon-shape .label{text-align:center;}#mermaid-svg-ScwynOftCphFgT41 .node.clickable{cursor:pointer;}#mermaid-svg-ScwynOftCphFgT41 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ScwynOftCphFgT41 .arrowheadPath{fill:#333333;}#mermaid-svg-ScwynOftCphFgT41 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ScwynOftCphFgT41 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ScwynOftCphFgT41 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ScwynOftCphFgT41 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ScwynOftCphFgT41 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ScwynOftCphFgT41 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ScwynOftCphFgT41 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ScwynOftCphFgT41 .cluster text{fill:#333;}#mermaid-svg-ScwynOftCphFgT41 .cluster span{color:#333;}#mermaid-svg-ScwynOftCphFgT41 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ScwynOftCphFgT41 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ScwynOftCphFgT41 rect.text{fill:none;stroke-width:0;}#mermaid-svg-ScwynOftCphFgT41 .icon-shape,#mermaid-svg-ScwynOftCphFgT41 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ScwynOftCphFgT41 .icon-shape p,#mermaid-svg-ScwynOftCphFgT41 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ScwynOftCphFgT41 .icon-shape rect,#mermaid-svg-ScwynOftCphFgT41 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ScwynOftCphFgT41 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ScwynOftCphFgT41 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ScwynOftCphFgT41 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Pattern 4: Multi-Agent
Agent A
Agent B
Agent C
Pattern 3: Planning
Decompose
Execute Steps
Synthesize
Pattern 2: Tool Use
Reasoning
Tool Call
Integrate Results
Pattern 1: Reflection
Generate
Self-Critique
Revise
本章聚焦 Pattern 4:Multi-Agent Collaboration(多智能体协作)。
1.2 架构演进:从单体到生态
#mermaid-svg-0N2HMKGkNakhclo6{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0N2HMKGkNakhclo6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0N2HMKGkNakhclo6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0N2HMKGkNakhclo6 .error-icon{fill:#552222;}#mermaid-svg-0N2HMKGkNakhclo6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0N2HMKGkNakhclo6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0N2HMKGkNakhclo6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0N2HMKGkNakhclo6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0N2HMKGkNakhclo6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0N2HMKGkNakhclo6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0N2HMKGkNakhclo6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0N2HMKGkNakhclo6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0N2HMKGkNakhclo6 .marker.cross{stroke:#333333;}#mermaid-svg-0N2HMKGkNakhclo6 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0N2HMKGkNakhclo6 p{margin:0;}#mermaid-svg-0N2HMKGkNakhclo6 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-0N2HMKGkNakhclo6 .cluster-label text{fill:#333;}#mermaid-svg-0N2HMKGkNakhclo6 .cluster-label span{color:#333;}#mermaid-svg-0N2HMKGkNakhclo6 .cluster-label span p{background-color:transparent;}#mermaid-svg-0N2HMKGkNakhclo6 .label text,#mermaid-svg-0N2HMKGkNakhclo6 span{fill:#333;color:#333;}#mermaid-svg-0N2HMKGkNakhclo6 .node rect,#mermaid-svg-0N2HMKGkNakhclo6 .node circle,#mermaid-svg-0N2HMKGkNakhclo6 .node ellipse,#mermaid-svg-0N2HMKGkNakhclo6 .node polygon,#mermaid-svg-0N2HMKGkNakhclo6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0N2HMKGkNakhclo6 .rough-node .label text,#mermaid-svg-0N2HMKGkNakhclo6 .node .label text,#mermaid-svg-0N2HMKGkNakhclo6 .image-shape .label,#mermaid-svg-0N2HMKGkNakhclo6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-0N2HMKGkNakhclo6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0N2HMKGkNakhclo6 .rough-node .label,#mermaid-svg-0N2HMKGkNakhclo6 .node .label,#mermaid-svg-0N2HMKGkNakhclo6 .image-shape .label,#mermaid-svg-0N2HMKGkNakhclo6 .icon-shape .label{text-align:center;}#mermaid-svg-0N2HMKGkNakhclo6 .node.clickable{cursor:pointer;}#mermaid-svg-0N2HMKGkNakhclo6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0N2HMKGkNakhclo6 .arrowheadPath{fill:#333333;}#mermaid-svg-0N2HMKGkNakhclo6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0N2HMKGkNakhclo6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0N2HMKGkNakhclo6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0N2HMKGkNakhclo6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0N2HMKGkNakhclo6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0N2HMKGkNakhclo6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0N2HMKGkNakhclo6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0N2HMKGkNakhclo6 .cluster text{fill:#333;}#mermaid-svg-0N2HMKGkNakhclo6 .cluster span{color:#333;}#mermaid-svg-0N2HMKGkNakhclo6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0N2HMKGkNakhclo6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0N2HMKGkNakhclo6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-0N2HMKGkNakhclo6 .icon-shape,#mermaid-svg-0N2HMKGkNakhclo6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0N2HMKGkNakhclo6 .icon-shape p,#mermaid-svg-0N2HMKGkNakhclo6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0N2HMKGkNakhclo6 .icon-shape rect,#mermaid-svg-0N2HMKGkNakhclo6 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0N2HMKGkNakhclo6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0N2HMKGkNakhclo6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0N2HMKGkNakhclo6 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
复杂度提升
分布式需求
Level 3: Ecosystem
HTTP
MCP
Service APython
Service BJava
MCP Tools
Level 2: Swarm
handoff
handoff
Triage Agent
Fraud Agent
Legal Agent
Level 1: Monolith
Single Agent+ Tools
Monolith单体Agent
Swarm进程内协作
Ecosystem微服务生态
第2章:Swarm 模式详解 (Official Way)
2.1 核心定义:Swarm ≠ 蜂群算法
在 LangGraph 语境下,Swarm 不是指分布式群体智能算法,而是指:
多个 Agent 通过 Handoffs(控制权移交)进行去中心化协作的设计模式。
关键特性:
- 去中心化:没有中央路由器,每个 Agent 自己决定下一步该谁接手。
- Handoff:不只是返回结果,而是传递完整的上下文和控制权。
- 共享状态:所有 Agent 操作同一个 State 对象。
2.2 官方推荐写法:Tool-Based Routing
LangGraph 官方推荐的 Multi-Agent 实现方式是:定义 transfer_to_X 工具,通过 Tool Call 触发条件边跳转。
2.2.1 实战场景:保险理赔系统
业务流程:
用户提交理赔
→ Triage Agent (分流)
→ Claim Processor (初审)
→ 风控 Agent / 法务 Agent (专业处理)
→ 结案
2.2.2 Step 1: 定义全局状态
from typing import Annotated, TypedDict
from langgraph.graph.message import add_messages
class InsuranceState(TypedDict):
"""全局共享状态"""
messages: Annotated[list, add_messages] # 对话历史(自动合并)
claim_id: str # 案件ID
risk_score: float # 风险评分
current_agent: str # 当前处理人
关键点:使用 add_messages 注解,LangGraph 会自动合并消息而不是覆盖。
2.2.3 Step 2: 创建 Transfer 工具
from langchain_core.tools import tool
def create_transfer_tool(target_agent: str):
"""工厂函数:生成移交工具"""
@tool(f"transfer_to_{target_agent}")
def transfer() –> str:
f"""将任务移交给 {target_agent} 继续处理。"""
return target_agent
return transfer
为什么这样设计?
- Tool 的返回值会被路由函数读取,决定下一个节点。
- Tool 的 docstring 会成为 LLM 的决策依据。
2.2.4 Step 3: 定义 Agent 节点
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# === Triage Agent ===
triage_tools = [
create_transfer_tool("ClaimProcessor")
]
def triage_node(state: InsuranceState):
"""分流专员:收集信息并转发"""
system_msg = SystemMessage(content="""
你是理赔分流专员。职责:
1. 询问用户案件编号和基本情况
2. 记录到系统后,调用 transfer_to_ClaimProcessor 移交
""")
response = llm.bind_tools(triage_tools).invoke(
[system_msg] + state["messages"]
)
return {"messages": [response]}
# === Claim Processor ===
processor_tools = [
create_transfer_tool("FraudDetector"),
create_transfer_tool("LegalAdvisor")
]
def processor_node(state: InsuranceState):
"""初审专员:计算风险并分流"""
system_msg = SystemMessage(content="""
你是理赔初审员。职责:
1. 分析案件,计算风险评分(0-100)
2. 如果风险 > 70,调用 transfer_to_FraudDetector
3. 否则调用 transfer_to_LegalAdvisor
""")
response = llm.bind_tools(processor_tools).invoke(
[system_msg] + state["messages"]
)
return {"messages": [response]}
# === Fraud Detector ===
def fraud_node(state: InsuranceState):
"""风控专员:终端节点,不再移交"""
system_msg = SystemMessage(content="你是风控专员,进行最终审核。")
response = llm.invoke([system_msg] + state["messages"])
return {"messages": [response]}
# === Legal Advisor ===
def legal_node(state: InsuranceState):
"""法务专员:终端节点"""
system_msg = SystemMessage(content="你是法务顾问,提供法律意见。")
response = llm.invoke([system_msg] + state["messages"])
return {"messages": [response]}
2.2.5 Step 4: 构建路由图
from langgraph.graph import StateGraph, END
# 1. 初始化图
workflow = StateGraph(InsuranceState)
# 2. 添加节点
workflow.add_node("Triage", triage_node)
workflow.add_node("ClaimProcessor", processor_node)
workflow.add_node("FraudDetector", fraud_node)
workflow.add_node("LegalAdvisor", legal_node)
# 3. 定义路由函数
def router(state: InsuranceState):
"""根据最后一条消息的 Tool Call 决定路由"""
last_msg = state["messages"][–1]
# 检查是否有工具调用
if hasattr(last_msg, "tool_calls") and last_msg.tool_calls:
tool_call = last_msg.tool_calls[0]
tool_name = tool_call["name"]
# 如果是 transfer_to_X 工具,返回目标节点名
if tool_name.startswith("transfer_to_"):
return tool_name.replace("transfer_to_", "")
# 否则结束
return END
# 4. 添加条件边(核心机制)
workflow.add_conditional_edges("Triage", router)
workflow.add_conditional_edges("ClaimProcessor", router)
# 5. 终端节点直接结束
workflow.add_edge("FraudDetector", END)
workflow.add_edge("LegalAdvisor", END)
# 6. 设置入口
workflow.set_entry_point("Triage")
# 7. 编译
app = workflow.compile()
关键设计点:
- add_conditional_edges(node, router):让 router 函数根据 state 决定下一步。
- router 通过解析 tool_calls 实现动态路由。
- 这是 LangGraph 官方推荐的 Handoff 标准写法。
2.2.6 运行测试
from langchain_core.messages import HumanMessage
result = app.invoke({
"messages": [HumanMessage(content="我要报案,案件号 C12345")],
"claim_id": "",
"risk_score": 0.0,
"current_agent": ""
})
print(result["messages"][–1].content)
执行流程:
Triage: 收集信息 → 调用 transfer_to_ClaimProcessor
↓
ClaimProcessor: 计算风险 → 调用 transfer_to_FraudDetector
↓
FraudDetector: 最终审核 → END
2.3 Swarm 模式总结
优点:
- 状态共享零成本(内存级)
- 开发调试简单
- 适合复杂业务流程
局限:
- 所有 Agent 必须在同一进程
- 无法跨语言、跨团队复用
- 受单机资源限制
何时使用:保险理赔、工单审批、复杂咨询等需要多角色紧密协作的场景。
第3章:分布式协作 (Distributed State)
3.1 问题:Swarm 的单点瓶颈
在上一章中,所有 Agent 必须在同一个 Python 进程里。但现实场景往往需要:
- 水平扩展:多个 Worker 处理并发请求。
- 跨地域部署:Triage 在北京,Fraud 在上海。
- 语言异构:Python 写的 Agent 调用 Java 写的风控服务。
核心挑战:如何让分布式的 Agent 共享状态?
3.2 解决方案:Shared State via Database
LangGraph 提供了 Checkpointer 机制,将 State 持久化到外部存储。
#mermaid-svg-g9tpElmdM9F1iGvB{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-g9tpElmdM9F1iGvB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-g9tpElmdM9F1iGvB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-g9tpElmdM9F1iGvB .error-icon{fill:#552222;}#mermaid-svg-g9tpElmdM9F1iGvB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-g9tpElmdM9F1iGvB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-g9tpElmdM9F1iGvB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-g9tpElmdM9F1iGvB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-g9tpElmdM9F1iGvB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-g9tpElmdM9F1iGvB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-g9tpElmdM9F1iGvB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-g9tpElmdM9F1iGvB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-g9tpElmdM9F1iGvB .marker.cross{stroke:#333333;}#mermaid-svg-g9tpElmdM9F1iGvB svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-g9tpElmdM9F1iGvB p{margin:0;}#mermaid-svg-g9tpElmdM9F1iGvB .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-g9tpElmdM9F1iGvB .cluster-label text{fill:#333;}#mermaid-svg-g9tpElmdM9F1iGvB .cluster-label span{color:#333;}#mermaid-svg-g9tpElmdM9F1iGvB .cluster-label span p{background-color:transparent;}#mermaid-svg-g9tpElmdM9F1iGvB .label text,#mermaid-svg-g9tpElmdM9F1iGvB span{fill:#333;color:#333;}#mermaid-svg-g9tpElmdM9F1iGvB .node rect,#mermaid-svg-g9tpElmdM9F1iGvB .node circle,#mermaid-svg-g9tpElmdM9F1iGvB .node ellipse,#mermaid-svg-g9tpElmdM9F1iGvB .node polygon,#mermaid-svg-g9tpElmdM9F1iGvB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-g9tpElmdM9F1iGvB .rough-node .label text,#mermaid-svg-g9tpElmdM9F1iGvB .node .label text,#mermaid-svg-g9tpElmdM9F1iGvB .image-shape .label,#mermaid-svg-g9tpElmdM9F1iGvB .icon-shape .label{text-anchor:middle;}#mermaid-svg-g9tpElmdM9F1iGvB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-g9tpElmdM9F1iGvB .rough-node .label,#mermaid-svg-g9tpElmdM9F1iGvB .node .label,#mermaid-svg-g9tpElmdM9F1iGvB .image-shape .label,#mermaid-svg-g9tpElmdM9F1iGvB .icon-shape .label{text-align:center;}#mermaid-svg-g9tpElmdM9F1iGvB .node.clickable{cursor:pointer;}#mermaid-svg-g9tpElmdM9F1iGvB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-g9tpElmdM9F1iGvB .arrowheadPath{fill:#333333;}#mermaid-svg-g9tpElmdM9F1iGvB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-g9tpElmdM9F1iGvB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-g9tpElmdM9F1iGvB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-g9tpElmdM9F1iGvB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-g9tpElmdM9F1iGvB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-g9tpElmdM9F1iGvB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-g9tpElmdM9F1iGvB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-g9tpElmdM9F1iGvB .cluster text{fill:#333;}#mermaid-svg-g9tpElmdM9F1iGvB .cluster span{color:#333;}#mermaid-svg-g9tpElmdM9F1iGvB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-g9tpElmdM9F1iGvB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-g9tpElmdM9F1iGvB rect.text{fill:none;stroke-width:0;}#mermaid-svg-g9tpElmdM9F1iGvB .icon-shape,#mermaid-svg-g9tpElmdM9F1iGvB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-g9tpElmdM9F1iGvB .icon-shape p,#mermaid-svg-g9tpElmdM9F1iGvB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-g9tpElmdM9F1iGvB .icon-shape rect,#mermaid-svg-g9tpElmdM9F1iGvB .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-g9tpElmdM9F1iGvB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-g9tpElmdM9F1iGvB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-g9tpElmdM9F1iGvB :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Worker 2 (Shanghai)
Shared State
Worker 1 (Beijing)
Write State
Read State
Triage Agent
RedisState Store
Fraud Agent
3.3 实战:Redis Checkpointer
3.3.1 安装依赖
pip install langgraph-checkpoint-redis redis
3.3.2 修改代码:启用持久化
from langgraph.checkpoint.redis import RedisSaver
import redis
# 1. 连接 Redis
redis_client = redis.Redis(host="localhost", port=6379, db=0)
# 2. 创建 Checkpointer
checkpointer = RedisSaver(redis_client)
# 3. 编译时传入
app = workflow.compile(checkpointer=checkpointer)
3.3.3 分布式运行
Worker 1(Triage 服务):
# worker_triage.py
from fastapi import FastAPI
from langserve import add_routes
# 只包含 Triage 节点的子图
triage_graph = create_triage_subgraph()
triage_app = triage_graph.compile(checkpointer=checkpointer)
api = FastAPI()
add_routes(api, triage_app, path="/triage")
# 启动:uvicorn worker_triage:api –port 8001
Worker 2(Fraud 服务):
# worker_fraud.py
fraud_graph = create_fraud_subgraph()
fraud_app = fraud_graph.compile(checkpointer=checkpointer)
api = FastAPI()
add_routes(api, fraud_app, path="/fraud")
# 启动:uvicorn worker_fraud:api –port 8002
核心机制:
3.4 Thread ID:分布式协作的钥匙
# 调用时必须指定 thread_id
config = {"configurable": {"thread_id": "claim-12345"}}
# Worker 1
result1 = triage_app.invoke(input_data, config=config)
# Worker 2(使用相同 thread_id)
result2 = fraud_app.invoke(None, config=config) # 自动恢复状态
最佳实践:
- 使用业务 ID(如 claim_id)作为 thread_id。
- 设置 TTL,定期清理过期状态。
3.5 分布式架构图
#mermaid-svg-jCSALW00lfb7F1Fw{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jCSALW00lfb7F1Fw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jCSALW00lfb7F1Fw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jCSALW00lfb7F1Fw .error-icon{fill:#552222;}#mermaid-svg-jCSALW00lfb7F1Fw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jCSALW00lfb7F1Fw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jCSALW00lfb7F1Fw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jCSALW00lfb7F1Fw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jCSALW00lfb7F1Fw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jCSALW00lfb7F1Fw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jCSALW00lfb7F1Fw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jCSALW00lfb7F1Fw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jCSALW00lfb7F1Fw .marker.cross{stroke:#333333;}#mermaid-svg-jCSALW00lfb7F1Fw svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jCSALW00lfb7F1Fw p{margin:0;}#mermaid-svg-jCSALW00lfb7F1Fw .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-jCSALW00lfb7F1Fw .cluster-label text{fill:#333;}#mermaid-svg-jCSALW00lfb7F1Fw .cluster-label span{color:#333;}#mermaid-svg-jCSALW00lfb7F1Fw .cluster-label span p{background-color:transparent;}#mermaid-svg-jCSALW00lfb7F1Fw .label text,#mermaid-svg-jCSALW00lfb7F1Fw span{fill:#333;color:#333;}#mermaid-svg-jCSALW00lfb7F1Fw .node rect,#mermaid-svg-jCSALW00lfb7F1Fw .node circle,#mermaid-svg-jCSALW00lfb7F1Fw .node ellipse,#mermaid-svg-jCSALW00lfb7F1Fw .node polygon,#mermaid-svg-jCSALW00lfb7F1Fw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jCSALW00lfb7F1Fw .rough-node .label text,#mermaid-svg-jCSALW00lfb7F1Fw .node .label text,#mermaid-svg-jCSALW00lfb7F1Fw .image-shape .label,#mermaid-svg-jCSALW00lfb7F1Fw .icon-shape .label{text-anchor:middle;}#mermaid-svg-jCSALW00lfb7F1Fw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jCSALW00lfb7F1Fw .rough-node .label,#mermaid-svg-jCSALW00lfb7F1Fw .node .label,#mermaid-svg-jCSALW00lfb7F1Fw .image-shape .label,#mermaid-svg-jCSALW00lfb7F1Fw .icon-shape .label{text-align:center;}#mermaid-svg-jCSALW00lfb7F1Fw .node.clickable{cursor:pointer;}#mermaid-svg-jCSALW00lfb7F1Fw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jCSALW00lfb7F1Fw .arrowheadPath{fill:#333333;}#mermaid-svg-jCSALW00lfb7F1Fw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jCSALW00lfb7F1Fw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jCSALW00lfb7F1Fw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jCSALW00lfb7F1Fw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jCSALW00lfb7F1Fw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jCSALW00lfb7F1Fw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jCSALW00lfb7F1Fw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jCSALW00lfb7F1Fw .cluster text{fill:#333;}#mermaid-svg-jCSALW00lfb7F1Fw .cluster span{color:#333;}#mermaid-svg-jCSALW00lfb7F1Fw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jCSALW00lfb7F1Fw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jCSALW00lfb7F1Fw rect.text{fill:none;stroke-width:0;}#mermaid-svg-jCSALW00lfb7F1Fw .icon-shape,#mermaid-svg-jCSALW00lfb7F1Fw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jCSALW00lfb7F1Fw .icon-shape p,#mermaid-svg-jCSALW00lfb7F1Fw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jCSALW00lfb7F1Fw .icon-shape rect,#mermaid-svg-jCSALW00lfb7F1Fw .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jCSALW00lfb7F1Fw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jCSALW00lfb7F1Fw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jCSALW00lfb7F1Fw :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
claim_id=12345
Write State
Routing
High Risk
Low Risk
Read State
Read State
用户请求
API Gateway
Triage Worker
Redisthread_id: 12345
Business Router
Fraud Worker
Legal Worker
返回结果
这才是企业级的 Swarm:去中心化 + 分布式。
第4章:微服务化标准 (LangServe)
4.1 为什么需要 LangServe?
在分布式 Swarm 中,我们需要将 Agent 暴露为 HTTP 服务。传统做法是手写 FastAPI 路由,但这会带来问题:
- 需要手动处理流式输出 (Streaming)
- 需要实现批处理 (Batch) 接口
- 缺乏标准化的错误处理
- 无法自动生成 OpenAPI 文档
LangServe 的价值:一行代码搞定所有。
4.2 核心 API:add_routes
from fastapi import FastAPI
from langserve import add_routes
app = FastAPI()
# 魔法:将任何 Runnable 变成 REST API
add_routes(
app,
your_agent_or_chain,
path="/agent",
enabled_endpoints=["invoke", "batch", "stream"]
)
自动生成的端点:
- POST /agent/invoke:同步调用
- POST /agent/batch:批量调用
- POST /agent/stream:流式输出(SSE)
- GET /agent/playground:可视化调试界面
- GET /agent/input_schema:输入 JSON Schema
- GET /agent/output_schema:输出 JSON Schema
4.3 实战:暴露保险 Swarm
# server.py
from fastapi import FastAPI
from langserve import add_routes
from insurance_swarm import app as swarm_app # 之前定义的 LangGraph
api = FastAPI(
title="Insurance Claims API",
version="1.0",
description="Distributed multi-agent insurance processing"
)
add_routes(
api,
swarm_app,
path="/claims",
enabled_endpoints=["invoke", "stream"]
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(api, host="0.0.0.0", port=8000)
启动服务后,访问 http://localhost:8000/docs 即可看到完整的 API 文档。
4.4 客户端调用:RemoteRunnable
# client.py
from langserve import RemoteRunnable
# 连接远程服务
remote_claims = RemoteRunnable("http://localhost:8000/claims")
# 像调用本地函数一样
result = remote_claims.invoke({
"messages": [{"role": "user", "content": "报案编号 C9527"}],
"claim_id": "",
"risk_score": 0.0
})
print(result["messages"][–1]["content"])
流式调用:
for chunk in remote_claims.stream(input_data):
print(chunk, end="", flush=True)
4.5 LangServe 的高级特性
1. 自动类型转换
# 服务端定义 Pydantic 模型
class ClaimInput(BaseModel):
claim_id: str
description: str
# 客户端自动校验
remote_claims.invoke(ClaimInput(claim_id="C123", description="车损"))
2. LangSmith 集成
# 只需设置环境变量,LangServe 自动上报追踪数据
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "your-key"
3. 自定义中间件
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"]
)
4.6 LangServe vs 手写 FastAPI
| 开发时间 | 5 分钟 | 2 小时 |
| 流式输出 | 自动支持 | 需手动实现 SSE |
| 批处理 | 自动优化 | 需自己写并发逻辑 |
| OpenAPI 文档 | 自动生成 | 需手写装饰器 |
| LangSmith 追踪 | 零配置 | 需集成 SDK |
结论:除非有特殊需求(如 WebSocket、gRPC),否则应该优先使用 LangServe。
第5章:标准化工具协议 (MCP)
5.1 MCP 的定位
在前面的章节中,我们解决了 Agent 之间的协作。但还有一个问题:如何连接外部工具?
传统做法:为每个 Agent 框架(LangChain、LlamaIndex、AutoGPT)都写一遍工具代码。
MCP (Model Context Protocol) 的愿景:Write once, run anywhere.
5.2 MCP 架构原理
#mermaid-svg-AdB9QKOM3lt4bczL{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AdB9QKOM3lt4bczL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AdB9QKOM3lt4bczL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AdB9QKOM3lt4bczL .error-icon{fill:#552222;}#mermaid-svg-AdB9QKOM3lt4bczL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AdB9QKOM3lt4bczL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AdB9QKOM3lt4bczL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AdB9QKOM3lt4bczL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AdB9QKOM3lt4bczL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AdB9QKOM3lt4bczL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AdB9QKOM3lt4bczL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AdB9QKOM3lt4bczL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AdB9QKOM3lt4bczL .marker.cross{stroke:#333333;}#mermaid-svg-AdB9QKOM3lt4bczL svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AdB9QKOM3lt4bczL p{margin:0;}#mermaid-svg-AdB9QKOM3lt4bczL .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-AdB9QKOM3lt4bczL .cluster-label text{fill:#333;}#mermaid-svg-AdB9QKOM3lt4bczL .cluster-label span{color:#333;}#mermaid-svg-AdB9QKOM3lt4bczL .cluster-label span p{background-color:transparent;}#mermaid-svg-AdB9QKOM3lt4bczL .label text,#mermaid-svg-AdB9QKOM3lt4bczL span{fill:#333;color:#333;}#mermaid-svg-AdB9QKOM3lt4bczL .node rect,#mermaid-svg-AdB9QKOM3lt4bczL .node circle,#mermaid-svg-AdB9QKOM3lt4bczL .node ellipse,#mermaid-svg-AdB9QKOM3lt4bczL .node polygon,#mermaid-svg-AdB9QKOM3lt4bczL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AdB9QKOM3lt4bczL .rough-node .label text,#mermaid-svg-AdB9QKOM3lt4bczL .node .label text,#mermaid-svg-AdB9QKOM3lt4bczL .image-shape .label,#mermaid-svg-AdB9QKOM3lt4bczL .icon-shape .label{text-anchor:middle;}#mermaid-svg-AdB9QKOM3lt4bczL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AdB9QKOM3lt4bczL .rough-node .label,#mermaid-svg-AdB9QKOM3lt4bczL .node .label,#mermaid-svg-AdB9QKOM3lt4bczL .image-shape .label,#mermaid-svg-AdB9QKOM3lt4bczL .icon-shape .label{text-align:center;}#mermaid-svg-AdB9QKOM3lt4bczL .node.clickable{cursor:pointer;}#mermaid-svg-AdB9QKOM3lt4bczL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AdB9QKOM3lt4bczL .arrowheadPath{fill:#333333;}#mermaid-svg-AdB9QKOM3lt4bczL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AdB9QKOM3lt4bczL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AdB9QKOM3lt4bczL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AdB9QKOM3lt4bczL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AdB9QKOM3lt4bczL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AdB9QKOM3lt4bczL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AdB9QKOM3lt4bczL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AdB9QKOM3lt4bczL .cluster text{fill:#333;}#mermaid-svg-AdB9QKOM3lt4bczL .cluster span{color:#333;}#mermaid-svg-AdB9QKOM3lt4bczL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AdB9QKOM3lt4bczL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AdB9QKOM3lt4bczL rect.text{fill:none;stroke-width:0;}#mermaid-svg-AdB9QKOM3lt4bczL .icon-shape,#mermaid-svg-AdB9QKOM3lt4bczL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AdB9QKOM3lt4bczL .icon-shape p,#mermaid-svg-AdB9QKOM3lt4bczL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AdB9QKOM3lt4bczL .icon-shape rect,#mermaid-svg-AdB9QKOM3lt4bczL .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AdB9QKOM3lt4bczL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AdB9QKOM3lt4bczL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AdB9QKOM3lt4bczL :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
MCP Servers
MCP Protocol Layer
AI Applications
LangChain Agent
LlamaIndex Agent
Claude Desktop
JSON-RPC 2.0
SQLite Server
Filesystem Server
GitHub Server
Your Custom Server
核心概念:
- MCP Server:独立进程,通过 stdio 或 HTTP 暴露能力。
- MCP Client:Agent 框架侧的适配器,负责发现和调用 Server。
- 通信协议:基于 JSON-RPC 2.0,传输层可以是 stdio、SSE、WebSocket。
5.3 MCP 的三种原语
MCP Server 可以暴露三种类型的能力:
1. Resources(资源):只读数据源
@mcp.resource("user://profile/{user_id}")
def get_user_profile(user_id: str):
return {"name": "Alice", "age": 30}
2. Tools(工具):可执行操作
@mcp.tool()
def send_email(to: str, subject: str, body: str):
"""发送邮件"""
# 实际发送逻辑
return "Email sent"
3. Prompts(提示词模板):预设提示
@mcp.prompt()
def code_review_prompt(language: str):
return f"Review this {language} code for security issues…"
5.4 实战:构建 SQLite MCP Server
5.4.1 服务端实现
# db_server.py
from mcp.server.fastmcp import FastMCP
import sqlite3
# 初始化 MCP Server
mcp = FastMCP("DatabaseService")
@mcp.tool()
def query_db(sql: str) –> str:
"""执行只读 SQL 查询
Args:
sql: SELECT 语句(不允许 UPDATE/DELETE)
Returns:
查询结果的 JSON 字符串
"""
# 安全检查
if not sql.strip().lower().startswith("select"):
return "Error: Only SELECT queries allowed"
try:
conn = sqlite3.connect("claims.db")
cursor = conn.cursor()
cursor.execute(sql)
results = cursor.fetchall()
conn.close()
return str(results)
except Exception as e:
return f"Error: {str(e)}"
@mcp.resource("db://schema")
def get_schema():
"""获取数据库表结构"""
conn = sqlite3.connect("claims.db")
cursor = conn.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
tables = cursor.fetchall()
conn.close()
return {"tables": tables}
if __name__ == "__main__":
mcp.run() # 默认通过 stdio 运行
5.4.2 客户端调用
# agent.py
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_react_agent
from langchain_openai import ChatOpenAI
async def main():
# 1. 连接 MCP Server
client = MultiServerMCPClient({
"database": {
"transport": "stdio",
"command": "python",
"args": ["db_server.py"]
}
})
# 2. 自动发现并加载工具
tools = await client.get_tools()
print(f"Loaded tools: {[t.name for t in tools]}")
# 输出: ['query_db']
# 3. 创建 Agent
llm = ChatOpenAI(model="gpt-4o")
agent = create_react_agent(llm, tools)
# 4. 运行
result = agent.invoke({
"input": "查询 claims 表中风险评分大于 80 的案件数量"
})
print(result["output"])
import asyncio
asyncio.run(main())
执行流程:
Agent: "需要查数据库"
→ 调用 query_db 工具
→ MCP Client 通过 stdio 发送 JSON-RPC 请求到 db_server.py
→ db_server.py 执行 SQL 并返回结果
→ Agent 收到结果并生成最终回答
5.5 MCP 的生态优势
官方 MCP Servers(开箱即用):
- @modelcontextprotocol/server-sqlite:SQLite 操作
- @modelcontextprotocol/server-filesystem:文件系统访问
- @modelcontextprotocol/server-github:GitHub API
- @modelcontextprotocol/server-brave-search:搜索引擎
使用方法:
# 安装
npm install -g @modelcontextprotocol/server-github
# 配置到 Agent
client = MultiServerMCPClient({
"github": {
"transport": "stdio",
"command": "mcp-server-github",
"env": {"GITHUB_TOKEN": "your-token"}
}
})
5.6 MCP vs 直接调用 API
| 重用性 | 一次编写,所有 Agent 框架可用 | 需为每个框架写一遍 |
| 安全性 | Server 控制权限,Agent 无需 API Key | Agent 需持有敏感凭证 |
| 可观测性 | Server 可统一记录日志 | 分散在各个 Agent 中 |
| 版本管理 | Server 独立升级 | Agent 代码耦合 |
最佳实践:
- 确定性工具(数据库、文件、API)→ 用 MCP
- 推理性任务(需要多轮对话)→ 用 Swarm/LangServe
5.7 MCP 与 Swarm 的协作
#mermaid-svg-UNsMIDnLUbkGBr65{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UNsMIDnLUbkGBr65 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UNsMIDnLUbkGBr65 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UNsMIDnLUbkGBr65 .error-icon{fill:#552222;}#mermaid-svg-UNsMIDnLUbkGBr65 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UNsMIDnLUbkGBr65 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UNsMIDnLUbkGBr65 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UNsMIDnLUbkGBr65 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UNsMIDnLUbkGBr65 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UNsMIDnLUbkGBr65 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UNsMIDnLUbkGBr65 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UNsMIDnLUbkGBr65 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UNsMIDnLUbkGBr65 .marker.cross{stroke:#333333;}#mermaid-svg-UNsMIDnLUbkGBr65 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UNsMIDnLUbkGBr65 p{margin:0;}#mermaid-svg-UNsMIDnLUbkGBr65 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-UNsMIDnLUbkGBr65 .cluster-label text{fill:#333;}#mermaid-svg-UNsMIDnLUbkGBr65 .cluster-label span{color:#333;}#mermaid-svg-UNsMIDnLUbkGBr65 .cluster-label span p{background-color:transparent;}#mermaid-svg-UNsMIDnLUbkGBr65 .label text,#mermaid-svg-UNsMIDnLUbkGBr65 span{fill:#333;color:#333;}#mermaid-svg-UNsMIDnLUbkGBr65 .node rect,#mermaid-svg-UNsMIDnLUbkGBr65 .node circle,#mermaid-svg-UNsMIDnLUbkGBr65 .node ellipse,#mermaid-svg-UNsMIDnLUbkGBr65 .node polygon,#mermaid-svg-UNsMIDnLUbkGBr65 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UNsMIDnLUbkGBr65 .rough-node .label text,#mermaid-svg-UNsMIDnLUbkGBr65 .node .label text,#mermaid-svg-UNsMIDnLUbkGBr65 .image-shape .label,#mermaid-svg-UNsMIDnLUbkGBr65 .icon-shape .label{text-anchor:middle;}#mermaid-svg-UNsMIDnLUbkGBr65 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UNsMIDnLUbkGBr65 .rough-node .label,#mermaid-svg-UNsMIDnLUbkGBr65 .node .label,#mermaid-svg-UNsMIDnLUbkGBr65 .image-shape .label,#mermaid-svg-UNsMIDnLUbkGBr65 .icon-shape .label{text-align:center;}#mermaid-svg-UNsMIDnLUbkGBr65 .node.clickable{cursor:pointer;}#mermaid-svg-UNsMIDnLUbkGBr65 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UNsMIDnLUbkGBr65 .arrowheadPath{fill:#333333;}#mermaid-svg-UNsMIDnLUbkGBr65 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UNsMIDnLUbkGBr65 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UNsMIDnLUbkGBr65 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UNsMIDnLUbkGBr65 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UNsMIDnLUbkGBr65 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UNsMIDnLUbkGBr65 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UNsMIDnLUbkGBr65 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UNsMIDnLUbkGBr65 .cluster text{fill:#333;}#mermaid-svg-UNsMIDnLUbkGBr65 .cluster span{color:#333;}#mermaid-svg-UNsMIDnLUbkGBr65 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UNsMIDnLUbkGBr65 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UNsMIDnLUbkGBr65 rect.text{fill:none;stroke-width:0;}#mermaid-svg-UNsMIDnLUbkGBr65 .icon-shape,#mermaid-svg-UNsMIDnLUbkGBr65 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UNsMIDnLUbkGBr65 .icon-shape p,#mermaid-svg-UNsMIDnLUbkGBr65 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UNsMIDnLUbkGBr65 .icon-shape rect,#mermaid-svg-UNsMIDnLUbkGBr65 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UNsMIDnLUbkGBr65 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UNsMIDnLUbkGBr65 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UNsMIDnLUbkGBr65 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
MCP Layer (工具连接)
Swarm Layer (内部协作)
handoff
query_db
create_issue
Triage Agent
Fraud Agent
Database MCP
GitHub MCP
设计原则:
- Swarm 管理内部协作逻辑
- MCP 处理外部工具调用
- 两者通过 Tools 接口无缝集成
第6章:架构总结与选型指南
6.1 技术栈对比
| 通信方式 | 内存共享 | 网络 + DB | HTTP | JSON-RPC |
| 延迟 | 微秒级 | 毫秒级 | 10-100ms | 5-20ms |
| 复杂度 | 低 | 中 | 中 | 低 |
| 扩展性 | 单机 | 水平扩展 | 水平扩展 | 独立扩展 |
| 适用场景 | 紧密协作流程 | 高并发分布式 | 微服务架构 | 工具标准化 |
6.2 选型决策树
#mermaid-svg-tfCKkUJ98wjgmSz1{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tfCKkUJ98wjgmSz1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tfCKkUJ98wjgmSz1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tfCKkUJ98wjgmSz1 .error-icon{fill:#552222;}#mermaid-svg-tfCKkUJ98wjgmSz1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tfCKkUJ98wjgmSz1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tfCKkUJ98wjgmSz1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tfCKkUJ98wjgmSz1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tfCKkUJ98wjgmSz1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tfCKkUJ98wjgmSz1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tfCKkUJ98wjgmSz1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tfCKkUJ98wjgmSz1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tfCKkUJ98wjgmSz1 .marker.cross{stroke:#333333;}#mermaid-svg-tfCKkUJ98wjgmSz1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tfCKkUJ98wjgmSz1 p{margin:0;}#mermaid-svg-tfCKkUJ98wjgmSz1 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-tfCKkUJ98wjgmSz1 .cluster-label text{fill:#333;}#mermaid-svg-tfCKkUJ98wjgmSz1 .cluster-label span{color:#333;}#mermaid-svg-tfCKkUJ98wjgmSz1 .cluster-label span p{background-color:transparent;}#mermaid-svg-tfCKkUJ98wjgmSz1 .label text,#mermaid-svg-tfCKkUJ98wjgmSz1 span{fill:#333;color:#333;}#mermaid-svg-tfCKkUJ98wjgmSz1 .node rect,#mermaid-svg-tfCKkUJ98wjgmSz1 .node circle,#mermaid-svg-tfCKkUJ98wjgmSz1 .node ellipse,#mermaid-svg-tfCKkUJ98wjgmSz1 .node polygon,#mermaid-svg-tfCKkUJ98wjgmSz1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tfCKkUJ98wjgmSz1 .rough-node .label text,#mermaid-svg-tfCKkUJ98wjgmSz1 .node .label text,#mermaid-svg-tfCKkUJ98wjgmSz1 .image-shape .label,#mermaid-svg-tfCKkUJ98wjgmSz1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-tfCKkUJ98wjgmSz1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tfCKkUJ98wjgmSz1 .rough-node .label,#mermaid-svg-tfCKkUJ98wjgmSz1 .node .label,#mermaid-svg-tfCKkUJ98wjgmSz1 .image-shape .label,#mermaid-svg-tfCKkUJ98wjgmSz1 .icon-shape .label{text-align:center;}#mermaid-svg-tfCKkUJ98wjgmSz1 .node.clickable{cursor:pointer;}#mermaid-svg-tfCKkUJ98wjgmSz1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tfCKkUJ98wjgmSz1 .arrowheadPath{fill:#333333;}#mermaid-svg-tfCKkUJ98wjgmSz1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tfCKkUJ98wjgmSz1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tfCKkUJ98wjgmSz1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tfCKkUJ98wjgmSz1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tfCKkUJ98wjgmSz1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tfCKkUJ98wjgmSz1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tfCKkUJ98wjgmSz1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tfCKkUJ98wjgmSz1 .cluster text{fill:#333;}#mermaid-svg-tfCKkUJ98wjgmSz1 .cluster span{color:#333;}#mermaid-svg-tfCKkUJ98wjgmSz1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tfCKkUJ98wjgmSz1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tfCKkUJ98wjgmSz1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-tfCKkUJ98wjgmSz1 .icon-shape,#mermaid-svg-tfCKkUJ98wjgmSz1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tfCKkUJ98wjgmSz1 .icon-shape p,#mermaid-svg-tfCKkUJ98wjgmSz1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tfCKkUJ98wjgmSz1 .icon-shape rect,#mermaid-svg-tfCKkUJ98wjgmSz1 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tfCKkUJ98wjgmSz1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tfCKkUJ98wjgmSz1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tfCKkUJ98wjgmSz1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
否
否
是
否
是
否
是
开始
是否需要调用外部工具?
使用 MCP Server
是否需要多个 Agent?
Single Agent
是否需要跨语言/跨团队?
是否需要高并发?
使用 LangServe
进程内 Swarm
分布式 Swarm + Redis
6.3 典型架构模式
模式 1:简单协作流程
场景:在线客服(分流 → 技术支持 / 售后)
方案:进程内 Swarm
理由:流程固定,状态简单,无需分布式
模式 2:高并发业务
场景:保险理赔(每天 10 万单)
方案:分布式 Swarm (Redis Checkpointer) + LangServe
理由:需要水平扩展,异步处理
模式 3:微服务生态
场景:企业 AI 中台(接入 50+ 业务系统)
方案:LangServe (Agent 服务) + MCP (工具标准化)
理由:跨团队协作,需要标准化接口
6.4 工程最佳实践
1. 死循环检测
def router_with_ttl(state: InsuranceState):
# 记录跳转次数
hop_count = state.get("_hop_count", 0)
if hop_count > 20:
return END # 强制终止
# 正常路由逻辑
next_node = normal_router(state)
state["_hop_count"] = hop_count + 1
return next_node
2. 状态快照
# 在关键节点保存快照,用于回滚
def processor_node(state: InsuranceState):
# 处理前快照
snapshot = state.copy()
try:
# 处理逻辑
result = process_claim(state)
except Exception as e:
# 回滚
return snapshot
return result
3. 分布式追踪
from langsmith import traceable
@traceable(run_type="agent")
def triage_node(state: InsuranceState):
# LangSmith 自动记录输入输出
...
查看追踪:访问 https://smith.langchain.com
4. 优雅降级
def call_remote_agent_with_fallback(url: str, input_data: dict):
try:
# 尝试调用远程服务
return RemoteRunnable(url).invoke(input_data)
except Exception as e:
# 降级到本地规则
return fallback_rule_engine(input_data)
6.5 性能优化建议
1. Redis Checkpointer 优化
# 设置 TTL,避免内存泄漏
redis_client.setex(
f"thread:{thread_id}",
86400, # 24 小时过期
state_json
)
2. LangServe 批处理
# 客户端批量调用,服务端自动并行处理
results = remote_agent.batch([
{"input": "case 1"},
{"input": "case 2"},
{"input": "case 3"}
])
3. MCP Server 连接池
# 使用 connection pooling 复用进程
client = MultiServerMCPClient({
"db": {
"transport": "stdio",
"command": "python",
"args": ["db_server.py"],
"pool_size": 5 # 预启动 5 个进程
}
})
6.6 安全考虑
1. Agent 权限隔离
# 为不同 Agent 分配不同的 MCP Server 权限
triage_tools = await client.get_tools(server="readonly_db")
fraud_tools = await client.get_tools(server="full_access_db")
2. 输入验证
from pydantic import BaseModel, validator
class ClaimInput(BaseModel):
claim_id: str
@validator("claim_id")
def validate_claim_id(cls, v):
if not v.startswith("C"):
raise ValueError("Invalid claim ID format")
return v
3. 速率限制
from fastapi_limiter import FastAPILimiter
from fastapi_limiter.depends import RateLimiter
# LangServe 服务添加限流
@app.post("/claims/invoke", dependencies=[Depends(RateLimiter(times=10, seconds=60))])
结语:架构的本质是控制复杂度
从吴恩达教授的四种模式到工业级的分布式 Swarm,我们学习了构建 Multi-Agent Systems 的完整路径:
设计原则:
- 内部用 Swarm:管理业务流程和协作逻辑。
- 外部用 MCP:连接数据库、API、文件等工具。
- 跨服务用 LangServe:实现微服务化和语言异构。
未来展望:
- Multi-modal Agents:融合视觉、语音的多模态协作。
- Human-in-the-loop:在关键决策点引入人类审核。
- Self-evolving Swarm:Agent 通过强化学习优化协作策略。
架构的本质不是技术的堆砌,而是在 复杂度、性能、可维护性 之间找到最优平衡。从 Monolith 到 Swarm 到 Ecosystem,每一步演进都应该基于真实的业务需求,而非技术炫技。
“The best architecture is the one you can actually maintain.”
— Martin Fowler
参考资料:
- Andrew Ng – Agentic Patterns
- LangGraph Multi-Agent Documentation
- Model Context Protocol Specification
- LangServe Documentation
网硕互联帮助中心







评论前必须登录!
注册