前端开发完整LangChain AI智能体实战(附源码)

作者:技术拾光

时间:2026-06-07

浏览:480

基于LangChain和LangGraph构建前端开发AI智能体,具备多轮对话记忆、工具调用(代码格式化、文件读取、数据转换)、自主决策(ReAct模式)及状态追踪能力,使用阿里云百炼qwen-plus模型,提供完整源码实现,支持多种前端开发场景,提升开发效率。

文章目录

前置知识

开工之前,得先确认几样基础能力已经到位。如果你已经熟悉了LangChain的几个核心概念,那接下来的内容会顺畅很多。

前端开发完整LangChain AI智能体实战(附源码)

  • LangChain 基础:ChatOpenAI、PromptTemplate 的基本用法——这是最底层的能力,相当于砖块和水泥。
  • Memory 机制:对话记忆的原理与实现——知道怎么让AI记住“你是谁”,而不是每轮都像第一次见面。
  • Tool 封装:自定义工具的开发规范——工具就像是AI的“手”,让它能做点什么实在事。
  • Agent 基础:ReAct 模式的理解——说白了就是“先想、再动”的决策循环。
  • LangGraph 入门:节点、边、状态的基本概念——这是整个系统的“骨架”,把各个模块串联起来。

如果上述知识点还不太牢固,建议先花点时间回顾一下。准备好了?那我们直接开始搭建一个完整的前端开发助手智能体。

项目整体架构设计

项目概述

这次实战的目标很明确:做一个能真正干活的前端开发助手。它不能只会聊天,还要会操作代码、读写文件、转换数据格式。具体能力如下:

  • 多轮对话记忆(跨会话持久化)——AI能记住之前的对话内容,哪怕你隔天再回来问,它也不会失忆。
  • 多种前端工具调用(代码格式化、文件读取、数据转换)——这些是前端开发最频繁的“小操作”,AI能替你搞定。
  • 自主决策与任务规划(ReAct 模式)——AI会分析你的需求,自己决定该调用哪个工具、怎么执行。
  • 可观测的执行流程(LangGraph 状态追踪)——每一步都有迹可循,方便调试和优化。

整体架构图

先看一张架构图,了解整体脉络:

graph TDA[用户输入] --> B[LangGraph 工作流]subgraph B [LangGraph 工作流]C[agent
推理] --> D[tools
执行]D --> E[should_continue
条件路由]E -->|有工具调用| CendB --> G[核心能力层]subgraph G [核心能力层]H[持久化记忆
JSON存储] -.-I[工具集
3个业务工具] -.-J[基础模型
阿里云百炼] -.-K[状态管理
LangGraph]end

模块划分

整个项目按职责拆成五个模块,每个模块负责一块独立的能力:

模块 文件 职责
配置模块 config.ts 环境变量、模型初始化
记忆模块 memory.ts 对话历史的持久化存储与加载
工具模块 tools/ 前端专用工具集(代码格式化、文件读取、数据转换)
工作流模块 workflow.ts LangGraph 状态图定义与编译
入口模块 index.ts 交互式命令行界面

第一步:项目初始化与环境配置

1.1 创建项目

先搭建脚手架。这部分比较机械,但每一步都不能少。

# 创建项目文件夹
mkdir ai-frontend-assistant
cd ai-frontend-assistant
# 初始化 npm 项目
npm init -y
# 安装依赖
npm install @langchain/openai @langchain/core @langchain/langgraph dotenv zod
npm install -D typescript @types/node tsx
# 创建源代码目录
mkdir src src/tools

1.2 环境变量配置

创建 .env 文件,把API Key、模型地址等敏感信息放进去:

# .env
DASHSCOPE_API_KEY=你的API Key
DASHSCOPE_API_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
# 记忆文件存储路径
MEMORY_FILE_PATH=./memory.json
# 最大迭代次数
MAX_AGENT_ITERATIONS=5

这里要注意,API Key千万不要硬编码在代码里,.gitignore里记得加上。

1.3 TypeScript 配置

TypeScript 配置直接贴过来,没什么悬念:

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

第二步:核心功能模块实现

2.1 配置模块

配置模块负责初始化模型和统一管理各种参数。选用阿里云百炼的qwen-plus版本,在性能和成本之间取了个平衡。温度设置为0.3,目的是让AI的决策更稳定——通俗点说,就是让它在同一问题上不会每次蹦出不同的答案。

