返回笔记首页

极速购 AI 客服系统 — 第三方库详解

主题配置

本文档列出项目用到的所有第三方库,说明每个库的作用、使用场景,以及在本项目中具体用到了哪些 API。


一、后端依赖(server/package.json)

1. express ^4.19.2

Node.js 最流行的 Web 框架,提供路由、中间件、请求/响应处理能力。

本项目用途

  • 创建 HTTP 服务,监听 3000 端口
  • 注册四个功能路由:/api/chat/api/agent/api/rag/api/graph
  • 处理 POST 请求的 JSON 请求体(配合 express.json() 中间件)
  • 实现 SSE(Server-Sent Events)流式响应,通过 res.write() 逐块推送数据
用到的 API
javascript
import express from 'express';

const app    = express();
const router = express.Router();

app.use(express.json());           // 解析请求体
app.use('/api/chat', chatRouter);  // 注册路由
app.listen(3000, callback);        // 启动服务

router.post('/stream', handler);   // 定义路由处理函数
res.setHeader('Content-Type', 'text/event-stream');  // SSE 响应头
res.write(`data: ${data}\n\n`);   // 流式推送数据
res.end();                         // 结束响应

2. cors ^2.8.5

处理跨域资源共享(CORS)问题。浏览器出于安全策略,默认禁止前端页面向不同域名的服务器发请求。前端运行在 localhost:5173,后端在 localhost:3000,端口不同属于跨域。

本项目用途

  • 允许前端 Vue3 应用访问后端 API,开发阶段不限制来源
用到的 API
javascript
import cors from 'cors';

app.use(cors());  // 允许所有来源(开发阶段)

// 生产环境应限制来源:
app.use(cors({ origin: 'https://yourdomain.com' }));

3. dotenv ^16.4.5

.env 文件读取环境变量并注入到 process.env,避免把 API Key、数据库密码等敏感信息硬编码在代码里。

本项目用途

  • 加载 DEEPSEEK_API_KEYZHIPU_API_KEYPG_HOST 等所有配置项
用到的 API
javascript
import 'dotenv/config';  // ES Module 写法,导入即自动加载 .env

// 之后任意地方可以读取
process.env.DEEPSEEK_API_KEY
process.env.PG_HOST

4. pg ^8.12.0

Node.js 的 PostgreSQL 客户端库(node-postgres),提供连接池和 SQL 查询能力。

本项目用途

  • 创建 PostgreSQL 连接池,供 pgvector 向量存储模块使用
  • pgvector 存储向量数据时底层调用 pool 执行 SQL
用到的 API
javascript
import pg from 'pg';

const { Pool } = pg;

export const pool = new Pool({
  host:     process.env.PG_HOST,
  port:     parseInt(process.env.PG_PORT),
  user:     process.env.PG_USER,
  password: process.env.PG_PASSWORD,
  database: process.env.PG_DATABASE,
});

// Pool 会自动管理连接,空闲时归还,高并发时排队
// PGVectorStore 直接接收 pool 实例,内部调用 pool.query() 执行 SQL

5. zod ^3.23.8

TypeScript 优先的数据结构验证和类型推导库。在 LangChain.js 的 Tool 定义中用于描述工具的参数 Schema,LangChain 会把 Schema 转成 JSON Schema 格式告诉 LLM,让模型知道调用工具时该传什么参数。

本项目用途

  • 定义订单查询工具、物流查询工具、用户订单查询工具的参数结构
用到的 API
javascript
import { z } from 'zod';

// 定义工具参数 schema
const schema = z.object({
  orderId: z.string().describe('订单号,格式为 ORD-xxx,例如 ORD-001'),
});

// z.object()        定义对象结构
// z.string()        字符串类型
// z.number()        数字类型
// .describe()       为字段添加描述,LLM 依靠这段文字理解该传什么值
// z.optional()      可选字段

二、LangChain 生态库

LangChain 拆分为多个 npm 包,各司其职,按需安装。


6. @langchain/core ^0.3.0

LangChain 的核心基础包,定义所有抽象接口和基础类型,其他包都依赖它。不包含任何具体实现,只有接口和工具类。

本项目用到的模块

**@langchain/core/prompts**— Prompt 模板

javascript
import { ChatPromptTemplate } from '@langchain/core/prompts';

// fromMessages 定义多角色消息模板
const prompt = ChatPromptTemplate.fromMessages([
  ['system', '你是客服助手...{current_time}'],  // 系统消息,定义角色
  ['placeholder', '{chat_history}'],             // 占位符,插入历史消息数组
  ['human', '{user_input}'],                     // 用户消息
]);

