从“能跑”到“能用”:我在智能客服 RAG 项目中踩过的两个生产级问题

最近在做一个智能客服 RAG 项目时,我发现很多网上的 Demo 都停留在“能跑通”的阶段,但真正落地到业务场景后,会遇到很多生产环境问题。

比如:

  • LLM 服务突然 503,整个客服系统直接不可用
  • 知识库文档更新后,向量库还是旧数据
  • 删除 PDF 后,ChromaDB 里仍然残留旧向量
  • 重复导入知识库后,向量数据越来越乱

这些问题在本地 Demo 里不明显,但一旦进入真实业务环境,就会直接影响用户体验。

这篇文章记录一下我在智能客服 RAG 项目里做的两个比较有价值的优化:

  1. LLM Fallback 兜底机制
  2. 知识库增量管理与文件同步机制

项目技术栈:

  • LangChain
  • ChromaDB
  • DeepSeek
  • 通义千问
  • MinerU
  • Python

一、LLM 服务不稳定?我给 RAG 做了 Fallback 兜底机制

1. 问题背景

一开始项目只接了 DeepSeek。

本地测试没问题,但实际调用时,经常会出现:

503 Service Unavailable
Service is too busy

特别是高峰期,模型接口不稳定。

如果整个系统只依赖一个 LLM,那么:

主模型挂了 = 整个客服系统挂了

这是生产环境里绝对不能接受的。


2. 我的解决方案:Fallback 兜底机制

核心思想很简单:

主模型失败
    ↓
自动切换备用模型
    ↓
保证服务不中断

整体架构:

用户请求
    ↓
┌─────────────────────┐
│   主模型 DeepSeek    │
└──────────┬──────────┘
           │ 失败
           ↓
┌─────────────────────┐
│   备用模型 通义千问   │
└──────────┬──────────┘
           │ 成功
           ↓
        返回结果

3. 核心代码实现

核心逻辑其实就是:

  • try 主模型
  • except 捕获异常
  • 自动切换备用模型

代码如下:

def chat(messages: list, temperature: float = 0.7) -> str:
    """
    与 LLM 对话(支持 Fallback 机制)
    """

    try:
        # 主模型
        primary_llm = get_llm(
            temperature,
            provider="primary"
        )

        result = _call_llm(primary_llm, messages)

        print("[LLM] 主模型调用成功")

        return result

    except Exception as e:

        print(f"[LLM] 主模型失败: {e}")

        # 未开启 fallback
        if not LLM_FALLBACK_ENABLED:
            raise

        print("[LLM] 触发 Fallback")

        try:
            # 备用模型
            fallback_llm = get_llm(
                temperature,
                provider="fallback"
            )

            result = _call_llm(
                fallback_llm,
                messages
            )

            print("[LLM] 备用模型调用成功")

            return result

        except Exception:
            raise Exception("主备模型全部失败")

4. 这里面真正重要的,不是 try-catch

很多人会觉得:

“这不就是 try-catch 吗?”

但真正重要的是:

生产级服务降级思维

因为:

服务降级 > 服务崩溃

即使备用模型:

  • 更慢
  • 更贵
  • 效果稍差

也比:

系统完全不可用

强得多。


5. 我还做了哪些生产级优化?

(1)配置化管理

LLM_FALLBACK_ENABLED = True

LLM_PRIMARY_TIMEOUT = 30

LLM_FALLBACK_TIMEOUT = 60

这样可以动态控制:

  • 是否开启兜底
  • 超时时间
  • 主备模型策略

(2)调用状态追踪

{
    "provider": "fallback",
    "model": "qwen-plus",
    "fallback_triggered": True
}

方便:

  • 日志监控
  • 故障排查
  • 分析模型稳定性

(3)预留熔断机制

后面还可以继续扩展:

连续失败 N 次
    ↓
暂停主模型调用
    ↓
定时恢复检测

这其实已经接近微服务里的:

  • 熔断
  • 降级
  • 高可用设计

了。


二、知识库最难的,其实不是“导入”,而是“同步”

