LangChain Agent 实战:工具调用与自主编排

LangChain Agent 实战的核心,是让大模型从”只会聊天”进化成”会动手做事”。当一个 LLM 被赋予一组工具(查天气、算账、检索知识库)并能在”思考—调用—观察”的循环里自主决定下一步,它就成了一个 Agent。本文从工具调用的底层机制讲起,用 LangChain 搭出第一个工具 Agent,再升级到多智能体编排,并给出生产落地的关键考量。2026 年,Agent 已经从概念走向工程落地,LangChain、LangGraph 等框架让编排变得可组合、可观测,开发者不必再从零造轮子。无论你是要做智能客服、数据分析助手还是自动化运维,这套范式都是起点。

一、什么是 Agent:LLM 驱动的自主闭环

普通对话模型是”一问一答”,而 Agent 是”目标驱动、多步执行”。它把大模型当作大脑,把工具当作手脚:模型先理解用户意图,决定要不要调用工具、调用哪个;工具返回结果后,模型再判断目标是否达成,没达成就继续循环,直到给出最终答案。这个”思考—行动—观察”的闭环,正是 Agent 与普通Completion 的本质区别。它与大模型工具调用(Function Calling)实战是同一能力的两层封装——底层是结构化函数调用,上层是循环编排。

二、工具调用是 Agent 的基石

把函数描述成 schema,交给模型决策

工具调用的本质,是把一个函数语义化成一个 JSON Schema,连同用户问题一起发给模型。模型不直接执行函数,而是返回它”想调用哪个工具、参数是什么”,真正的执行由你的运行时完成,再把结果回填给模型。下面是一个最简的天气工具描述:

# 工具调用的本质:把一个函数描述成结构化 schema 交给模型
tool_schema = {
    "name": "get_weather",
    "description": "查询某城市当前天气",
    "parameters": {
        "type": "object",
        "properties": {
            "city": {"type": "string", "description": "城市名,如 北京"}
        },
        "required": ["city"]
    }
}
# 模型返回 {"name": "get_weather", "arguments": {"city": "北京"}}
# 由你的运行时真正执行函数,并把结果回填给模型继续推理

三、用 LangChain 搭建第一个工具 Agent

LangChain 把”工具定义—提示词—执行循环”标准化了。用 @tool 装饰器声明工具,再配合 ReAct 风格的 prompt,几行代码就能跑通一个会算账的 Agent:

from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langchain.agents import create_react_agent, AgentExecutor
from langchain import hub

@tool
def calculator(expr: str) -> str:
    """计算数学表达式,例如 calculator('2**10 + 3')。"""
    try:
        return str(eval(expr, {"__builtins__": {}}, {}))
    except Exception as e:
        return f"错误: {e}"

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
prompt = hub.pull("hwchase17/react")
agent = create_react_agent(llm, [calculator], prompt)
executor = AgentExecutor(agent=agent, tools=[calculator], verbose=True)
print(executor.invoke({"input": "2 的 10 次方加 3 等于多少?"})["output"])

运行后你会看到 Agent 自己决定调用 calculator、传入 2**10 + 3、拿到结果再组织成自然语言。注意示例里用 eval 仅为演示,真实生产务必替换为安全的表达式解析库(如 ast.literal_eval 或 sympy),避免任意代码执行风险。

四、把 RAG 接入 Agent:让模型”先查后答”

单一工具还不够。把检索能力也封装成工具,Agent 就能在回答前先查知识库,大幅降低幻觉。这与大模型 RAG 进阶:混合检索与重排序是一脉相承的能力——区别在于,RAG 是”先检索再生成”的流水线,而 Agent 是”模型自行判断何时该检索”。下面把检索封装成一个可调用工具:

from langchain_core.tools import tool
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings

@tool
def search_kb(query: str) -> str:
    """检索内部知识库,用于回答产品与政策类问题。"""
    vectordb = Chroma(persist_directory="./kb", embedding=OpenAIEmbeddings())
    docs = vectordb.similarity_search(query, k=3)
    return "\n\n".join(d.page_content for d in docs)

