LangChain的提示模板template中的{tool_names}和{agent_scratchpad}

与一般程序不同:LangChain的提示模板template中的{tool_names}和{agent_scratchpad}变量并不需显示指定,它们会自动被填充。

LangChain的提示模板template如下示例:

python 复制代码
from langchain.prompts import PromptTemplate
template = ('''
    '尽你所能用中文回答以下问题。如果能力不够你可以使用以下工具:\n\n'
    '{tools}\n\n
    Use the following format:\n\n'
    'Question: the input question you must answer\n'
    'Thought: you should always think about what to do\n'
    'Action: the action to take, should be one of [{tool_names}]\n'
    'Action Input: the input to the action\n'
    'Observation: the result of the action\n'
    '... (this Thought/Action/Action Input/Observation can repeat N times)\n'
    'Thought: I now know the final answer\n'
    'Final Answer: the final answer to the original input question\n\n'
    'Begin!\n\n'
    'Question: {input}\n'
    'Thought:{agent_scratchpad}' 
    '''
)

其中的{tool_names}和{agent_scratchpad}变量在程序中并没有被取代,而其它变量(如,{tools})则被明确替换(如,tools = load_tools(["serpapi", "llm-math"], llm=llm))。

本文分析这种情况。

一、template中{tool_names}

template中{tool_names} 是一个占位符,用于在提示模板中动态插入工具名称列表。在 ReAct 模式中,{tool_names} 用于指示大模型在执行过程中可以选择的工具列表。

在 LangChain 中,{tool_names} 的自动填充逻辑如下:
{tool_names} 不是你手动赋值的,而是 LangChain 的 ReAct Agent(create_react_agent + AgentExecutor)在执行阶段(invoke 调用时),自动从传入的 tools 列表中提取工具名称并动态代入 ------ 这是 LangChain 为 ReAct 范式 Agent 内置的变量绑定逻辑。

步骤1:加载工具 → 确定工具名称列表(数据源)
python 复制代码
tools = load_tools(["serpapi", "llm-math"], llm=llm)
  • 这一步是 {tool_names} 变量值的数据源load_tools 加载了两个工具,对应的核心信息如下(LangChain 内部存储):

    传入的工具标识 LangChain 内部对应的工具名 工具功能
    "serpapi" "Search"(或保留"serpapi") 联网搜索
    "llm-math" "Calculator"(或保留"llm-math") 数学计算
  • 这些工具名称会被 LangChain 提取,作为 {tool_names} 的最终代入值。

步骤2:创建 ReAct Agent → 绑定工具与模板变量的映射(关键铺垫)
python 复制代码
agent = create_react_agent(llm, tools, prompt)
  • create_react_agent 是 LangChain 为 ReAct 范式(思考-行动-观察)封装的核心函数,它内置了对 ReAct 模板变量的处理逻辑
    • 会自动识别模板中的 {tools}{tool_names}{agent_scratchpad} 等 ReAct 范式的固定变量;
    • 会建立「模板变量 ↔ 工具信息」的映射:
      • {tools} → 映射为工具的详细描述(名称+功能+使用方式);
      • {tool_names} → 映射为工具名称的列表(如 ["Search", "Calculator"]["serpapi", "llm-math"]);
      • {agent_scratchpad} → 映射为 Agent 思考过程的中间结果(后续填充)。
  • 这一步只是"绑定映射关系",还没实际代入值(因为还没有具体的执行上下文)。
步骤3:AgentExecutor 执行(invoke)→ 动态代入所有变量(最终代入)
python 复制代码
agent_executor.invoke({"input": "你的问题..."})

这是 {tool_names}实际代入的核心时机:

  1. 当调用 invoke 时,AgentExecutor 会先收集所有需要填充的变量:
    • {input}:填充为你传入的用户问题("目前市场上玫瑰花的一般进货价格是多少?...");
    • {tool_names}:从 tools 列表中提取工具名称,填充为类似 ["serpapi", "llm-math"]["Search", "Calculator"] 的字符串(格式为逗号分隔,比如 "serpapi, llm-math");
    • {tools}:填充为工具的详细描述(比如"Search:用于联网搜索最新信息;Calculator:用于数学计算");
    • {agent_scratchpad}:初始为空,后续Agent思考过程中会逐步填充(Thought/Action/Observation 等步骤)。
  2. AgentExecutor 会将这些变量值动态填充到 PromptTemplate 中,生成完整的提示词发送给大模型;
  3. 大模型基于填充后的完整提示词,按照 ReAct 格式输出思考和行动步骤(比如选择 Search 工具查玫瑰进货价,再用 Calculator 工具计算加价5%的定价)。

验证:打印填充后的 Prompt(直观看到 {tool_names} 的代入值)

你可以在代码中添加以下验证代码,直接查看 {tool_names} 被代入后的实际值:

python 复制代码
# 新增:手动模拟 Agent 填充变量的过程,打印完整 Prompt
from langchain.agents.format_scratchpad import format_log_to_str
from langchain.schema import AgentFinish, AgentAction

