普通chain和 Agent 调用工具的核心区别

对比维度 普通 Chain 调工具 Agent 调工具
决策权 人提前写死调用时机、顺序和次数 大模型自主决定是否调用、调用哪个、调用几次
执行流程 线性固定,一次走完 「思考→调用工具→再思考」的多轮循环
适用场景 流程固定的任务(如 RAG、标准化数据处理) 复杂、不确定的任务(如自由数据分析、问题排查)

简单总结:Chain 是「你安排好工具怎么用」,Agent 是「告诉模型有这些工具,它自己按需使用」。

 普通 Chain(提示词模板):

  • 普通 Chain:写好模板占位符,手动填参数,按固定流程执行

##LCEL表达式
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI

# 定义对话模板
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个中国历史专家,回答简洁明了"),
    ("user", "{input}")
])

# StrOutputParser 把 AIMessage 转成纯字符串
parser = StrOutputParser()

# 三段式链:Prompt → LLM → Parser
chain = prompt | llm | parser

result = chain.invoke({"input": "秦始皇统一六国的顺序是什么?"})
print(result)  
#加了parser这个输出解析器之后,链的输出就是纯字符串,不需要 .content即可打印

这种方法需要把用户问题传入提示词,一并发送给大模型,做出解答。

  • 普通 Chain 的本质:固定执行流水线

Chain 是「写死步骤的执行管道」:输入 → 提示词模板 → LLM → 输出,全程流程线性固定。大模型不会自己决定「要不要调用工具、调用哪个工具、传什么参数、调用几次」,所有调用逻辑都由你提前编排好。 最典型的例子就是 RAG 检索链:检索工具是提前固定在流程里的,每次提问都会走检索步骤,模型没有选择权。

  • 普通Chain 调用工具的两种常见方式

  1. 手动串联工具步骤(硬编码) 你可以用 RunnableLambda 把工具函数插进 Chain 里,提前规定好「第几步调用工具」。大模型只负责生成工具参数,不负责决策是否调用。 比如你可以设计固定流程:先让 LLM 从用户问题里提取年份参数 → 自动调用时间计算工具 → 把计算结果塞回提示词 → LLM 整理成最终答案。整个链路是你设计好的,模型没有选择权。
  2. 单次工具调用链(bind_tools) LangChain 支持给 LLM 直接用 bind_tools 绑定工具,让模型输出工具调用参数。但它只会输出一次调用指令,不会像 Agent 一样形成「调用工具 → 拿到结果 → 再推理 → 再调用」的多轮循环,也不会自己判断是否需要多次调用工具。

 Agent调用工具:

Agent 的核心特点:

你只需要定义「有什么工具、工具能干什么」,以及「给模型设定角色和规则」;至于什么时候调用工具、用哪个工具、参数填什么,全部由大模型自主决策,Agent 框架负责执行调度

from langchain.tools import tool
from langchain_openai import ChatOpenAI
from langchain.agents import create_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import requests

# ─── 第一步:定义工具 ───
@tool
def get_weather(location: str) -> str:
    """查询指定城市的天气"""
    url = f"https://wttr.in/{location}?format=j1&lang=zh"
    data = requests.get(url).json()
    current = data['current_condition'][0]
    desc = current['weatherDesc'][0]['value']
    temp = current['temp_C']
    return f"{location}:{desc},{temp}°C"


tools = [get_weather]

# ─── 第二步:配置大模型 ───

# ─── 第三步:创建 Agent ───
# create_agent 把模型、工具、prompt 组装成 Agent
agent = create_agent(
    model=llm,
    tools=tools,
    system_prompt="你是一名智能助手,可以调用工具帮助用户解决问题。"
)
# ─── 第四步:使用 ───
res = agent.invoke({"messages": [
    {"role": "user",
     "content": "成都的天气怎么样?"
     }]})
print(res['messages'][-1].content)
print(res)

举例:为什么在这个Agent的提示词中没有用户输入的占位符?沙箱函数的入参code也找不到?

