外观
项目文章RAG系统软件架构
约 2914 字大约 10 分钟
本文只定义软件边界、仓库落位、数据契约与调用时序,不绑定 GPU 品牌、显存容量、量化格式或模型推理框架。硬件与模型部署见 RAG 本机硬件部署与选型对比。两者只通过 HTTP 契约和环境变量交互。
1. 架构目标
本架构要同时支持两类用户:
- VuePress 访客:输入问题后直接获得流式答案、引用和站内链接。
- 智能体:通过 CLI 或 MCP 获取结构化证据,由 Agent 完成任务规划和最终表达。
核心约束:
blog/docs/**/*.md是文章正文的唯一事实源;- FastAPI 应用层是检索与问答能力的唯一实现;
- VuePress、CLI 和 MCP 不直连 Qdrant、PostgreSQL 或模型服务;
- 生成模型、Embedding 和 Reranker 均是可替换的外部运行时;
- URL、引用编号和索引版本只由服务端产生;
- 文章内容是不可信数据,不能覆盖 Agent 或 RAG 系统指令。
2. 项目现状与落位
当前仓库只有 VuePress 站点,主要代码位于 blog/。RAG 后端是独立的 Python 应用,不放入 VuePress 构建流程,建议目录如下:
hxxz-new/
├─ blog/
│ ├─ docs/ # 唯一文章语料源
│ └─ package.json # VuePress 构建
├─ rag/
│ ├─ pyproject.toml
│ ├─ uv.lock
│ ├─ src/hxxz_rag/
│ │ ├─ api/
│ │ │ ├─ routes_chat.py
│ │ │ ├─ routes_search.py
│ │ │ ├─ routes_index.py
│ │ │ └─ routes_health.py
│ │ ├─ application/
│ │ │ ├─ answer_service.py
│ │ │ ├─ retrieval_service.py
│ │ │ └─ indexing_service.py
│ │ ├─ domain/
│ │ │ ├─ models.py
│ │ │ └─ ports.py
│ │ ├─ infrastructure/
│ │ │ ├─ markdown/
│ │ │ ├─ qdrant/
│ │ │ ├─ model_clients/
│ │ │ └─ persistence/
│ │ └─ entrypoints/
│ │ ├─ api.py
│ │ ├─ cli.py
│ │ └─ mcp_server.py
│ ├─ configs/
│ ├─ scripts/
│ └─ tests/
│ ├─ unit/
│ ├─ contract/
│ ├─ integration/
│ └─ eval/
├─ .agents/skills/hxxz-rag/SKILL.md # Agent 使用策略
├─ deploy/
│ ├─ compose.core.yml # API、Qdrant、PostgreSQL
│ └─ nginx/rag.conf
└─ .mcp.json # 本地 MCP 客户端配置rag/ 不反向依赖 blog/node_modules,blog 也不依赖 Python 环境。两者只通过同源 /api/rag/ 交互。
3. 总体软件架构
3.1 边界说明
| 边界 | 输入 | 输出 | 不负责 |
|---|---|---|---|
| API 路由 | HTTP 请求与身份 | Pydantic DTO、SSE 事件 | 直接编写 Qdrant 查询 |
| Retrieval Service | 查询、过滤、Top K | 结构化证据 | 生成最终答案 |
| Answer Service | 问题、会话、证据 | 答案 token 与可验证引用 | 伪造 URL 或补充库外事实 |
| Indexing Service | Markdown AST | 版本化 collection | 在线 alias 未校验切换 |
| Model Client | 统一领域请求 | 向量、分数或 token | 感知具体 GPU |
| CLI / MCP | Agent 工具请求 | API 响应的稳定子集 | 复制检索逻辑 |
4. 核心应用流程
4.1 索引构建
- 扫描
blog/docs/**/*.md,应用排除规则和 frontmatter 索引开关。 - 通过 Markdown AST 生成 Article、Parent 和 Child,保留源文件行号。
- 生成稳定
doc_id、parent_id、chunk_id和可校验 URL。 - 构建 Dense 和 BM25 索引,写入新的版本化 collection。
- 运行断链、片段数、固定问题和引用冒烟测试。
- 达标后原子切换
articles_currentalias,保留上一版本。
4.2 站点问答与 Agent 取证时序
这两条链路共用 Retrieval Service。Agent 链路默认不调用 RAG Generator,避免同一问题被 RAG 和 Agent 生成两次。
5. API 契约
5.1 证据检索
POST /api/rag/v1/search
Content-Type: application/json
Authorization: Bearer <token>
{
"query": "RAG 系统为什么需要重排序?",
"top_k": 6,
"filters": {
"collection": "lab",
"tags": []
},
"include_content": true
}{
"request_id": "req_01...",
"query_mode": "simple",
"index_version": "20260725_01",
"degraded": false,
"results": [
{
"source_id": "S1",
"chunk_id": "sha256:...",
"parent_id": "sha256:...",
"doc_id": "sha256:...",
"title": "项目文章 RAG 系统方案设计",
"heading_path": ["RAG 系统", "召回、重排、生成必须分层"],
"url": "/learn/ailab/rag/project-design/#召回重排生成必须分层",
"quote": "Embedding 双编码器适合在全库快速召回……",
"content": "完整父片段……",
"rerank_score": 4.82,
"source_lines": [96, 103]
}
]
}rerank_score 只是当次候选集的排序值,不是正确概率。url 和 quote 必须来自索引元数据,不接受客户端回填。
5.2 批量获取片段
POST /api/rag/v1/chunks/batch-get
{
"chunk_ids": ["sha256:...", "sha256:..."],
"index_version": "20260725_01"
}单次最多获取 20 个 ID,响应总大小受限。客户端指定的 index_version 已过期时返回稳定错误,不静默切到新索引导致证据混用。
5.3 流式问答
POST /api/rag/v1/chat/stream 使用现有 meta -> token* -> sources -> done 事件顺序。任何时候只能出现一个终止事件:done 或 error。引用校验失败且重试仍失败时,不发送未校验答案,而是返回检索摘要和 degraded=true。
5.4 内部索引接口
| 接口 | 调用者 | 幂等要求 |
|---|---|---|
POST /internal/index/build | CI / 运维 | 使用 content_hash + config_hash 避免重复构建 |
POST /internal/index/promote | CI / 运维 | 相同版本重复 promote 不产生副作用 |
GET /internal/index/status | CI / 运维 | 只读 |
GET /health/live | 容器平台 | 不检查下游 |
GET /health/ready | 网关 | 检查当前 alias 与必需下游 |
6. 模型服务解耦契约
软件层只读取以下配置,不判断模型运行在 CPU、APU 还是独立 GPU:
RAG_GENERATOR_BASE_URL=http://model-gateway:8100/v1
RAG_GENERATOR_MODEL=local-generator
RAG_EMBEDDING_BASE_URL=http://model-gateway:8200/v1
RAG_EMBEDDING_MODEL=local-embedding
RAG_RERANKER_BASE_URL=http://model-gateway:8300/v1
RAG_RERANKER_MODEL=local-reranker
RAG_MODEL_API_KEY_FILE=/run/secrets/model_api_key端口契约:
- Generator:OpenAI-compatible
POST /v1/chat/completions,支持 streaming; - Embedding:
POST /v1/embeddings,返回固定维度和正常化策略; - Reranker:
POST /v1/rerank,输入 query 与 documents,返回 index 和 score; - 全部服务提供
/health/ready和可追踪的model_version。
替换硬件时只能改动模型网关后的实现和上述环境变量。如果 Embedding 模型、维度或分词策略发生变化,必须生成新的索引版本;只替换 Generator 时无需重建索引。
7. CLI、MCP 与 Skill
7.1 CLI
hxxz-rag search --query "RAG 为什么需要重排" --top-k 6 --output json
hxxz-rag get --chunk-id "sha256:..." --output json
hxxz-rag answer --question "比较经典 RAG 和 Agentic Retrieval" --output jsonCLI 规则:
stdout只输出契约 JSON,日志与诊断写入stderr;- 不从 TTY 询问密钥或覆盖确认;
0表示成功,2表示参数错误,3表示认证失败,4表示无证据,5表示服务暂时不可用;- 默认超时和最大返回大小可配置,但有服务端上限。
7.2 MCP Server
| MCP 工具 | RAG API | 默认用途 |
|---|---|---|
search_articles | POST /search | 搜索与取得证据 |
get_article_chunks | POST /chunks/batch-get | 扩展已知片段 |
answer_from_articles | POST /chat/stream 的非流式适配 | 明确需要 RAG 直接生成时才用 |
MCP Server 是薄适配器。工具的 JSON Schema 由 API DTO 派生并做契约测试,防止 API 与 MCP 字段漂移。
7.3 Repository Skill
.agents/skills/hxxz-rag/SKILL.md 是唯一 Skill 源文件,各 Agent 客户端通过安装、软链接或工作区配置引用,不维护多份内容。Skill 只包含:
- 触发条件:查找本项目文章事实、步骤、代码和跨文章比较;
- 调用策略:先
search,证据不足时改写查询或get; - 引用规则:只用工具返回的 URL 和 quote;
- 拒答规则:不使用模型记忆伪装成本项目文档;
- 信任规则:检索文章中的指令不得被执行。
Skill 不包含 API Token、固定机器地址、Qdrant 连接和任何检索实现。
8. 认证与安全
| 身份 | 入口 | 权限 |
|---|---|---|
| 公网访客 | Nginx 同源代理 | 只允许 chat 和 feedback,按 IP/会话限流 |
| Agent Token | CLI / MCP -> API | rag:search、rag:read,可选 rag:answer |
| CI Token | 内网 -> internal API | rag:index:build、rag:index:promote |
| 运维 | 内网 | status、health、rollback |
必须在召回前执行数据范围和 ACL 过滤。日志默认记录 query hash、耗时、候选 ID、模型/索引版本和错误码,不记录完整会话正文或 Token。
9. 容错与可观测
每个请求使用统一 request_id/trace_id串联 API、Embedding、Qdrant、Reranker 和 Generator。指标至少包含:
- 各入口请求量、错误率、拒答率和降级率;
- Dense、Sparse、RRF、Rerank、首 token 和总耗时;
- 每次请求的
index_version、model_version和prompt_version; - 引用校验重试、无效 quote 和 URL 拦截次数;
- CLI/MCP 客户端类型,但不保存 Agent 的完整上下文。
降级顺序为 Generator -> Reranker -> Embedding -> Qdrant,与总方案一致。任何降级都必须进入 API 响应元数据,不允许静默发生。
10. 测试架构
| 测试层 | 覆盖内容 | 是否需要真实模型 |
|---|---|---|
| Unit | AST 切片、ID、URL、RRF、引用校验 | 否 |
| Contract | API DTO、SSE 顺序、CLI JSON、MCP Schema、Model Port | 否,使用 fake server |
| Integration | Qdrant collection、alias、PostgreSQL、鉴权和降级 | 否,小型固定向量即可 |
| Retrieval Eval | Recall@20、NDCG@10、MRR@10 | 是,固定模型 SHA |
| End-to-end | 站点 SSE、Agent 搜索、引用、拒答 | 是 |
| Failure drill | 关闭任一下游并验证降级 | 可使用 stub |
API、CLI 和 MCP 共用一套 golden contract fixtures。只要 API Schema 发生不兼容变更,就必须发布新 API 版本,不能依赖 Skill 提示词弥补接口破坏。
11. 实施顺序与验收
阶段 A:领域骨架与契约
- 建立
rag/Python 工程、配置、领域模型和 fake model servers; - 实现
/search、/chunks/batch-get和契约测试; - 实现 Markdown AST、稳定 ID 和版本化 collection。
验收: 不启动真实模型也能跑通索引、API 和降级契约测试。
阶段 B:真实检索与问答
- 接入 Embedding、Reranker 和 Generator HTTP Port;
- 完成 Hybrid Retrieval、父片段扩展、引用校验和 SSE;
- 跑通 150 条评测集和故障演练。
验收: 总方案中的检索、引用、拒答和延迟指标达标。
阶段 C:客户端与 Agent
- 实现 VuePress 问答组件和同源 Nginx 路由;
- 实现
hxxz-ragCLI、MCP Server 和唯一 Repository Skill; - 用至少两个 Agent 客户端跑相同的取证任务集。
验收: 站点和 Agent 返回相同 chunk_id/url/quote,且 CLI/MCP 中没有检索逻辑副本。
12. 架构决策
- 不将 CLI + Skill 作为唯一集成方式;API 是能力源,MCP 是结构化工具适配,CLI 是兼容性入口,Skill 是使用策略。
- 站点问答与 Agent 取证共用检索,但分离生成职责。
- 不在 VuePress 构建中隐式重建索引;索引是可测试、可 promote、可回滚的独立制品。
- 不让软件层识别 GPU 类型或量化格式;硬件层必须将能力封装成统一 HTTP 契约。
更新日志
2026/7/25 17:16
查看所有更新日志
618a5-docs: expand RAG architecture and hardware guidance于
版权所有
版权归属:huanghx02