Vue创建一个简单的Agent聊天——工具调用

实现工具调用

工具调用,就是需要的操作内容(查询浏览器、天气......)封装成一个函数,让ai自行判断并调用。 先让ai调用工具,再将工具返回的数据喂给ai再调用对话

实现工具函数

ts 复制代码
import { tool } from "@langchain/core/tools";

const GAO_DE_KEY = import.meta.env.VITE_AMAP_KEY;

// 获取ip信息
export const ip_tool = async () => {
  const data = {
    key: GAO_DE_KEY,
  }
  const params = new URLSearchParams(data);
  const url = `https://restapi.amap.com/v3/ip?${params}`;
  const response = await fetch(url);
  const ip_data = await response.json();
  return ip_data;
}

// 获取天气信息
export const weather_tool = async () => {
  const ipInfo = await ip_tool();
  const data = {
    key: GAO_DE_KEY,
    city: ipInfo.city,
    extensions: "all",
    output: "json"
  };
  const params = new URLSearchParams(data);
  const url = `https://restapi.amap.com/v3/weather/weatherInfo?${params}`;
  const response = await fetch(url);
  const weather_data = await response.json();
  return weather_data
}

// 获取当前时间
export const date_tool = () => {
  const now = new Date();
  const year = now.getFullYear();
  const month = String(now.getMonth() + 1).padStart(2, '0');
  const day = String(now.getDate()).padStart(2, '0');
  const hours = String(now.getHours()).padStart(2, '0');
  const minutes = String(now.getMinutes()).padStart(2, '0');
  const seconds = String(now.getSeconds()).padStart(2, '0');

  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}

const weatherTool = tool(weather_tool, {
  name: "weather",
  description: "获取当前天气信息,不需要输入参数"
})

const ipTool = tool(ip_tool, {
  name: "ip",
  description: "获取当前IP地址"
})

const dateTool = tool(date_tool, {
  name: "date",
  description: "获取当前日期和时间"
})

export const tools = [weatherTool, ipTool, dateTool]

修改Agent.ts

ts 复制代码
import type { Chat } from "@/types/chat";
import { AIMessage, HumanMessage } from "@langchain/core/messages";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { RunnableSequence } from "@langchain/core/runnables";
import { ChatOpenAI } from "@langchain/openai";
import { tools } from "./tools";

function createLLM() {
  // 创建LLM并绑定工具
  const llm = new ChatOpenAI({
    model: import.meta.env.VITE_AI_MODEL,
    apiKey: import.meta.env.VITE_API_KEY,
    configuration: {
      baseURL: import.meta.env.VITE_AI_BASE_URL,
    },
    temperature: 0.7, // ai生成的内容丰富度
    streaming: true,
  })

  return llm.bindTools(tools) // 绑定工具,让AI知道有哪些工具可用
}


......


export default async function chat(
  input: string,
  onChat: (text: string) => void,
  messages: Chat[] = []) {

  // 历史记录
  const allMessages: Array<HumanMessage | AIMessage> = []

  // 遍历历史聊天记录
  for (const msg of messages) {
    if (msg.role === "user") {
      allMessages.push(new HumanMessage(msg.messages))
    } else if (msg.role === "assistant") {
      allMessages.push(new AIMessage(msg.messages))
    }
  }

  // 添加当前用户信息
  allMessages.push(new HumanMessage(input))

  const chain = createChain()

  // 第一次调用:AI决定是否使用工具
  const res = await chain.stream({
    chat_history: allMessages,
    input: input,
  })

  let resText = ""
  let hasToolCalls = false
  const toolCalls: Array<{ name: string; args: Record<string, unknown> }> = []

  // 处理流式响应
  for await (const chunk of res) {


    // 检查是需要工具调用
    if (chunk.tool_calls && chunk.tool_calls.length > 0) {
      hasToolCalls = true
      toolCalls.push(...chunk.tool_calls.map(tc => ({
        name: tc.name,
        args: tc.args
      })))
    } else {
      const content = String(chunk.content) ?? ""
      resText += content
      if (onChat) {
        onChat(String(chunk.content) || "")
      }
    }


  }

  // 如果有工具调用,执行工具并继续对话
  if (hasToolCalls && toolCalls.length > 0) {
    // 执行工具调用
    const toolResults: string[] = [] // 存储工具调用结果

    for (const tc of toolCalls) {
      const tool = tools.find(t => t.name === tc.name) // 找到工具
      if (tool) {
        try {
          // 调用工具
          const result = await tool.invoke(tc.args)
          toolResults.push(`工具 ${tc.name} 执行结果: ${JSON.stringify(result)}`)
        } catch (error) {
          toolResults.push(`工具 ${tc.name} 执行失败: ${error}`)
        }
      }
    }

    // 将工具结果添加到消息历史(并非页面显示的内容,这里是已经传递来的参数)
    allMessages.push(new AIMessage(resText))
    allMessages.push(new HumanMessage(`工具执行结果:\n${toolResults.join('\n')}`))

    // 第二次调用:AI根据工具结果生成最终回复
    const finalRes = await chain.stream({
      chat_history: allMessages,
      input: "请根据工具执行结果回答用户的问题",
    })

    for await (const chunk of finalRes) {
      const content = String(chunk.content) ?? ""
      resText += content
      if (onChat) {
        onChat(String(chunk.content) || "")
      }
    }
  }

  return resText
}

目录

相关推荐
牛奶1 小时前
开发者的"奇技淫巧":那些让你效率翻倍的实战技巧
前端·后端·程序员
Timo来了1 小时前
indexDB的用法示例
前端
walking9571 小时前
重新学习前端之设计模式与架构
前端·javascript·面试
walking9571 小时前
重新学习前端之TypeScript
前端·javascript·面试
walking9571 小时前
重新学习前端之Linux
前端·vue.js·面试
walking9571 小时前
重新学习前端之CSS
前端·vue.js·面试
walking9571 小时前
重新学习前端之Git
前端·vue.js·面试
walking9571 小时前
重新学习前端之小程序
前端
魔术师Grace1 小时前
AI让我退化成原始人了
前端·程序员