如果你希望大模型不仅能回答你的问题,还能读取本地的文件,就需要为它绑定上对应的工具。在 `langchain` 中,绑定的方式很简单——直接调用 `model.bindTools` 即可。
接下来,我们来探讨如何定义工具。
## 定义工具
在定义工具时,首先要弄清楚它的“使用说明书”是什么样的。工具本质上是一个函数,既然是函数,就必须接收参数,也就是有入参。因此,你需要明确三件事:名称、描述、入参。
```ja vascript
const obj = {
// 工具的名称
name: 'read_file',
// 对这个工具的人类语言描述
description: '对这个工具的人类语言描述',
// 工具入参
schema: z.object({
filePath: z.string().describe('要读取的文件路径'),
}),
}
```
编写完说明书后,接下来就是工具本身——也就是那个函数。逻辑非常简单:传入文件路径,读取内容并返回。
```ja vascript
// 很简单,就是一个函数,函数中的逻辑如下,实现了读取文件内容并返回
const fun = async ({ filePath }) => {
const content = await fs.readFile(filePath, 'utf-8');
console.log(` [工具调用] read_file("${filePath}") - 成功读取${content.length}字节`);
return `文件内容:\n${content}`;
}
```
最后一步,用 langchain 的 `tool` 函数把上面这两个参数传进去,工具就算定义好了。
```ja vascript
tool(fun, obj)
```
## 调用工具
在前面绑定好工具之后,我们调用了模型的 `invoke` 函数。但请注意,模型并不会直接去执行工具,而是返回当前用户问题可以使用的工具列表。
- 用户问:“你好”。模型返回一个工具空数组——它自己判断,这次请求不需要调用任何工具。
- 用户问:“读取某个文件”。大模型会检索自身,看是否有合适的工具。如果有,就在返回数据中把可用的工具带出来。
数组中的每一个工具都包含:工具的实际入参(模型通过第一次用户的提问识别到的)、工具的唯一 id(每次调用都会随机生成一个新的)、工具的名称。
当模型返回了工具调用数组,我们需要遍历这个数组,依次调用每一个工具函数。注意,调用工具不是使用 `模型的 invoke`,而是 `工具的 invoke`。
```ja vascript
// invoke的参数就是工具的入参,这个入参可以从模型返回的数组中获取
const result = await tool.invoke(toolCall.args);
```
拿到工具的结果之后,用 langchain 的 `new ToolMessage` 来新建一条消息:
```ja vascript
new ToolMessage({
content: toolResults[index],
tool_call_id: toolCall.id,
})
```
最后,将所有消息再次发送给模型,模型输出最终回答。
### Q&A
**Q1:为什么需要 `new ToolMessage`,而不是将工具返回结果直接拼接到前面用户的提问后面?**
**A1:**
(1)像 GPT-4、Claude 这类支持 function calling 的模型,在训练过程中大量接触的对话格式就是这种结构化模式:
```
User → Assistant(tool_calls) → ToolMessage → Assistant(回复)
```
也就是说,模型在训练时学会了:收到带有 tool_call_id 的 ToolMessage = 这是我刚才调用的工具返回的结果,需要借助这些内容来作答。如果改用字符串拼接,将工具结果塞进 HumanMessage 或普通文本中,这种模式在训练数据里非常罕见,模型就没有明确的“如何使用”的经验。
(2)角色与用途清晰
在结构化消息体系里,每一条消息的语义都非常明确:
| 消息类型 | 模型的理解 |
| --- | --- |
| HumanMessage | 用户说了什么、想实现什么 |
| AIMessage + tool_calls | 我决定调用这些工具 |
| ToolMessage | 这些工具返回的结果,是对我上一轮决策的反馈 |
| AIMessage | 基于工具结果给出最终回答 |
模型会将 ToolMessage 视为“我主动调用的工具的结果”,而不是“用户新说的话”。如果采用字符串拼接,工具结果很容易被当作普通上下文或用户输入,导致模型不清楚这是自己刚才调用的工具的结果,从而处理方式产生偏差。
(3)注意力与因果关系
在多轮对话中,模型对不同消息类型的处理权重各不相同。ToolMessage 在训练阶段被视作“对上一次 tool_calls 的回应”,因此模型会更自然地把注意力集中在:用这些结果来回答用户最初的问题。这样能够维持清晰的因果链条,避免干扰。
```Agent开发工具01实战指南
```html 大语言模型(LLM)本身不具备直接操控外部资源的能力,例如无法读取你本地存储的文件。那么,如何让它实现这些功能呢?关键在于为它“绑定”相应的工具。在 Langchain 框架中,这一操作非常简洁——只需使用 `model bindTools` 即可完成绑定。接下来,自然是定义所需的工
```html
大语言模型(LLM)本身不具备直接操控外部资源的能力,例如无法读取你本地存储的文件。那么,如何让它实现这些功能呢?关键在于为它“绑定”相应的工具。在 Langchain 框架中,这一操作非常简洁——只需使用 `model.bindTools` 即可完成绑定。接下来,自然是定义所需的工具。
## 工具
大语言模型本身并不具备工具的功能,比如读取文件的特性。
如果你希望大模型不仅能回答你的问题,还能读取本地的文件,就需要为它绑定上对应的工具。在 `langchain` 中,绑定的方式很简单——直接调用 `model.bindTools` 即可。
接下来,我们来探讨如何定义工具。
## 定义工具
在定义工具时,首先要弄清楚它的“使用说明书”是什么样的。工具本质上是一个函数,既然是函数,就必须接收参数,也就是有入参。因此,你需要明确三件事:名称、描述、入参。
```ja vascript
const obj = {
// 工具的名称
name: 'read_file',
// 对这个工具的人类语言描述
description: '对这个工具的人类语言描述',
// 工具入参
schema: z.object({
filePath: z.string().describe('要读取的文件路径'),
}),
}
```
编写完说明书后,接下来就是工具本身——也就是那个函数。逻辑非常简单:传入文件路径,读取内容并返回。
```ja vascript
// 很简单,就是一个函数,函数中的逻辑如下,实现了读取文件内容并返回
const fun = async ({ filePath }) => {
const content = await fs.readFile(filePath, 'utf-8');
console.log(` [工具调用] read_file("${filePath}") - 成功读取${content.length}字节`);
return `文件内容:\n${content}`;
}
```
最后一步,用 langchain 的 `tool` 函数把上面这两个参数传进去,工具就算定义好了。
```ja vascript
tool(fun, obj)
```
## 调用工具
在前面绑定好工具之后,我们调用了模型的 `invoke` 函数。但请注意,模型并不会直接去执行工具,而是返回当前用户问题可以使用的工具列表。
- 用户问:“你好”。模型返回一个工具空数组——它自己判断,这次请求不需要调用任何工具。
- 用户问:“读取某个文件”。大模型会检索自身,看是否有合适的工具。如果有,就在返回数据中把可用的工具带出来。
数组中的每一个工具都包含:工具的实际入参(模型通过第一次用户的提问识别到的)、工具的唯一 id(每次调用都会随机生成一个新的)、工具的名称。
当模型返回了工具调用数组,我们需要遍历这个数组,依次调用每一个工具函数。注意,调用工具不是使用 `模型的 invoke`,而是 `工具的 invoke`。
```ja vascript
// invoke的参数就是工具的入参,这个入参可以从模型返回的数组中获取
const result = await tool.invoke(toolCall.args);
```
拿到工具的结果之后,用 langchain 的 `new ToolMessage` 来新建一条消息:
```ja vascript
new ToolMessage({
content: toolResults[index],
tool_call_id: toolCall.id,
})
```
最后,将所有消息再次发送给模型,模型输出最终回答。
### Q&A
**Q1:为什么需要 `new ToolMessage`,而不是将工具返回结果直接拼接到前面用户的提问后面?**
**A1:**
(1)像 GPT-4、Claude 这类支持 function calling 的模型,在训练过程中大量接触的对话格式就是这种结构化模式:
```
User → Assistant(tool_calls) → ToolMessage → Assistant(回复)
```
也就是说,模型在训练时学会了:收到带有 tool_call_id 的 ToolMessage = 这是我刚才调用的工具返回的结果,需要借助这些内容来作答。如果改用字符串拼接,将工具结果塞进 HumanMessage 或普通文本中,这种模式在训练数据里非常罕见,模型就没有明确的“如何使用”的经验。
(2)角色与用途清晰
在结构化消息体系里,每一条消息的语义都非常明确:
| 消息类型 | 模型的理解 |
| --- | --- |
| HumanMessage | 用户说了什么、想实现什么 |
| AIMessage + tool_calls | 我决定调用这些工具 |
| ToolMessage | 这些工具返回的结果,是对我上一轮决策的反馈 |
| AIMessage | 基于工具结果给出最终回答 |
模型会将 ToolMessage 视为“我主动调用的工具的结果”,而不是“用户新说的话”。如果采用字符串拼接,工具结果很容易被当作普通上下文或用户输入,导致模型不清楚这是自己刚才调用的工具的结果,从而处理方式产生偏差。
(3)注意力与因果关系
在多轮对话中,模型对不同消息类型的处理权重各不相同。ToolMessage 在训练阶段被视作“对上一次 tool_calls 的回应”,因此模型会更自然地把注意力集中在:用这些结果来回答用户最初的问题。这样能够维持清晰的因果链条,避免干扰。
```
如果你希望大模型不仅能回答你的问题,还能读取本地的文件,就需要为它绑定上对应的工具。在 `langchain` 中,绑定的方式很简单——直接调用 `model.bindTools` 即可。
接下来,我们来探讨如何定义工具。
## 定义工具
在定义工具时,首先要弄清楚它的“使用说明书”是什么样的。工具本质上是一个函数,既然是函数,就必须接收参数,也就是有入参。因此,你需要明确三件事:名称、描述、入参。
```ja vascript
const obj = {
// 工具的名称
name: 'read_file',
// 对这个工具的人类语言描述
description: '对这个工具的人类语言描述',
// 工具入参
schema: z.object({
filePath: z.string().describe('要读取的文件路径'),
}),
}
```
编写完说明书后,接下来就是工具本身——也就是那个函数。逻辑非常简单:传入文件路径,读取内容并返回。
```ja vascript
// 很简单,就是一个函数,函数中的逻辑如下,实现了读取文件内容并返回
const fun = async ({ filePath }) => {
const content = await fs.readFile(filePath, 'utf-8');
console.log(` [工具调用] read_file("${filePath}") - 成功读取${content.length}字节`);
return `文件内容:\n${content}`;
}
```
最后一步,用 langchain 的 `tool` 函数把上面这两个参数传进去,工具就算定义好了。
```ja vascript
tool(fun, obj)
```
## 调用工具
在前面绑定好工具之后,我们调用了模型的 `invoke` 函数。但请注意,模型并不会直接去执行工具,而是返回当前用户问题可以使用的工具列表。
- 用户问:“你好”。模型返回一个工具空数组——它自己判断,这次请求不需要调用任何工具。
- 用户问:“读取某个文件”。大模型会检索自身,看是否有合适的工具。如果有,就在返回数据中把可用的工具带出来。
数组中的每一个工具都包含:工具的实际入参(模型通过第一次用户的提问识别到的)、工具的唯一 id(每次调用都会随机生成一个新的)、工具的名称。
当模型返回了工具调用数组,我们需要遍历这个数组,依次调用每一个工具函数。注意,调用工具不是使用 `模型的 invoke`,而是 `工具的 invoke`。
```ja vascript
// invoke的参数就是工具的入参,这个入参可以从模型返回的数组中获取
const result = await tool.invoke(toolCall.args);
```
拿到工具的结果之后,用 langchain 的 `new ToolMessage` 来新建一条消息:
```ja vascript
new ToolMessage({
content: toolResults[index],
tool_call_id: toolCall.id,
})
```
最后,将所有消息再次发送给模型,模型输出最终回答。
### Q&A
**Q1:为什么需要 `new ToolMessage`,而不是将工具返回结果直接拼接到前面用户的提问后面?**
**A1:**
(1)像 GPT-4、Claude 这类支持 function calling 的模型,在训练过程中大量接触的对话格式就是这种结构化模式:
```
User → Assistant(tool_calls) → ToolMessage → Assistant(回复)
```
也就是说,模型在训练时学会了:收到带有 tool_call_id 的 ToolMessage = 这是我刚才调用的工具返回的结果,需要借助这些内容来作答。如果改用字符串拼接,将工具结果塞进 HumanMessage 或普通文本中,这种模式在训练数据里非常罕见,模型就没有明确的“如何使用”的经验。
(2)角色与用途清晰
在结构化消息体系里,每一条消息的语义都非常明确:
| 消息类型 | 模型的理解 |
| --- | --- |
| HumanMessage | 用户说了什么、想实现什么 |
| AIMessage + tool_calls | 我决定调用这些工具 |
| ToolMessage | 这些工具返回的结果,是对我上一轮决策的反馈 |
| AIMessage | 基于工具结果给出最终回答 |
模型会将 ToolMessage 视为“我主动调用的工具的结果”,而不是“用户新说的话”。如果采用字符串拼接,工具结果很容易被当作普通上下文或用户输入,导致模型不清楚这是自己刚才调用的工具的结果,从而处理方式产生偏差。
(3)注意力与因果关系
在多轮对话中,模型对不同消息类型的处理权重各不相同。ToolMessage 在训练阶段被视作“对上一次 tool_calls 的回应”,因此模型会更自然地把注意力集中在:用这些结果来回答用户最初的问题。这样能够维持清晰的因果链条,避免干扰。
```来源:https://juejin.cn/post/7626578013837328425
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。
相关推荐
补充同频道和同主题内容,方便继续浏览更多相关内容。
同类最新
继续查看同栏目最近更新的文章。
快速提升工作效率与准确性的5种AI生成Excel方法
如今,借助人工智能自动生成Excel表格,或者说利用AI技术辅助数据处理,已从前沿概念转变为现代办公的标配工具。在技术浪潮推动下,越来越多企业发现,让AI参与数据工作,不仅能显著提升效率,更关键的是能够大幅降低因重复劳动或人为疏忽导致的错误率。从金融风控到市场洞察,再到人力资源盘点,这项技术正在各个
信息爆炸时代用AI写作工具轻松写出高质量年终总结
适合需求: 又到年末,你是否也感到手头的年终总结像一座难以翻越的大山?海量信息铺天盖地,数据琐碎繁杂,想要写出一份既有深度又亮眼的年度报告,确实颇具挑战。不过别担心,如今有了AI写作工具的助力,这件事可以变得轻松很多。比如借助WPS AI这类智能助手,你完全能让年终总结告别枯燥,变得既凝练有力,又充
公文写作效率与质量提升的五大实用技巧
公文写作需注意词语精准性与格式规范,结合行业特点选择术语。WPSAI等工具可一键生成文档、智能创作,提升效率。未来需平衡技术与规范,应对不同行业特殊需求。明确目的、组织内容、调整语言风格是高效沟通的关键。
五分钟配好向日葵MCP,AI替你远程安装OpenClaw详细教程
前言 近期,MCP(Model Context Protocol)概念在技术圈热度飙升,几乎各类工具都试图接入。向日葵也顺势推出了MCP Server,将其成熟的远程控制能力开放给AI调用。起初我并未特别关注,直到随口对AI说了一句:“帮我看看家里电脑桌面上有啥?”结果它确实自动连接并截取了屏幕截图
五个实用技巧提升公文写作专业性与效率
公文写作需平衡专业性与效率,标题应简洁准确。WPSAI通过一键生成文档、智能内容创作等功能提升效率。写作需语言规范、结构清晰,避免用词不当、逻辑混乱等常见错误,并遵循统一格式要求。
