首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >3天改造一个烂尾智能客服:RAG双阶段检索与记忆压缩实战

3天改造一个烂尾智能客服:RAG双阶段检索与记忆压缩实战

原创
作者头像
KANWOJIANJIE
修改2026-07-30 11:43:32
修改2026-07-30 11:43:32
830
举报

3天改造一个烂尾智能客服:RAG双阶段检索与记忆压缩实战

接手过一个"烂尾项目":一个基于FastAPI + 智谱AI glm-4的智能客服,知识库是几百MB的静态文件,每次用户提问都要把整个知识库扫一遍再塞给大模型。响应时间8秒起,Token消耗惊人,答案准确率只有52%。

问题根源一句话就能说清:把大模型当数据库用了。

这篇文章记录了我3天内的改造方案和踩坑经验。

一、检索层改造:从关键词匹配到双阶段召回

原方案用的是关键词匹配(BM25),召回精度极低。改造方向很明确:引入向量检索 + 重排序(Rerank)双阶段策略

第一阶段用轻量Embedding模型(text-embedding-3-small)从向量库中快速召回Top-20候选片段。第二阶段用交叉编码器(cross-encoder/ms-marco-MiniLM-L-6-v2)对这20个片段逐一精排,只取Top-3送入大模型。

代码语言:javascript
复制
# 双阶段检索核心逻辑
def hybrid_retrieve(query, top_k=3):
    # 第一阶段:向量召回20个候选
    candidates = vector_store.similarity_search(
        query, 
        k=20, 
        filter={"source": "knowledge_base"}
    )
    # 第二阶段:精排取前3
    reranked = reranker.rerank(
        query, 
        candidates, 
        top_n=top_k,
        return_scores=True
    )
    return reranked

实测结果: 答案准确率从52%提升到89%,响应时间从8秒降到1.2秒。关键在于第一阶段保证召回率,第二阶段保证精确率,分工明确。

二、记忆压缩:解决多轮对话的Token爆炸

单轮问答是玩具,多轮对话才是产品。但简单地把全部历史对话塞进Prompt,Token消耗会呈指数级增长。

我的方案是滑动窗口 + 摘要压缩:保留最近3轮完整对话,更早的历史由大模型异步生成摘要,以system message的形式注入上下文。

代码语言:javascript
复制
# 记忆压缩实现
def compress_history(history, max_turns=3):
    if len(history) <= max_turns:
        return history
    
    recent = history[-max_turns:]  # 保留最近3轮
    old = history[:-max_turns]     # 较早的历史
    
    # 异步生成摘要(用更便宜的模型)
    summary = lightweight_llm.summarize(
        str(old), 
        max_tokens=200,
        prompt="请用100字以内概括以上对话的核心内容"
    )
    
    # 摘要作为system消息前置
    return [{"role": "system", "content": f"历史对话摘要:{summary}"}] + recent

效果: 单次对话的平均Token成本从8000降到2000,且用户无感知。需要注意两点:

  1. 摘要生成用便宜的小模型(如glm-4-flash),不要用主力模型
  2. 摘要异步生成,不要阻塞主流程

三、被忽略的工程化细节

算法改造只占了一半工作量,另一半是工程兜底。三个最关键的改动:

1. 可观测性:每个LLM调用必须记录耗时和Token数

代码语言:javascript
复制
# 封装LLM调用,自动记录指标
def call_llm_with_metrics(prompt):
    start = time.time()
    response = llm.invoke(prompt)
    cost = time.time() - start
    
    # 写入时序数据库,用于监控告警
    metrics.record(
        latency=cost,
        tokens=response.usage.total_tokens,
        model="glm-4"
    )
    return response

2. 降级方案:大模型接口挂了怎么办

代码语言:javascript
复制
def get_response_with_fallback(query):
    try:
        return llm.invoke(query, timeout=3)
    except TimeoutError:
        # 降级为规则匹配 + 缓存命中
        return cache.get(query) or default_reply("系统繁忙,请稍后重试")

3. 测试先行:每个改动必须有对应的回归测试用例

改造过程中为检索模块写了17个测试用例,覆盖边界情况(空查询、超长查询、特殊字符等),确保后续迭代不引入新Bug。

写在最后

三天改造结束后,这个智能客服项目成功上线,日均处理200+咨询,连续运行两个月无一起严重超时事故

回顾整个过程,最深的感触是:AI应用开发的难点不在于调用大模型API,而在于如何把检索、记忆、降级、可观测性这些模块有序地组合成一个可靠的产品。

如果你也在做一个"快跑通了但总觉得不踏实"的AI项目,建议先停下来,把这三件事做了:

  1. 检索是否做到了"先粗后精"?
  2. 多轮对话是否做了记忆压缩?
  3. 每个外部依赖是否有降级方案?

这三件事做完,项目的可靠性会上一个台阶。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 3天改造一个烂尾智能客服:RAG双阶段检索与记忆压缩实战
    • 一、检索层改造:从关键词匹配到双阶段召回
    • 二、记忆压缩:解决多轮对话的Token爆炸
    • 三、被忽略的工程化细节
    • 写在最后
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档