// src/config.ts
import { ChatOpenAI } from "@langchain/openai";
import dotenv from "dotenv";
dotenv.config();

// 模型配置
export const MODEL_CONFIG = {
  apiKey: process.env.DASHSCOPE_API_KEY!,
  baseURL: process.env.DASHSCOPE_API_URL!,
  model: "qwen-plus", // 使用 plus 版本,平衡性能和成本
  temperature: 0.3, // 低温度,提高决策稳定性
  maxTokens: 2048,
};

// 创建模型实例
export function createModel() {
  return new ChatOpenAI({
    apiKey: process.env.DASHSCOPE_API_KEY,
    configuration: {
      baseURL: process.env.DASHSCOPE_API_URL,
    },
    model: MODEL_CONFIG.model,
    temperature: MODEL_CONFIG.temperature,
    maxTokens: MODEL_CONFIG.maxTokens,
  });
}

// Agent 配置
export const AGENT_CONFIG = {
  maxIterations: parseInt(process.env.MAX_AGENT_ITERATIONS || "5"),
  systemPrompt: `你是一个专业的前端开发助手,具备以下能力:
1. 代码格式化:使用 code_formatter 工具
2. 文件读取:使用 file_reader 工具
3. 数据转换:使用 data_converter 工具
请遵循 ReAct 模式:
- 先分析用户需求,决定是否需要调用工具
- 需要时调用合适的工具,等待结果后再继续
- 完成任务后用友好的方式回复用户
注意:最多调用 5 次工具,避免无限循环。`,
};

// 记忆配置
export const MEMORY_CONFIG = {
  filePath: process.env.MEMORY_FILE_PATH || "./memory.json",
  maxHistoryTokens: 4000, // 最大历史 tokens
  maxMessages: 20, // 最大消息条数
};

系统提示词写得尽量清晰直接,明确告诉AI它有哪三个工具可用,以及该遵守什么样的工作流程。这是在给AI设定行为边界,免得它天马行空。

2.2 记忆模块(持久化)

记忆模块是整个项目中最容易被低估却至关重要的部分。一个没有记忆的AI助手,每次对话都是一张白纸——那和刚安装好还没配置的软件有什么区别?

代码实现了一个MemoryManager类,内部维护着一个Map结构的会话池。核心逻辑包括:

  • load():从JSON文件加载之前保存的全部会话,并恢复为BaseMessage实例。
  • save():将当前所有会话序列化后写入本地文件。
  • 滑动窗口机制:当单个会话的消息数超过上限(20条)时,会裁剪掉最早的记录——这既是性能考量,也是为了让AI的上下文保持在合理的“视野”之内。

完整代码如下:

// src/memory.ts
import { BaseMessage, HumanMessage, AIMessage } from "@langchain/core/messages";
import fs from "fs/promises";
import path from "path";
import { MEMORY_CONFIG } from "./config";

// 会话状态接口
export interface SessionState {
  sessionId: string;
  messages: BaseMessage[];
  createdAt: number;
  updatedAt: number;
  metadata: Record;
}

// 记忆管理器类
export class MemoryManager {
  private sessions: Map = new Map();
  private filePath: string;

  constructor(filePath: string = MEMORY_CONFIG.filePath) {
    this.filePath = path.resolve(filePath);
  }

  // 加载持久化数据
  async load(): Promise {
    try {
      const content = await fs.readFile(this.filePath, "utf-8");
      const data = JSON.parse(content);
      // 恢复会话数据
      for (const session of data.sessions || []) {
        // 将存储的普通对象转换回 BaseMessage 实例
        const messages = (session.messages || []).map((msg: any) => {
          if (msg.type === "human") {
            return new HumanMessage(msg.content);
          }
          return new AIMessage(msg.content);
        });
        this.sessions.set(session.sessionId, { ...session, messages });
      }
      console.log(` 已加载 ${this.sessions.size} 个会话`);
    } catch (error) {
      // 文件不存在,使用空状态
      console.log(" 未找到记忆文件,将创建新会话");
    }
  }

  // 保存到持久化存储
  async save(): Promise {
    const data = {
      sessions: Array.from(this.sessions.values()).map(session => ({
        ...session,
        messages: session.messages.map(msg => ({
          type: msg._getType(),
          content: msg.content,
        })),
      })),
      lastUpdated: Date.now(),
    };
    await fs.writeFile(this.filePath, JSON.stringify(data, null, 2));
    console.log(`? 已保存 ${this.sessions.size} 个会话`);
  }

