当前位置: 首页 > news >正文

【LLM LangChain】AgentExecutor 创建带工具的Agent+加入BufferMemory+支持多用户记忆 demos

文章目录

    • 使用工具
    • 加入记忆
      • BufferMemory关键方法 / 接口
      • 简单BufferMemory实现代码
      • 支持多用户 session 记忆
      • 用法示例:
      • 清除历史

使用工具

import { NextRequest, NextResponse } from "next/server";
import { StreamingTextResponse } from "ai";// LangChain
import { ChatOpenAI } from "@langchain/openai";
import { DynamicTool } from "@langchain/core/tools";
import { initializeAgentExecutorWithOptions } from "langchain/agents";export const runtime = "edge";// 定义一个示例工具(加法器)
const addTool = new DynamicTool({name: "add_numbers",description: "Add two numbers together. 输入格式: 'a,b'",func: async (input: string) => {const [a, b] = input.split(",").map(Number);return (a + b).toString();},
});export async function POST(req: NextRequest) {try {const body = await req.json();const messages = body.messages ?? [];const currentMessageContent = messages[messages.length - 1].content;// 初始化模型const model = new ChatOpenAI({model: "qwen-plus",apiKey: process.env.MODEL_API_KEY,configuration: {baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",},});// 初始化 AgentExecutorconst executor = await initializeAgentExecutorWithOptions([addTool], // 工具列表model,{agentType: "openai-functions", // 使用函数调用式 agentverbose: true,});// 注意:AgentExecutor 本身不返回异步 stream// 需要手动封装流式输出// const stream = await executor.stream({//   input: currentMessageContent,// }); // executor.stream() → 迭代器,不是字符串流// const stream = await executor.stream({//   input: currentMessageContent,//   chat_history: [],   //  而不是 "" 或 undefined // 必须补上// });//  把 agent 的迭代结果转成 string 流async function* streamText() {for await (const step of await executor.stream({input: currentMessageContent,chat_history: [], // 必须是数组})) {if (step.output) {yield step.output; // 只推送最终的文本}}}return new StreamingTextResponse(streamText());// return new StreamingTextResponse(stream);} catch (e: any) {return NextResponse.json({ error: e.message },{ status: e.status ?? 500 });}
}

加入记忆

  • BufferMemory 是 LangChain 中最基础的记忆类型,它会把对话的完整历史记录按顺序保存在内存中,并在每次调用模型时作为上下文传入。它的特点是实现简单、语义完整,非常适合短对话场景或 Demo。但因为历史会不断累积,如果对话太长,可能会导致上下文超出模型的 token 限制。因此,BufferMemory 更适合用在 短会话应用,或者作为入门理解 LangChain 记忆机制的基础组件。

BufferMemory关键方法 / 接口

方法 / 属性类型作用典型用法
memoryKeystring存储对话历史的 key 名(默认 "history",常配合 MessagesPlaceholdernew BufferMemory({ memoryKey: "chat_history" })
inputKeystring指定用户输入在上下文对象中的 keynew BufferMemory({ inputKey: "input" })
outputKeystring指定模型输出在上下文对象中的 keynew BufferMemory({ outputKey: "output" })
returnMessagesboolean是否以 BaseMessage[] 的形式保存历史,而不是字符串拼接returnMessages: true
chatHistoryChatMessageHistory内部维护的消息记录对象(有 addUserMessage / addAIMessage / clear 等方法)memory.chatHistory.clear()
loadMemoryVariables(inputs)Promise<Record<string, any>>读取 memory 中的历史记录,返回 { [memoryKey]: ... }await memory.loadMemoryVariables({})
saveContext(inputs, outputs)Promise<void>把一次对话的输入输出存进 memoryawait memory.saveContext({ input: "你好" }, { output: "你好!" })
clear()Promise<void>清空整个 memory 的历史await memory.clear()

简单BufferMemory实现代码

// chat.tsx
import { NextRequest, NextResponse } from "next/server";
import { StreamingTextResponse } from "ai";// LangChain
import { ChatOpenAI } from "@langchain/openai";
import { DynamicTool } from "@langchain/core/tools";
import { initializeAgentExecutorWithOptions } from "langchain/agents";
import { BufferMemory } from "langchain/memory";export const runtime = "edge";// 示例工具:加法器
const addTool = new DynamicTool({name: "add_numbers",description: "Add two numbers together. 输入格式: 'a,b'",func: async (input: string) => {const [a, b] = input.split(",").map(Number);return (a + b).toString();},
});// 创建一个全局 memory(可以改成 RedisMemory、UpstashMemory,支持多用户)
// const memory = new BufferMemory({
//   memoryKey: "chat_history", // 这里要和 MessagesPlaceholder 对应
//   returnMessages: true, // 让它存 BaseMessage,而不是拼成字符串
// });
const memory = new BufferMemory({memoryKey: "chat_history",inputKey: "input",   // 指定输入的 keyoutputKey: "output", // 指定输出的 keyreturnMessages: true,
});export async function POST(req: NextRequest) {try {const body = await req.json();const messages = body.messages ?? [];const currentMessageContent = messages[messages.length - 1].content;// 初始化模型const model = new ChatOpenAI({model: "qwen-plus",apiKey: process.env.MODEL_API_KEY,configuration: {baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",},});// 初始化 AgentExecutor,并挂载 Memoryconst executor = await initializeAgentExecutorWithOptions([addTool],model,{agentType: "openai-functions",verbose: true,memory, // 注入记忆, 这样每次调用 agent 都会带上历史对话});// 把 agent 的迭代结果转成 string 流async function* streamText() {console.log("Agent 处理用户输入:", currentMessageContent);// 假如你是新闻分析师for await (const step of await executor.stream({input: currentMessageContent,chat_history: await memory.loadMemoryVariables({}).then(v => v.chat_history),})) {if (step.output) {yield step.output;}}}return new StreamingTextResponse(streamText());} catch (e: any) {return NextResponse.json({ error: e.message },{ status: e.status ?? 500 });}
}

支持多用户 session 记忆

  • 你现在的 BufferMemory 是全局的,所有用户都会共享一份 chat_history
  • 我们要在请求里传一个 sessionId,然后为每个 session 维护独立的 memory。
  • 下面是完整代码:
// chat.tsx
import { NextRequest, NextResponse } from "next/server";
import { StreamingTextResponse } from "ai";// LangChain
import { ChatOpenAI } from "@langchain/openai";
import { DynamicTool } from "@langchain/core/tools";
import { initializeAgentExecutorWithOptions } from "langchain/agents";
import { BufferMemory } from "langchain/memory";export const runtime = "edge";// 示例工具:加法器
const addTool = new DynamicTool({name: "add_numbers",description: "Add two numbers together. 输入格式: 'a,b'",func: async (input: string) => {const [a, b] = input.split(",").map(Number);return (a + b).toString();},
});// 内存缓存(每个 session 独立)
const memoryStore = new Map<string, BufferMemory>();// 工厂函数:获取或创建某个 session 的 memory
function getMemory(sessionId: string) {if (!memoryStore.has(sessionId)) {memoryStore.set(sessionId,new BufferMemory({memoryKey: "chat_history",inputKey: "input",outputKey: "output",returnMessages: true,}));}return memoryStore.get(sessionId)!;
}export async function POST(req: NextRequest) {try {const body = await req.json();const { messages = [], sessionId = "default" } = body;if (!sessionId) {throw new Error("sessionId 不能为空");}const currentMessageContent = messages[messages.length - 1]?.content ?? "";// 获取当前用户的独立 memoryconst memory = getMemory(sessionId);// 初始化模型const model = new ChatOpenAI({model: "qwen-plus",apiKey: process.env.MODEL_API_KEY,configuration: {baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",},});// 初始化 AgentExecutor,并挂载用户独立 Memoryconst executor = await initializeAgentExecutorWithOptions([addTool],model,{agentType: "openai-functions",verbose: true,memory,});// 把 agent 的迭代结果转成 string 流async function* streamText() {console.log(`[${sessionId}] 用户输入:`, currentMessageContent);for await (const step of await executor.stream({input: currentMessageContent,chat_history: await memory.loadMemoryVariables({}).then(v => v.chat_history),})) {if (step.output) {yield step.output;}}}return new StreamingTextResponse(streamText());} catch (e: any) {return NextResponse.json({ error: e.message },{ status: e.status ?? 500 });}
}

用法示例:

客户端调用时带上 sessionId

await fetch("/api/chat", {method: "POST",body: JSON.stringify({sessionId: "user123", // 每个用户/会话唯一messages: [{ role: "user", content: "假如你是新闻分析师" }],}),
});

清除历史

    // 获取当前用户的独立 memoryconst memory = getMemory(sessionId);// 执行清理if (clearHistory) {await memory.clear();return NextResponse.json({ message: `Session ${sessionId} 历史已清理` });}
http://www.dtcms.com/a/411514.html

相关文章:

  • 图书馆网站建设教程专业网站建设咨询
  • Qwen2.5 0.5b转换到iree上支持的文件
  • 做网站和平台多少钱网络营销seo是什么
  • Qt常用控件之QCalendarWidget
  • 做金属小飞机的网站怎么做网络推广网站
  • 利用php做网站教程吃货盒子 wordpress
  • 行政事业单位网站建设直播网站如何做
  • 安装xdebug调试工具(docker容器+vscode编辑器+xdebug)
  • 成都seo培训学校济宁网站建设seo
  • SpringBoot邮件发送的5大隐形地雷与避坑实战指南
  • 撼动GPT-5地位?阿里万亿参数Qwen3-Max模型发布,使用教程来了
  • 三亚市住房和城乡建设厅网站防城港网站设计
  • 西安网址开发 网站制作网站后台管理系统设计
  • HCIP-IoT 真题详解(章节D),嵌入式基础与南向开发 /Part2
  • 如何修改wordpress模板首页宽度做企业网站排名优化要多少钱
  • 守护品牌信誉,激光镭射防伪标签为您筑起安全防线
  • 网站开发课程有哪些龙岩兼职网招聘
  • Unity 虚拟仿真实验中设计模式的使用 ——状态模式(State Pattern)
  • 常见限流策略对比
  • 福建省城乡和建设厅网站陕西网站开发公司
  • 宝山手机网站制作公司那个可以做棋牌网站
  • 360免费建站怎么样排名优化软件点击
  • 如何用vw实现B站手机端底部的《打开app看你想看的视频?》
  • 做自己的网站挣钱境外网站服务器
  • 疑问:hfish的一个bug,很奇怪
  • 河北电子商务网站建设中国住房和城乡建设部网站造价师注册
  • 大连市网站建设大良网站建设基本流程
  • vue3+ts实现拖拽缩放,全屏
  • 酒店网站开发方案用php做的网站论文
  • Python03——逻辑判断