LangChain中定义好的工具,会被LangChain智能体框架和大模型识别,自动会被LangChain调用,并把调用的送给大模型,由大模型做整合后回复给智能体,不需要大模型应用程序员根据大模型的输出去显式的调用定义好的tool

这正是 LangChain 和 "原生 OpenAI API 手写工具调用" 的本质区别LangChain 把 "工具调用的全流程自动化" 做成了框架内置能力程序员只需要做两件事:

  1. 用标准方式定义工具(@tool装饰器等)
  2. 把工具列表传给 AgentExecutor

剩下的所有环节:工具定义转模型格式、解析模型的工具调用指令、执行本地函数、把结果返回给模型、循环判断是否需要继续调用、直到生成最终回答,全部由 LangChain 框架自动完成,不需要你写任何if model_output.has_tool_call() then call_tool()这样的显式判断和调用代码


一、先看最直观的对比:原生 API vs LangChain

1. 原生 OpenAI API 手写工具调用(需要你显式写调用逻辑)

python

运行

# 原生API:你必须手动处理整个工具调用循环
import openai

client = openai.OpenAI()
tools = [{"type": "function", "function": {"name": "add", "parameters": {...}}}]

def add(a, b): return a + b

# 你必须手写这个循环
def chat(query):
    messages = [{"role": "user", "content": query}]
    while True:
        # 1. 调用模型
        res = client.chat.completions.create(model="gpt-3.5-turbo", messages=messages, tools=tools)
        msg = res.choices[0].message
        messages.append(msg)
        
        # 2. 你必须显式判断:模型有没有要调用工具
        if not msg.tool_calls:
            return msg.content  # 没有就返回
        
        # 3. 你必须显式解析工具调用参数
        for tool_call in msg.tool_calls:
            func_name = tool_call.function.name
            args = eval(tool_call.function.arguments)
            
            # 4. 你必须显式调用对应的本地函数
            if func_name == "add":
                result = add(**args)
            
            # 5. 你必须显式把结果拼回消息列表
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": str(result)
            })

所有标粗的步骤,都是你必须手写的胶水代码,每加一个工具就要改一次判断逻辑。

2. LangChain Agent(完全自动,无显式调用代码)

python

运行

from langchain.tools import tool
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate

# 1. 你只需要定义工具
@tool
def add(a: int, b: int) -> int:
    """计算两个整数的和"""
    return a + b

# 2. 你只需要初始化框架
llm = ChatOpenAI(model="gpt-3.5-turbo")
tools = [add]
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是会用工具的助手"),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}")
])
agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools)

# 3. 你只需要一行调用,剩下的全自动
result = agent_executor.invoke({"input": "123+456等于多少"})
print(result["output"])

没有任何if判断、没有任何工具调用解析、没有任何结果拼接代码,全部由框架自动完成。


二、LangChain 自动化工具调用的完整内部流程

我把你说的这句话,拆解成框架内部精确的执行步骤,你就能清楚看到每一步是谁在做:

plaintext

用户输入 → AgentExecutor(框架)启动循环
    ↓
1. 框架自动:把你定义的所有@tool工具,转换成大模型能理解的JSON Schema格式
    ↓
2. 框架自动:把用户输入+历史对话+工具定义,打包成标准消息发给大模型
    ↓
3. 大模型决策:要不要调用工具?调用哪个?传什么参数?
    ↓
4. 框架自动:解析大模型返回的工具调用指令(不需要你写解析代码)
    ↓
5. 框架自动:根据工具名,找到你定义的本地Python函数,自动传入参数执行
    ↓
6. 框架自动:把工具执行结果,包装成标准的ToolMessage,追加到对话历史
    ↓
7. 框架自动:把包含工具结果的完整历史,再次发给大模型
    ↓
8. 大模型判断:信息足够了吗?
    → 不够:回到步骤4,继续自动调用下一个工具
    → 足够:生成最终回答
    ↓
9. 框架自动:终止循环,把最终回答返回给你

核心结论

  • 大模型只做决策(要不要调、调哪个、传什么)
  • LangChain 框架只做执行(解析、调用、拼接、循环)
  • 你只做定义(工具是什么、提示词是什么)

三、为什么 LangChain 能做到这一点?

因为它把工具调用的通用逻辑全部抽象成了框架内置组件:

  1. @tool装饰器:统一了工具的定义标准,自动生成name/description/args_schema
  2. create_tool_calling_agent:自动实现了 "工具定义转模型格式" 和 "模型输出转工具调用指令" 的双向转换
  3. AgentExecutor:内置了无限循环调度器,自动处理 "调用→返回→再调用" 的整个流程,直到模型生成最终回答
  4. 标准化消息体系AIMessagetool_calls字段、ToolMessage专门存工具结果,框架内部所有组件都能统一识别

四、补充:你仍然可以干预的地方(可选)

虽然基础调用完全自动,但 LangChain 也提供了钩子让你在需要时干预流程,这些都是可选的,不影响核心自动化:

  • 自定义工具执行前 / 后的回调函数(日志、监控)
  • 设置最大调用次数(max_iterations),防止无限循环
  • 自定义错误处理(工具报错时怎么返回给模型)
  • 强制终止循环(return_intermediate_steps

五、一句话总结

你说的这句话,就是 LangChain Agent 框架存在的核心意义之一:它把 "工具调用" 从一个需要程序员手动编码的复杂流程,变成了一个只需要声明式定义的配置项。你只需要告诉框架 "我有这些工具可用",剩下的所有脏活累活,框架全帮你干了。

需要我给你加一个带日志的完整示例,打印出框架内部每一步自动执行的细节,让你直观看到工具是怎么被自动调用的吗?

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