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
}

目录

相关推荐
随风一样自由14 小时前
【前端独角兽】刚进入前端领域的前端开发用jQuery还是Vue3?
前端·javascript·vue3·jquery
IMPYLH14 小时前
HTML 的 <data> 元素
前端·html
YHHLAI15 小时前
[特殊字符] 面试中的 Promise —— 从入门到精通
前端·javascript·面试
kyriewen17 小时前
我扒了 GPT-5.6 的全部编程跑分——有一项 OpenAI 没敢放出来
前端·gpt·ai编程
前端H17 小时前
微前端 v3 架构升级:从加载隔离到状态协同
前端·架构·状态模式
陆枫Larry18 小时前
小程序包体积优化:用 SVG 图片替换 iconfont,并保留 CSS 控色能力
前端
环境栈笔记20 小时前
多账号浏览器选型检查清单:Profile、权限、Session 和任务日志怎么评估
前端·人工智能·后端·自动化
前端炒粉20 小时前
手写promise
java·前端·javascript
LaughingZhu20 小时前
智能体经典范式构建:ReAct、Plan-and-Solve 与 Reflection
前端·react.js·前端框架
jsonbro20 小时前
手把手带你实现发布订阅模式
前端·vue.js·设计模式