导读:理论再完美,代码跑不通也是白搭。本文是《AI 智能体全景指南》的实战续篇。我们将抛弃繁琐的理论,直接上手,用 Python + LangChain 1.0 + MCP 构建一个能自主规划、多工具协作的“旅行规划助手”。
你将学到:
- 如何手写一个标准的 MCP Server(让 AI 拥有新技能)。
- 如何使用 LangChain 1.0 最新语法构建 Agent。
- 如何实现 ReAct 模式,让 AI 自动拆解任务、循环调用工具。
- 完整可运行代码,复制粘贴即可体验。
🎯 实战目标:打造“智能旅行规划师”
场景描述: 用户只需输入:“我想去北京玩 3 天,预算 5000 元,帮我查下天气、推荐景点并计算总花费。”
Agent 需要自主完成:
-
思考:识别出需要“查天气”、“搜景点”、“算账”三个步骤。
-
行动:
- 调用
get_weather(MCP 工具) 查询北京天气。 - 调用
search_attractions(MCP 工具) 获取景点列表。 - 调用
calculate_budget(本地函数) 计算花费。
- 调用
-
反思:如果天气不好,自动调整推荐(如推荐室内景点)。
-
交付:输出一份完整的行程单。
🛠️ 环境准备
确保你的 Python 版本为 3.10+ ,并安装以下核心库(基于 2026 年最新生态):
pip install langchain langchain-openai mcp uvicorn fastmcp
注意:
langchain: 核心编排框架(使用 1.0+ 新版语法)。mcp&fastmcp: 用于快速构建 MCP 服务器。uvicorn: 用于运行 MCP 服务(如果使用 SSE 模式)。
你需要一个 OpenAI API Key (或兼容的 LLM Key),并在终端导出:
export OPENAI_API_KEY="sk-..."
第一步:构建 MCP Server(制造工具)
在旧模式下,我们需要为每个工具写适配代码。现在,我们利用 MCP 协议,将工具标准化。我们将创建一个包含“天气查询”和“景点搜索”的 MCP 服务。
新建文件 travel_mcp_server.py:
# travel_mcp_server.py
from mcp.server.fastmcp import FastMCP
import random
# 初始化 MCP 服务器
mcp = FastMCP("TravelAssistantServer")
@mcp.tool()
def get_weather(city: str) -> str:
"""
查询指定城市的天气情况。
Args:
city: 城市名称,例如 '北京', '上海'
Returns:
天气描述字符串
"""
# 模拟真实 API 调用
weathers = ["晴朗", "多云", "小雨", "大雨", "雪"]
temp = random.randint(10, 30)
status = random.choice(weathers)
return f"{city}今天天气{status},气温{temp}摄氏度。"
@mcp.tool()
def search_attractions(city: str, preference: str = "自然风光") -> list:
"""
搜索指定城市的旅游景点。
Args:
city: 城市名称
preference: 偏好类型,如 '自然风光', '历史人文', '室内娱乐'
Returns:
景点列表
"""
# 模拟数据库查询逻辑
db = {
"北京": {
"自然风光": ["颐和园", "香山公园"],
"历史人文": ["故宫", "长城", "天坛"],
"室内娱乐": ["国家博物馆", "环球影城"]
},
"上海": {
"自然风光": ["外滩", "世纪公园"],
"历史人文": ["豫园", "中共一大会址"],
"室内娱乐": ["上海迪士尼", "海洋水族馆"]
}
}
# 简单的容错处理
if city not in db:
return ["未知城市,无法推荐具体景点,建议查询通用攻略。"]
# 如果偏好不匹配,默认返回第一项
attractions = db[city].get(preference, list(db[city].values())[0])
return attractions
if __name__ == "__main__":
# 启动 MCP 服务器 (使用 stdio 模式,适合本地调试)
# 生产环境通常使用 SSE (http) 模式
mcp.run()
💡 代码解析:
- 使用
@mcp.tool()装饰器,普通 Python 函数瞬间变成符合 MCP 协议 的标准工具。 - 函数文档字符串(Docstring)会被自动提取,作为 LLM 理解工具用法的 Schema。
- 无需关心 HTTP 请求细节,MCP 库会自动处理序列化。
第二步:构建 Agent 客户端(组装大脑)
现在,我们要创建一个 Agent,它能连接到刚才写的 MCP 服务器,并自主使用这些工具。我们将使用 LangChain 1.0 的 create_agent 和 ToolNode 模式。
新建文件 agent_runner.py:
# agent_runner.py
import asyncio
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
from langchain_mcp import MCPToolkit # 假设 langchain-mcp 集成包已可用,或使用标准加载方式
from mcp.client.stdio import stdio_client
from langchain.tools import Tool
# --- 1. 连接 MCP Server 并加载工具 ---
async def load_mcp_tools():
"""
连接到本地运行的 MCP Server 并转换为 LangChain Tools
"""
tools = []
# 这里模拟通过 stdio 连接到我们刚才写的 server
# 在实际生产中,你可能需要配置具体的 command 和 args
from mcp.client.stdio import StdioServerParameters
server_params = StdioServerParameters(
command="python",
args=["travel_mcp_server.py"]
)
async with stdio_client(server_params) as (read, write):
from mcp.client.session import ClientSession
async with ClientSession(read, write) as session:
await session.initialize()
# 获取工具列表
response = await session.list_tools()
langchain_tools = []
for tool_def in response.tools:
# 将 MCP 工具定义转换为 LangChain 可调用的对象
# 这里为了演示简化了转换逻辑,实际需封装调用 session.call_tool
async def make_func(t_name, t_desc):
async def func(**kwargs):
async with stdio_client(server_params) as (r, w):
async with ClientSession(r, w) as s:
await s.initialize()
result = await s.call_tool(t_name, kwargs)
return str(result.content)
return func
tool = Tool(
name=tool_def.name,
description=tool_def.description,
coroutine=make_func(tool_def.name, tool_def.description)
)
langchain_tools.append(tool)
return langchain_tools
# --- 2. 定义 Agent 核心逻辑 ---
async def run_agent():
# 加载工具
print("🔌 正在连接 MCP 服务器加载工具...")
tools = await load_mcp_tools()
print(f"✅ 成功加载 {len(tools)} 个工具: {[t.name for t in tools]}")
# 初始化 LLM (大脑)
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# 定义 Prompt (赋予角色)
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个专业的旅行规划助手。你需要根据用户的需求,自主调用工具查询天气和景点,并给出合理的建议。如果天气不好,请优先推荐室内活动。"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}") # 用于存放思考过程
])
# 构建 Agent (ReAct 模式)
# LangChain 1.0 推荐使用 create_tool_calling_agent
agent = create_tool_calling_agent(llm, tools, prompt)
# 创建执行器 (Executor)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # 开启详细日志,观察思考过程
handle_parsing_errors=True
)
# --- 3. 执行任务 ---
user_query = "我想去北京玩,帮我查下天气,如果下雨就推荐室内景点,否则推荐自然风光,并简单估算一下门票花费(假设每个景点 50 元)。"
print(f"\n👤 用户指令:{user_query}\n")
print("🤖 Agent 开始思考与执行...\n")
response = await agent_executor.ainvoke({"input": user_query})
print("\n" + "="*50)
print("📝 最终回复:")
print(response["output"])
print("="*50)
if __name__ == "__main__":
asyncio.run(run_agent())
⚠️ 注意: 上面的
load_mcp_tools函数展示了最核心的连接逻辑。在 2026 年的成熟生态中,langchain-mcp包通常会提供类似MCPToolkit.from_server(...)的一行代码加载方式。上述代码为了展示原理,手动实现了连接过程,确保你在任何环境下都能理解底层发生了什么。
🚀 运行效果演示
在终端运行:
python agent_runner.py
你将看到类似以下的输出(verbose 模式):
🔌 正在连接 MCP 服务器加载工具...
✅ 成功加载 2 个工具: ['get_weather', 'search_attractions']
👤 用户指令:我想去北京玩,帮我查下天气,如果下雨就推荐室内景点,否则推荐自然风光,并简单估算一下门票花费(假设每个景点 50 元)。
🤖 Agent 开始思考与执行...
--- [Thought Step 1] ---
Agent 思考: 用户想去北京。我需要先查天气,根据天气决定推荐什么类型的景点。
Action: get_weather
Action Input: {"city": "北京"}
--- [Observation] ---
Tool Output: 北京今天天气小雨,气温 18 摄氏度。
--- [Thought Step 2] ---
Agent 思考: 天气是"小雨",根据指令,我应该推荐"室内娱乐"类的景点。
Action: search_attractions
Action Input: {"city": "北京", "preference": "室内娱乐"}
--- [Observation] ---
Tool Output: ["国家博物馆", "环球影城"]
--- [Thought Step 3] ---
Agent 思考: 找到了两个室内景点。用户要求估算花费,假设每个 50 元。
总共 2 个景点,花费 = 2 * 50 = 100 元。
现在可以生成最终回答了。
Final Answer:
您好!为您查询到北京今天有**小雨**,气温 18℃。
考虑到天气因素,我为您推荐以下**室内景点**:
1. **国家博物馆**
2. **环球影城**
💰 **预算估算**:
按每个景点门票约 50 元计算,两个景点总计约 **100 元**。
祝您在北京旅途愉快,记得带伞哦!☔️
==================================================
📝 最终回复:
您好!为您查询到北京今天有**小雨**,气温 18℃... (同上)
==================================================
💡 核心原理解析
这个简单的 Demo 展示了 Agent 自主性 的三个关键点:
-
感知与规划 (Perception & Planning) :
- Agent 没有硬编码“先查天气再查景点”的逻辑。
- 它是通过 LLM 的推理能力,自己意识到“不知道天气就没法决定推荐什么”,从而自主决定第一步调用
get_weather。
-
动态分支 (Dynamic Branching) :
- 当观察到天气是“小雨”时,Agent 自动调整了下一步策略,将
preference参数设为“室内娱乐”。 - 如果是“晴朗”,它会自动改为“自然风光”。这就是 ReAct (Reasoning + Acting) 模式的威力。
- 当观察到天气是“小雨”时,Agent 自动调整了下一步策略,将
-
标准化连接 (MCP) :
travel_mcp_server.py和agent_runner.py是完全解耦的。- 如果你想增加一个“查酒店”的功能,只需在 Server 端加一个
@mcp.tool()函数,客户端代码完全不用改,Agent 会自动发现新工具并学会使用它。
🌟 进阶:如何扩展到生产级?
这个 Demo 只是起点。在 2026 年的生产环境中,你还需要考虑:
-
记忆模块 (Memory) :
- 引入
ConversationBufferMemory或向量数据库,让 Agent 记住用户之前的偏好(如“我不喜欢爬山”)。
- 引入
-
多 Agent 协作 (Multi-Agent) :
- 使用 LangGraph 编排多个 Agent。例如:一个“搜索 Agent”负责查资料,一个“财务 Agent”负责算账,一个“写作 Agent”负责生成报告。
-
人类反馈 (Human-in-the-loop) :
- 在执行敏感操作(如“支付退款”)前,暂停执行,请求人类确认。
-
部署 MCP Server:
- 将 MCP Server 部署为 SSE (Server-Sent Events) 模式,挂在 Docker 容器中,供多个 Agent 实例远程调用。
📝 总结
通过这篇实战,我们完成了从 0 到 1 的突破:
- ✅ 理解了 MCP 如何让工具开发变得像写普通函数一样简单。
- ✅ 掌握了 LangChain 构建自主 Agent 的核心流程。
- ✅ 见证了 AI 从“被动问答”到“主动规划”的质变。
下一步行动: 尝试修改 travel_mcp_server.py,接入真实的 API(如高德地图、OpenWeather),让你的 Agent 真正具备改变现实世界的能力!
欢迎关注的我的公众号《码上未来》,一起交流AI前沿技术!
扫码二维码加我微信进群聊AI