架构与数据流¶
CG_RAG 是独立的法规检索、受约束生成与视觉隐患识别服务。稳定入口是 src.live_retriever_service,HTTP 与 MCP 共用同一个 CgRagService 实例,避免两套接口产生不同检索结果或缓存状态。
模块边界¶
| 模块 | 当前责任 |
|---|---|
src.live_retriever_service |
读取启动参数、建立 FastAPI/MCP lifespan、装配配置和服务 |
src.retrieval.live_retriever_config |
定义 full、usual、usual_plus_law profile 与检索模型配置 |
src.retrieval.live_retriever_manager |
按 scope 延迟加载、驻留复用、warmup、lease 与安全释放 retriever |
src.retrieval.retrieve_service |
scope 归一化、fingerprint、cache、召回、rerank、hydration 和 timing |
src.retrieval.hydration_service |
构建/读取 SQLite sidecar,补全文档元数据 |
src.generation.constrained_generation_service |
生成 prompt、调用 provider、解析候选索引并格式化条文回答 |
src.retrieval.cg_rag_stream |
完整 RAG 与 SSE 事件编排 |
src.retrieval.cg_rag_vision |
图片解码、直接 VQA、agent 运行时装配与进程内检索回调 |
src.agentic.vision_agentic_runtime |
连续视觉 agent:观察、工具调用、证据整理、最终回答校验 |
src.retrieval.cg_rag_health |
health/profile 摘要与敏感信息脱敏 |
src.retrieval.cg_rag_service |
对 HTTP/MCP 保持稳定的 facade |
src.retrieval.cg_rag_http |
参数校验、request ID、线程桥接、SSE、取消与 HTTP 错误映射 |
src.retrieval.cg_rag_mcp |
Streamable HTTP MCP 工具与统一 envelope |
Retrieve-Rerank 数据流¶
request
→ Pydantic 校验与 scope 归一化
→ 计算当前 profile fingerprint
→ 查询内存 TTL/LRU cache
→ RetrieverManager.acquire(scope)
→ hybrid retrieve
→ 本地或远端 rerank
→ hydration sidecar 补全 law_name/article/contents/source
→ 复制公开结果并释放 lease
→ 写入 cache
→ response
manager 的 lease 很重要:scope 切换或服务释放时,不会关闭仍被并发请求使用的 retriever。响应只携带复制后的公开字段,不在 lease 外继续访问底层检索对象。
Constrained Generation 数据流¶
query + retrieval_docs
→ 文档数量与字段归一化
→ 服务端受约束 prompt
→ provider adapter 组装 thinking / stream / response_format
→ OpenAI-compatible Chat Completions
→ 收集 content 与可选 reasoning
→ 解析 JSON 候选索引
→ 校验索引范围、去重、max_items 截断
→ 由服务器使用原始 retrieval_docs 格式化 answer_text
模型只选择候选索引,不能自己写入新的法规条文。最终条文名称、编号和正文来自服务器持有的检索结果,因此生成失败不会污染 retrieval 数据。
完整 RAG 与 SSE¶
rag 依次执行 retrieve-rerank 和 constrained generation。rag/stream 使用同一业务步骤,但把阶段状态、检索结果和答案 token 转为 SSE。当前 answer token 是对已经校验完成的 answer_text 做分块发送,不代表把未经校验的上游模型 token 直接透传给客户端。
视觉隐患识别数据流¶
自由生成只有一次模型调用:
images(base64) + question
→ Pydantic 校验、张数与体积上限
→ base64 解码为内存字节,不落盘
→ 注入判定标准条款清单的 system prompt
→ OpenAI-compatible 多模态 Chat Completions
→ 去除 think 块并返回隐患报告
智能生成把上面的单次调用换成 agent 循环,并让模型自己决定检索问题:
images + question
→ 观察,模型决定是否调用 cg_retrieve_rerank
→ 进程内执行 retrieve-rerank(不经过 MCP 或 HTTP)
→ 证据编号、去重、截断
→ 重新观察,必要时继续检索,最多 max_search_rounds 轮
→ 生成最终回答
→ 校验公开协议与 [cite^n] 引用契约,不通过则重试
→ 返回带引用的隐患报告
关键差异在检索这一跳。Question App 的视觉 agent 通过 MCP 调用 CG_RAG,每轮检索都要一次网络往返;agent 运行在 CG_RAG 内部时,检索回调直接调用同一实例的 retrieve_rerank,共享同一份缓存、容量限制器和 profile fingerprint。
引用编号只能来自本轮工具返回的证据,判定标准条款清单属于 system prompt 而非检索证据,因此不能充当引用。回答未携带有效引用时会被判为协议失败并重试。
缓存一致性¶
retrieve cache key 包含:
- normalized scope;
- 去首尾空白的 query;
- topk;
- profile fingerprint。
fingerprint 摘要覆盖当前 profile、语料/索引文件、embedding 与 rerank 配置。索引文件或模型配置变化后会形成新 key,旧条目只能等待 TTL/LRU 淘汰,不会被新请求误用。LLM generation 不进入该缓存。
并发和取消¶
检索与生成各自使用容量限制器,默认并发均为 16,默认最多等待 5 秒。SSE 客户端断开时,HTTP 层取消 CancellationContext;检索、生成和上游 streaming 调用在安全检查点停止。若工作线程中的 generator 尚未退出,HTTP 层最多协作等待 1 秒,不会从另一个线程强制关闭正在运行的 generator。
边界¶
仓库内所有 VLM 调用都在 CG_RAG。 Question App 通过 HTTP 调用本服务的 vision/* 接口,自己不再组装多模态请求,也不再持有第二套判定标准或 agent 循环——同一份实现同时服务内部产品与外部调用方。
CG_RAG 负责法规检索、受约束生成和图片隐患识别,不负责网页会话、模型选择 UI、附件存储、聊天历史或用户权限。视觉接口接收调用方传入的图片字节并在内存中处理,不做上传管理、不落盘、不保留任何会话状态;这些产品逻辑属于调用方,不在本站点的公开边界内。