import traceback
from io import StringIO
from contextlib import redirect_stdout
from langchain.tools import tool
# ============================================================
# 定义沙箱的"白名单"——只有这些库和数据可以被代码访问
# ============================================================
#环境初始化——初始化上下文(传入必要的变量:三张表)和权限控制(限制沙箱能访问的模块/函数)
SANDBOX_GLOBALS = {
    # 数据:Agent 可以分析这三张表
    "employees_df": employees_df,
    "products_df":  products_df,
    "orders_df":    orders_df,
    # 工具库:Agent 可以用这些库做分析和画图
    "pd":  pd,       # pandas — 数据处理
    "plt": plt,      # matplotlib — 基础绑图
    "sns": sns,      # seaborn — 统计可视化
    "np":  np,       # numpy — 数值计算
}


##定义函数
@tool
def execute_python_code(code: str) -> str:
    """
    执行 Python 代码进行数据分析和可视化。

    可用变量:
      - employees_df: 员工表 DataFrame(字段:id, name, department, salary, hire_date)
      - products_df:  产品表 DataFrame(字段:id, product_name, category, price, stock)
      - orders_df:    订单表 DataFrame(字段:id, employee_id, product_id, quantity, order_date)
      - pd:   pandas 库
      - plt:  matplotlib.pyplot
      - sns:  seaborn
      - np:   numpy

    使用示例:
      # 统计各部门平均薪资
      result = employees_df.groupby('department')['salary'].mean()
      print(result)

      # 画柱状图
      plt.figure(figsize=(10, 6))
      employees_df.groupby('department')['salary'].mean().plot(kind='bar')
      plt.title('各部门平均薪资')
      plt.show()
    """
   
    # 准备隔离的执行环境
    # globals_dict 提供白名单变量,locals_dict 收集执行过程中产生的新变量
    exec_globals = dict(SANDBOX_GLOBALS)
    exec_locals = {} 

    output_buffer = StringIO()#output_buffer是io.StringIO的实例

    ##代码执行器——代码执行
    try:
        with redirect_stdout(output_buffer):
            exec(code, exec_globals, exec_locals)
        result = output_buffer.getvalue()#output_buffer.getvalue()会返回缓冲区中存储的所有文本内容。
        # 如果代码没有 print 任何东西,给个默认提示
        if not result.strip():
            result = "✅ 代码执行成功(无文本输出,可能已生成图表)"
        return f"执行成功:\n{result}"
    
    #安全防护——异常捕获
    except Exception as e:
        # 出错时返回完整的错误堆栈,方便 Agent 自我修正
        error_detail = traceback.format_exc()
        return f"❌ 执行出错:{e}\n\n{error_detail}"

print("✅ Python 代码执行沙箱创建成功")



##搭建可视化 Agent
from langchain.agents import create_agent
# ============================================================
# 定义可视化 Agent 的 System Prompt
# ============================================================
VISUALIZATION_PROMPT = """你是一名资深数据分析师,精通 Python、Pandas 和 Matplotlib 数据可视化。
## 可用数据
1. employees_df — 员工表(字段:id, name, department, salary, hire_date)
2. products_df  — 产品表(字段:id, product_name, category, price, stock)
3. orders_df    — 订单表(字段:id, employee_id, product_id, quantity, order_date)

## 工作流程
1. 理解用户的分析需求
2. 用 execute_python_code 工具编写并执行 Python 代码
3. 先做数据探索(head、describe、info),再做深入分析
4. 用中文解释分析结果,给出业务洞察

## 代码规范
- 绑图前设置中文字体:plt.rcParams['font.sans-serif'] = ['SimHei', 'PingFang SC', 'DejaVu Sans']
- 设置 plt.rcParams['axes.unicode_minus'] = False
- 图表尺寸统一用 plt.figure(figsize=(10, 6))
- 必须添加标题、坐标轴标签,让图表自解释
- 用 print() 输出关键统计量,不要只画图不说话
- 图表标题用英文(避免渲染问题),但用中文向用户解释结果

## 注意事项
- 每次只执行一段完整的代码,不要拆成多段
- 先探索数据结构,再做分析——不要上来就画图
- 结果要有业务洞察,不只是"最大值是 XXX"
"""
# ============================================================
# 组装可视化 Agent(langchain 1.3.1 写法)
# ============================================================
#将工具函数存入列表,供LangChain Agent调用
viz_tools = [execute_python_code] 

