一、你的大模型,其实是个"残疾人"

你肯定遇到过:

👤:“今天北京天气怎么样?” 🤖:“抱歉,我无法获取实时信息,我的知识截止到……”

大模型很聪明,但它有个致命短板——没有手脚。它会想,不会做。不能上网查资料,不会调 API,连 3 × 5 都算不利索(是的,LLM 做算术是靠"背"的,不是"算"的)。

那怎么办?给它装上手脚。

这篇文章就带你干一件事:给 LLM 装上两个工具,让它变成一个能自己干活、自己决策的 Agent。不光写代码,还拆开看它里面到底是怎么转的。

读完这篇文章,当别人跟你聊 Agent 的时候,你脑子里浮现的不是什么高大上的概念,而是一个简单的 while 循环


二、Agent 到底是什么?

一句话:

Agent = LLM + 工具 + 决策循环

画成图长这样:

图1:Agent 架构示意——LLM + 工具 + 决策循环

图1:Agent 架构示意——LLM + 工具 + 决策循环

三个角色的分工:

  • LLM(大脑):负责推理——“现在该干什么?需要哪个工具?传什么参数?”
  • 工具(手脚):能被调用的 Python 函数——查天气、算数学、搜网页,什么都能包
  • 决策循环(指挥官):一个 while 循环——“LLM 说要调工具?好,调完把结果塞回去,继续问它下一步”

跟工作流最大的区别:工作流是固定剧本(A→B→C),Agent 是即兴发挥——它根据中间结果自己决定下一步走哪条路。

什么时候用 Agent?

  • ✅ 多步推理:先查 A,根据结果决定查 B 还是 C
  • ✅ 需要外部信息:联网搜索、数据库查询、API 调用
  • ✅ 组合工具:“搜索 + 计算 + 发邮件”
  • ❌ 单轮问答:直接用 LLM,别上 Agent 杀鸡用牛刀
  • ❌ 固定流程:用普通 if/else 比 Agent 更快更可靠

三、先别急着用框架——30 行代码手写一个 Agent

这一章是全文最重要的部分。跳过框架,直面 Agent 的本质。

在引入 LangChain 之前,先用纯 Python 写一个原生 Agent。30 行代码,让你看到 Agent 底层就是一个 while 循环

3.1 准备工作:一个 LLM + 两个工具

from openai import OpenAI# 初始化 LLM(用 DeepSeek,便宜够用;换成 OpenAI/通义千问只改 base_url)client = OpenAI(    api_key="your-api-key",    base_url="https://api.deepseek.com")# 定义两个工具函数# ⚠️ 注意:docstring 是给 LLM 看的"使用说明书",写烂了它就不会用def search_weather(city: str) -> str:    """查询指定城市的实时天气。当用户询问天气时使用此工具。        Args:        city: 城市名称,如 北京、上海    """    weather_db = {        "北京": "小雨 22°C,东北风 3 级",        "上海": "多云 28°C,东南风 2 级",        "深圳": "雷阵雨 30°C,湿度 85%"    }    return weather_db.get(city, f"未找到{city}的天气信息")def calculate(expression: str) -> str:    """执行数学计算。当用户需要算数时使用此工具。        Args:        expression: 数学表达式,如 '3 + 5 * 2'、'100 * (20 + 30)'    """    try:        # eval 仅演示用,生产环境请用更安全的方式        result = eval(expression, {"__builtins__": {}}, {})        return f"计算结果: {result}"    except Exception as e:        return f"计算出错: {e}"

3.2 核心:30 行 Agent 循环