# 把检索结果作为工具交给 Agent,模型会自行决定何时调用
executor = AgentExecutor(agent=agent, tools=[calculator, search_kb], verbose=True)

现在用户问”你们的退货政策是什么”,Agent 会主动调用 search_kb 拉取内部文档再作答,而不是凭记忆编造。关于向量库的选型,可参考Chroma 向量数据库实战Dify + Ollama 本地知识库的搭建方式。

五、多 Agent 编排:主管-子智能体模式

当任务变复杂,一个 Agent 包揽所有工具会越来越笨重。更稳妥的做法是”分而治之”:一个主管(Supervisor)负责理解意图并把子任务路由给专精的子智能体。这与MCP 与 A2A 协议选型里”多智能体协同”的思路一致——用清晰的边界取代一个大而全的黑盒。用 LangGraph 可以很直观地搭出这个结构:

# 用 langgraph 搭建主管-子智能体(简化示意)
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode

def supervisor(state):
    # 真实实现会用 LLM 做路由判断
    return "retriever" if "怎么" in state["input"] else "calculator"

builder = StateGraph(dict)
builder.add_node("supervisor", supervisor)
builder.add_node("retriever", ToolNode([search_kb]))
builder.add_node("calculator", ToolNode([calculator]))
builder.set_entry_point("supervisor")
builder.add_edge("retriever", END)
builder.add_edge("calculator", END)
graph = builder.compile()

主管只做”调度员”,子智能体各自持有专属工具集。好处是职责清晰、提示词聚焦、便于独立测试,也更容易做权限与审计隔离。实际项目中,主管的路由本身也可以用 LLM 完成(通过 function-calling 选择子智能体),并用显式的 handoff 传递上下文,避免信息在节点间丢失。复杂业务(如”先查政策、再算折扣、最后生成工单”)几乎都该走这条路径。

六、生产落地的关键考量

限步数、流式输出、对接自建推理

Demo 能跑不等于能上线。生产环境必须给 Agent 加护栏:限制最大迭代步数防止死循环、开启流式输出改善体验、用 return_intermediate_steps 留存推理链路便于排查。若推理成本敏感,可把底座模型换成自建服务:

# 生产环境:限步数 + 流式 + 错误处理
executor = AgentExecutor(
    agent=agent,
    tools=[calculator, search_kb],
    max_iterations=8,         # 防止 Agent 陷入无限循环
    handle_parsing_errors=True,
    return_intermediate_steps=True,
    verbose=True,
)
# 与 vLLM 等自建推理服务对接时,把 base_url 指向本地即可
# llm = ChatOpenAI(model="Qwen2.5-7B", base_url="http://localhost:8000/v1", api_key="EMPTY")
for chunk in executor.stream({"input": "帮我查退货政策再算算折扣"}):
    print(chunk)

关于高吞吐推理服务的部署与调优,可参考vLLM 部署实战:高吞吐 LLM 推理服务调优。把 Agent 编排层与推理层解耦,既能独立扩缩容,也方便在不同模型间做 A/B。

七、常见坑对照

现象解法
工具描述含糊模型乱调用或拒绝调用description 写清”何时用、参数含义”
无步数上限Agent 死循环烧 token设 max_iterations + 超时
直接 eval 用户输入任意代码执行漏洞用安全表达式解析或白名单
RAG 与 Agent 混淆所有问题都强行检索让模型自主判断,区分流水线
单 Agent 工具过多路由混乱、提示词膨胀拆为主管-子智能体架构
缺少中间步骤日志线上出问题无法定位留存 intermediate_steps 并告警

落地节奏建议:先用单工具跑通 ReAct 闭环,再把 RAG、计算器、API 逐步挂成工具,最后用主管-子智能体拆分复杂业务。当你把”工具”当作一等公民、把”编排”当作架构问题时,Agent 才真正从玩具变成生产力。

上一篇 API 文档生成实战:Swagger 与 SpringDoc
下一篇 Docker 镜像瘦身实战:多阶段构建与层优化