100 行代码搞一个带 MCP 工具的 AI Agent:LangGraph + DeepSeek V4 实战
搞了两天,我把一个带 MCP 工具的 AI Agent 用 100 行代码跑起来了。
用的 LangGraph 做流程编排,DeepSeek V4 做推理,自定义 MCP Server 给 Agent 提供外部工具能力。效果比我预想的好——Agent 能自己决定什么时候调工具、调哪个工具、怎么处理工具返回的结果。
这篇文章就把核心代码和思路拆开讲清楚。
先说清楚我们要干嘛
目标很简单:搭一个 Agent,能根据用户的问题,自主决定要不要调用外部工具。
比如用户问"帮我查一下北京的天气然后发邮件给老板",Agent 应该能:
- 理解意图 → 拆成"查天气"和"发邮件"两步
- 调天气工具 → 拿到数据
- 调用邮件工具 → 发送结果
- 回复用户"搞定了"
不用手写 if-else 判断流程,全靠 LLM 自己推理决策。
先把环境搭好
需要装的东西不多:
pip install langgraph langchain-core langchain-deepseek mcp
主要依赖:
- LangGraph — 管理 Agent 的状态机和执行流程
- langchain-deepseek — DeepSeek V4 的 LangChain 集成
- mcp — MCP 协议的 Python SDK,给 Agent 挂工具用
核心代码:一个能调工具的 Agent
整个 Agent 的核心就三个部分:
1. 定义一个 MCP Tool
from mcp import Tool
def get_weather(city: str) -> str:
"""查天气(模拟)"""
return f"{city},25°C,晴"
weather_tool = Tool(
name="get_weather",
description="查询指定城市的天气",
input_schema={
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名"}
},
"required": ["city"]
},
handler=get_weather
)
MCP 的 Tool 定义很干净——一个 name、一个 description、一个 input_schema(JSON Schema 格式)、一个 handler 函数。Agent 看到 description 就知道这个工具能干嘛,看到 input_schema 就知道怎么调。
2. 用 LangGraph 搭 Agent 流程
from langgraph.graph import StateGraph, END
from typing import TypedDict, List, Optional
from langchain_deepseek import ChatDeepSeek
class AgentState(TypedDict):
messages: List
tool_calls: Optional[List]
llm = ChatDeepSeek(model="deepseek-v4", temperature=0)
def call_llm(state: AgentState):
"""LLM 决定下一步:直接回答 or 调工具"""
response = llm.invoke(state["messages"])
return {"messages": [response]}
def execute_tools(state: AgentState):
"""执行 LLM 要求调用的工具"""
last_msg = state["messages"][-1]
results = []
for tc in last_msg.tool_calls:
tool = tools[tc["name"]]
result = tool.handler(**tc["args"])
results.append(result)
return {"messages": results, "tool_calls": None}
### 构建图
graph = StateGraph(AgentState)
graph.add_node("llm", call_llm)
graph.add_node("tools", execute_tools)
graph.add_conditional_edges(
"llm",
lambda s: "tools" if s.messages[-1].tool_calls else END,
{"tools": "tools", END: END}
)
graph.add_edge("tools", "llm")
graph.set_entry_point("llm")
agent = graph.compile()
核心逻辑就一个循环:LLM 决定 → 如果要调工具就去执行 → 执行完回来再问 LLM → 直到 LLM 觉得够了直接回答。
3. 跑起来
result = agent.invoke({
"messages": [{"role": "user", "content": "北京的天气怎么样?"}]
})
print(result["messages"][-1].content)
100 行不到,一个能自主调工具的 Agent 就跑起来了。
完整代码
全文加起来 90 多行,核心逻辑就上面那三块。完整代码我放 GitHub 了:[链接]
流程总结:
用户提问 → LLM 推理 → 需要工具吗?
├─ 需要 → 执行 MCP Tool → 结果送回 LLM → 再推理
└─ 不需要 → 直接回答 → 结束
踩坑记录
工具返回太长 LLM 会"忘"
MCP Tool 返回的数据如果太大,LLM 的上下文会被冲淡,导致后续推理质量下降。解决方案:工具只返回摘要,详细信息走引用。
Tool 命名要够直白
MCP 的 Tool discovery 靠的是 description 字段。description 写得模糊,LLM 可能不调或者调错。比如 get_weather 的 description 写"查天气"比"获取气象数据"好得多——LLM 更倾向用简单的词匹配意图。
循环陷阱
如果 LLM 持续判断"需要调工具",会陷入无限循环。要设最大迭代次数:
agent = graph.compile(max_iterations=5) # 最多调5轮工具
进阶还能怎么玩
- 多个 Tool 并行调用 — LLM 一次要求调多个工具时,可以并行执行再汇总
- Tool 之间传数据 — 第一个工具的输出作为第二个工具的输入(MCP 的 context 机制)
- Human-in-the-loop — 关键操作前让 Agent 先问用户确认
写在最后
100 行代码,一个带 MCP 工具的 AI Agent。MCP 协议让工具接入变得标准化,LangGraph 让流程编排变得声明式——两者搭在一起,效果比我想象的好得多。
下一步我准备往生产环境方向走:加缓存、加错误重试、加日志追踪。有进展再写文章分享。
更多推荐



所有评论(0)