# 1. 提取工具名称(模拟 LangChain 内部逻辑)
tool_names = [tool.name for tool in tools]  # 提取工具名,比如 ["Search", "Calculator"]
print("=== 提取的 tool_names 变量值 ===")
print(tool_names)  # 输出:['Search', 'Calculator'](或 ['serpapi', 'llm-math'],取决于工具定义)

# 2. 填充模板变量(模拟 AgentExecutor 执行时的填充逻辑)
filled_prompt = prompt.format(
    tools="\n".join([f"{tool.name}: {tool.description}" for tool in tools]),  # 工具详细描述
    tool_names=", ".join(tool_names),  # 工具名称列表(代入 {tool_names})
    input="目前市场上玫瑰花的一般进货价格是多少?如果我在此基础上加价5%,应该如何定价?",  # 用户输入
    agent_scratchpad=""  # 初始为空的思考过程
)
print("\n=== 填充 {tool_names} 后的完整 Prompt ===")
print(filled_prompt)
验证输出示例:
复制代码
=== 提取的 tool_names 变量值 ===
['Search', 'Calculator']

=== 填充 {tool_names} 后的完整 Prompt ===
    尽你所能用中文回答以下问题。如果能力不够你可以使用以下工具:

    Search: A search engine. Useful for when you need to answer questions about current events...
    Calculator: Useful for when you need to do math calculations...

    Use the following format:

    Question: the input question you must answer
    Thought: you should always think about what to do
    Action: the action to take, should be one of [Search, Calculator]  # {tool_names} 被代入为 Search, Calculator
    Action Input: the input to the action
    Observation: the result of the action
    ... 
    Thought: I now know the final answer
    Final Answer: the final answer to the original input question

    Begin!

    Question: 目前市场上玫瑰花的一般进货价格是多少?如果我在此基础上加价5%,应该如何定价?
    Thought:

思考

  1. 为什么无需手动赋值?

    LangChain 的 create_react_agent 是为 ReAct 范式定制的,它知道 ReAct 模板必须包含 {tool_names}{tools}{agent_scratchpad} 这些变量,因此会自动从传入的 tools 参数中提取对应值,无需开发者手动绑定。

  2. 代入时机?

    • PromptTemplate.from_template(template) 只是解析模板结构(识别哪些是变量),并未赋值;
    • 真正的代入发生在 agent_executor.invoke(...) 执行时,每次调用都会动态填充 (比如更换 tools 列表,{tool_names} 会自动更新)。

总结

  1. {tool_names} 的代入主体:不是手动赋值,而是 LangChain 的 ReAct Agent(create_react_agent + AgentExecutor)自动处理;
  2. 代入数据源:从 load_tools 加载的 tools 列表中提取工具名称;
  3. 代入时机:在 agent_executor.invoke() 执行时动态填充(而非创建 PromptTemplate 时);
  4. 核心逻辑:LangChain 为 ReAct 范式内置了模板变量与工具信息的映射,简化了开发者的编码工作。

简单说:你只需要告诉 LangChain "用哪些工具(tools)",它就会自动把工具名称填到 {tool_names} 里,无需手动干预。

二、template中{scratchpad}

scratchpad 本意是草稿纸、便签本 (也译作"暂存区"),在 LangChain 的 Agent 提示模板中,{agent_scratchpad} 是 Agent 的「思考过程草稿区」------ 专门用来记录 Agent 每一轮的"思考(Thought)-行动(Action)-行动输入(Action Input)-观察(Observation)"中间过程,相当于 Agent 推理时用的"草稿纸"。

生活化类比

你解一道数学题(比如"玫瑰进价加价5%定价"):

  • 草稿纸(scratchpad):你会在纸上写下"第一步:查玫瑰进价→约2元/枝;第二步:计算2×1.05=2.1元"------ 这些中间步骤就是草稿;
  • 最终答案:草稿纸写满后,你总结出"定价2.1元/枝"。

Agent 的 scratchpad 就是这个"草稿纸":它不存储最终答案,只存储 Agent 推理过程中的每一步思考和行动结果,支撑 Agent 完成多轮推理。

scratchpad 的核心作用(结合 ReAct 范式)

你的代码用的是 ReAct 范式 Agent(思考-行动-观察循环),scratchpad 是这个循环能跑起来的核心载体

  1. 累积多轮推理过程 :ReAct Agent 不是一步得出答案,而是多轮循环(比如先搜玫瑰进价→再计算加价),scratchpad 会把每一轮的"Thought+Action+Action Input+Observation"都记录下来;
  2. 让 Agent 有"记忆" :每一轮循环时,scratchpad 会被代入到提示模板中,Agent 能基于"上一轮的草稿"继续推理,而不是每次从零开始;
  3. 透明化推理逻辑 :通过 scratchpad 能清晰看到 Agent"为什么选这个工具""行动结果是什么",方便调试(比如代码中 verbose=True 会打印出这个过程)。

scratchpad 在代码中的执行过程(玫瑰定价例子)

设代码里的问题"查玫瑰进价+加价5%定价",则{agent_scratchpad} 的填充和使用过程如下:

