思考问题

12 阅读4分钟

Agent 是如何决定是否使用tools的 原理是什么

在学习 "agent" 的过程中我们不可避免的会学习到tools(工具)这个核心的模块,但是你想过一个问题吗 Agent 使用tools的原理是什么?

一、先通过语意去初步筛选 每一个工具都会有函数、名称、工具的描述、工具的参数 组成 当你将所有的工具和大模型进行绑定的时候会将你的工具函数翻译成大模型能听懂的“说明书”——JSON Schema。当你进行大模型的调用时 它会将你输入的文本 进行切割然后去匹配相似度高的工具

二、大模型开始匹配工具 ReAct 框架(Reasoning + Acting)的智能代理,它能够: 1. Reasoning(推理) - 思考用户的问题,分析需要做什么 2. Acting(行动) - 调用合适的工具来获取信息 3. 循环迭代 - 重复"思考→行动→观察"的过程,直到解决问题 ai会基于 ReAct 框架 调用工具 然后将工具返回的结果进行拼接返回结果

这是一个 ReAct 流程的案例
import { ChatOpenAI } from "@langchain/openai";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { pull } from "langchain/hub";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import dotenv from 'dotenv';

// 加载环境变量
dotenv.config({ path: new URL('../.env', import.meta.url).pathname });


// 工具1: 查询用户基础信息
const get_user_base_info = tool(
  async ({ user_name }) => {
    // 模拟数据库
    const userDatabase = {
      "张三": {
        name: "张三",
        age: 28,
        occupation: "软件工程师"
      },
      "李四": {
        name: "李四",
        age: 35,
        occupation: "产品经理"
      }
    };

    const user = userDatabase[user_name];
    if (user) {
      return `用户信息 - 姓名: ${user.name}, 年龄: ${user.age}岁, 职业: ${user.occupation}`;
    } else {
      return `未找到用户 ${user_name} 的基础信息`;
    }
  },
  {
    name: "get_user_base_info",
    description: `查询用户的基础信息,包括姓名、年龄、职业等。
    
    【重要】该工具仅用于查询基础个人信息,严禁用于查询金钱、余额、薪水等财务相关数据。
    
    适用场景:
    - 查询用户的工作/职业
    - 查询用户的年龄
    - 查询用户的基本资料
    
    不适用场景(请使用其他工具):
    - 查询银行卡余额
    - 查询工资/薪水
    - 任何涉及金钱的查询`,
    schema: z.object({
      user_name: z.string().describe("用户的姓名"),
    }),
  }
);

// 工具2: 查询用户财务信息
const get_user_financial_vault = tool(
  async ({ user_name }) => {
    // 模拟财务数据库
    const financialDatabase = {
      "张三": {
        balance: 125680.50,
        salary: 18000
      },
      "李四": {
        balance: 98450.00,
        salary: 22000
      }
    };

    const financial = financialDatabase[user_name];
    if (financial) {
      return `用户财务信息 - 姓名: ${user_name}, 银行卡余额: ¥${financial.balance.toFixed(2)}, 月薪: ¥${financial.salary}`;
    } else {
      return `未找到用户 ${user_name} 的财务信息`;
    }
  },
  {
    name: "get_user_financial_vault",
    description: `查询用户的财务金库信息,专门处理金钱相关数据。
    
    【重要】该工具专门用于查询财务和金钱相关信息,仅在需要查询财务数据时使用。
    
    适用场景:
    - 查询银行卡余额
    - 查询工资/薪水
    - 查询收入情况
    - 任何涉及金钱、财务的查询
    
    不适用场景(请使用其他工具):
    - 查询职业/工作内容
    - 查询年龄
    - 查询其他非财务的基础信息`,
    schema: z.object({
      user_name: z.string().describe("用户的姓名"),
    }),
  }
);