# 工具的 JSON Schema —— 告诉 LLM 每个工具叫什么、有什么参数TOOLS = [    {        "type": "function",        "function": {            "name": "search_weather",            "description": "查询指定城市的实时天气。当用户询问天气时使用。",            "parameters": {                "type": "object",                "properties": {                    "city": {"type": "string", "description": "城市名称,如 北京、上海"}                },                "required": ["city"]            }        }    },    {        "type": "function",        "function": {            "name": "calculate",            "description": "执行数学计算。当用户需要算数时使用。",            "parameters": {                "type": "object",                "properties": {                    "expression": {"type": "string", "description": "数学表达式,如 3+5*2"}                },                "required": ["expression"]            }        }    }]# 工具分发器:根据工具名找到对应函数并执行def execute_tool(name: str, args: dict) -> str:    tools = {"search_weather": search_weather, "calculate": calculate}    return tools[name](**args)# 🎯 Agent 的核心:一个 while 循环def run_agent(user_query: str, max_steps: int = 10) -> str:    """原生 Agent —— 揭示 Agent 就是 LLM + 工具 + while 循环"""        # 系统提示:告诉 LLM 它是谁、有什么工具、怎么用    system_prompt = """你是一个能调用工具的智能助手。你有以下工具可用:- search_weather(city):查询指定城市的实时天气- calculate(expression):执行数学计算使用规则:1. 分析用户需求,判断是否需要调用工具2. 如果需要,调用对应工具获取信息3. 根据工具返回的结果,给出最终答案4. 如果不需要工具,直接回答用户问题"""        # messages 是 Agent 的"记忆"——每一轮对话都追加进去    messages = [        {"role": "system", "content": system_prompt},        {"role": "user", "content": user_query}    ]        for step in range(max_steps):        # ① 调用 LLM:把所有对话历史 + 工具定义发给它        response = client.chat.completions.create(            model="deepseek-chat",            messages=messages,            tools=TOOLS        )                msg = response.choices[0].message        messages.append(msg)  # LLM 的回复也存进记忆                # ② 如果 LLM 直接回复(没调工具),循环结束        if not msg.tool_calls:            return msg.content                # ③ 执行 LLM 要求的工具调用,结果追加回 messages        for tool_call in msg.tool_calls:            tool_name = tool_call.function.name            tool_args = eval(tool_call.function.arguments)  # JSON → dict                        print(f"  🔧 调用工具: {tool_name}({tool_args})")            result = execute_tool(tool_name, tool_args)            print(f"  📋 工具返回: {result}")                        messages.append({                "role": "tool",                "tool_call_id": tool_call.id,                "content": result            })        return "达到最大步数限制,任务未完成"# ── 跑一个试试 ──if __name__ == "__main__":    result = run_agent("北京天气怎么样?如果下雨提醒我带伞。")    print(f"\n💬 Agent 最终回答:\n{result}")

3.3 跑起来看看 Agent 的"思考过程"

用户: 北京天气怎么样?如果下雨提醒我带伞。  🔧 调用工具: search_weather({'city': '北京'})  📋 工具返回: 小雨 22°C,东北风 3 级💬 Agent 最终回答:北京现在是 小雨,气温 22°C,东北风 3 级。今天有雨,出门记得带伞!🌂

发生了什么?Agent 做了两件事:

  1. 推理——“用户想知道天气,我得调 search_weather”
  2. 行动——调了工具,看到结果是"小雨",判断需要提醒带伞

这就是 ReAct(Reasoning + Acting)——推理和行动交替进行,直到任务完成。

图2:ReAct 推理流程——思考→行动→观察

图2:ReAct 推理流程——思考→行动→观察

什么时候自己手写 Agent?

  • ✅ 学习原理——没有比手写一遍更好的理解方式
  • ✅ 极简场景——只有一两个工具,不想引入框架依赖
  • ❌ 工具多、需要错误重试、需要流式输出——这时该上框架了

四、三个核心机制——从代码里提炼原理

上面 30 行代码,藏了 Agent 的三个核心机制。

4.1 决策循环(Agent Loop)

本质就是这一段:

for step in range(max_steps):    response = llm.invoke(messages)     # 调 LLM    if not response.tool_calls:         # LLM 不调工具了?        return response.content         # → 结束    for tc in response.tool_calls:      # 否则        result = execute(tc)            # → 执行工具        messages.append(result)         # → 结果追加到对话

