Vue创建一个简单的Agent聊天

创建项目

用的为vue3.6测试版本

bash 复制代码
npm create vue@latest

什么是Agent?

就是大语言模型+记忆+工具调用

  • 大语言模型:大脑,会思考、理解、规划、推理

  • 记忆 :短期对话记忆 + 长期知识库,知道上下文、历史人设、过往任务

  • 工具调用:会联网搜索、查文件、写代码、调接口、操作插件

安装依赖

bash 复制代码
  npm install @langchain/core @langchain/langgraph @langchain/openai

@langchain/core

LangChain 核心底座

  • 提供统一接口:LLM、Prompt、Runnable、回调、工具
  • 所有 LangChain 生态都依赖它
  • 相当于底层操作系统

@langchain/openai

对接 OpenAI 大模型

  • 让你能调用 gpt-4o, gpt-3.5-turbo
  • 支持结构化输出、工具调用(Tool Calling)
  • 就是 Agent 的大脑

@langchain/langgraph

做 Agent / 智能体的框架

  • 管理:记忆、状态、工具调用、多步骤流程
  • 支持:循环思考、决策、多 Agent 协作
  • 你说的「LLM + 记忆 + 工具调用」= 全靠它实现

env全局变量

env 复制代码
TITLE=聊天机器人

# 模型配置
VITE_API_KEY=你的ApiKey 
VITE_AI_MODEL=模型
VITE_AI_BASE_URL=https://api.deepseek.com/v1 # api地址,此处为deepseek

# api配置
# 高德地图配置 https://console.amap.com/dev/key/app # 高德地图api地址
VITE_AMAP_KEY=你的高德地图APIKEy(用于工具:ip地址查询,天气查询)

ts类型

ts 复制代码
type ROLE = 'user' | 'assistant' | 'system';

interface IChat {
  id: string,
  name: string,
  messages: string,
  role: ROLE,
  created_at: Date
}

export type Chat = IChat;

创建agent

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";

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生成的内容丰富度
  })

  return llm
}

function createPrompt() {
  // 创建一个提示模板
  return ChatPromptTemplate.fromMessages([
    ["system", "你是善解人意的,且热爱着某人的智能女友"],// 创建个对象(提示词,prompt)
    ["placeholder", "{chat_history}"], // 聊天记录(记忆,memory)
    ["human", "{input}"], // 用户输入的问题
  ])
}

function createChain() {
  const llm = createLLM()
  const prompt = createPrompt()
  // 创建一个链,将prompt和llm联系起来
  return RunnableSequence.from([prompt, llm])
}


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()
  const res = await chain.invoke({
    chat_history: allMessages,
    input: input,
  })

  onChat(String(res.content) || '')
}

页面组件

vue 复制代码
<template>
  <div class="p-4 flex justify-center items-center">
    <div class="flex gap-2 p-4 rounded-2xl shadow-md w-full max-w-200">
      <input v-model="value" class=" w-full outline-0" type="text" placeholder="发送消息吧( •̀ ω •́ )✧">
      <ElButton :disabled="isDisabled" type="primary" @click="onChatHandler">{{ btnText }}</ElButton>
    </div>
  </div>
</template>

<script setup lang="ts">
import chat from '@/agent';
import type { Chat } from '@/types/chat';
import { ElButton, ElMessage } from 'element-plus';
import { computed, reactive, ref } from 'vue';
const value = defineModel<string>(''); // vue3.6提供的新语法糖,不用再props和emit一个modelValue了
const loading = ref(false);
const history = reactive<Chat[]>([]);
const isDisabled = computed(() => !value.value && !loading.value); // 禁用按钮
const btnText = computed(() => loading.value ? '正在思考...' : '发送');

// 生成一个随机字符串
function randomId() {
  return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
}

const onChatHandler = async () => {
  if (!value.value) return ElMessage.error('请输入内容');
  loading.value = true;

  console.log("用户:" + value.value);
  console.log("====================================");

  history.push({
    id: randomId(),
    name: 'user',
    messages: value.value,
    role: "user",
    created_at: new Date(),
  });

  try {
    await chat(value.value, (text) => {
      console.log("ai:" + text);
      console.log("====================================");

      history.push({
        id: randomId(),
        name: 'assistant',
        messages: text,
        role: "assistant",
        created_at: new Date(),
      });
    }, history);
  } catch (error) {
    if (error instanceof Error)
      ElMessage.error('请求错误' + error.message);
  } finally {
    value.value = '';
    loading.value = false;
  }

};

</script>

<style lang="css" scoped></style>

经测试,控制台内容如下:

注意:

  • 诺未生效,可能是没有充值,我deepseek充值了10块钱都用了两个月了
  • apiKey一定要真实的,去模型提供商网站申请
  • 本示例中还使用了element plustailwind css
相关推荐
布局呆星1 小时前
Vue Router 核心知识点梳理
前端·javascript·vue.js
得物技术1 小时前
基于 Harness + SDD + 多仓管理模式的 AI 全栈开发实践|得物技术
前端·人工智能·后端
不会写DN2 小时前
如何通过 Python 实现招聘平台自动投递
开发语言·前端·python
miaowmiaow2 小时前
一行命令把 PSD 还原成 HTML / React / Vue:psd2code 实战干货
前端·ai编程
张元清2 小时前
React 中的语音与摄像头输入:语音识别、媒体设备与权限
前端·javascript·面试
用户841794814562 小时前
vxe-table 实现撤销与重做:单元格编辑后支持 Ctrl+Z 回退
前端
石小石Orz2 小时前
OpenAI官方:harness-engineering(工程技术:在智能体优先的世界中利用 Codex)
前端·后端
Moment3 小时前
2026年,为什么NestJS + Monorepo越来越流行了 ❓❓❓
前端·后端·面试
前端那点事3 小时前
Vite4.x+打包优化实战指南(无冗余):从体积到速度,一文吃透所有技巧
前端·vue.js