LangChain Agent 上手
基于 LangChain Agent 官方文档 整理,入门 Agent 开发。
一、Agent 是什么
1.1 一句话定义
Agent(智能体)= LLM + 工具 + 循环调度 。官方原话:An agent is a model calling tools in a loop until a given task is complete.
工具就像是电饭锅,不会自己主动想煮饭,而是需要用户指令才能触发。
对比普通大模型:
| 能力 | 普通 LLM | Agent |
|---|---|---|
| 回答问题 | ✅ | ✅ |
| 自主规划步骤 | ❌ | ✅ |
| 调用外部工具 | ❌ | ✅ |
| 循环执行直到任务完成 | ❌ | ✅ |
1.2 ReAct 循环:Agent 的心跳
Agent 的核心工作模式是 ReAct(Reasoning + Acting)循环,由三个步骤持续交替:
erlang
推理(Reason)→ 行动(Act)→ 观测(Observation)→ 推理(Reason)→ ...
- 推理:LLM 分析当前问题和已有信息,判断下一步该做什么
- 行动:调用外部工具(搜索、计算器、API、数据库等)
- 观测:接收工具返回结果,回到推理步骤继续
循环往复,直到 LLM 认为任务完成,输出最终答案。
1.3 Agent 技术演进路径
markdown
纯 LLM 只聊天、问答,无外部能力
↓
Tool Calling LLM 具备「单步调用工具」能力
↓
Agent 工具调用 + 循环调度,多步自主执行
↓
MCP + Agent 工具标准化为 MCP 服务,Agent 统一对接
二、核心组件
create_agent 是一个高度可配置的 harness(脚手架),它将模型、提示词、工具和中间件组装在一起。以下是五个核心参数:
2.1 Model(模型)
传入模型标识字符串 "provider:model" 或已初始化的模型实例:
python
from langchain.agents import create_agent
agent = create_agent("openai:gpt-4o", tools=tools)
2.2 Tools(工具)
传入任意 Python 可调用对象、LangChain 工具或工具字典。支持 @tool 装饰器快速定义:
python
from langchain.tools import tool
@tool
def search(query: str) -> str:
"""Search for information."""
return f"Results for: {query}"
agent = create_agent("openai:gpt-4o", tools=[search])
2.3 System Prompt(系统提示词)
定义 Agent 的角色和行为规则。接受字符串或 SystemMessage:
python
agent = create_agent(
"openai:gpt-4o",
tools=tools,
system_prompt="你是一个有帮助的助手,回答要简洁准确。",
)
2.4 Structured Output(结构化输出)
通过 response_format= 让 Agent 直接返回符合 Pydantic 模型的验证数据:
python
from pydantic import BaseModel
class Answer(BaseModel):
summary: str
confidence: float
agent = create_agent("openai:gpt-4o", tools=tools, response_format=Answer)
result = agent.invoke({"messages": [{"role": "user", "content": "总结 AI 趋势"}]})
result["structured_response"] # Answer(summary=..., confidence=...)
2.5 Name(名称)
可选标识符,在多 Agent 系统中用于区分不同的 Agent:
python
agent = create_agent("openai:gpt-4o", tools=tools, name="research_assistant")
三、快速上手:第一个 Agent
以下是一个完整的电商助手示例,展示 ReAct 循环的实际运作。
3.1 定义 MCP 工具
使用 FastMCP 定义两个工具:产品搜索和库存查询:
python
# products_mcp.py
from mcp.server.fastmcp import FastMCP
PRODUCT_DATABASE = {
"无线耳机": [
{"id": "WH-1000XM5", "name": "索尼 WH-1000XM5", "popularity": 95, "price": 299},
{"id": "QC45", "name": "Bose QuietComfort 45", "popularity": 88, "price": 329},
{"id": "AIRMAX", "name": "苹果 AirPods Max", "popularity": 92, "price": 549},
{"id": "PXC550", "name": "森海塞尔 PXC 550", "popularity": 76, "price": 299},
{"id": "HT450", "name": "JBL Tune 760NC", "popularity": 82, "price": 99}
],
"游戏鼠标": [
{"id": "GPW", "name": "罗技 G Pro 无线", "popularity": 90, "price": 129},
{"id": "VIPER", "name": "雷蛇 Viper V2 Pro", "popularity": 87, "price": 149},
{"id": "DAV3", "name": "雷蛇 DeathAdder V3", "popularity": 85, "price": 119}
],
"笔记本电脑": [
{"id": "MBP14", "name": "MacBook Pro 14英寸", "popularity": 94, "price": 1999},
{"id": "XPS13", "name": "戴尔 XPS 13", "popularity": 89, "price": 1299},
{"id": "TPX1", "name": "ThinkPad X1 Carbon", "popularity": 86, "price": 1499}
]
}
INVENTORY_DATABASE = {
"WH-1000XM5": {"stock": 10, "location": "仓库-A"},
"QC45": {"stock": 0, "location": "仓库-B"},
"AIRMAX": {"stock": 5, "location": "仓库-C"},
"PXC550": {"stock": 15, "location": "仓库-A"},
"HT450": {"stock": 25, "location": "仓库-B"},
"GPW": {"stock": 8, "location": "仓库-C"},
"VIPER": {"stock": 12, "location": "仓库-A"},
"DAV3": {"stock": 3, "location": "仓库-B"},
"MBP14": {"stock": 7, "location": "仓库-C"},
"XPS13": {"stock": 0, "location": "仓库-A"},
"TPX1": {"stock": 4, "location": "仓库-B"}
}
mcp = FastMCP("产品服务")
@mcp.tool()
def search_products(query: str) -> str:
"""搜索产品并返回按受欢迎度排序的结果"""
keyword_mapping = {
"无线耳机": ["无线耳机", "蓝牙耳机", "头戴式耳机", "耳机"],
"游戏鼠标": ["游戏鼠标", "电竞鼠标", "鼠标"],
"笔记本电脑": ["笔记本电脑", "笔记本", "手提电脑", "电脑"]
}
matched_category = None
for category, keywords in keyword_mapping.items():
if any(keyword in query for keyword in keywords):
matched_category = category
break
if matched_category and matched_category in PRODUCT_DATABASE:
products = PRODUCT_DATABASE[matched_category]
sorted_products = sorted(products, key=lambda x: x['popularity'], reverse=True)
result = f"找到 {len(sorted_products)} 个匹配 '{query}' 的产品:\n"
for i, product in enumerate(sorted_products, 1):
result += f"{i}. {product['name']} (ID: {product['id']}) - 受欢迎度: {product['popularity']}% - ¥{product['price']}\n"
return result
@mcp.tool()
def check_inventory(product_id: str) -> str:
"""检查特定产品的库存状态"""
if product_id in INVENTORY_DATABASE:
stock_info = INVENTORY_DATABASE[product_id]
status = "有库存" if stock_info['stock'] > 0 else "缺货"
return f"产品 {product_id}: {status} ({stock_info['stock']} 件库存) - 位置: {stock_info['location']}"
else:
return f"未找到产品ID: {product_id}"
if __name__ == "__main__":
mcp.run(transport="stdio")
3.2 创建 Agent 并追踪 ReAct 循环
python
from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient
from pathlib import Path
from llm import llm
import asyncio
async def get_tools():
client = MultiServerMCPClient({
"products": {
"transport": "stdio",
"command": "python",
"args": [str(Path(__file__).parent / "products_mcp.py")],
}
})
tools = await client.get_tools()
return tools
def track_react_cycle(messages):
"""追踪并可视化 ReAct 循环全过程"""
print("ReAct循环步骤分析:")
step = 1
for msg in messages:
msg_type = msg.__class__.__name__
if msg_type == "AIMessage" and hasattr(msg, 'tool_calls') and msg.tool_calls:
print(f"\n🔄 步骤{step}: Reasoning + Acting")
for tool_call in msg.tool_calls:
print(f" 🛠️ 工具调用: {tool_call['name']}({tool_call['args']})")
step += 1
elif msg_type == "ToolMessage":
print(f" 📋 观察结果: {msg.content[:80]}...")
elif msg_type == "AIMessage" and not (hasattr(msg, 'tool_calls') and msg.tool_calls):
print(f"\n✅ 最终回答: {msg.content}")
async def main():
tools = await get_tools()
agent = create_agent(
llm,
tools=tools,
system_prompt="""你是电商助手,遵循ReAct模式:
1. 先推理用户需求
2. 选择合适的工具执行操作
3. 基于工具结果进行下一步推理
4. 重复直到获得完整答案""",
)
result = await agent.ainvoke({
"messages": [{"role": "user", "content": "查找当前最受欢迎的无线耳机并检查是否有库存"}]
})
print("\n" + "=" * 40)
print("📊 最终结果:")
for msg in result['messages']:
if hasattr(msg, 'content'):
print(f"{msg.__class__.__name__}: {msg.content}")
print("=" * 40)
print()
track_react_cycle(result['messages'])
if __name__ == "__main__":
asyncio.run(main())
运行输出示例:
css
🔄 步骤1: Reasoning + Acting
🛠️ 工具调用: search_products({"query": "无线耳机"})
📋 观察结果: 找到 5 个匹配 '无线耳机' 的产品: ...
🔄 步骤2: Reasoning + Acting
🛠️ 工具调用: check_inventory({"product_id": "WH-1000XM5"})
📋 观察结果: 产品 WH-1000XM5: 有库存 (10 件库存) ...
✅ 最终回答: 最受欢迎的无线耳机是索尼 WH-1000XM5...
这就是 ReAct 循环的真实运作:Agent 先搜索产品 → 拿到结果后推理 → 再查库存 → 最终给出完整答案。
四、调用方式:Invoke 与 Streaming
4.1 invoke ------ 获取最终结果
python
result = agent.invoke({
"messages": [{"role": "user", "content": "搜索 AI 新闻并总结"}]
})
print(result["messages"][-1].content)
4.2 stream ------ 实时查看中间过程
当 Agent 执行多个步骤时,invoke 会等到全部完成才返回。用 stream 可以实时看到每一步:
python
from langchain.messages import AIMessage, HumanMessage
for chunk in agent.stream({
"messages": [{"role": "user", "content": "搜索 AI 新闻并总结"}]
}, stream_mode="values"):
latest_message = chunk["messages"][-1]
if latest_message.content:
if isinstance(latest_message, HumanMessage):
print(f"User: {latest_message.content}")
elif isinstance(latest_message, AIMessage):
print(f"Agent: {latest_message.content}")
elif latest_message.tool_calls:
print(f"Calling tools: {[tc['name'] for tc in latest_message.tool_calls]}")
这对前端开发特别有用------可以做「打字机效果」或「正在调用工具」的实时提示。
五、状态持久化(Checkpoint)
Agent 底层基于 LangGraph,自带会话记忆能力。通过接入 Redis,可以实现跨进程、跨重启的对话持久化:
python
import asyncio
import os
import dotenv
from llm import llm
from langchain.agents import create_agent
from langgraph.checkpoint.redis import AsyncRedisSaver
from langchain_mcp_adapters.client import MultiServerMCPClient
dotenv.load_dotenv()
REDIS_URI = os.getenv("REDIS_URI", "redis://localhost:6379")
async def main():
client = MultiServerMCPClient({
"baidu_map": {
"transport": "streamable_http",
"url": f"https://mcp.map.baidu.com/mcp?ak={os.getenv('BAIDU_MAP_AK')}",
}
})
tools = await client.get_tools()
async with AsyncRedisSaver.from_conn_string(REDIS_URI) as checkpointer:
await checkpointer.setup()
agent = create_agent(
llm,
tools,
checkpointer=checkpointer,
system_prompt="你是一个地图助手。当用户询问天气、地址、路线、地点等信息时,必须使用工具查询。如果用户问之前说过的事,从对话历史中回忆。",
)
thread_id = "user_001"
config = {"configurable": {"thread_id": thread_id}}
print(f"🤖 Agent 已启动(会话ID: {thread_id},存档于 Redis)")
print("输入 exit 退出,重启程序后对话历史不会丢失\n")
while True:
try:
question = input("你:").strip()
if not question:
continue
if question.lower() in ("exit", "quit", "q"):
print("再见!(对话已存入 Redis,下次启动继续聊)")
break
result = await agent.ainvoke(
{"messages": [("user", question)]},
config,
)
print(f"\n助手:{result['messages'][-1].content}\n")
except KeyboardInterrupt:
print("\n再见!(对话已存入 Redis)")
break
if __name__ == "__main__":
asyncio.run(main())
关键点:
checkpointer负责自动保存每轮对话状态thread_id区分不同用户 / 会话- 程序重启后,相同
thread_id自动恢复历史
以上是 Agent 的基础用法------概念、核心组件、创建调用、状态持久化。下面进入进阶篇:中间件扩展、多 Agent 协作、MCP 集成。
六、中间件(Middleware):Agent 的扩展系统
中间件是 create_agent 最强大的扩展机制。每个中间件处理一个关注点,在 Agent 循环的合适时机介入,且可以自由组合。官方文档将中间件分为六个大类:
6.1 执行环境(Execution Environment)
给 Agent 一个真正的工作空间------不只是调用工具,还能读写文件、执行代码:
python
from deepagents.backends import StateBackend
from deepagents.middleware import FilesystemMiddleware
agent = create_agent(
model="anthropic:claude-sonnet-4-6",
tools=[search],
middleware=[FilesystemMiddleware(backend=StateBackend())],
)
6.2 上下文管理(Context Management)
随着 Agent 运行,历史消息、工具结果、中间步骤不断累积,会撑爆上下文窗口。这套中间件解决此问题:
- SummarizationMiddleware:对话过长时自动总结历史,防止越聊越慢、越聊越贵
- MemoryMiddleware:启动时加载持久化指令,让知识跨会话延续
- SkillsMiddleware:按需加载领域知识,而非一次性塞入全部
python
from deepagents.middleware import (
FilesystemMiddleware, MemoryMiddleware,
SkillsMiddleware, SummarizationMiddleware
)
agent = create_agent(
model=model,
tools=[search],
middleware=[
FilesystemMiddleware(backend=backend),
SummarizationMiddleware(model=model, backend=backend),
MemoryMiddleware(backend=backend, sources=["./AGENTS.md"]),
SkillsMiddleware(backend=backend, sources=["./skills/"]),
],
)
6.3 规划与委派(Planning & Delegation)
复杂任务往往超出单个上下文窗口的处理能力。委派机制让主 Agent 把工作拆成小块,交给子 Agent------每个子 Agent 在自己的隔离上下文中运行。主 Agent 只负责协调,上下文始终保持整洁。
这是「多 Agent 协作」的核心基础。
6.4 容错(Fault Tolerance)
生产环境中会出现开发时少见的故障------限流、超时、瞬时 API 错误。容错中间件在基础设施层处理这些:
- ModelRetryMiddleware:模型调用失败自动重试
- ToolRetryMiddleware:工具调用失败自动重试
工具报错不再直接崩掉整个 Agent,模型可以收到友好错误并重试或调整参数。
6.5 安全护栏(Guardrails)
有些策略不能只靠提示词------需要确定性地强制执行,无论模型做什么:
- PIIMiddleware:自动检测和脱敏个人身份信息
- 在模型调用前后做内容过滤
6.6 人类介入(Human-in-the-Loop)
完全自主并不总是合适的。高危操作(转账、删数据)先暂停,等人工确认:
python
# 流程:Agent 想调用高危工具 → 暂停 → 人工同意 → 继续执行
Agent 暂停等待,人类审批 / 编辑 / 拒绝,然后继续执行。
6.7 自定义中间件
如果内置中间件不满足需求,可以写自己的中间件------一行装饰器就能介入 Agent 循环的任意节点。
七、多 Agent 协作(Multi-Agent)
当一个 Agent 不够用时,用多个 Agent 协同工作。核心模式:将每个子 Agent 封装为工具,供主 Agent 调度。
下面是一个完整的跨平台出行示例------携程订机票、美团订酒店、滴滴打车:
python
"""
基于 LangChain create_agent 实现 A2A 协作,
模拟携程订机票、美团订酒店、滴滴打车的跨平台智能协作流程。
核心设计:
- 用 create_agent 创建三个独立子 Agent,每个绑定专属业务工具
- 将每个子 Agent 封装为 @tool,供主 Agent 调用
- 主 Agent 也用 create_agent 创建,智能调度子 Agent 完成出行闭环
简单说:A2A = 多个 create_agent 子 Agent + 一个 create_agent 主 Agent 调度。
"""
import os
from langchain.agents import create_agent
from langchain.tools import tool
from llm import llm
# ===================== 模拟业务工具 =====================
@tool("ctrip_book_flight", description="预订机票,参数:departure出发地、arrival目的地、date出行日期(格式2026-02-01)")
def ctrip_book_flight(departure: str, arrival: str, date: str) -> str:
"""携程订机票"""
return (
f"【携程机票预订成功】\n"
f"出发地:{departure}\n目的地:{arrival}\n出行日期:{date}\n"
f"航班号:CA1885(北京首都T3→上海浦东T2)\n"
f"起飞时间:14:00\n降落时间:16:30\n"
f"座位:经济舱34A\n电子客票号:999-1234567890"
)
@tool("meituan_book_hotel", description="预订酒店,参数:city城市、near_by附近地标、check_in入住日期、check_out离店日期")
def meituan_book_hotel(city: str, near_by: str, check_in: str, check_out: str) -> str:
"""美团订酒店"""
return (
f"【美团酒店预订成功】\n"
f"城市:{city}\n位置:{near_by}附近\n"
f"入住日期:{check_in}\n离店日期:{check_out}\n"
f"酒店名称:上海浦东机场铂尔曼大酒店\n"
f"房型:豪华大床房(含双人自助早餐)\n"
f"房号:1508\n预订号:MT20260201001"
)
@tool("didi_book_taxi", description="预约打车,参数:start起点、end终点、time用车时间")
def didi_book_taxi(start: str, end: str, time: str) -> str:
"""滴滴打车"""
return (
f"【滴滴打车预约成功】\n"
f"起点:{start}\n终点:{end}\n用车时间:{time}\n"
f"车型:滴滴快车(舒适型)\n司机姓名:王师傅\n"
f"车牌号:沪A12345\n预估费用:35元\n预计接驾时间:16:35"
)
# ===================== 用 create_agent 创建三个独立子 Agent =====================
ctrip_agent = create_agent(
llm,
tools=[ctrip_book_flight],
name="携程机票Agent",
system_prompt=(
"你是携程机票预订专家,用户说要订机票时,调用 ctrip_book_flight 工具完成预订。"
"从用户输入中提取 departure、arrival、date 参数。"
"如果用户没提供出发地和目的地,默认 departure='北京', arrival='上海'。"
"如果用户没提供日期,默认 date='2026-02-01'。"
"返回工具执行结果,不要添加额外内容。"
),
)
meituan_agent = create_agent(
llm,
tools=[meituan_book_hotel],
name="美团酒店Agent",
system_prompt=(
"你是美团酒店预订专家,用户说要订酒店时,调用 meituan_book_hotel 工具完成预订。"
"从用户输入中提取 city、near_by、check_in、check_out 参数。"
"如果用户没提供城市,默认 city='上海';没提供地标,默认 near_by='浦东机场'。"
"如果用户没提供日期,默认 check_in='2026-02-01', check_out='2026-02-02'。"
"返回工具执行结果,不要添加额外内容。"
),
)
didi_agent = create_agent(
llm,
tools=[didi_book_taxi],
name="滴滴打车Agent",
system_prompt=(
"你是滴滴打车预约专家,用户说要打车时,调用 didi_book_taxi 工具完成预约。"
"从用户输入中提取 start、end、time 参数。"
"如果用户没提供起点,默认 start='上海浦东机场T2'。"
"如果用户没提供终点,默认 end='上海浦东机场铂尔曼大酒店'。"
"如果用户没提供时间,默认 time='2026-02-01 16:40'。"
"返回工具执行结果,不要添加额外内容。"
),
)
# ===================== 将子 Agent 封装为供主 Agent 调用的工具 =====================
@tool("call_ctrip_agent", description="调用携程机票Agent预订机票。传入用户需求描述,如'订北京到上海2026-02-01的机票'")
def call_ctrip_agent(query: str) -> str:
print("🔍 [主Agent] 调度 → 携程机票Agent")
result = ctrip_agent.invoke({"messages": [("user", query)]})
return result["messages"][-1].content
@tool("call_meituan_agent", description="调用美团酒店Agent预订酒店。传入用户需求描述,如'订上海浦东机场附近2026-02-01入住的酒店'")
def call_meituan_agent(query: str) -> str:
print("🔍 [主Agent] 调度 → 美团酒店Agent")
result = meituan_agent.invoke({"messages": [("user", query)]})
return result["messages"][-1].content
@tool("call_didi_agent", description="调用滴滴打车Agent预约打车。传入用户需求描述,如'从上海浦东机场打车到酒店'")
def call_didi_agent(query: str) -> str:
print("🔍 [主Agent] 调度 → 滴滴打车Agent")
result = didi_agent.invoke({"messages": [("user", query)]})
return result["messages"][-1].content
# ===================== 用 create_agent 创建主协调 Agent =====================
master_agent = create_agent(
llm,
tools=[call_ctrip_agent, call_meituan_agent, call_didi_agent],
name="出行总协调Agent",
system_prompt="""你是出行总协调助手,负责调度三个子Agent完成完整的出行服务闭环。
子Agent和调用顺序:
1. call_ctrip_agent --- 预订机票
2. call_meituan_agent --- 预订酒店
3. call_didi_agent --- 预约打车
工作流程:
- 必须按顺序调用所有三个子Agent
- 每个子Agent调用完成后,基于其结果调用下一个
- 最终将所有结果整合为一份清晰的出行报告,包含机票、酒店、打车三部分信息""",
)
# ===================== 主程序 =====================
if __name__ == "__main__":
print("=" * 60)
print("🔧 初始化 Agent 架构")
print(" ├── 携程机票Agent (create_agent + ctrip_book_flight)")
print(" ├── 美团酒店Agent (create_agent + meituan_book_hotel)")
print(" ├── 滴滴打车Agent (create_agent + didi_book_taxi)")
print(" └── 出行总协调Agent (create_agent + 3个子Agent工具)")
print("=" * 60 + "\n")
print("🚀 A2A 协作测试正式开始 🚀\n")
result = master_agent.invoke({
"messages": [("user", "安排2026-02-01从北京飞上海的完整行程,需要订机票、酒店和打车")]
})
print("\n" + "=" * 60)
print("📊 最终出行报告:")
print("=" * 60)
print(result["messages"][-1].content)
print("=" * 60)
八、MCP + Agent:工具的标准化
如果你把 MCP 作为工具接入 create_agent,还能额外获得:
- 统一调用:计算器、百度地图、数据库、文件服务......全变成统一工具,Agent 自动判断该调用哪个
- 服务鉴权:中间件里加入 Token 验证,不同用户只能用自己权限内的 MCP 工具
- 负载均衡 / 故障转移:生产级 MCP 服务治理
这背后是 MCP(Model Context Protocol)的标准化能力------让 Agent 对接所有 MCP 服务就像对接本地函数一样简单。
九、总结
| 层次 | 内容 | 关键 API |
|---|---|---|
| 核心组件 | Model、Tools、System Prompt、Structured Output、Name | create_agent() |
| 调用方式 | 同步/异步、invoke/stream | .invoke() / .stream() |
| 状态持久化 | 会话记忆、Redis 持久化 | checkpointer= |
| 中间件 | 执行环境、上下文管理、容错、安全、人工介入 | middleware=[] |
| 多 Agent | 子 Agent 封装为工具,主 Agent 调度 | @tool + create_agent |
| MCP 集成 | 工具标准化、鉴权、负载均衡 | MultiServerMCPClient |
create_agent 从简单的单 Agent 到复杂的多 Agent 协作、从开发环境的快速原型到生产环境的容错与安全------它用同一套 API 覆盖了全场景。中间件体系让你按需取用。