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
}

目录

相关推荐
仿生狮子2 小时前
OpenAI 攻下千禧年难题之后,开发者似乎无动于衷
前端·数学·vibecoding
Lvan的前端笔记6 小时前
docker:每个前端项目一个 Nginx 容器还是只有一个Nginx容器
前端·nginx·docker
三天不学习6 小时前
Egg.js 4 突然爆火,原因是否归结于AI 原生落地需求爆发?
前端·javascript·全栈·egg.js
xcs194057 小时前
前端 vue 的前端页面debugger 进不去
前端·javascript·vue.js
明月_清风8 小时前
Deno 终局来了:从挑战 Node 到被 Cloudflare 收编
前端·后端·node.js
Csvn9 小时前
框架性能优化
前端
回眸&啤酒鸭11 小时前
【回眸】OpenSwarm 多智能体协作系统实战指南
大数据·前端·人工智能
用户693717500138411 小时前
2026,程序员的时代拐点到了
android·前端·后端
大龄秃头程序员11 小时前
一次 iBeacon + BLE 无感解锁方案的实现记录
前端
小兔子11 小时前
Python 的 GIL 与 free-threading:3.13 之后「去 GIL」走到哪一步了
前端