// invoke 时传入变量替换占位符
await prompt.invoke({
  current_time: '2025-03-26',
  chat_history: [...],
  user_input: '我的订单在哪里',
});

**@langchain/core/output_parsers**— 输出解析器

javascript
import { StringOutputParser } from '@langchain/core/output_parsers';

// LLM 返回的是 AIMessage 对象,StringOutputParser 提取其中的文本内容
const parser = new StringOutputParser();

// 在 LCEL 管道末尾使用:
const chain = prompt | model | parser;
// 最终 invoke 返回字符串,而不是 AIMessage 对象

**@langchain/core/runnables**— 可运行单元

javascript
import { RunnableSequence, RunnablePassthrough } from '@langchain/core/runnables';

// RunnableSequence.from([...]) 把多个步骤串联成一个链
// 等价于 LCEL 的 a | b | c 语法,更适合复杂 RAG 流程
const chain = RunnableSequence.from([
  { context: retriever.pipe(formatDocs), question: new RunnablePassthrough() },
  prompt,
  model,
  parser,
]);

// RunnablePassthrough 把输入原封不动传递到下一步
// 在 RAG 中用于把用户问题同时传给检索器和最终 Prompt

**@langchain/core/messages**— 消息类型

javascript
import { HumanMessage } from '@langchain/core/messages';

// 把字符串包装成 LangChain 的消息对象
// LangGraph 的 state.messages 要求使用 Message 对象
new HumanMessage('用户输入的文字');
// 其他类型:AIMessage(AI 回复)、SystemMessage(系统消息)

**@langchain/core/documents**— 文档类型

javascript
import { Document } from '@langchain/core/documents';

// 向量化之前,文本需要包装成 Document 对象
new Document({
  pageContent: '文档内容',
  metadata:    { source: 'products.md' },  // 可存任意元数据,检索后可引用
});

**@langchain/core/tools**— 工具定义

javascript
import { tool } from '@langchain/core/tools';

// 定义一个可被 Agent 调用的工具
export const getOrderInfoTool = tool(
  async ({ orderId }) => {           // 执行函数
    return JSON.stringify(result);
  },
  {
    name:        'getOrderInfo',     // 工具名,LLM 用这个名字调用
    description: '查询订单详情...',  // 工具描述,影响 LLM 是否选择这个工具
    schema:      z.object({ ... }), // 参数结构
  }
);

7. @langchain/openai ^0.3.0

LangChain 的 OpenAI 集成包。DeepSeek 和智谱 AI 都兼容 OpenAI 的 API 协议,所以本项目用这个包对接所有模型,只需替换 baseURLapiKey

本项目用到的类

**ChatOpenAI**— 对话模型

javascript
import { ChatOpenAI } from '@langchain/openai';

const model = new ChatOpenAI({
  modelName:    'deepseek-chat',
  openAIApiKey: process.env.DEEPSEEK_API_KEY,
  configuration: {
    baseURL: 'https://api.deepseek.com/v1',  // 替换为 DeepSeek 地址
  },
  temperature: 0.7,
  streaming:   false,
});

// invoke 发送消息,返回 AIMessage
const response = await model.invoke([new HumanMessage('你好')]);

// stream 流式调用,返回 AsyncIterable
for await (const chunk of await model.stream(messages)) {
  process.stdout.write(chunk.content);
}

**OpenAIEmbeddings**— 向量化模型

javascript
import { OpenAIEmbeddings } from '@langchain/openai';

const embeddings = new OpenAIEmbeddings({
  modelName:    'embedding-3',        // 智谱 AI 的 Embedding 模型
  openAIApiKey: process.env.ZHIPU_API_KEY,
  configuration: {
    baseURL: 'https://open.bigmodel.cn/api/paas/v4',
  },
});

// embedQuery 把一段文字转成向量(数字数组)
const vector = await embeddings.embedQuery('蓝牙耳机多少钱');
// 返回类似 [0.12, -0.34, 0.87, ...] 的 1536 维向量

// embedDocuments 批量转换,入库时使用
const vectors = await embeddings.embedDocuments(['文档1', '文档2']);

8. @langchain/community ^0.3.0

LangChain 的社区集成包,包含大量第三方工具和数据库的集成实现,官方核心包以外的内容都在这里。

本项目用到的模块

**PGVectorStore**— PostgreSQL 向量存储

javascript
import { PGVectorStore } from '@langchain/community/vectorstores/pgvector';

// 入库:把文档切片向量化并存入 PostgreSQL
await PGVectorStore.fromDocuments(chunks, embeddings, {
  pool,
  tableName: 'knowledge_embeddings',
  columns: {
    idColumnName:       'id',
    vectorColumnName:   'embedding',   // 存向量的列
    contentColumnName:  'content',     // 存原始文本的列
    metadataColumnName: 'metadata',    // 存元数据的列(来源文件名等)
  },
});