1. 我发现网上很多 RAG Demo 都有一个问题

很多教程都是:

python import.py

然后:

PDF → 向量化 → ChromaDB

看起来没问题。

但真实项目里会出现:

  • PDF 更新了怎么办?
  • PDF 删除了怎么办?
  • 重复导入怎么办?
  • 多目录怎么办?
  • 同名文件怎么办?

如果不处理:

向量库会越来越脏

2. 我最后采用了“声明式同步”思路

我把整个知识库管理,类比成 Git 工作流。


3. 整体架构设计

data/raw/
    ↓
data/processed/
    ↓
ChromaDB

对应关系:

模块 类比
raw Git 工作目录
processed 暂存区
ChromaDB 仓库
import_record.json 提交记录

用户只需要在工作区放PDF,然后点击一下,系统自动同步。


三、我实现了四种状态自动检测

1. 新增文件

PDF 存在
JSON 无记录

处理:

解析 → 向量化 → 写入记录

2. 文件更新

通过:

文件修改时间(mtime)

自动检测。

代码:

def is_file_updated(filename, file_path):

    current_mtime = get_file_mtime(file_path)

    record_mtime = f.get("file_mtime", "")

    return current_mtime > record_mtime

如果文件被替换:

删除旧向量
重新导入

3. 文件删除

如果:

JSON 有记录
但 PDF 不存在

系统自动:

删除 Markdown
删除向量
删除记录

4. 无变化

直接跳过。

这样整个知识库:

只处理变化部分

不需要每次全量重建。


四、我重点解决了“幂等性”问题

这是很多 RAG 项目容易忽略的。


什么叫幂等性?

简单理解:

重复运行结果一致

比如:

python import_knowledge.py
python import_knowledge.py
python import_knowledge.py

不会:

  • 重复向量
  • 重复 chunk
  • 数据污染

我的解决方案

每次导入前:

collection.delete(
    where={"source": source}
)

先删旧数据。

再插入:

collection.add(...)

这样:

永远只有一份最新数据

五、我还解决了“同名文件冲突”问题

很多知识库系统都有:

同名文件覆盖

问题。

比如:

实验一/说明书.pdf
实验二/说明书.pdf

我的解决方案

source 使用:

完整相对路径

例如:

实验一/说明书/说明书.md

chunk id:

ids = [
    f"{source_name}_chunk_{i:04d}"
]

这样:

全局唯一

不会冲突。


六、最后的效果

现在整个知识库管理已经变成:

用户只负责放文件
系统自动同步

支持:

  • 新增
  • 更新
  • 删除
  • 重试失败文件
  • 全量重建
  • 增量同步

命令:

python import_knowledge.py

python import_knowledge.py --force

python import_knowledge.py --reimport "文件名"

python import_knowledge.py --reimport-failed

python import_knowledge.py --status

七、我对 RAG 项目最大的一个理解变化

以前做 RAG:

重点一直放在:

  • Prompt
  • Embedding
  • Chunk
  • Recall

但真正做项目后发现:

工程化能力
才是真正拉开差距的地方

因为:

Demo 能跑 ≠ 系统能用

真正上线后:

  • 高可用
  • 数据同步
  • 幂等性
  • 熔断降级
  • 可观测性

这些问题才是核心。


八、总结

这次项目里,我主要做了两件事:

1. LLM Fallback 机制

解决:

模型服务不稳定

实现:

主模型失败自动切换备用模型

核心价值:

服务降级,而不是服务崩溃

2. 知识库增量同步机制

解决:

知识库数据混乱

实现:

自动检测新增/更新/删除

核心价值:

让知识库真正可维护

九、后续优化方向

目前还准备继续优化:

  • 多级 Fallback
  • 熔断恢复机制
  • 混合检索(BM25 + 向量)
  • Rerank
  • 知识库版本管理
  • Agent 化客服系统
  • LangGraph 工作流

如果你也在做 RAG 项目,希望这篇文章能给你一些工程化方向上的参考。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