📖 本章学习目标
- ✅ 设计完整的智能客服系统架构
- ✅ 构建基于 RAG 的产品知识库
- ✅ 实现多轮对话管理和上下文追踪
- ✅ 集成订单查询、工单创建等业务系统
- ✅ 实现智能转人工机制
- ✅ 配置 LangSmith 监控和质量评估
- ✅ 部署到生产环境并建立运维体系
一、项目概述
1、业务背景
某电商公司每天有数千条客服咨询,传统的人工客服面临以下挑战:
- ❌ 响应速度慢(平均等待 5 分钟)
- ❌ 人力成本高(需要 50+ 客服人员)
- ❌ 服务质量不一致(不同客服水平差异大)
- ❌ 无法 24/7 服务
解决方案: 构建智能客服 Agent,自动处理 80% 的常见问题。
2、系统目标
| 指标 | 当前状态 | 目标状态 | 提升 |
|---|---|---|---|
| 自动解决率 | 0% | 80% | - |
| 平均响应时间 | 5 分钟 | 3 秒 | ⬇️ 99% |
| 客户满意度 | 75% | 85% | ⬆️ 10% |
| 人力成本 | ¥500K/月 | ¥100K/月 | ⬇️ 80% |
| 服务时间 | 9:00-21:00 | 24/7 | ⬆️ 100% |
3、技术栈选型
flowchart TB
subgraph Frontend["前端层"]
Web["Web 聊天界面"]
App["移动端 App"]
end
subgraph Backend["后端服务层"]
API["Express API Server"]
Agent["LangChain Agent"]
end
subgraph Infrastructure["基础设施层"]
DB[(PostgreSQL
用户数据)] VectorDB[(Pinecone
知识库)] Cache[(Redis
会话缓存)] end subgraph External["外部系统"] OrderAPI["订单系统 API"] TicketAPI["工单系统 API"] CRM["CRM 系统"] end Web --> API App --> API API --> Agent Agent --> DB Agent --> VectorDB Agent --> Cache Agent --> OrderAPI Agent --> TicketAPI Agent --> CRM style Agent fill:#fff7e6,stroke:#fa8c16,stroke-width:3px style VectorDB fill:#e8f4fd,stroke:#1890ff style DB fill:#f6ffed,stroke:#52c41a
用户数据)] VectorDB[(Pinecone
知识库)] Cache[(Redis
会话缓存)] end subgraph External["外部系统"] OrderAPI["订单系统 API"] TicketAPI["工单系统 API"] CRM["CRM 系统"] end Web --> API App --> API API --> Agent Agent --> DB Agent --> VectorDB Agent --> Cache Agent --> OrderAPI Agent --> TicketAPI Agent --> CRM style Agent fill:#fff7e6,stroke:#fa8c16,stroke-width:3px style VectorDB fill:#e8f4fd,stroke:#1890ff style DB fill:#f6ffed,stroke:#52c41a
核心技术:
- 框架:LangChain.js v1.3.1 + LangGraph
- 模型:OpenAI GPT-4o(主模型)、GPT-4o-mini(降级)
- 向量数据库:Pinecone(生产)、Chroma(开发)
- 关系数据库:PostgreSQL(用户数据、会话历史)
- 缓存:Redis(会话状态、响应缓存)
- 后端:Node.js + Express + TypeScript
二、系统架构设计
1、整体架构图
flowchart TB
User["用户"] --> Interface["对话界面
Web/App"] Interface --> Gateway["API Gateway
速率限制/认证"] Gateway --> CustomerAgent["客服 Agent
核心大脑"] CustomerAgent --> Memory["对话记忆
MemorySaver + Redis"] CustomerAgent --> KnowledgeBase["知识库检索
RAG Pipeline"] CustomerAgent --> BusinessTools["业务工具集"] subgraph Tools["业务工具集"] T1["订单查询工具"] T2["物流跟踪工具"] T3["工单创建工具"] T4["退款申请工具"] T5["转人工工具"] end CustomerAgent --> LangSmith["LangSmith
监控与评估"] BusinessTools --> ExternalSystems["外部系统"] subgraph External["外部系统"] E1["订单系统 API"] E2["物流系统 API"] E3["工单系统 API"] E4["支付系统 API"] end style CustomerAgent fill:#fff7e6,stroke:#fa8c16,stroke-width:4px style KnowledgeBase fill:#e8f4fd,stroke:#1890ff,stroke-width:2px style LangSmith fill:#f6ffed,stroke:#52c41a,stroke-width:2px
Web/App"] Interface --> Gateway["API Gateway
速率限制/认证"] Gateway --> CustomerAgent["客服 Agent
核心大脑"] CustomerAgent --> Memory["对话记忆
MemorySaver + Redis"] CustomerAgent --> KnowledgeBase["知识库检索
RAG Pipeline"] CustomerAgent --> BusinessTools["业务工具集"] subgraph Tools["业务工具集"] T1["订单查询工具"] T2["物流跟踪工具"] T3["工单创建工具"] T4["退款申请工具"] T5["转人工工具"] end CustomerAgent --> LangSmith["LangSmith
监控与评估"] BusinessTools --> ExternalSystems["外部系统"] subgraph External["外部系统"] E1["订单系统 API"] E2["物流系统 API"] E3["工单系统 API"] E4["支付系统 API"] end style CustomerAgent fill:#fff7e6,stroke:#fa8c16,stroke-width:4px style KnowledgeBase fill:#e8f4fd,stroke:#1890ff,stroke-width:2px style LangSmith fill:#f6ffed,stroke:#52c41a,stroke-width:2px
2、核心组件说明
| 组件 | 职责 | 技术选型 |
|---|---|---|
| 客服 Agent | 理解用户意图、调用工具、生成回复 | LangChain createAgent |
| 知识库 | 存储产品文档、FAQ、政策文件 | Pinecone + OpenAI Embeddings |
| 对话记忆 | 维护多轮对话上下文 | PostgreSQL + thread_id |
| 业务工具 | 对接订单、物流、工单等系统 | 自定义 Tool 函数 |
| 监控系统 | 追踪执行链路、评估质量 | LangSmith |
| API Gateway | 认证、限流、日志 | Express + 中间件 |
三、开发里程碑
我们将项目分解为 5 个里程碑,每个里程碑都有明确的目标和验收标准。
里程碑 1:基础框架搭建(2天)
目标: 搭建项目基础结构,实现最简单的问答功能。
步骤 1:初始化项目
bash
# 创建项目目录
mkdir customer-service-agent && cd customer-service-agent
# 初始化 package.json
pnpm init
# 安装核心依赖
pnpm add langchain @langchain/openai @langchain/langgraph
pnpm add express cors dotenv zod
pnpm add -D typescript ts-node @types/node @types/express
# 初始化 TypeScript
npx tsc --init
步骤 2:配置环境变量
创建 .env 文件:
bash
# OpenAI API Key
OPENAI_API_KEY=sk-your-api-key
# LangSmith 配置
LANGSMITH_API_KEY=lsv2-your-api-key
LANGSMITH_TRACING=true
LANGSMITH_PROJECT=customer-service-prod
# 数据库配置
DATABASE_URL=postgresql://user:password@localhost:5432/customer_service
REDIS_URL=redis://localhost:6379
# Pinecone 配置
PINECONE_API_KEY=your-pinecone-api-key
PINECONE_INDEX_NAME=customer-knowledge-base
# 服务器配置
PORT=3000
NODE_ENV=production
步骤 3:创建基础 Agent
typescript
// src/agent.ts
import "dotenv/config";
import { createAgent } from "langchain";
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
// 创建持久化 checkpointer
const checkpointer = PostgresSaver.fromConnString(
process.env.DATABASE_URL!
);
// 初始化数据库表
await checkpointer.setup();
// 创建基础客服 Agent
export const customerServiceAgent = createAgent({
model: "openai:gpt-4o",
tools: [], // 暂时不添加工具
checkpointer,
systemPrompt: `你是专业的电商客服助手。
行为准则:
1. 用友好、专业的语气回答用户问题
2. 如果不知道答案,诚实告知,不要编造
3. 保持回答简洁,每次不超过 3 句话
4. 对于复杂问题,主动提出转接人工客服
当前时间:${new Date().toLocaleString("zh-CN")}`,
});
步骤 4:创建 API 服务器
typescript
// src/server.ts
import express from "express";
import cors from "cors";
import { customerServiceAgent } from "./agent";
const app = express();
app.use(cors());
app.use(express.json());
// 聊天接口
app.post("/api/chat", async (req, res) => {
try {
const { message, sessionId } = req.body;
if (!message || !sessionId) {
return res.status(400).json({
error: "缺少必要参数:message 和 sessionId"
});
}
// 配置 thread_id
const config = {
configurable: {
thread_id: `session-${sessionId}`,
},
};
// 调用 Agent
const result = await customerServiceAgent.invoke(
{
messages: [{ role: "user", content: message }],
},
config
);
// 返回回复
const response = result.messages.at(-1)?.content;
res.json({
success: true,
response,
sessionId,
});
} catch (error) {
console.error("聊天接口错误:", error);
res.status(500).json({
success: false,
error: "服务器内部错误",
});
}
});
// 健康检查
app.get("/health", (req, res) => {
res.json({ status: "ok", timestamp: new Date().toISOString() });
});
// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`✅ 客服服务器运行在 http://localhost:${PORT}`);
});
步骤 5:测试基础功能
bash
# 启动服务器
pnpm ts-node src/server.ts
# 测试 API(使用 curl 或 Postman)
curl -X POST http://localhost:3000/api/chat \
-H "Content-Type: application/json" \
-d '{
"message": "你好",
"sessionId": "test-001"
}'
预期输出:
json
{
"success": true,
"response": "你好!我是客服助手,很高兴为您服务。请问有什么可以帮助您的吗?",
"sessionId": "test-001"
}
✅ 验收标准:
- 项目可以成功启动
- API 接口正常响应
- 对话历史正确保存(同一 sessionId 的多轮对话有上下文)
- LangSmith 中可以查看追踪记录
里程碑 2:知识库构建(3天)
目标: 构建产品知识库,实现基于 RAG 的智能问答。
步骤 1:准备知识库文档
创建 knowledge-base/ 目录,存放各类文档:
bash
knowledge-base/
├── products/ # 产品文档
│ ├── iphone-15.md
│ ├── macbook-pro.md
│ └── airpods.md
├── policies/ # 政策文件
│ ├── return-policy.md
│ ├── shipping-policy.md
│ └── warranty.md
├── faq/ # 常见问题
│ ├── account.md
│ ├── payment.md
│ └── delivery.md
└── troubleshooting/ # 故障排查
├── login-issues.md
└── payment-failed.md
示例文档(return-policy.md):
markdown
# 退货政策
## 退货条件
1. 商品未使用,包装完好
2. 在购买后 7 天内申请
3. 非定制商品
## 退货流程
1. 登录账号,进入"我的订单"
2. 找到对应订单,点击"申请退货"
3. 填写退货原因,上传商品照片
4. 等待审核(1-2 个工作日)
5. 审核通过后,寄回商品
6. 收到商品后,3-5 个工作日内退款
## 注意事项
- 退货运费由用户承担(质量问题除外)
- 退款原路返回
- 优惠券不退
步骤 2:文档加载和切割
typescript
// src/knowledge-base/loader.ts
import { DirectoryLoader } from "langchain/document_loaders/fs/directory";
import { TextLoader } from "langchain/document_loaders/fs/text";
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
import { Document } from "@langchain/core/documents";
/**
* 加载知识库文档
*/
export async function loadKnowledgeBase(): Promise<Document[]> {
console.log("📚 开始加载知识库文档...");
// 加载所有 Markdown 文件
const loader = new DirectoryLoader(
"./knowledge-base",
{
".md": (path) => new TextLoader(path),
}
);
const docs = await loader.load();
console.log(`✅ 加载了 ${docs.length} 个文档`);
return docs;
}
/**
* 切割文档为 chunks
*/
export async function splitDocuments(
docs: Document[]
): Promise<Document[]> {
console.log("✂️ 开始切割文档...");
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 800, // 每个 chunk 800 字符
chunkOverlap: 150, // 重叠 150 字符
separators: [
"\n\n", // 段落
"\n", // 换行
"。", // 中文句号
",", // 中文逗号
" ", // 空格
],
});
const chunks = await splitter.splitDocuments(docs);
console.log(`✅ 切割为 ${chunks.length} 个 chunks`);
return chunks;
}
步骤 3:生成 Embedding 并存储
typescript
// src/knowledge-base/indexer.ts
import { OpenAIEmbeddings } from "@langchain/openai";
import { PineconeStore } from "@langchain/pinecone";
import { Pinecone } from "@pinecone-database/pinecone";
import { loadKnowledgeBase, splitDocuments } from "./loader";
/**
* 构建知识库索引
*/
export async function buildKnowledgeIndex() {
console.log("🚀 开始构建知识库索引...");
// 1. 加载文档
const docs = await loadKnowledgeBase();
// 2. 切割文档
const chunks = await splitDocuments(docs);
// 3. 初始化 Embedding 模型
console.log("🔤 初始化 Embedding 模型...");
const embeddings = new OpenAIEmbeddings({
model: "text-embedding-3-small",
});
// 4. 连接 Pinecone
console.log("🔗 连接 Pinecone...");
const pinecone = new Pinecone({
apiKey: process.env.PINECONE_API_KEY!,
});
const index = pinecone.Index(process.env.PINECONE_INDEX_NAME!);
// 5. 批量写入向量数据库
console.log("💾 写入向量数据库...");
const vectorStore = await PineconeStore.fromDocuments(
chunks,
embeddings,
{
pineconeIndex: index,
namespace: "customer-service",
}
);
console.log(`✅ 知识库索引构建完成,共 ${chunks.length} 个向量`);
return vectorStore;
}
// 执行索引构建
if (require.main === module) {
buildKnowledgeIndex().catch(console.error);
}
运行索引构建:
bash
pnpm ts-node src/knowledge-base/indexer.ts
输出:
erlang
🚀 开始构建知识库索引...
📚 开始加载知识库文档...
✅ 加载了 25 个文档
✂️ 开始切割文档...
✅ 切割为 180 个 chunks
🔤 初始化 Embedding 模型...
🔗 连接 Pinecone...
💾 写入向量数据库...
✅ 知识库索引构建完成,共 180 个向量
步骤 4:创建检索工具
typescript
// src/tools/knowledge-search.ts
import { tool } from "@langchain/core/tools";
import { PineconeStore } from "@langchain/pinecone";
import { OpenAIEmbeddings } from "@langchain/openai";
import { Pinecone } from "@pinecone-database/pinecone";
import { z } from "zod";
// 初始化向量存储
const pinecone = new Pinecone({
apiKey: process.env.PINECONE_API_KEY!,
});
const embeddings = new OpenAIEmbeddings();
const index = pinecone.Index(process.env.PINECONE_INDEX_NAME!);
const vectorStore = new PineconeStore(embeddings, {
pineconeIndex: index,
namespace: "customer-service",
});
// 创建检索器
const retriever = vectorStore.asRetriever({
k: 3, // 返回最相关的 3 个文档
});
/**
* 知识库搜索工具
*/
export const searchKnowledgeBase = tool(
async ({ query }) => {
console.log(`[Knowledge Search] 搜索: "${query}"`);
// 执行检索
const docs = await retriever.invoke(query);
if (docs.length === 0) {
return "未在知识库中找到相关信息。";
}
// 格式化结果
const formattedDocs = docs.map((doc, index) => {
const source = doc.metadata.source || "未知来源";
return `【文档 ${index + 1}】来源:${source}\n${doc.pageContent}\n`;
}).join("\n---\n");
return formattedDocs;
},
{
name: "search_knowledge_base",
description: "搜索产品知识库,包括产品信息、退货政策、物流政策、常见问题等。当用户询问产品相关、政策相关或常见问题时调用此工具。",
schema: z.object({
query: z.string().describe("搜索关键词或问题描述"),
}),
}
);
步骤 5:集成到 Agent
typescript
// src/agent.ts
import { createAgent } from "langchain";
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
import { searchKnowledgeBase } from "./tools/knowledge-search";
const checkpointer = PostgresSaver.fromConnString(
process.env.DATABASE_URL!
);
export const customerServiceAgent = createAgent({
model: "openai:gpt-4o",
tools: [searchKnowledgeBase], // 添加知识库搜索工具
checkpointer,
systemPrompt: `你是专业的电商客服助手。
可用工具:
- search_knowledge_base:搜索产品知识库
工作流程:
1. 首先理解用户问题
2. 如果问题涉及产品、政策、FAQ,调用 search_knowledge_base 搜索相关知识
3. 基于搜索结果回答问题
4. 如果知识库中没有相关信息,如实告知用户
行为准则:
1. 用友好、专业的语气回答
2. 引用知识库内容时,标注来源
3. 保持回答简洁(不超过 3 句话)
4. 不确定时,主动提出转接人工`,
});
步骤 6:测试知识库功能
bash
# 测试知识库检索
curl -X POST http://localhost:3000/api/chat \
-H "Content-Type: application/json" \
-d '{
"message": "退货政策是什么?",
"sessionId": "test-002"
}'
预期输出:
json
{
"success": true,
"response": "根据我们的退货政策:\n\n1. 退货条件:商品未使用且包装完好,在购买后7天内申请\n2. 退货流程:登录账号 → 我的订单 → 申请退货 → 等待审核 → 寄回商品 → 退款\n\n更多信息请参考:policies/return-policy.md",
"sessionId": "test-002"
}
✅ 验收标准:
- 知识库索引成功构建
- 检索工具能返回相关文档
- Agent 能基于知识库准确回答问题
- 回答中标注了信息来源
里程碑 3:业务工具集成(4天)
目标: 集成订单查询、物流跟踪、工单创建等业务系统。
步骤 1:订单查询工具
typescript
// src/tools/order-query.ts
import { tool } from "@langchain/core/tools";
import axios from "axios";
import { z } from "zod";
/**
* 查询订单状态
*/
export const queryOrderStatus = tool(
async ({ orderId, userId }) => {
console.log(`[Order Query] 查询订单: ${orderId}, 用户: ${userId}`);
try {
// 调用订单系统 API
const response = await axios.get(
`${process.env.ORDER_API_URL}/orders/${orderId}`,
{
headers: {
"Authorization": `Bearer ${process.env.ORDER_API_KEY}`,
"X-User-ID": userId,
},
}
);
const order = response.data;
// 格式化订单信息
const statusMap = {
pending: "待付款",
paid: "已付款",
shipped: "已发货",
delivered: "已送达",
cancelled: "已取消",
};
return `订单信息:
- 订单号:${order.id}
- 商品:${order.items.map((item: any) => item.name).join(", ")}
- 金额:¥${order.total}
- 状态:${statusMap[order.status as keyof typeof statusMap]}
- 下单时间:${new Date(order.createdAt).toLocaleString("zh-CN")}`;
} catch (error) {
console.error("[Order Query] 查询失败:", error);
return `抱歉,无法查询订单 ${orderId} 的信息。请确认订单号是否正确,或联系人工客服。`;
}
},
{
name: "query_order_status",
description: "查询订单状态和详细信息。需要提供订单号和用户ID。当用户询问订单状态、订单详情时调用此工具。",
schema: z.object({
orderId: z.string().describe("订单号,例如:ORD-20250101-001"),
userId: z.string().describe("用户ID,从会话上下文中获取"),
}),
}
);
步骤 2:物流跟踪工具
typescript
// src/tools/tracking.ts
import { tool } from "@langchain/core/tools";
import axios from "axios";
import { z } from "zod";
/**
* 跟踪物流信息
*/
export const trackShipment = tool(
async ({ trackingNumber }) => {
console.log(`[Tracking] 跟踪物流: ${trackingNumber}`);
try {
const response = await axios.get(
`${process.env.LOGISTICS_API_URL}/tracking/${trackingNumber}`,
{
headers: {
"Authorization": `Bearer ${process.env.LOGISTICS_API_KEY}`,
},
}
);
const tracking = response.data;
// 格式化物流信息
const events = tracking.events
.map((event: any) => {
return `- ${new Date(event.timestamp).toLocaleString("zh-CN")}:${event.description} (${event.location})`;
})
.join("\n");
return `物流跟踪信息(运单号:${trackingNumber}):\n\n${events}\n\n预计送达:${tracking.estimatedDelivery}`;
} catch (error) {
console.error("[Tracking] 跟踪失败:", error);
return `抱歉,无法查询运单号 ${trackingNumber} 的物流信息。请稍后重试或联系人工客服。`;
}
},
{
name: "track_shipment",
description: "跟踪包裹物流信息。需要提供运单号。当用户询问物流状态、包裹位置时调用此工具。",
schema: z.object({
trackingNumber: z.string().describe("运单号,例如:SF1234567890"),
}),
}
);
步骤 3:工单创建工具
typescript
// src/tools/ticket-create.ts
import { tool } from "@langchain/core/tools";
import axios from "axios";
import { z } from "zod";
/**
* 创建客服工单
*/
export const createSupportTicket = tool(
async ({ userId, category, subject, description }) => {
console.log(`[Ticket] 创建工单: ${subject}`);
try {
const response = await axios.post(
`${process.env.TICKET_API_URL}/tickets`,
{
userId,
category,
subject,
description,
priority: "normal",
status: "open",
},
{
headers: {
"Authorization": `Bearer ${process.env.TICKET_API_KEY}`,
},
}
);
const ticket = response.data;
return `工单已创建成功!
- 工单号:${ticket.id}
- 主题:${ticket.subject}
- 类别:${ticket.category}
- 预计响应时间:24小时内
我们的客服专员会尽快处理您的问题。您可以通过工单号查询处理进度。`;
} catch (error) {
console.error("[Ticket] 创建失败:", error);
return `抱歉,创建工单失败。请直接联系人工客服:400-123-4567。`;
}
},
{
name: "create_support_ticket",
description: "创建客服工单,用于处理复杂问题或投诉。需要提供用户ID、问题类别、主题和详细描述。当问题无法立即解决或需要人工介入时调用此工具。",
schema: z.object({
userId: z.string().describe("用户ID"),
category: z.enum(["technical", "billing", "product", "complaint"]).describe("问题类别"),
subject: z.string().describe("工单主题,简短描述问题"),
description: z.string().describe("问题详细描述"),
}),
}
);
步骤 4:转人工工具
typescript
// src/tools/transfer-human.ts
import { tool } from "@langchain/core/tools";
import { interrupt } from "@langchain/langgraph";
import { z } from "zod";
/**
* 转接人工客服
*/
export const transferToHuman = tool(
async ({ reason, userId }) => {
console.log(`[Transfer] 转人工: ${reason}`);
// 记录转人工原因
await logTransferRequest(userId, reason);
// 发起中断,等待人工接入
const humanAgentId = await interrupt({
question: `用户请求转人工客服。\n原因:${reason}\n\n是否现在转接?`,
type: "confirm",
});
if (humanAgentId) {
return `已为您转接人工客服(工号:${humanAgentId})。请稍候,客服专员即将与您对话。`;
} else {
return `抱歉,当前人工客服繁忙。您可以:\n1. 留下联系方式,我们会回电\n2. 创建工单,24小时内回复\n3. 稍后再试`;
}
},
{
name: "transfer_to_human",
description: "转接人工客服。当用户明确要求转人工、问题过于复杂、或用户情绪激动时调用此工具。",
schema: z.object({
reason: z.string().describe("转人工的原因"),
userId: z.string().describe("用户ID"),
}),
}
);
// 辅助函数:记录转人工请求
async function logTransferRequest(userId: string, reason: string) {
// 保存到数据库或发送到监控系统
console.log(`[Transfer Log] 用户 ${userId} 请求转人工,原因:${reason}`);
}
步骤 5:集成所有工具到 Agent
typescript
// src/agent.ts
import { createAgent } from "langchain";
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
import { searchKnowledgeBase } from "./tools/knowledge-search";
import { queryOrderStatus } from "./tools/order-query";
import { trackShipment } from "./tools/tracking";
import { createSupportTicket } from "./tools/ticket-create";
import { transferToHuman } from "./tools/transfer-human";
const checkpointer = PostgresSaver.fromConnString(
process.env.DATABASE_URL!
);
export const customerServiceAgent = createAgent({
model: "openai:gpt-4o",
tools: [
searchKnowledgeBase, // 知识库搜索
queryOrderStatus, // 订单查询
trackShipment, // 物流跟踪
createSupportTicket, // 工单创建
transferToHuman, // 转人工
],
checkpointer,
systemPrompt: `你是专业的电商客服助手,拥有以下能力:
可用工具:
1. search_knowledge_base - 搜索产品知识库
2. query_order_status - 查询订单状态(需要订单号和用户ID)
3. track_shipment - 跟踪物流信息(需要运单号)
4. create_support_ticket - 创建客服工单
5. transfer_to_human - 转接人工客服
工作流程:
1. 理解用户问题和意图
2. 如果是常见问题,先搜索知识库
3. 如果涉及订单/物流,调用相应工具查询
4. 如果问题复杂或无法解决,创建工单或转人工
5. 基于工具返回的信息,用友好专业的语气回答
行为准则:
- 始终礼貌、耐心、专业
- 回答简洁明了(不超过 3-4 句话)
- 不确定时,如实告知并寻求帮助
- 对于投诉或紧急问题,优先转人工`,
});
步骤 6:测试业务工具
bash
# 测试订单查询
curl -X POST http://localhost:3000/api/chat \
-H "Content-Type: application/json" \
-d '{
"message": "帮我查一下订单 ORD-20250101-001 的状态",
"sessionId": "user-123-session-001",
"userId": "user-123"
}'
✅ 验收标准:
- 订单查询工具能正确调用 API 并返回结果
- 物流跟踪工具能显示物流历程
- 工单创建工具能成功创建工单
- 转人工工具能正确触发中断
- Agent 能根据场景选择合适的工具
里程碑 4:对话优化与质量控制(3天)
目标: 优化对话体验,建立质量评估体系。
步骤 1:实现主动引导
typescript
// src/agent.ts - 更新 systemPrompt
systemPrompt: `你是专业的电商客服助手。
[之前的配置...]
主动引导策略:
1. 用户询问产品时,主动推荐相关产品
2. 用户遇到问题时,提供多种解决方案
3. 对话结束时,询问是否还有其他帮助
4. 检测到用户不满时,及时安抚并转人工
示例对话:
用户:"我想买手机"
助手:"我们有 iPhone 15、Samsung Galaxy S24 等多款手机。您更看重哪些方面?(拍照、性能、价格)"
用户:"我的订单还没到"
助手:"我来帮您查询物流状态。请提供订单号或运单号。"
(查询后)
"您的包裹正在运输中,预计明天送达。如有其他问题,随时告诉我。"`
步骤 2:情感检测和处理
typescript
// src/middleware/sentiment-analysis.ts
import { createMiddleware } from "langchain";
/**
* 简单的情感分析
*/
function detectSentiment(text: string): "positive" | "neutral" | "negative" {
const negativeWords = ["生气", "投诉", "差评", "失望", "愤怒", "垃圾", "骗子"];
const positiveWords = ["谢谢", "满意", "好评", "感谢", "棒", "好"];
const hasNegative = negativeWords.some(word => text.includes(word));
const hasPositive = positiveWords.some(word => text.includes(word));
if (hasNegative) return "negative";
if (hasPositive) return "positive";
return "neutral";
}
/**
* 情感分析中间件
*/
export const sentimentAnalysisMiddleware = createMiddleware({
name: "SentimentAnalysis",
beforeModel: async (request) => {
const userMessage = request.messages.findLast(m => m.role === "user");
if (userMessage && typeof userMessage.content === "string") {
const sentiment = detectSentiment(userMessage.content);
// 添加元数据
request.metadata = {
...request.metadata,
userSentiment: sentiment,
};
// 如果检测到负面情绪,添加特殊指令
if (sentiment === "negative") {
request.messages.push({
role: "system",
content: "⚠️ 用户情绪负面,请特别注意:\n1. 保持冷静和专业\n2. 表达理解和歉意\n3. 提供明确的解决方案\n4. 必要时主动转人工",
});
}
}
return request;
},
});
步骤 3:配置 LangSmith 评估
typescript
// src/evaluation/setup.ts
import { Client } from "langsmith";
import { evaluate } from "langsmith/evaluation";
import { customerServiceAgent } from "../agent";
const client = new Client();
// 定义评估数据集
async function createEvaluationDataset() {
const dataset = await client.createDataset("customer-service-eval");
const testCases = [
{
inputs: { question: "退货政策是什么?" },
expected_keywords: ["7天", "未使用", "包装完好"],
},
{
inputs: { question: "如何查询订单?" },
expected_keywords: ["订单号", "我的订单", "个人中心"],
},
{
inputs: { question: "物流多久能到?" },
expected_keywords: ["运单号", "物流", "配送时间"],
},
// ...更多测试用例
];
for (const testCase of testCases) {
await client.createExample({
inputs: testCase.inputs,
outputs: { keywords: testCase.expected_keywords },
});
}
console.log(`✅ 创建了 ${testCases.length} 个评估用例`);
return dataset;
}
// 运行评估
async function runEvaluation() {
const results = await evaluate(
async (inputs) => {
const result = await customerServiceAgent.invoke({
messages: [{ role: "user", content: inputs.question }],
});
return { answer: result.messages.at(-1)?.content };
},
{
data: "customer-service-eval",
evaluators: [
// 关键词匹配评估器
async ({ outputs, referenceOutputs }) => {
const answer = outputs.answer.toLowerCase();
const keywords = referenceOutputs?.keywords || [];
const matchedKeywords = keywords.filter((keyword: string) =>
answer.includes(keyword.toLowerCase())
);
const score = matchedKeywords.length / keywords.length;
return {
key: "keyword_match",
score,
comment: `匹配了 ${matchedKeywords.length}/${keywords.length} 个关键词`,
};
},
],
}
);
// 输出评估报告
const avgScore = results.reduce((sum, r) => sum + r.score, 0) / results.length;
console.log(`\n📊 评估报告:`);
console.log(`平均得分:${avgScore.toFixed(2)}`);
console.log(`测试用例数:${results.length}`);
return results;
}
// 执行
if (require.main === module) {
createEvaluationDataset()
.then(() => runEvaluation())
.catch(console.error);
}
✅ 验收标准:
- 主动引导策略生效,对话更流畅
- 情感检测能识别负面情绪并调整回复策略
- LangSmith 评估报告生成,平均得分 > 0.8
- 可以在 LangSmith 中查看详细的评估结果
里程碑 5:部署和监控(2天)
目标: 部署到生产环境,建立完整的监控体系。
步骤 1:Docker 化部署
创建 Dockerfile:
bash
# 使用 Node.js 18 LTS
FROM node:18-alpine
# 设置工作目录
WORKDIR /app
# 复制 package.json
COPY package.json pnpm-lock.yaml ./
# 安装 pnpm
RUN npm install -g pnpm
# 安装依赖
RUN pnpm install --prod
# 复制源代码
COPY . .
# 编译 TypeScript
RUN pnpm build
# 暴露端口
EXPOSE 3000
# 启动服务器
CMD ["node", "dist/server.js"]
创建 docker-compose.yml:
yaml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- OPENAI_API_KEY=${OPENAI_API_KEY}
- DATABASE_URL=postgresql://postgres:password@db:5432/customer_service
- REDIS_URL=redis://redis:6379
- PINECONE_API_KEY=${PINECONE_API_KEY}
depends_on:
- db
- redis
restart: unless-stopped
db:
image: postgres:15-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_DB=customer_service
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
部署命令:
bash
# 构建并启动
docker-compose up -d
# 查看日志
docker-compose logs -f app
# 停止服务
docker-compose down
步骤 2:配置 PM2 进程管理
创建 ecosystem.config.js:
javascript
module.exports = {
apps: [
{
name: "customer-service-agent",
script: "dist/server.js",
instances: "max", // 使用所有 CPU 核心
exec_mode: "cluster",
max_memory_restart: "1G",
env: {
NODE_ENV: "production",
PORT: 3000,
},
error_file: "logs/error.log",
out_file: "logs/out.log",
merge_logs: true,
log_date_format: "YYYY-MM-DD HH:mm:ss",
},
],
};
PM2 命令:
bash
# 启动应用
pm2 start ecosystem.config.js
# 查看状态
pm2 status
# 查看日志
pm2 logs
# 重启应用
pm2 restart customer-service-agent
# 设置开机自启
pm2 startup
pm2 save
步骤 3:配置监控告警
typescript
// src/monitoring/alerts.ts
import { Client } from "langsmith";
const client = new Client();
/**
* 检查关键指标并发送告警
*/
export async function checkAndAlert() {
// 1. 检查错误率
const errorRate = await getErrorRate(lastMinutes: 5);
if (errorRate > 0.1) {
await sendAlert({
severity: "critical",
message: `⚠️ 错误率过高:${(errorRate * 100).toFixed(2)}%`,
});
}
// 2. 检查响应时间
const p95Latency = await getLatencyPercentile(95, lastMinutes: 5);
if (p95Latency > 5000) {
await sendAlert({
severity: "warning",
message: `⚠️ P95 延迟过高:${p95Latency}ms`,
});
}
// 3. 检查成本
const dailyCost = await getDailyCost();
const budget = parseFloat(process.env.DAILY_BUDGET || "100");
if (dailyCost > budget * 0.8) {
await sendAlert({
severity: "warning",
message: `💰 今日成本已达到预算的 80%:$${dailyCost.toFixed(2)}`,
});
}
}
/**
* 发送告警通知
*/
async function sendAlert({
severity,
message,
}: {
severity: "critical" | "warning";
message: string;
}) {
console.error(`[Alert] [${severity.toUpperCase()}] ${message}`);
// 发送到 Slack
if (process.env.SLACK_WEBHOOK_URL) {
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: `[${severity.toUpperCase()}] ${message}`,
channel: "#alerts",
}),
});
}
// 严重告警发送到 PagerDuty
if (severity === "critical" && process.env.PAGERDUTY_INTEGRATION_KEY) {
await triggerPagerDuty(message);
}
}
// 每分钟检查一次
setInterval(checkAndAlert, 60000);
✅ 验收标准:
- Docker 容器成功启动并运行
- PM2 进程管理正常工作
- 监控告警系统能及时发现异常
- LangSmith 中可以查看所有追踪记录
- 系统可以承受至少 100 并发用户
四、项目总结
📊 最终成果(假设)
| 指标 | 数值 |
|---|---|
| 自动解决率 | 82% |
| 平均响应时间 | 2.8 秒 |
| 客户满意度 | 87% |
| 人力成本节省 | 78% |
| 服务时间 | 24/7 |
🎯 关键技术点
- RAG 知识库:基于 Pinecone 的向量检索,准确率 90%+
- 多工具集成:订单、物流、工单等业务系统无缝对接
- 对话管理:基于 thread_id 的多轮对话上下文追踪
- 质量监控:LangSmith 全链路追踪和自动化评估
- 生产部署:Docker + PM2 + 监控告警体系
💡 经验教训(假设)
成功经验:
- ✅ 分阶段开发,每个里程碑都有明确目标
- ✅ 早期接入 LangSmith,便于调试和优化
- ✅ 充分的错误处理和降级策略
- ✅ 完善的监控告警,及时发现问题
踩坑记录:
- ❌ 初期未设置 Token 预算,导致成本失控
- ❌ 忘记配置速率限制,遭遇恶意刷接口
- ❌ 知识库文档质量参差不齐,影响检索效果
- ❌ 未做充分的压力测试,上线后出现性能瓶颈