// 查询:初始化并创建检索器
const vectorStore = await PGVectorStore.initialize(embeddings, config);
const retriever   = vectorStore.asRetriever({ k: 4 }); // 每次返回最相似的 4 条

// 检索器的工作原理:
// 1. 把用户问题用同一个 Embedding 模型转成向量
// 2. 在数据库里用余弦相似度找最近的 k 条向量
// 3. 返回对应的文档片段

9. langchain ^0.3.0

LangChain 主包,包含 Agent、文本切分等高级功能。

本项目用到的模块

**createReactAgent**+ **AgentExecutor** — ReAct Agent

javascript
import { createReactAgent, AgentExecutor } from 'langchain/agents';

// createReactAgent 把 LLM + 工具 + Prompt 组装成 Agent
const agent = createReactAgent({
  llm:    model,     // 使用的语言模型
  tools:  allTools,  // 可以调用的工具列表
  prompt: agentPrompt,
});

// AgentExecutor 负责运行 Agent 的思考循环
// Thought → Action → Observation → Thought → ... → Final Answer
const executor = new AgentExecutor({
  agent,
  tools:                   allTools,
  maxIterations:           5,      // 最多循环 5 次防止死循环
  returnIntermediateSteps: true,   // 返回每步的工具调用记录
  verbose:                 true,   // 终端打印推理过程,调试用
});

const result = await executor.invoke({ input: '查询订单 ORD-001' });
result.output             // 最终回答
result.intermediateSteps  // 每步的 tool / toolInput / observation

**RecursiveCharacterTextSplitter**— 文档切分

javascript
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';

const splitter = new RecursiveCharacterTextSplitter({
  chunkSize:    500,  // 每个切片最多 500 个字符
  chunkOverlap: 50,   // 相邻切片重叠 50 个字符,防止语义在边界处断裂
});

// 传入 Document 数组,返回切分后的 Document 数组
const chunks = await splitter.splitDocuments(docs);

// 切分策略(按优先级尝试分隔符):
// 1. 段落(\n\n)
// 2. 换行(\n)
// 3. 句号
// 4. 空格
// 5. 单个字符
// 优先在自然边界切分,保留语义完整性

10. @langchain/langgraph ^0.2.0

LangGraph 是 LangChain 生态里专门用于构建有状态、多步骤、可分支工作流的库,把 AI 工作流建模成有向图。

本项目用到的 API

**StateGraph**— 图结构

javascript
import { StateGraph, START, END } from '@langchain/langgraph';

const graph = new StateGraph(GraphState)
  .addNode('intentRouter', intentRouterNode)    // 注册节点(普通 async 函数)
  .addNode('orderAgent',   orderAgentNode)
  .addEdge(START, 'intentRouter')               // 固定边:入口 → 意图识别
  .addConditionalEdges(                         // 条件边:根据状态值分流
    'intentRouter',
    routeByIntent,           // 路由函数,返回下一个节点名
    {
      orderAgent:  'orderAgent',   // 路由函数返回 'orderAgent' → 走这条边
      ragNode:     'ragNode',
      generalChat: 'generalChat',
    }
  )
  .addEdge('orderAgent', 'answerSynthesizer')   // 所有路径汇合
  .addEdge('answerSynthesizer', END);           // 出口

const compiled = graph.compile();              // 编译,生成可执行图

// 流式执行,每个节点完成后推送一次更新
const stream = await compiled.stream(
  { userInput: '查询订单', messages: [...] },
  { streamMode: 'updates' }  // updates 模式:每步只推送变化的字段
);

for await (const update of stream) {
  const [nodeName, nodeState] = Object.entries(update)[0];
  // nodeName: 刚执行完的节点名
  // nodeState: 该节点写入的字段
}

**Annotation**+ **MessagesAnnotation** — 状态定义

javascript
import { Annotation, MessagesAnnotation } from '@langchain/langgraph';

export const GraphState = Annotation.Root({
  ...MessagesAnnotation.spec,  // 内置消息列表管理,自动处理消息追加

  intent: Annotation({
    reducer: (_, next) => next,  // reducer 定义更新策略:直接替换
    default: () => '',           // 初始值
  }),

  orderResult: Annotation({
    reducer: (_, next) => next,
    default: () => null,
  }),
});

// 每个节点是普通 async 函数,接收完整 State,返回要更新的字段
const myNode = async (state) => {
  const { userInput } = state;       // 读取已有字段
  return { intent: 'order' };        // 只返回需要更新的字段
};

