深入浅出 LangChain —— 第十一章:实战一 智能客服系统

📖 本章学习目标

  • ✅ 设计完整的智能客服系统架构
  • ✅ 构建基于 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

核心技术:

  • 框架: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

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

🎯 关键技术点

  1. RAG 知识库:基于 Pinecone 的向量检索,准确率 90%+
  2. 多工具集成:订单、物流、工单等业务系统无缝对接
  3. 对话管理:基于 thread_id 的多轮对话上下文追踪
  4. 质量监控:LangSmith 全链路追踪和自动化评估
  5. 生产部署:Docker + PM2 + 监控告警体系

💡 经验教训(假设)

成功经验:

  • ✅ 分阶段开发,每个里程碑都有明确目标
  • ✅ 早期接入 LangSmith,便于调试和优化
  • ✅ 充分的错误处理和降级策略
  • ✅ 完善的监控告警,及时发现问题

踩坑记录:

  • ❌ 初期未设置 Token 预算,导致成本失控
  • ❌ 忘记配置速率限制,遭遇恶意刷接口
  • ❌ 知识库文档质量参差不齐,影响检索效果
  • ❌ 未做充分的压力测试,上线后出现性能瓶颈

📚 延伸阅读


下一章:《第十二章 ------ 实战二:代码助手 Agent》

相关推荐
SmartBrain1 小时前
Harness 工程建设与 AI 平台建设对比
大数据·人工智能·华为·aigc
深海鱼在掘金1 小时前
深入浅出 LangChain —— 第十章:上下文工程与安全护栏
人工智能·langchain·agent
qcx231 小时前
【AI Agent通识九课】 04 · AI 的双车道 — 安全怎么保
人工智能·安全·agent·ai agent·warp
这张生成的图像能检测吗1 小时前
(论文速读)CPC-DG:基于分类器预测一致性和领域泛化的旋转机械跨域故障诊断方法
人工智能·机器学习·故障诊断
向日葵花籽儿1 小时前
斯坦福 CS146S - Wk01 编码入门 LLM 和人工智能开发
人工智能
lbb 小魔仙1 小时前
Ubuntu 22.04 + Windows 本地部署 AI 大模型完全指南:Ollama + Python 调用实战(附国内加速配置)
人工智能·windows·python·ubuntu
IT老兵20251 小时前
nvidia nemo-toolkit框架应用问题汇总
人工智能·python·机器学习·nemo
shamalee1 小时前
2026高效会议纪要:Gemini3.1Pro一键搞定
人工智能
qq_160144871 小时前
零基础两个月后,我拿到了AI应用方向的offer,低门槛转行真实路径
人工智能