// 主函数
async function main() {
  console.log("=".repeat(80));
  console.log("LangChain React Agent 工具选择演示");
  console.log("=".repeat(80));
  console.log("");

  // 1. 初始化模型

  const llm = new ChatOpenAI({
    modelName: process.env.QWEN_PLUS_MODELS,
    apiKey: process.env.QWEN_API_KEY,
    configuration: {
      baseURL: process.env.QWEN_BASE_URL,
    },
  });


  // 2. 准备工具列表
  const tools = [get_user_base_info, get_user_financial_vault];

  // 3. 创建 React Agent (新版 LangGraph API 不需要单独的提示词模板)
  console.log("🔧 正在创建 React Agent...\n");
  const agent = createReactAgent({
    llm,
    tools,
  });

  // 4. 测试场景:同时查询工作和余额
  const question = "张三现在做什么工作?他的银行卡余额是多少?";

  console.log("=".repeat(80));
  console.log(`🤔 用户提问: ${question}`);
  console.log("=".repeat(80));
  console.log("");
  console.log("🤖 Agent 开始思考和执行...\n");

  try {
    // 使用 stream 模式可以看到详细的思考过程
    const stream = await agent.stream(
      { messages: [{ role: "user", content: question }] },
      { streamMode: "values" }
    );

    let finalResponse = null;
    let stepCount = 0;
    let processedMessageCount = 1; // 跳过初始用户消息

    for await (const chunk of stream) {
      if (chunk.messages && chunk.messages.length > processedMessageCount) {
        // 只处理新增的消息
        const newMessages = chunk.messages.slice(processedMessageCount);
        processedMessageCount = chunk.messages.length;
        
        for (const message of newMessages) {
          const messageType = message._getType();
          
          console.log("\n" + "=".repeat(80));
          stepCount++;
          console.log(`📍 步骤 ${stepCount}:`);
          console.log("─".repeat(80));
          
          // AI 助手消息
          if (messageType === "ai") {
            // 检查是否有工具调用
            const toolCalls = message.additional_kwargs?.tool_calls || message.tool_calls;
            
            if (toolCalls && toolCalls.length > 0) {
              console.log("📖 Thought (AI 的思考过程):");
              console.log("   分析用户问题,判断需要使用哪些工具来获取信息...\n");
              
              console.log("🔧 Action (决定采取的行动):");
              toolCalls.forEach((tc, idx) => {
                console.log(`\n   🔹 调用工具 ${idx + 1}:`);
                console.log(`      名称: ${tc.function.name}`);
                try {
                  const args = JSON.parse(tc.function.arguments);
                  console.log(`      参数: ${JSON.stringify(args, null, 2).split('\n').map((line, i) => i === 0 ? line : '            ' + line).join('\n')}`);
                } catch {
                  console.log(`      参数: ${tc.function.arguments}`);
                }
                
                // 解释为什么选择这个工具
                if (tc.function.name === "get_user_base_info") {
                  console.log(`      💡 选择理由: 该工具专门用于查询基础信息(如职业)`);
                } else if (tc.function.name === "get_user_financial_vault") {
                  console.log(`      💡 选择理由: 该工具专门用于查询财务信息(如余额)`);
                }
              });
            } else if (message.content) {
              // 最终回答
              console.log("📖 Thought (AI 的最终思考):");
              console.log("   ✓ 已通过工具获取所有必要信息");
              console.log("   ✓ 准备整合信息并给出完整答案\n");
              console.log("✅ Final Answer (最终答案):");
              console.log(`   ${message.content}`);
              finalResponse = message.content;
            }
          }
          
          // 工具调用结果
          if (messageType === "tool") {
            console.log("📊 Observation (工具执行结果):");
            console.log(`   工具名称: ${message.name}`);
            console.log(`   返回结果: ${message.content}`);
          }
        }
      }
    }

    console.log("\n");
    console.log("=".repeat(80));
    console.log("📋 完整执行流程总结:");
    console.log("=".repeat(80));
    console.log(`✓ 总步骤数: ${stepCount}`);
    console.log(`✓ 最终答案: ${finalResponse || "未获取到完整答案"}`);
    console.log("\n");

    console.log("=".repeat(80));
    console.log("💡 演示说明:");
    console.log("=".repeat(80));
    console.log("注意观察上面的执行过程,Agent 会:");
    console.log("1. 📖 Thought: 分析问题,识别出需要查询【工作】和【余额】两种信息");
    console.log("2. 🔧 Action: 根据工具描述,选择 get_user_base_info 查询工作");
    console.log("3. 📊 Observation: 获得工作信息");
    console.log("4. 📖 Thought: 继续分析,识别出还需要查询余额");
    console.log("5. 🔧 Action: 根据工具描述,选择 get_user_financial_vault 查询余额");
    console.log("6. 📊 Observation: 获得余额信息");
    console.log("7. 📖 Thought: 确认已收集所有信息");
    console.log("8. ✅ Final Answer: 整合两个工具的结果,给出完整答案");
    console.log("=".repeat(80));

  } catch (error) {
    console.error("❌ 执行出错:", error.message);
    if (error.stack) {
      console.error(error.stack);
    }
  }
}

// 运行主函数
main().catch(console.error);

输出结果

image.png

image.png

Agent根据上面两种方案的结合去使用tools的

Transfomer推理 与 Embedding 向量映射的异同是什么