主题
需求分析与技术选型
从这一章开始,我们将离开"单个知识点讲解"的模式,进入综合项目实战阶段。前面九章我们学习了模型 I/O、RAG、记忆、多模态、LCEL、Agent、流式/异步/中间件——这些不是孤立的技术点,而是构建一个完整 AI 应用的积木。本章和下一章的目标,就是把所有积木搭成一座真正能用的建筑。
一个真实的智能客服场景
想象你是一家 SaaS 公司的技术负责人,公司产品叫 CloudDesk——一个在线协作平台。每天有几百个客户通过网页聊天窗口、邮件或工单系统提出问题:
- "你们的免费版支持几个团队成员?"
- "我想把数据从 Notion 迁移过来,有导入工具吗?"
- "上个月我付了专业版但被扣了两次钱,怎么退款?"
- "API 返回 503 错误是什么意思?"
目前这些问题全靠人工客服处理。问题很明显:响应慢(平均等待 5 分钟)、成本高(每个坐席月薪 8000+)、覆盖时间有限(只能覆盖工作时间)。你的老板给你下达了一个任务:用 AI 构建一套智能客服系统,能够自动回答 70% 以上的常见问题,复杂问题再转人工。
这就是我们要构建的系统。它不是一个玩具 demo,而是一个需要考虑知识库管理、意图识别、多轮对话、人工接管、日志审计、性能监控等维度的生产级应用。
从业务需求到技术需求
在动手写代码之前,我们需要先把模糊的业务诉求翻译成精确的技术需求。这个过程叫做需求分析,它是任何项目最关键也最容易被跳过的步骤。
核心功能需求
经过与产品团队和客服团队的沟通,我们梳理出以下核心功能:
第一,基于产品知识的自动问答。这是系统的主干能力。用户问产品相关的问题(定价、功能、使用方法),系统能够从公司的产品文档、FAQ、帮助中心文章中检索出准确信息并生成自然语言回复。这对应我们已经学过的 RAG 技术。
第二,意图识别与路由分流。不是所有问题都适合 AI 回答。用户可能想查订单状态、申请退款、投诉服务态度——这些需要走不同的处理流程。系统需要在接收到用户消息的第一时间判断"这个问题该由谁来处理",然后分发给对应的处理器。这涉及 分类/路由 能力。
第三,多轮对话上下文保持。用户不会一次就把话说完。典型对话可能是这样的:
用户: 你们免费版有什么限制?
AI: 免费版最多支持 5 个成员,存储空间 2GB...
用户: 那 专业版呢?
AI: 专业版支持无限成员,存储空间 100GB...
用户: 好的,我要升级,怎么操作?注意第三轮——用户说"那 专业版呢?"和"我要升级",这两句话脱离上下文是无法理解的。系统必须记住前面的对话内容才能正确回应。这对应 记忆(Memory) 组件。
第四,人工接管机制。AI 不是万能的。当遇到以下情况时,系统应该主动将对话转给人工客服:
- 连续多次无法回答用户的问题
- 用户明确表示"转人工"/"找真人"
- 检测到用户情绪激动(投诉类场景)
- 涉及敏感操作(退款、账号封禁等)
这个机制在行业内被称为 Handoff(交接)。
非功能性需求
除了功能本身,一个生产级系统还必须满足这些质量属性:
| 维度 | 要求 | 对应技术手段 |
|---|---|---|
| 响应速度 | 首个 token 在 2 秒内出现 | 流式输出(stream) |
| 并发能力 | 支持 100+ 用户同时在线 | 异步编程(async/await) |
| 答案准确性 | 产品相关问题准确率 > 85% | RAG 检索质量优化 |
| 可观测性 | 每次对话可追溯、可审计 | 中间件日志 + 回调 |
| 安全性 | 不泄露内部信息、不执行危险操作 | 内容审核中间件 |
你会发现,右边的"对应技术手段"恰好就是我们前九章学过的全部内容。这不是巧合——LangChain 的设计哲学就是把这些工程关注点以统一的方式组织起来。
系统架构总览
基于以上分析,我们的智能客服系统包含以下核心模块:
┌─────────────────────────────────────────────────┐
│ 用户接入层 │
│ (Web 聊天 / API / 邮件 / 工单) │
└──────────────┬──────────────────────────┬────────┘
│ │
┌──────▼──────┐ ┌────────▼────────┐
│ 意图识别器 │ │ 内容审核器 │
│ (Intent │ │ (Content │
│ Classifier) │ │ Auditor) │
└──────┬──────┘ └────────┬────────┘
│ │
┌─────────┼─────────┬───────────────┘
│ │ │
┌───▼───┐ ┌──▼───┐ ┌──▼──────────┐ ┌──────────────┐
│ RAG │ │ 记忆 │ │ 人工接管 │ │ 通用闲聊/兜底 │
│ 问答链 │ │ 组件 │ │ Handoff │ │ Fallback │
└───┬───┘ └──┬───┘ └──┬──────────┘ └──────────────┘
│ │ │
└────┬────┴─────────┘
│
┌────▼────────────┐
│ LLM 核心 │
│ (ChatModel) │
└─────────────────┘每个模块都对应一节的内容:
- 10.2 — 构建 RAG 问答链(加载产品知识库)
- 10.3 — 实现意图识别与分流路由
- 10.4 — 集成人工接管(Handoff)机制
技术选型决策
在具体实现之前,我们需要对每个关键组件做技术选型。选型的原则是:优先选择 LangChain 生态内已验证的方案,避免重复造轮子;同时在必要的地方保留定制空间。
LLM 选型
对于客服场景,模型的选型需要平衡三个因素:成本、响应速度、中文理解能力。
| 方案 | 成本(每百万 token) | 响应速度 | 中文能力 | 推荐度 |
|---|---|---|---|---|
| GPT-4o | ~$2.5(输入)/ $10(输出) | 快 | 优秀 | ⭐⭐⭐⭐ 生产首选 |
| GPT-4o-mini | ~$0.15 / $0.6 | 很快 | 良好 | ⭐⭐⭐⭐⭐ 性价比最优 |
| Claude 3.5 Sonnet | ~$3 / $15 | 快 | 优秀 | ⭐⭐⭐⭐ 长文本优势 |
| 本地部署(Qwen/Llama) | 免费(需 GPU) | 取决于硬件 | 良好 | ⭐⭐⭐ 数据敏感场景 |
推荐策略:开发测试阶段用 GPT-4o-mini 降低成本,生产环境根据实际效果决定是否升级到 GPT-4o。代码层面通过环境变量切换,不做硬编码:
python
import os
from langchain_openai import ChatOpenAI
def get_llm(model_name: str = None):
model_name = model_name or os.getenv("LLM_MODEL", "gpt-4o-mini")
return ChatOpenAI(
model=model_name,
temperature=0.1,
streaming=True,
)
llm = get_llm()向量数据库选型
客服知识库的规模通常在几千到几万条文档之间,属于中小规模。我们对比几种方案:
| 方案 | 特点 | 适用场景 |
|---|---|---|
| Chroma | 内嵌式、零配置、Python 原生 | 开发和小规模生产 ✅ 推荐 |
| FAISS | Facebook 出品、纯内存、极快 | 纯检索、不需要持久化查询 |
| Milvus | 分布式、支持十亿级向量 | 大规模生产环境 |
本项目选择 Chroma 作为默认向量存储。原因很简单:它是 LangChain 生态中集成度最高的向量数据库,无需额外安装服务器,一行代码就能启动,非常适合作为教程项目的起点。后续如果需要扩展到更大规模,替换底层存储只需修改 retriever 的创建逻辑,上层链路完全不受影响。
python
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
vectorstore = Chroma(
collection_name="cloud_desk_kb",
embedding_function=embeddings,
persist_directory="./chroma_db",
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})记忆方案选型
客服场景的记忆需求比较特殊:需要按会话隔离(每个用户的对话互不干扰),同时需要持久化(用户关闭浏览器后再回来,历史记录还在)。我们在第 5 章学过的 RunnableWithMessageHistory 正好满足这两个需求:
python
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.chat_history import InMemoryChatMessageHistory
store = {}
def get_session_history(session_id: str):
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
with_memory = RunnableWithMessageHistory(
runnable=base_chain,
get_session_history=get_session_history,
input_messages_key="question",
history_messages_key="chat_history",
)开发阶段用内存存储即可,生产环境可以无缝切换到 Redis 或 SQLite 后端。
框架选型总结
| 组件 | 选择 | 理由 |
|---|---|---|
| 编排框架 | LangChain v1.0 + LCEL | 统一的声明式语法 |
| LLM | ChatOpenAI (GPT-4o-mini) | 成本低、速度快、中文好 |
| Embedding | OpenAIEmbeddings | 质量高、生态兼容性好 |
| 向量存储 | Chroma | 零配置、开发友好 |
| 记忆 | RunnableWithMessageHistory | 会话隔离 + 可持久化 |
| Web 框架 | FastAPI | 异步原生、流式支持好 |
| 流式协议 | SSE (Server-Sent Events) | 浏览器原生支持 |
项目结构规划
在开始编码之前,先确定文件结构。一个好的项目结构能让代码的组织一目了然:
customer-service/
├── knowledge_base/ # 产品知识库原始文档
│ ├── pricing.md # 定价方案
│ ├── features.md # 功能介绍
│ ├── faq.md # 常见问题
│ └── policies.md # 政策条款
├── config.py # 全局配置(模型、API Key 等)
├── rag_pipeline.py # RAG 检索增强管线
├── intent_classifier.py # 意图识别与分流
├── memory_manager.py # 会话记忆管理
├── handoff.py # 人工接管机制
├── content_auditor.py # 内容审核中间件
├── chatbot.py # 主系统:组装所有模块
├── server.py # FastAPI 服务端
└── main.py # 入口 / CLI 交互这个结构遵循了单一职责原则:每个文件负责一个明确的模块,模块之间通过清晰的接口交互。接下来三节,我们会逐一实现每个模块,最后在 10.4 节把它们组装成一个完整的系统。
常见误区
误区一:先写代码再想需求。很多开发者拿到项目就急着开始 import 和写 class,结果写到一半发现架构不对,推倒重来。"磨刀不误砍柴工"——花 30 分钟做需求分析和技术选型,能避免后面数小时的返工。
误区二:什么都想一步到位。看到上面的架构图就想着一次性实现所有功能——流式输出、异步、审核、限流、监控……这是不现实的。正确的做法是 MVP 思维:先用最简单的方式跑通主流程(RAG 问答 → 记忆 → 输出),然后再逐层叠加中间件和能力。
误区三:忽略非功能性需求。只关心"能不能答对问题",不关心"响应有多快""并发撑不撑得住""出了问题怎么排查"。在生产环境中,非功能性需求的失败往往比功能 bug 更致命——一个响应超时的客服系统比一个偶尔答错的系统更让用户崩溃。