11. nodemon ^3.1.4(devDependency)

开发阶段的文件监听工具,监听 src/ 目录下的文件变化,有改动时自动重启 Node.js 服务,省去手动停止再启动的操作。

本项目用途

  • pnpm dev 实际执行的是 nodemon src/index.js
  • 修改任何后端代码后自动重启,不需要手动操作
仅在开发时使用,生产部署时用 **pnpm start**(直接 node 命令)

三、前端依赖(client/package.json)

12. vue ^3.4.0

Vue 3 前端框架,本项目用 Composition API 风格开发。

本项目用到的 API

javascript
import { ref, nextTick } from 'vue';

// ref      创建响应式变量,修改后视图自动更新
const messages  = ref([]);
const streaming = ref(false);

// nextTick 在 DOM 更新完成后执行回调
// 用于消息发送后滚动到底部(需等 DOM 渲染新消息后再滚动)
await nextTick();
messagesRef.value.scrollTop = messagesRef.value.scrollHeight;

13. vue-router ^4.3.0

Vue 3 官方路由库,管理前端页面导航,实现单页应用(SPA)的多页面切换,不需要刷新整个页面。

本项目用到的 API

javascript
import { createRouter, createWebHistory } from 'vue-router';

const router = createRouter({
  history: createWebHistory(),  // HTML5 History 模式,URL 不带 #
  routes: [
    { path: '/',      component: ChatView  },
    { path: '/agent', component: AgentView },
    { path: '/rag',   component: RagView   },
    { path: '/graph', component: GraphView },
  ],
});

// 在模板中使用
// <router-link to="/agent">订单查询</router-link>  导航链接
// <router-view />                                  渲染当前路由对应的组件
// router-link-active                               当前路由匹配时自动添加的 CSS 类

14. vite ^5.0.0(devDependency)

前端构建工具,开发时提供极快的热更新服务器,生产时打包成静态文件。

本项目用途

  • pnpm dev 启动开发服务器,端口 5173,修改 .vue 文件后浏览器自动更新
  • 处理 Vue SFC(Single File Component)的编译
  • 生产打包:pnpm build 输出到 dist/ 目录

15. @vitejs/plugin-vue ^5.0.0(devDependency)

Vite 的 Vue 3 插件,让 Vite 能够理解和编译 .vue 文件(SFC 格式,包含 <template>/<script>/<style> 三段式结构)。

配置

javascript
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [vue()],  // 注册插件,Vite 处理 .vue 文件时调用
});

四、各库的依赖关系

markdown
项目代码
  │
  ├── express ──────────────── HTTP 服务框架
  │     └── cors             跨域中间件
  │
  ├── dotenv ───────────────── 环境变量加载
  │
  ├── pg ───────────────────── PostgreSQL 连接
  │
  ├── zod ──────────────────── 工具参数 Schema 校验
  │
  └── LangChain 生态
        │
        ├── @langchain/core ──────── 抽象接口层(被所有 LC 包依赖)
        │     ├── prompts          Prompt 模板
        │     ├── output_parsers   输出解析
        │     ├── runnables        LCEL 管道
        │     ├── messages         消息类型
        │     ├── documents        文档类型
        │     └── tools            工具定义
        │
        ├── @langchain/openai ────── 模型接入
        │     ├── ChatOpenAI       对话模型(DeepSeek/通用)
        │     └── OpenAIEmbeddings 向量化模型(智谱/百炼)
        │
        ├── @langchain/community ─── 第三方集成
        │     └── PGVectorStore    pgvector 向量存储
        │
        ├── langchain ────────────── 高级功能
        │     ├── createReactAgent  ReAct Agent 构建
        │     ├── AgentExecutor     Agent 执行器
        │     └── RecursiveCharacterTextSplitter  文档切分
        │
        └── @langchain/langgraph ─── 有状态工作流
              ├── StateGraph        图结构
              ├── Annotation        状态定义
              └── MessagesAnnotation 消息状态管理

五、包体积与安装说明

pnpm install 执行后,实际安装的包远不止上面列出的,因为每个包还有自己的依赖(间接依赖)。以下是主要包的间接依赖说明:

包名 主要间接依赖 说明
@langchain/openai openai
官方 SDK
底层调用 OpenAI 兼容接口
@langchain/community @pinecone-database/pinecone
按需加载,不影响包体积
@langchain/langgraph @langchain/core 核心依赖
express body-parser
path-to-regexp
路由和请求处理
pg pg-pool
pg-protocol
连接池和协议实现

pnpm 使用硬链接和符号链接存储包,多个项目共享同一份依赖,磁盘占用比 npm 小很多。