每次循环 = 一轮 Thought → Action → Observationmax_steps 是安全阀——防止 LLM 在一个问题上反复横跳(比如工具返回格式不对,LLM 反复重试同一个工具)。

4.2 工具调用(Tool Calling)

关键认知:LLM 不执行工具,它只"说"要调哪个、传什么参数。

真正干活的是 execute_tool 函数——它根据 LLM 的"指令"去调对应的 Python 函数。LLM 的角色更像一个指挥官:它看战场形势,下令"炮兵轰左边",但炮弹不是它打的。

工具描述决定一切。LLM 完全靠 descriptionparameters 来判断什么时候调用哪个工具。你写 description: "Does stuff.",它就乱调。你写 description: "查询指定城市的实时天气,当用户询问天气时使用",它就能精准匹配。

4.3 上下文管理(Messages)

messages 列表就是 Agent 的"记忆":

messages = [    {"role": "system", "content": "你有这些工具:weather, calculate..."},    {"role": "user", "content": "北京天气?"},    {"role": "assistant", "tool_calls": [search_weather("北京")]},  # LLM 说:我要调工具    {"role": "tool", "content": "小雨 22°C"},                       # 工具返回结果    {"role": "assistant", "content": "北京小雨 22°C,带伞!"}       # LLM 综合判断后回答]

规则很简单:只追加,不删除,不重置。LLM 每轮能看到完整的"我做过什么",才能做出正确的下一步决策。

局限也很明显:对话太长 messages 会撑爆上下文窗口。LangChain 的 SummarizationMiddleware 就是为这个设计的——长对话自动压缩历史。

图3:三大核心机制——循环、工具调用、上下文

图3:三大核心机制——循环、工具调用、上下文


五、用 LangChain 重写——感受框架帮你干了什么

手写版 30 行。现在用 LangChain 的 create_agent 重写,看看框架帮你省掉了什么。

5.1 三行代码搞定

from langchain.agents import create_agentfrom langchain.tools import toolfrom langchain_openai import ChatOpenAI# 定义工具 —— @tool 装饰器自动把函数转成 LangChain Tool@tooldef search_weather(city: str) -> str:    """查询指定城市的实时天气。当用户询问天气时使用。"""    weather_db = {"北京": "小雨 22°C", "上海": "多云 28°C", "深圳": "雷阵雨 30°C"}    return weather_db.get(city, f"未找到{city}的天气")@tooldef calculate(expression: str) -> str:    """执行数学计算。当用户需要算数时使用。"""    result = eval(expression, {"__builtins__": {}}, {})    return f"计算结果: {result}"# 一行建 Agentagent = create_agent(    model=ChatOpenAI(model="deepseek-chat", temperature=0),    tools=[search_weather, calculate],    system_prompt="你是一个有用的助手,帮用户查天气、算数学。")# 调用result = agent.invoke({    "messages": [{"role": "user", "content": "北京天气怎么样?如果下雨提醒我带伞"}]})print(result["messages"][-1].content)

5.2 LangChain 帮你干了什么

对着第三章的手写版,看看哪些脏活不用自己干了:

手写版你要做的LangChain 自动处理
手动写 JSON Schema 描述工具@tool 装饰器自动提取函数签名和 docstring 生成 schema
手动写 while 循环内置 Agent Loop(底层是 LangGraph 图引擎)
手动解析 tool_calls 并 dispatch自动解析、分发、执行
手动构造 {"role": "tool", ...} 消息自动追加 ToolMessage 到 messages
手动管理 max_steps 防死循环max_iterations 参数(默认 25)
无错误处理ModelRetryMiddleware + ToolRetryMiddleware 自动重试
看不到内部过程agent.stream(stream_mode="updates") 一行看每一步

说白了,LangChain 没有发明什么新技术——它只是把 while 循环、工具分发、消息管理这些"脏活累活"帮你封装了。核心机制完全一样

什么时候用 LangChain Agent?

  • ✅ 快速原型——不想从零搭循环和错误处理
  • ✅ 工具较多——手动 dispatch 太麻烦
  • ✅ 需要中间件——PII 检测、上下文压缩、人工审批节点
  • ❌ 学习原理阶段——先用原生手写搞懂本质

六、create_agent 底层长什么样?

如果你好奇 create_agent 一行代码背后到底发生了什么,这一节给你拆开看。

6.1 底层是 LangGraph 的状态图

create_agent 不是一个黑盒。它底层构建了一个 LangGraph 有向图:

图4:LangGraph 状态图——create_agent 底层架构

图4:LangGraph 状态图——create_agent 底层架构

两个核心节点:

  • model 节点:把 messages(含工具结果)发给 LLM,等它回复
  • tools 节点:解析 LLM 的 tool_calls,执行对应的 Python 函数,结果追加回 messages

一条条件边(router):检查 LLM 回复里有没有 tool_calls。有 → 跳到 tools 节点;没有 → 结束,返回最终答案。

6.2 旧版 vs 新版

如果你看过一些老教程,可能会见到 AgentExecutor + create_tool_calling_agent 的写法。那是旧 API。

AgentExecutor(旧)create_agent(新)
循环方式Python while 循环LangGraph 图节点跳转
状态管理agent_scratchpad 变量拼进 promptAgentState (TypedDict),显式管理
流式输出困难(需要 hack)原生 stream() 支持
可扩展性有限中间件系统(10+ 内置中间件)
建议❌ 新项目别用了✅ 唯一推荐方式

如果你现在开始学 Agent,直接上 create_agent,别在旧 API 上浪费时间。


七、别踩这些坑(附学习路线)

三个最常见的坑

1. 工具描述写太烂

LLM 全靠 docstring 判断什么时候调用哪个工具。你写 """Does stuff.""",它永远不知道什么时候调。正确的写法:

# ❌ 模糊@tooldef f(x: str) -> str:    """Do something."""    ...# ✅ 清晰:名称 + 用途 + 参数说明@tooldef search_web(query: str) -> str:    """搜索互联网获取最新信息。    当需要实时数据或 LLM 训练数据中不包含的信息时使用。        Args:        query: 2-10 个词的搜索关键词    """    ...

2. 忘设 max_iterations

Agent 有时会在同一个工具上反复调用(比如结果格式不对,LLM 以为没调成功)。不设上限它就一直循环。create_agent 默认 25 步,大多数场景够用,复杂任务可以调大,但一定设个值。

3. 工具返回太多数据

工具一次返回 5000 行的 JSON,messages 瞬间暴涨。超出上下文窗口后 LLM 会"失忆"——前面的关键信息被截断了。解决办法:工具内部做摘要,或使用 SummarizationMiddleware

推荐学习路线

手写原生 Agent(30 行)           ← 搞懂本质:while 循环 + tool calling        ↓用 create_agent 重写              ← 体会框架帮你省了什么        ↓加更多工具 + 看 stream 输出        ← 理解每一步在发生什么        ↓读 langgraph/prebuilt/tool_node.py ← 想深入就去看源码

假如你从2026年开始学大模型,按这个步骤走准能稳步进阶。

接下来告诉你一条最快的邪修路线,

3个月即可成为模型大师,薪资直接起飞。
img

阶段1:大模型基础

img

阶段2:RAG应用开发工程

img

阶段3:大模型Agent应用架构

img

阶段4:大模型微调与私有化部署

img

配套文档资源+全套AI 大模型 学习资料,朋友们如果需要可以微信扫描下方二维码免费领取【保证100%免费】👇👇
在这里插入图片描述
img

img

img

img
img

配套文档资源+全套AI 大模型 学习资料,朋友们如果需要可以微信扫描下方二维码免费领取【保证100%免费】👇👇

在这里插入图片描述

更多推荐