  // 获取或创建会话
  getOrCreateSession(sessionId: string): SessionState {
    if (this.sessions.has(sessionId)) {
      return this.sessions.get(sessionId)!;
    }
    const newSession: SessionState = {
      sessionId,
      messages: [],
      createdAt: Date.now(),
      updatedAt: Date.now(),
    };
    this.sessions.set(sessionId, newSession);
    return newSession;
  }

  // 添加消息到会话
  addMessage(sessionId: string, message: BaseMessage): void {
    const session = this.getOrCreateSession(sessionId);
    session.messages.push(message);
    session.updatedAt = Date.now();
    // 限制消息数量(滑动窗口)
    if (session.messages.length > MEMORY_CONFIG.maxMessages) {
      session.messages = session.messages.slice(-MEMORY_CONFIG.maxMessages);
    }
  }

  // 获取会话历史
  getHistory(sessionId: string): BaseMessage[] {
    const session = this.getOrCreateSession(sessionId);
    return [...session.messages];
  }

  // 清空会话
  async clearSession(sessionId: string): Promise {
    this.sessions.delete(sessionId);
    await this.save();
  }

  // 获取所有会话列表
  listSessions(): { sessionId: string; createdAt: number; updatedAt: number; messageCount: number }[] {
    return Array.from(this.sessions.values())map(session => ({
      sessionId: session.sessionId,
      createdAt: session.createdAt,
      updatedAt: session.updatedAt,
      messageCount: session.messages.length,
    }));
  }
}

2.3 工具模块实现

工具 1:代码格式化

代码格式化工具其实是三个工具里最“有门槛”的。示例中提供了一个最基本的JavaScript和JSON格式化实现,生产环境可以集成Prettier等专业工具。这里最重要的是Zod校验逻辑——参数类型、默认值、校验规则都定义清楚,AI在调用时才能少犯错。

// src/tools/code-formatter.ts
import { tool } from "@langchain/core/tools";
import { z } from "zod";

// 模拟代码格式化(生产环境可集成 prettier)
function formatJavaScript(code: string, indentSize: number = 2): string {
  const indent = " ".repeat(indentSize);
  return code
    .split("n")
    .map(line => line.trim())
    .map(line => {
      if (line.match(/^[})]]/)) return line;
      if (line.match(/[({[]$/)) return indent + line;
      return indent + line;
    })
    .join("n");
}

function formatJSON(code: string, indentSize: number = 2): string {
  try {
    const obj = JSON.parse(code);
    return JSON.stringify(obj, null, indentSize);
  } catch {
    return "错误:无效的 JSON 格式";
  }
}

export const codeFormatter = tool(
  async ({ code, language, indentSize = 2 }) => {
    try {
      if (!code || code.trim().length === 0) {
        return "错误:代码内容不能为空";
      }
      let formattedCode: string;
      switch (language) {
        case "javascript":
        case "typescript":
          formattedCode = formatJavaScript(code, indentSize);
          break;
        case "json":
          formattedCode = formatJSON(code, indentSize);
          break;
        default:
          return `暂不支持的语言: ${language},支持:javascript, typescript, json`;
      }
      return ` 代码格式化完成:n\`\`\`${language}n${formattedCode}n\`\`\``;
    } catch (error) {
      return `格式化失败:${error instanceof Error  error.message : String(error)}`;
    }
  },
  {
    name: "code_formatter",
    description: `格式化前端代码,使代码更美观易读。使用场景:用户提供未格式化的代码、粘贴的代码排版混乱时。支持的语言:javascript, typescript, json`,
    schema: z.object({
      code: z.string().describe("需要格式化的原始代码"),
      language: z.enum(["javascript", "typescript", "json"]).describe("代码语言类型"),
      indentSize: z.number().min(2).max(8).default(2).describe("缩进空格数,默认2"),
    }),
  }
);

工具 2:文件读取

文件读取工具在安全上做了额外约束——只能读取项目目录内的文件,且默认限制了1MB大小。这是一个很必要的安全防护:你不希望AI能随意读取系统敏感文件。