# 同样用 create_agent 一行搞定,和 SQL Agent 的创建方式完全一致
visualization_agent = create_agent(
    model=llm,
    tools=viz_tools,
    system_prompt=VISUALIZATION_PROMPT,
)

print("数据可视化 Agent 创建完成")

一、核心结论

code 参数不是手动传入的,也不需要在提示词里写占位符 —— 它是由大模型(LLM)在推理过程中自动生成的,再由 Agent 框架自动传给 execute_python_code 函数。

二、逐层拆解:code 到底从哪来

1. @tool 装饰器做了什么?

你写的 @tool 不只是个装饰,它会自动完成两件事:

  • execute_python_code 函数包装成 LangChain 标准的 Tool 对象
  • 自动提取函数名、参数列表、docstring(函数说明),生成一段「工具说明书」

这段说明书会和你的系统提示词一起,在调用时塞给大模型。相当于告诉 LLM:

你现在有一个工具叫 execute_python_code,它接收一个字符串类型的参数 code,作用是执行 Python 数据分析代码;可用的变量有 employees_df、pd、plt 等。

大模型看到这份说明书,就知道调用这个工具时,必须生成一个叫 code 的字符串参数

2. 大模型如何生成 code?

当你调用 Agent 时,比如:

visualization_agent.invoke("帮我统计各部门的平均薪资")

LangChain 会在后台把这些内容一起发给 LLM:

  • 你的 VISUALIZATION_PROMPT 系统提示(角色、数据字段、工作流程、代码规范)
  • 用户的问题:"帮我统计各部门的平均薪资"
  • 工具列表的「说明书」(包含 execute_python_code 的参数要求)

LLM 会按照 ReAct 思考逻辑推理:

  1. 用户要统计各部门平均薪资
  2. 我需要用 execute_python_code 工具来执行 pandas 代码
  3. 已知 employees_df 有 department 和 salary 字段
  4. 生成对应代码:
    result = employees_df.groupby('department')['salary'].mean()
    print(result)
    
  5. 按照工具调用格式,把这段代码作为 code 参数的值,输出给 Agent 框架

简单说:code 是 LLM 根据用户问题 + 数据字段 + 工具规则,自己「写」出来的

三、完整调用链路:一次提问的完整旅程

我把你这段代码的完整执行流程串起来,你就彻底懂了:

  1. 用户发起提问

    visualization_agent.invoke("分析各部门平均薪资并画柱状图")
    
  2. Agent 组装上下文,发给 LLM 框架自动把「系统提示 + 用户问题 + 工具说明」拼成对话,发给大模型。

    注意:你的系统提示词里不需要写 {user_input} 占位符 —— 用户问题是作为「用户消息」单独传入的,系统提示是固定的角色规则,二者由框架自动拼接,不需要你手动写模板变量。

  3. LLM 推理并生成工具调用 LLM 输出结构化的工具调用指令,大概长这样(不同模型格式略有区别):

    工具名称:execute_python_code
    工具参数:{
        "code": "plt.figure(figsize=(10, 6))\nemployees_df.groupby('department')['salary'].mean().plot(kind='bar')\n..."
    }
    
  4. Agent 框架执行工具 框架解析 LLM 的输出,自动调用:

    execute_python_code(code="LLM生成的那段代码字符串")
    

    函数内部用 exec() 在沙箱环境里运行代码,捕获输出和异常,返回结果。

  5. 结果回传 LLM,生成最终回答 工具执行结果(比如统计数值、图表生成成功提示)会被框架塞回对话里,再发给 LLM。 LLM 基于执行结果,用中文整理成业务洞察,返回给你。


四、补充:为什么提示词里没有用户输入占位符?

这是 LangChain create_agent 封装后的结果:

  • 底层的 Agent 模板里其实已经内置了用户输入、工具描述、思考过程的占位符
  • 你写的 VISUALIZATION_PROMPT 只是系统角色提示(System Prompt),负责设定身份、规则、数据说明
  • 用户的问题在你调用 .invoke() 时传入,框架会自动把它放到对话的「用户消息」位置,不需要你手动写 {question}

如果你用的是普通的 PromptTemplate + Chain,才需要自己写占位符;而 Agent 是更高层的封装,把输入输出、工具调度、多轮思考都帮你做了。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