【大模型冷知识】你知道 Langchain 中有几种工具绑定的方式吗?
碎碎念 1: chat_model.bind_tools vs create_agent 中的工具绑定
Langchain 中支持多种为大模型绑定工具的方式, 常用的有两种, 一种是直接调用 chat_model 的 bind_tools, 一种是在 create_agent 时通过参数tools来指定, 两种方式的使用示例如下:
这两种直接绑定工具方式的区别是什么? 在使用过程中你有没有疑惑, 并进行对比过?
方式 1 直接调用 bind_tools
# 方式 1: bind_tools
model = chat_model.bind_tools([retriever_tool])
resp = model.invoke([messages[-1]])
这种方式, 是只把工具的信息注册给大模型(本质是拼接到 prompt 中), 让大模型直到有那些工具可以调用, 然后大模型则会根据请求来决策需要调用哪个工具, 并生成 ToolCall 对象来封装要调用的工具名称以及参数信息, 然后得到 ToolCall 对象后需要你手动去解析ToolCall 对象中的调用信息,并调用对应的函数, 这种调用方式处于 API 更低的层次, 但给你更多的控制权, 由你来控制工具如何进行调用.
ToolCall对象结构示例:
{ "type": "tool_call", # 固定值,表示这是一个工具调用 "name": "retriever", # 工具名称(字符串) "args": { # 工具参数(字典) "query": "什么是芯片?" }, "id": "call_abc123" # 唯一标识符(字符串) }
- type:固定为 “tool_call”
- name:工具的名称,对应你定义工具时的名字(如 ‘retriever’)
- args:工具的输入参数,是一个字典,包含工具函数需要的所有参数
- id:工具调用的唯一 ID,用于将工具结果与调用关联起来
拿到ToolCall 对象后, 需要调用函数, ToolNode就是一个专门负责执行工具调用节点类, 所以这也是在 LangGraph 工作流编排中通常要加入 ToolNode 节点的原因, 先通过bind_tool绑定工具, 后让ToolNode执行工具. ToolNode节点内部封装了解析 ToolCall 对象/提取工具名称和参数/执行工具函数以及最后的将工具输出封装成ToolMessage 的完整流程.
# 绑定工具(让模型知道有哪些工具)
model = chat_model.bind_tools([retriever_tool])
# ---
执行工具(实际调用工具):
builder.add_node('retrieve', ToolNode([retriever_tool]))
ToolNode 的流程图:
从 ToolCall 到结构化输出的应用
由于ToolCall 对象的参数信息解析控制权交给了开发者, 所以衍生出了很多的非工具场景调用的玩法, 比如结构化输出. 在很多落地场景下都需要模型输出json 结构化的结果, 但当前支持 json 结构化能力的模型相对较少, 这其中 OpenAI 模型家族原生就对结构化能力支持的比较好的, 比如通过原生 OpenAI API 去调用gpt4.1模型时时, 可以直接设置参数response_format来设置输出结果格式的json_schema
原生 OpenAI API 设置结构化输出示例:
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Extract user info"}],
response_format={
"type": "json_schema",
"json_schema": {
"name": "UserInfo",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"email": {"type": "string"}
},
"required": ["name", "age", "email"]
},
"strict": True
}
}
)
# 返回的是 JSON 字符串,需要解析
result = json.loads(response.choices[0].message.content)
print(result) # {"name": "...", "age": ..., "email": "..."}
那针对原生不支持结构化输出能力的模型怎么办? 简单方式是在 prompt 中指定输出结果结构, 并给出 one-shot 示例, 但结果不是很稳定(相信有朋友是深有同感的), 而一种稳定的结果结构化输出就是借助 ToolCall 对象, 只要该模型支持 FunctionCall 能力, 就能借助该方式.
思路很简单, 就是将要输出的结构的结构定义伪装成一个工具调用信息, 让模型输出一个ToolCall 对象, 基于该对象不是去调用工具, 而是解析工具的参数作为结构化的输出. 其实Langchain 中的.with_structured_output(...)方法就是这么做的
方式 2: create_agent的工具绑定
# 方式 2: create_agent中的 tools 参数指定
from langchain.agents import create_agent
agent = create_agent(
model="gpt-4o",
tools=[retriever_tool],
)
result = agent.invoke({"messages": [...]})
这种方式就完全将工具注册到你创建的Agent 对象中了, 由 Agent 执行引擎自动解析大模型输出的 ToolCall 对象来完成工具的调用, 这个过程完全是黑盒的/自动的, 无需你做任何的人工干预, 属于抽象度较高的 API 层次.
更多推荐
所有评论(0)