// src/tools/file-reader.ts
import { tool } from "@langchain/core/tools";
import { z } from "zod";
import fs from "fs/promises";
import path from "path";

export const fileReader = tool(
  async ({ filePath, maxSize = 1024 * 1024 }) => {
    try {
      // 安全检查:防止路径遍历攻击
      const resolvedPath = path.resolve(filePath);
      if (!resolvedPath.startsWith(process.cwd())) {
        return ` 错误:无法访问项目目录外的文件:${filePath}`;
      }
      // 检查文件是否存在
      const stats = await fs.stat(resolvedPath).catch(() => null);
      if (!stats) {
        return ` 错误:文件不存在:${filePath}`;
      }
      // 检查文件大小
      if (stats.size > maxSize) {
        return ` 错误:文件过大(${(stats.size / 1024).toFixed(2)} KB),超过限制(${maxSize / 1024} KB)`;
      }
      // 读取文件
      const content = await fs.readFile(resolvedPath, "utf-8");
      // 获取文件扩展名用于语法高亮
      const ext = path.extname(filePath).slice(1) || "text";
      return ` 文件内容(${filePath}):n\`\`\`${ext}n${content}n\`\`\``;
    } catch (error) {
      return `读取文件失败:${error instanceof Error  error.message : String(error)}`;
    }
  },
  {
    name: "file_reader",
    description: `读取本地文件内容。使用场景:用户需要查看代码文件、阅读文档、分析配置文件时。限制:只能读取项目目录内的文件,默认最大 1MB。`,
    schema: z.object({
      filePath: z.string().describe("文件路径,支持相对路径(如 ./src/index.ts)"),
      maxSize: z.number().optional().describe("最大文件大小(字节),默认 1MB"),
    }),
  }
);

工具 3:数据转换

数据转换工具支持JSON与CSV之间的互转。CSV解析部分写了个简易实现,如果生产环境需要处理复杂CSV(比如带换行符的字段),建议换成专业的csv-parse库。

// src/tools/data-converter.ts
import { tool } from "@langchain/core/tools";
import { z } from "zod";

export const dataConverter = tool(
  async ({ input, fromFormat, toFormat }) => {
    try {
      let result: string;
      // JSON 转 CSV
      if (fromFormat === "json" && toFormat === "csv") {
        const data = JSON.parse(input);
        if (!Array.isArray(data) || data.length === 0) {
          return "错误:JSON 必须是非空数组";
        }
        const headers = Object.keys(data[0]);
        const rows = data.map(obj =>
          headers.map(header => {
            const value = obj[header];
            if (value === undefined || value === null) return "";
            if (typeof value === "object") return JSON.stringify(value);
            const str = String(value);
            return str.includes(",") || str.includes('"') ? `"${str.replace(/"/g, '""')}"` : str;
          }).join(",")
        );
        result = [headers.join(","), ...rows].join("n");
        return ` JSON 转 CSV 成功:n\`\`\`csvn${result}n\`\`\``;
      }
      // CSV 转 JSON
      if (fromFormat === "csv" && toFormat === "json") {
        const lines = input.trim().split("n");
        const headers = lines[0].split(",").map(h => h.trim().replace(/^"|"$/g, ""));
        const records = lines.slice(1).map(line => {
          // 简单的 CSV 解析(生产环境建议使用专业库)
          const values: string[] = [];
          let current = "";
          let inQuotes = false;
          for (let i = 0; i < line.length; i++) {
            const char = line[i];
            if (char === '"') {
              inQuotes = !inQuotes;
            } else if (char === "," && !inQuotes) {
              values.push(current.trim().replace(/^"|"$/g, ""));
              current = "";
            } else {
              current += char;
            }
          }
          values.push(current.trim().replace(/^"|"$/g, ""));
          return headers.reduce((obj, header, idx) => {
            obj[header] = values[idx] || "";
            return obj;
          }, {} as Record);
        });
        result = JSON.stringify(records, null, 2);
        return ` CSV 转 JSON 成功:n\`\`\`jsonn${result}n\`\`\``;
      }
      return ` 不支持的转换类型 ${fromFormat} -> ${toFormat},支持:json->csv 或 csv->json`;
    } catch (error) {
      if (error instanceof SyntaxError) {
        return `错误:JSON 格式解析失败 - ${error.message}`;
      }
      return `转换失败:${error instanceof Error  error.message : String(error)}`;
    }
  },
  {
    name: "data_converter",
    description: `数据格式转换工具,支持 JSON 和 CSV 之间的互相转换。使用场景:用户需要将 JSON 数据导出为表格格式,或将 CSV 数据转换为 JSON 结构。`,
    schema: z.object({
      input: z.string().describe("需要转换的原始数据内容"),
      fromFormat: z.enum(["json", "csv"]).describe("源数据格式"),
      toFormat: z.enum(["json", "csv"]).describe("目标数据格式"),
    }),
  }
);

