agent之zero-shot reacty的实例

104 阅读4分钟

这种Agent使用ReAct(Retrieve-and-Act)框架,该框架通过理解工具的描述来选择最合适的工具执行任务。Zero-shot意味着Agent不需要针对特定任务进行训练,而是可以基于工具的描述直接进行推断。

这边有四种类型,他们的特性如下:

特性chat-conversational-react-descriptionstructured-chat-zero-shot-react-descriptionchat-zero-shot-react-descriptionzero-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)中等(正则或简单解析)高(严格格式要求)
灵活性高(自然交互)低(强制结构化)中等(较灵活)低(格式固定)
实现基础ConversationalAgentStructuredChatAgent基础 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次方

  • 代码

    1. 初始化工具,这边用到三个工具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()
          ];
      
    2. 创建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)}`);
      
    3. 使用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,
          };
      };
      
    4. 完整的代码如下,需要替换成自己的 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();
      ​