阶段1:初始状态 → scratchpad 为空

调用 agent_executor.invoke() 时,{agent_scratchpad} 初始值为空,模板填充后这部分是空白:

复制代码
Thought:
阶段2:第一轮推理 → scratchpad 首次填充

Agent 第一次思考后生成:

复制代码
Thought: 我需要先查目前玫瑰花的一般进货价格,应该用Search工具。
Action: Search
Action Input: 2026年玫瑰花一般进货价格
Observation: 2026年玫瑰花批发进货价约为2元/枝(单头玫瑰),多头玫瑰约5元/枝。

这些内容会被 LangChain 自动写入 scratchpad,此时 {agent_scratchpad} 被替换为上述文本,模板中对应的部分变成:

复制代码
Thought: 我需要先查目前玫瑰花的一般进货价格,应该用Search工具。
Action: Search
Action Input: 2026年玫瑰花一般进货价格
Observation: 2026年玫瑰花批发进货价约为2元/枝(单头玫瑰),多头玫瑰约5元/枝。
Thought:
阶段3:第二轮推理 → scratchpad 累积内容

Agent 基于上一轮的 scratchpad 继续思考,生成新的步骤:

复制代码
Thought: 现在我有了进货价,需要计算加价5%后的定价,应该用Calculator工具。
Action: Calculator
Action Input: 2×1.05, 5×1.05
Observation: 2×1.05=2.1,5×1.05=5.25

这些内容被追加到 scratchpad 中,模板里的 {agent_scratchpad} 变成:

复制代码
Thought: 我需要先查目前玫瑰花的一般进货价格,应该用Search工具。
Action: Search
Action Input: 2026年玫瑰花一般进货价格
Observation: 2026年玫瑰花批发进货价约为2元/枝(单头玫瑰),多头玫瑰约5元/枝。
Thought: 现在我有了进货价,需要计算加价5%后的定价,应该用Calculator工具。
Action: Calculator
Action Input: 2×1.05, 5×1.05
Observation: 2×1.05=2.1,5×1.05=5.25
Thought:
阶段4:最终推理 → scratchpad 停止累积

Agent 认为已有足够信息,生成最终答案,scratchpad 不再追加:

复制代码
Thought: I now know the final answer
Final Answer: 目前单头玫瑰花一般进货价约2元/枝,加价5%后定价2.1元/枝;多头玫瑰花进货价约5元/枝,加价5%后定价5.25元/枝。

思考

  1. 谁来填充 scratchpad?

    不是你手动赋值,而是 LangChain 的 AgentExecutor 在每一轮"思考-行动-观察"循环中,自动收集并追加内容到 agent_scratchpad ,然后代入到提示模板中------和你之前问的 {tool_names} 类似,是 LangChain 为 ReAct Agent 内置的逻辑。

  2. scratchpad 为什么是空的初始状态?

    Agent 第一次推理时还没有任何思考/行动记录,所以初始 {agent_scratchpad} 为空;每一轮循环后,LangChain 会把新的中间步骤追加进去,形成"累积式草稿"。

  3. 和普通变量的区别?

    • {input}:固定的用户问题(一次性赋值);
    • {tool_names}:固定的工具名称列表(一次性赋值);
    • {agent_scratchpad}:动态累积的中间过程(多轮循环中持续更新)。

体现智能体的"记忆能力"

  1. 核心含义scratchpad(agent_scratchpad)是 Agent 的「思考草稿纸」,存储多轮"思考-行动-观察"的中间过程;
  2. 核心作用:支撑 ReAct Agent 的多轮推理,让 Agent 能基于历史思考继续推导,而非从零开始;
  3. 填充逻辑:由 LangChain 的 AgentExecutor 自动累积填充,初始为空,每轮循环追加新的中间步骤;
  4. 本质价值:透明化 Agent 的推理过程,同时让 Agent 具备"记忆思考过程"的能力,是 ReAct 范式的核心变量之一。

简单说:没有 scratchpad,Agent 就只能"一步式思考",无法完成"先搜价格→再算加价"的多步骤任务------它是 Agent 实现复杂推理的"记忆载体"。

相关推荐
蛇皮划水怪5 小时前
深入浅出LangChain4J
java·langchain·llm
、BeYourself7 小时前
LangChain4j 流式响应
langchain
、BeYourself7 小时前
LangChain4j之Chat and Language
langchain
qfljg9 小时前
langchain usage
langchain
kjkdd12 小时前
6.1 核心组件(Agent)
python·ai·语言模型·langchain·ai编程
渣渣苏17 小时前
Langchain实战快速入门
人工智能·python·langchain
小天呐17 小时前
01—langchain 架构
langchain
香芋Yu20 小时前
【LangChain1.0】第九篇 Agent 架构设计
langchain·agent·架构设计
组合缺一21 小时前
Solon AI (Java) v3.9 正式发布:全能 Skill 爆发,Agent 协作更专业!仍然支持 java8!
java·人工智能·ai·llm·agent·solon·mcp
kjkdd21 小时前
5. LangChain设计理念和发展历程
python·语言模型·langchain·ai编程