RAG Gateway Stack:从文档入库到可追溯回答的工程复盘

RAG Gateway Stack 已经从框架学习与服务骨架推进到文档入库、普通问答、Agent 和观测工作台。一次问答的价值,不只是生成了文字,还包括能解释检索范围、回查引用、观察工具调用,并在断线后继续接收同一次生成。

本文于 2026-10-03 对照 GitHub 已发布提交 0771a82 核对。这里区分源码实现、仓库验证入口和真实运行证据:功能存在不等于本文已在当前机器运行整套服务,也不等于生产验收完成。

1. 当前系统的边界

1
2
3
4
5
6
7
React Workbench
-> C++ Drogon Gateway:外部 API、上传、鉴权、限流、SSE 代理
-> FastAPI:文档、会话、检索、普通 Chat、Agent 与监控
-> MySQL:文档、chunk、消息、引用、任务、Agent Trace
-> LanceDB:chunk 向量和召回元数据
-> Redis / Celery:任务传递、异步解析与向量化
-> Embedding / CrossEncoder / OpenAI-compatible LLM

浏览器只访问 Gateway,FastAPI 保留为内部业务入口。C++ 处理协议与流量边界,Python 承担变化更快的 RAG 和 Agent 编排;前端用于调试、演示与观察执行流程。

1.1 数据以谁为准

组件 保存什么 工程含义
MySQL 文档状态、chunk 正文、消息、citations、任务和 Trace 结构化业务事实来源
LanceDB 向量、chunk 标识与检索元数据 可重建的检索索引
Redis Celery broker / result backend、可选的网关限流状态 任务与短期运行状态

已发布检索实现使用 LanceDB。网站中的 FAISS 学习笔记 仍用于理解向量检索基础,不能据此把当前项目描述成 FAISS 或 BM25 + FAISS 系统。

2. 从文件和网页进入同一条入库链路

文件通过 POST /v1/documents 上传;网页通过 POST /v1/documents/web 抓取正文并保存为普通文档。文件入口支持 Markdown、TXT、JSON、CSV、PDF、DOCX 和 XLSX。网页入口不负责登录页面或复杂反爬。

1
2
3
4
5
6
7
创建文档记录
-> Celery parse_document
-> 解析与切片
-> MySQL 写入 chunks
-> Celery build_embedding
-> Embedding 与 LanceDB 索引写入
-> 文档可检索

解析和 embedding 是两个任务,任务元数据记录 queued、document_loaded、document_chunked、chunks_written、embedding_started 等阶段。工作台因此可以显示 Parsing、Chunking、Embedding、Indexed 或 Failed,而不是仅显示一个没有依据的总体百分比。

2.1 上传成功不等于建库完成

上传响应包含文档和任务标识;调用方还需要通过 GET /v1/tasks/{task_id} 查询阶段,并确认文档索引状态。网页导入返回的 status_url 可能是内部路径,外部客户端应依据 task_id 请求 Gateway 的 /v1/tasks/ 入口。

3. 普通 RAG 怎样形成可回查答案

1
2
3
4
5
6
7
8
9
10
用户问题
-> 创建 user message
-> 在 indexed 文档范围内生成 query embedding
-> LanceDB 召回 chunk_id
-> MySQL 批量补齐正文与来源
-> CrossEncoder rerank
-> 组装 Prompt
-> LLM 生成
-> 保存 assistant message 与 citations
-> done

没有传 doc_id / doc_ids 时,检索面向当前可用的 indexed 文档;传入后限制到指定文档。召回分数、重排分数、原始 rank 和最终 rank 分别保留,便于判断问题发生在召回、回表还是重排。

citations 保存文档和 chunk 标识,可以从已保存的消息重新查询。当前引用粒度是消息与 chunk;没有逐句 answer span 时,工作台展示本次检索证据,不虚构逐句来源连线。

4. Agent 增加的是决策与执行记录

4.1 只读工具和三层记忆

当前工具包括 knowledge_search、get_document_detail、list_ready_documents 和 list_message_citations。Agent 注入用户长期记忆、会话摘要及近期消息,再进行意图路由和工具循环。

工具结果 处理方式
ok=true 将 data 作为工具观察加入上下文
ok=false 记录错误与 Trace,允许根据已有观察降级回答

明显的知识库问题会通过轻量意图路由优先执行检索;问候和普通闲聊不会被强制检索。这个规则针对当前系统,不代表一个通用的意图分类器。

4.2 Trace 与答案事件

1
agent_step -> tool_call -> tool_result -> delta -> final -> done

工作台展示 run、step、工具名和事件 ID。工具返回时,LanceDB 召回、MySQL 回表和 rerank 信息可能一起到达,界面据真实事件推进,不编造逐节点实时耗时。当前 Agent 的最终答案在运行完成后一次性通过 delta 输出;普通 Chat 的 token 增量与 Agent 步骤流要分别理解。

5. SSE 续传为什么必须复用同一次运行

普通 Chat 首次请求携带 content,Gateway 创建用户消息,并通过 X-User-Message-ID 返回标识。重连移除 content,使用同一个 user_message_id 与 Last-Event-ID。Agent 使用同一个 session 与 trace_id 识别运行。

1
2
3
4
首次请求:创建消息 / 运行 -> 接收并保存事件 ID
断线:生成继续执行
重连:复用消息 / trace 标识 + Last-Event-ID -> 补发后续事件
终止:answer + citations 先落库 -> done

重发原始内容会被视为新消息,可能重复生成。源码默认在完成后保留续传状态 15 分钟;这份状态在进程内,过期或进程重启后不能保证恢复。状态缺失时返回明确错误,不静默重新生成。

6. 工作台能证明什么

React Workbench 展示文档索引阶段、Execution Flow、回答与引用、Agent SSE Trace 和依赖状态。它把“请求正在进行”拆成可观察的事实,但不能代替业务验收。

验证层次 当前仓库提供的入口 仍需记录的证据
逻辑回归 检索、切片、工具、记忆、Trace、续传测试 对应提交、测试命令与实际输出
服务联通 /internal/health、/health、监控概览 依赖可用性与降级状态
完整用户路径 scripts/e2e_all.sh 上传、任务完成、回答与引用回查
模型质量 固定问题与 bad case 分析 召回、重排和答案支撑程度

运行与验收笔记 给出当前启动命令、API 示例与失败路径。本文没有在博客构建环境执行模型、数据库或跨服务 E2E。

7. 后续工作的实际边界

  • Gateway SSE 代理仍按活跃流占用受限数量的 OS thread;高并发需要进一步验证传输模型。
  • 对外没有历史 Sessions / Agent Runs 列表接口,前端刷新后的历史展示受此限制。
  • LanceDB 没有独立健康接口,没有索引证据时不能显示已就绪。
  • 多租户隔离、生产密钥管理、部署编排及逐句证据映射仍不属于当前完成能力。

下一轮应先固定问题集和断线测试,保存运行环境、提交、引用与失败日志,再围绕测得的瓶颈调整系统。学习笔记和实现源码都可以保留,但项目进度最终要落在可重复的用户路径上。

参考实现