三个工具写完后,通过统一的入口文件导出,方便工作流模块集中调用。

// src/tools/index.ts
import { codeFormatter } from "./code-formatter";
import { fileReader } from "./file-reader";
import { dataConverter } from "./data-converter";

// 所有工具列表
export const ALL_TOOLS = [codeFormatter, fileReader, dataConverter];

// 工具名称到工具的映射
export const TOOLS_BY_NAME = Object.fromEntries(
  ALL_TOOLS.map(tool => [tool.name, tool])
);

// 按类别获取工具
export const getToolsByCategory = (category: "code" | "file" | "data") => {
  switch (category) {
    case "code":
      return [codeFormatter];
    case "file":
      return [fileReader];
    case "data":
      return [dataConverter];
    default:
      return ALL_TOOLS;
  }
};

2.4 工作流模块(LangGraph 整合)

这是整个项目的核心骨架。工作流模块负责把Agent、Tools、Memory串接成一个闭环的、可观测的执行流程。

实现思路是这样的:

  • Agent节点:负责推理决策。它接收用户的输入、当前的记忆历史,然后决定是直接回复还是调用某个工具。
  • 工具节点:直接使用LangGraph预置的ToolNode,它会根据Agent返回的tool_calls自动调度对应的工具。
  • 条件路由:shouldContinue函数判断是继续(还有工具调用)还是结束(直接回复或达到最大迭代次数)。

这个循环结构的意思是:AI先“想”一下,如果需要工具就去调用,拿到结果后再回来“想”下一步。整个过程最多重复5次,避免无限循环。

// src/workflow.ts
import { StateGraph, START, END } from "@langchain/langgraph";
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { AIMessage, BaseMessage } from "@langchain/core/messages";
import { createModel, AGENT_CONFIG } from "./config";
import { ALL_TOOLS } from "./tools";
import { MemoryManager } from "./memory";

// 扩展状态类型
type gentState = {
  messages: BaseMessage[];
  intent?: "technical" | "billing" | "general";
  resolution?: string;
  sessionId: string;
  iterationCount: number;
}

// 创建 Agent 工作流
export async function createAgentWorkflow(memoryManager: MemoryManager) {
  const model = createModel();
  // 绑定工具到模型
  const modelWithTools = model.bindTools(ALL_TOOLS);

  // Agent 节点:推理决策
  async function agentNode(state: AgentState) {
    // 注入系统提示词
    const systemMessage = {
      role: "system",
      content: AGENT_CONFIG.systemPrompt,
    };
    // 获取历史消息
    const history = memoryManager.getHistory(state.sessionId);
    // 构建消息列表
    const messages = [
      systemMessage,
      ...history,
      ...state.messages,
    ];
    const response = await modelWithTools.invoke(messages);
    // 保存 AI 回复到记忆
    memoryManager.addMessage(state.sessionId, response);
    return {
      messages: [response],
      iterationCount: (state.iterationCount || 0) + 1,
    };
  }

  // 工具节点(使用 LangGraph 预置)
  const toolNode = new ToolNode(ALL_TOOLS);

  // 条件路由:决定是否继续循环
  function shouldContinue(state: AgentState) {
    const lastMessage = state.messages[state.messages.length - 1] as AIMessage;
    // 检查是否达到最大迭代次数
    if (state.iterationCount >= AGENT_CONFIG.maxIterations) {
      console.log(" 达到最大迭代次数,强制终止");
      return END;
    }
    // 如果模型调用了工具,继续到工具节点
    if (lastMessage.tool_calls && lastMessage.tool_calls.length > 0) {
      return "tools";
    }
    // 否则结束
    return END;
  }

  // 构建状态图
  const workflow = new StateGraph({
    channels: {
      messages: { value: (a, b) => [...(a || []), ...(b || [])] },
      sessionId: { value: (_, b) => b },
      iterationCount: { value: (_, b) => b  0 },
    },
  })
    .addNode("agent", agentNode)
    .addNode("tools", toolNode)
    .addEdge(START, "agent")
    .addConditionalEdges("agent", shouldContinue)
    .addEdge("tools", "agent"); // 工具执行完后回到 agent

  return workflow.compile();
}

