这种Agent使用ReAct(Retrieve-and-Act)框架,该框架通过理解工具的描述来选择最合适的工具执行任务。Zero-shot意味着Agent不需要针对特定任务进行训练,而是可以基于工具的描述直接进行推断。
这边有四种类型,他们的特性如下:
| 特性 | chat-conversational-react-description | structured-chat-zero-shot-react-description | chat-zero-shot-react-description | zero-shot-react-description |
|---|---|---|---|---|
| 目标模型 | 对话式 LLM(如 GPT-4 Chat) | 对话式 LLM(如 GPT-4 Chat) | 对话式 LLM(如 GPT-4 Chat) | 通用 LLM(如 text-davinci-003) |
| 输入格式 | 消息列表 + 对话历史 | 消息列表(系统提示要求 JSON) | 消息列表 | 单次字符串提示 |
| 输出格式 | 自然语言(需解析) | 结构化数据(如 JSON) | 自然语言或半结构化 | 固定格式(如 [Action] input) |
| 对话上下文 | 支持(内置 ChatMemory) | 支持(但更关注单次输出) | 支持(基本对话历史) | 不支持 |
| 提示设计 | 自然语言 + 工具描述 + 历史 | 系统提示要求结构化输出 | 自然语言 + 工具描述 | 静态提示 + 工具描述 |
| 解析复杂度 | 高(需正则解析) | 低(直接解析 JSON) | 中等(正则或简单解析) | 高(严格格式要求) |
| 灵活性 | 高(自然交互) | 低(强制结构化) | 中等(较灵活) | 低(格式固定) |
| 实现基础 | ConversationalAgent | StructuredChatAgent | 基础 ReAct Chat 实现 | 基础 ReAc |
使用场景的区别:
| 代理类型 | 适用场景 | 示例 | 优点 | 缺点 |
|---|---|---|---|---|
| chat-conversational-react-description | 需要自然对话的聊天机器人或客服系统,支持多轮交互和上下文记忆 | 用户问“今天天气如何”,接着问“明天呢”,代理能记住上下文 | 用户体验好,交互自然 | 输出解析复杂,可能不一致 |
| structured-chat-zero-shot-react-description | 自动化任务、后台工作流、需要可靠结构化输出的系统 | 代理调用工具并将结果传递给 API 或数据库 | 输出一致,易于集成 | 不够自然,对模型要求高 |
| chat-zero-shot-react-description | 对话式任务,但无需复杂记忆或结构化输出,适合轻量多轮交互 | 简单的多轮问答或工具调用 | 平衡灵活性和对话支持 | 不如 conversational 自然,不如 structured 可靠 |
| zero-shot-react-description | 单次任务,无需对话上下文,适用于简单推理或工具调用 | 计算数学问题(如 5+3)、单次搜索 | 简单直接,适合传统 LLM |
代码示例
-
依赖的环境:
开发语言:nodejs
package.json的核心依赖:
"@langchain/community": "^0.3.35", "@langchain/core": "^0.3.42 ", "@langchain/langgraph": "^0.2.51", "@langchain/ollama": "^0.2.0", "langchain": "^0.2.4", "langfuse-langchain": "^3.36.0",
大模型:ollama平台中的qwen2.5:14b,ollama也是本地部署的大模型管理平台
搜索引擎:searXNGSearch,这边使用自己搭建的搜索引擎,当然也可以使用SerpAPI
- 用例说明:
输入对内容:
姚明的妻子是谁?请获取当前的具体时间来计算她当前的年龄?她年龄的0.76次方是多少?
正确的处理逻辑是:
1)使用搜索引擎搜索姚明的妻子
2)使用搜索引擎检索妻子的出生日期
3)使用currentDateTool获取当前的日期
4)计算她的年龄
5)计算她年龄的0.76次方
-
代码
-
初始化工具,这边用到三个工具currentDateTool、searXNGSearchTool、Calculator,其中currentDateTool、searXNGSearchTool是自己封装的工具
//获取当前时间工具 const currentDateTool = tool(async (input)=> { return new Date().toISOString(); }, { name:"currentDateTool", description:"A tool to get the current date and time.", schema:z.object() }) //搜索引擎工具 const searXNGSearchTool = tool( async (input)=> { try { // 发送搜索请求到 SearXNG // console.info("searXNGSearchTool called>>>") const response = await axios.get(`http://ip:端口/search`, { params: { q: input, // 搜索关键词 format: "json" // 返回 JSON 格式 // engines: "google" // 可选:指定搜索引擎(默认使用所有引擎) }, }); // 提取搜索结果 const results = response.data.results || []; if (results.length === 0) { return "No results found."; } // 格式化结果(标题 + 摘要 + 链接) const formattedResults = results .slice(0, 5) // 限制返回前 5 条结果,避免过长 .map((result, index) => { return `${index + 1}. ${result.title}\n ${result.content || "No snippet available"}\n ${result.url}`; }) .join("\n\n"); return formattedResults || "No relevant data extracted."; } catch (error) { console.error("SearXNG search failed:", error.message); return `Error: ${error.message}`; } }, { name:"searXNGSearchTool", description:"A tool to search the web using a self-hosted SearXNG instance.", schema:z.string() } ); const tools = [ searXNGSearchTool, currentDateTool, new Calculator() ]; -
创建agents与执行,此用例支持agentType的类型有:structured-chat-zero-shot-react-description、zero-shot-react-description与-zero-shot-react-description
const executor = await initializeAgentExecutorWithOptions(tools, model, { // agentType: "structured-chat-zero-shot-react-description", // agentType: "zero-shot-react-description", agentType: "chat-zero-shot-react-description", verbose: true, }); console.log("Loaded agent."); const input = `姚明的妻子是谁?请获取当前的具体时间来计算她当前的年龄?她年龄的0.76次方是多少?`; // const input = `当前的具体时间`; console.log(`Executing with input "${input}"...`); const result = await executor.invoke({ input }); console.log(`Got output ${JSON.stringify(result, null, 2)}`); -
使用zero-shot-react-description类型是,由于使用currentDateTool工具是没有输入参的,当前有输出的解析有bug,这边也进行处理,使用覆盖模式替换ZeroShotAgentOutputParser的parse方法
ZeroShotAgentOutputParser.prototype.parse= async function (text) { if (text.includes(this.finishToolName)) { const parts = text.split(this.finishToolName); const output = parts[parts.length - 1].trim(); return { returnValues: { output }, log: text, }; } //处理没有输入参数的情况,如:\nAction: currentDateTool\nAction Input let match = null; if(text.endsWith("Action Input")){ match = /Action:([\s\S]*?)(?:\nAction Input:([\s\S]*?))?$/.exec(text+":"); }else { match = /Action:([\s\S]*?)(?:\nAction Input:([\s\S]*?))?$/.exec(text); } if (!match) { throw new Error(`Could not parse LLM output: ${text}`); } const tool = match[1].trim(); const toolInput = match[2] ? match[2].trim().replace(/^("+)(.*?)(\1)$/, "$2") : ""; return { tool, toolInput, log: text, }; }; -
完整的代码如下,需要替换成自己的 Ollama 本地地址与searXNGSearch地址
import { initializeAgentExecutorWithOptions,ZeroShotAgentOutputParser } from "langchain/agents"; import { Calculator } from "@langchain/community/tools/calculator"; import {ChatOllama} from "@langchain/ollama"; import {tool} from "@langchain/core/tools"; import axios from "axios"; import {z} from "zod"; ZeroShotAgentOutputParser.prototype.parse= async function (text) { if (text.includes(this.finishToolName)) { const parts = text.split(this.finishToolName); const output = parts[parts.length - 1].trim(); return { returnValues: { output }, log: text, }; } //处理没有输入参数的情况,如:\nAction: currentDateTool\nAction Input let match = null; if(text.endsWith("Action Input")){ match = /Action:([\s\S]*?)(?:\nAction Input:([\s\S]*?))?$/.exec(text+":"); }else { match = /Action:([\s\S]*?)(?:\nAction Input:([\s\S]*?))?$/.exec(text); } if (!match) { throw new Error(`Could not parse LLM output: ${text}`); } const tool = match[1].trim(); const toolInput = match[2] ? match[2].trim().replace(/^("+)(.*?)(\1)$/, "$2") : ""; return { tool, toolInput, log: text, }; }; export const run = async () => { const model = new ChatOllama({ baseUrl: 'http://ip:端口', // 默认 Ollama 本地地址 model: 'qwen2.5:14b', // 替换为你运行的模型名称,例如 "mistral" 或 "llama3" temperature: 0 }) //获取当前时间工具 const currentDateTool = tool(async (input)=> { return new Date().toISOString(); }, { name:"currentDateTool", description:"A tool to get the current date and time.", schema:z.object() }) //搜索引擎工具 const searXNGSearchTool = tool( async (input)=> { try { // 发送搜索请求到 SearXNG // console.info("searXNGSearchTool called>>>") const response = await axios.get(`http://ip:端口/search`, { params: { q: input, // 搜索关键词 format: "json" // 返回 JSON 格式 // engines: "google" // 可选:指定搜索引擎(默认使用所有引擎) }, }); // 提取搜索结果 const results = response.data.results || []; if (results.length === 0) { return "No results found."; } // 格式化结果(标题 + 摘要 + 链接) const formattedResults = results .slice(0, 5) // 限制返回前 5 条结果,避免过长 .map((result, index) => { return `${index + 1}. ${result.title}\n ${result.content || "No snippet available"}\n ${result.url}`; }) .join("\n\n"); return formattedResults || "No relevant data extracted."; } catch (error) { console.error("SearXNG search failed:", error.message); return `Error: ${error.message}`; } }, { name:"searXNGSearchTool", description:"A tool to search the web using a self-hosted SearXNG instance.", schema:z.string() } ); const tools = [ searXNGSearchTool, currentDateTool, new Calculator() ]; const executor = await initializeAgentExecutorWithOptions(tools, model, { // agentType: "structured-chat-zero-shot-react-description", // agentType: "chat-conversational-react-description", // agentType: "zero-shot-react-description", agentType: "chat-zero-shot-react-description", verbose: true, }); console.log("Loaded agent."); const input = `姚明的妻子是谁?请获取当前的具体时间来计算她当前的年龄?她年龄的0.76次方是多少?`; console.log(`Executing with input "${input}"...`); const result = await executor.invoke({ input }); console.log(`Got output ${JSON.stringify(result, null, 2)}`); }; run();
-