2.5 主入口与交互界面

最后是CLI交互界面。它支持几个实用命令:/new新建会话、/list查看会话列表、/clear清空当前会话、/exit退出。输入普通文本时,会调用工作流进行对话处理。

值得一提的是SIGINT信号的处理——按Ctrl+C时,程序会先保存记忆再退出,避免数据丢失。

// src/index.ts
import { HumanMessage } from "@langchain/core/messages";
import { createAgentWorkflow } from "./workflow";
import { MemoryManager } from "./memory";
import readline from "readline";

// 创建命令行交互界面
function createReadlineInterface() {
  return readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
}

// 打印欢迎信息
function printWelcome() {
  console.log("n" + "=".repeat(60));
  console.log(" ? 前端开发助手 AI 智能体");
  console.log("=".repeat(60));
  console.log("可用工具:");
  console.log(" 代码格式化 (code_formatter)");
  console.log(" 文件读取 (file_reader)");
  console.log(" 数据转换 (data_converter)");
  console.log("指令:");
  console.log(" /new - 开始新会话");
  console.log(" /list - 查看会话列表");
  console.log(" /clear - 清空当前会话");
  console.log(" /exit - 退出程序");
  console.log("=".repeat(60) + "n");
}

// 主函数
async function main() {
  // 初始化记忆管理器
  const memoryManager = new MemoryManager();
  await memoryManager.load();
  // 创建工作流
  const app = await createAgentWorkflow(memoryManager);
  // 当前会话 ID
  let currentSessionId = `session_${Date.now()}`;
  printWelcome();
  console.log(`? 当前会话: ${currentSessionId}n`);
  const rl = createReadlineInterface();

  // 处理用户输入
  const processInput = async (input: string) => {
    const trimmed = input.trim();
    // 处理命令
    if (trimmed === "/exit") {
      await memoryManager.save();
      console.log("n? 再见!n");
      rl.close();
      process.exit(0);
      return;
    }
    if (trimmed === "/new") {
      currentSessionId = `session_${Date.now()}`;
      console.log(`n 已创建新会话: ${currentSessionId}n`);
      rl.prompt();
      return;
    }
    if (trimmed === "/list") {
      const sessions = memoryManager.listSessions();
      console.log("n? 会话列表:");
      for (const session of sessions) {
        const marker = session.sessionId === currentSessionId ? "? " : "  ";
        console.log(`${marker}${session.sessionId} (${session.messageCount} 条消息)`);
      }
      console.log("");
      rl.prompt();
      return;
    }
    if (trimmed === "/clear") {
      await memoryManager.clearSession(currentSessionId);
      console.log("n? 当前会话已清空n");
      rl.prompt();
      return;
    }
    // 空输入处理
    if (trimmed === "") {
      rl.prompt();
      return;
    }
    // 正常对话处理
    console.log("n? AI 思考中...n");
    const startTime = Date.now();
    try {
      // 保存用户消息到记忆
      const userMessage = new HumanMessage(trimmed);
      memoryManager.addMessage(currentSessionId, userMessage);
      // 执行工作流
      const result = await app.invoke({
        messages: [userMessage],
        sessionId: currentSessionId,
        iterationCount: 0,
      });
      // 获取最终回复
      const lastMessage = result.messages[result.messages.length - 1];
      const responseTime = ((Date.now() - startTime) / 1000).toFixed(1);
      console.log(` 回复 (${responseTime}s):n`);
      console.log(lastMessage.content);
      console.log(`n? 迭代次数: ${result.iterationCount}n`);
    } catch (error) {
      console.error(`n 错误: ${error}n`);
    }
    rl.prompt();
  };

  rl.on("line", processInput);
  rl.prompt();
}

// 优雅退出处理
process.on("SIGINT", async () => {
  console.log("nn 正在保存记忆...");
  const memoryManager = new MemoryManager();
  await memoryManager.save();
  console.log(" 记忆已保存n");
  process.exit(0);
});

main();

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章