历史存档 · 本报告为较早阶段产物,部分技术描述已过时(本地模型、Workflow 绑定等)。最新架构请见 架构总览
目录 · TABLE OF CONTENTS

你可以从哪里开始读

PROJECT ANALYSIS · AZ

PIKE-RAG面向医学文档问答的检索增强服务

AZ R&D「China MVR Smart Tool(POC)」的底层平台。基于微软 PIKE-RAG 框架,围绕多轮迭代检索、Azure 文档智能解析与四路混合召回构建,交付一套可部署的知识库问答 API。历时约三个月,由 Python 服务演进为 Python + .NET 双后端 Agentic 平台。

419
次提交 · 约 14 周持续开发
5
核心贡献者 · 含 Microsoft 顾问
119K
行代码 · Python / C# / TS 三语言
709
临床文档入库 · 方案 / SOP / 质量文档
01 — 起源与时间线

它从什么时候开始的?

项目于 2026 年 3 月 19 日由一次 “initial commit” 拉起,到最后一次提交 2026 年 6 月 24 日,跨度约三个月、14 个开发周。四月与五月是绝对主力期,两个月贡献了 331 次提交(占全部的 79%),六月进入收尾与部署加固。

FIRST COMMIT
2026-03-19
Chen 拉起初始仓库,同日 Yanzhi Li 补充技术分析文档,确立项目定位。
LATEST COMMIT
2026-06-24
部署加固、Milvus 迁移与 .NET Agentic 平台收口。
PEAK WEEK
W15 · 58 次
4 月中旬,Docker 化与 RAGAS 评测框架密集落地。
月度提交分布2026-03 → 2026-06
2026-03
9
2026-04
159
2026-05
PEAK · 172
172
2026-06
79
提交类型构成Conventional Commits
feat
133
fix
125
refactor
38
docs
34
chore
32
📖 APPENDIX · 深入阅读
419 个 commit 讲的故事
项目演进史 · 五幕故事 · 十次艰难时刻 · 开发者代码风格画像
主报告告诉你项目是什么——这一份告诉你项目是怎么来的。基于 git log 全量分析,复原从 2026-03-19 首个 commit 到 06-24 交付的完整故事线,并从每人真实修改的文件量化他们的技术能力与代码风格偏好
02 — 贡献者

谁写了这些代码?

五位实际贡献者。Yong Ma 是绝对核心,独立贡献 235 次提交(占 56%),横跨 C# 后端、Python API、Web 前端与文档;ShuaiHua Du 主攻部署、后端与数据库迁移。Kelvin Kuang 来自 Microsoft,作为 PIKE-RAG 框架方提供顾问支持。

Yong Ma
技术负责人 · 全栈
235commits
pike-agentapiwebdocs
ShuaiHua Du
部署 · 后端
151commits
deploypike-agentdb
chewa / Chen
基础设施
29commits
deployvllminit
Kelvin Kuang
Microsoft 顾问
3commits
microsoft
Yanzhi Li
技术文档
1commit
docs

注:git 历史中出现 7 个署名身份,其中 “Chen” 与 “chewa-git” 共用同一邮箱(lulali@outlook.com),“Kelvin Kuang / Kelvin kuang” 为大小写差异,实际归并为 5 位贡献者。

03 — PIKE-RAG 在这个项目里做什么

给临床试验文档一个「会查证的助手」

这个项目是 AZ R&D 的 China MVR Smart Tool(POC)。业务场景很具体:临床质量管理(CQM)与运营团队面对海量的研究方案(CSP)、SOP、质量文档,需要在其中快速、准确地查证信息。PIKE-RAG 承担的正是「文档理解 + 可溯源问答」这一核心引擎——把散落在数百份 PDF 里的临床知识,变成一句提问就能得到带出处、可核对的答案。

709
知识库文档 · 单个 KB kb_<id>
390
PDF 文档 · 另含 124 docx · 118 pptx
10
文档分类体系 · SOP / CSP / 报告…
9
自动抽取的业务元数据标签
DOCUMENT TAXONOMY · 文档分类体系

入库先分类,检索可按类过滤

CSP临床研究方案 · Clinical Study Protocol SOP标准操作规程 · Standard Operating Procedure WI工作指导 · 作业指导书 GUID指南 · Guideline RPT研究报告 · Report FRM表单 / 模板 · Form / Template STND标准 / 规范 · Specification POL政策 · Policy · MEMO 备忘 · Other 其他
EXTRACTED METADATA · 自动抽取的业务字段

每份文档进库时打上临床标签

study_code研究方案编号,从 CSP 封面/文件名抽取,如 Study-AStudy-B 等(脱敏占位)
therapeutic_area治疗领域:Oncology 肿瘤 / Respiratory 呼吸 / CVRM 心血管肾脏代谢
sponsor申办者,从 CSP 封面 Sponsor 字段抽取
version · effective_date版本号与生效日期,从页头/文件名抽取,保证引用到正确版本
language语言:CN / EN / CN-EN——中英混排是该库常态

数据来源:MVR_Doc/3-Test/DocumentList_0603.csv(709 条真实文档清单)与 db_migration/seeds/seed_profiling_defaults.py(分类体系与标签定义)。研究方案示例已按客户要求脱敏(10 个真实 Study 涵盖 Oncology / Respiratory / CVRM 三大治疗领域)。

04 — 核心业务逻辑(RAG)

不是「单轮检索 + 直接回答」

PIKE-RAG 的核心是一套多轮迭代式检索增强流程:由 LLM 规划器(Planner)驱动,反复判断「是否还需要更多证据」并自主生成新的检索 query,直到证据充分或达到跳数上限,才结合累积上下文生成带引用的答案。整条链路建立在文档智能解析与四路混合召回之上。

A · 文档入库流水线(原始文档 → 可检索文档)

一份 PDF 从上传到"能被搜到",要过 4 关。关键区别不是"存了向量"而已——PIKE-RAG 会把每份文档拆成 section(章节)/ table(表格)/ figure(图片) 三种可检索单元,让检索器能同时找"哪段话在说这事"、"哪张表列了这数据"、"哪张图展示了这结构"。

STEP 1 · 上传
用户丢一份文档进来
前端提交 PDF/Word/PPT/扫描件到 S3 对象存储,后端登记一个异步入库任务——不阻塞用户,后台慢慢做。
技术
S3(cn-north-1) + Hangfire 异步队列
STEP 2 · 解析
把 PDF 拆成"可读的结构"
Azure DI(本地容器版)版面识别——识别哪里是标题、正文、表格、图片。输出 markdown + 结构化 JSON,还会导出每页图片和表格的裁切图
技术
form-recognizer/layout-4.0
本地 :5000 · 不走 Azure 云
STEP 3 · 增强
让 LLM 给表格/图片写"说明"
Azure DI 只知道"这里有个表格"、"这里有张图",但不知道"这个表在说什么"。所以调 LLM(本地 Qwen2.5-72B) 给每个表格和图生成标题 + 描述文字——让它们也变得"可搜"。同时合并/切分过短或过长的 section,平衡语义完整性和检索粒度。
技术
Qwen2.5-72B-AWQ 本地推理
视觉描述 + section 长度归一
STEP 4 · 入库
拆成 3 种可检索单元
每份文档最终产出三类 JSONL 记录:
· section — 一段话(检索主力)
· table — 一张表(结构化查证)
· figure — 一张图(图文检索)
每条记录由 BGE-M3 生成 1024 维向量后写入 Milvus 向量库,同时建 BM25 稀疏索引。
技术
BGE-M3 embedding(1024d)
Milvus 2.5.10 · Dense + Sparse 双索引
🛣️ 旁路 · BYPASS非 PDF 直读通道
Word/Markdown/纯文本这类本身就有结构的文档,跳过 Azure DI 版面识别——直接用 LangChain loader 读文本,进入 STEP 3 增强,快得多也省 GPU。只有 PDF 和扫描件才需要走完整 4 步。

为什么这么设计?不做 STEP 3 增强,LLM 检索时只会拿到孤零零一段"表 3.1"、"图 5-2"——没上下文没意义。让 LLM 提前用自然语言"解释每个表和图在说什么",相当于给 chunks 打上"业务说明",大幅提升召回相关性和引用可读性——用户看引用 [3] 弹出的是"表 3.1:阿卡替尼剂量调整表",而不是一个空白表格框。

🔒 AI STACK · 生产实录
用的是本地 AI,不是云端 APIVerified 2026-07-08

很多人以为医药 RAG 就是"套一层 OpenAI API"——本项目完全相反。因为客户是 AZ R&D 的临床质量团队,数据是未上市药物的 CSP(临床研究方案)——数据敏感度是 GxP + 商业机密双高,数据出不了 AZ 内网。所以整套 AI 栈都部署在 AZ 内网的 GPU VM 上——LLM、Embedding、Reranker、文档解析全部本地化,零外网调用。

LLM · 主力推理
Qwen2.5-72B-AWQ
本地 · vLLM
阿里通义千问 720 亿参数,AWQ 4-bit 量化后单张 A100 可跑。承担 决策 / query 提议 / 答案生成 / Agent 工具调用 全部推理任务。
endpoint: https://127.0.0.1:6080
协议: OpenAI 兼容 API
自签证书 · TLS skip verify
EMBEDDING · 向量化
BGE-M3
本地 · vLLM
BAAI 出品的多语言 embedding 模型。把 chunks 转成 1024 维向量写入 Milvus。支持中英混排——重要,因为库里 22% 是 CN-EN 混合文档。
endpoint: https://127.0.0.1:6081
输出维度: 1024(DENSE_DIM)
相似度: COSINE
RERANKER · 精排
BGE-Reranker-v2-M3
本地 · vLLM
召回后的二次排序——把召回的 Top-K 用 cross-encoder 精排,把最相关的 chunks 排到前面。提升答案证据的精准度。
endpoint: https://127.0.0.1:6082
类型: cross-encoder
与 A100 共卡运行
DOC INTELLIGENCE
Azure DI · Layout 4.0
本地容器
微软文档智能本地容器版(不走 Azure 云),负责 PDF/扫描件的版面识别——把 PDF 转成结构化的 markdown + 表格 + 图片元数据。
endpoint: http://127.0.0.1:5000
镜像: form-recognizer/layout-4.0
需 EULA + Billing + ApiKey
📊 数据全景 · 配套基础设施(全部内网 · 无云端依赖)
向量数据库
Milvus 2.5.10 · 集群自建(namespace=staging)· etcd + MinIO 三件套 · 集合结构:chunk_id + document_id + kb_id + content + metadata + dense_vector(1024) + sparse_vector
融合排序算法
Milvus 原生 hybrid_search + RRFRanker(k=60) · Dense(COSINE) + Sparse(BM25) 两路并发 · Reciprocal Rank Fusion 融合,不需要人工调加权系数
检索参数
retrieve_k = 16(默认 Top-16 召回)· 用户可通过 request.retrieve_k 覆盖 · Dense/Sparse 两路各召回 16 条,融合后取并集去重
多轮 Planner
round_limit = 5(默认最多 5 跳)· 用户可通过 request.max_hop 覆盖 · 每轮由 LLM 判断"证据是否充足",不足则自主生成新 query,直至满足或达上限
GPU 硬件
Nvidia A100(共享)· 上跑 LLM 72B + Embedding + Reranker 三个模型 · 共用一个 vLLM 镜像(vllm/vllm-openai:v0.21.0)不同端口
部署 namespace
Nomad · default namespace · 4 个 job:qwen2-5-72b-awq / bge-m3 / bge-reranker-v2-m3 / document-intelligence · 与应用层(staging namespace)物理隔离
🚫 明确不用什么

不用 Azure OpenAI GPT-4/GPT-5 云端 API(初期代码有相关适配器,已弃用)· 不用 OpenAI text-embedding-3(settings.pyEMBEDDING_DEPLOYMENT 默认值只是兜底占位)· 不用云端 Azure DI(cognitiveservices.azure.com 已切本地容器)· 不用 Anthropic Claude / Google Gemini——整条推理链路对外零 API 调用,连商用 CDN 都不走。

B · 四路混合召回(并集去重 · 稳定排序)

Dense默认

向量语义相似度检索。Embedding 走本地 bge-m3(1024 维),底层是 Milvus 2.5.10 原生 hybrid_search。已从 Chroma 完全迁移。

Sparse默认

BM25 关键词检索,由 Milvus 内建(不再走外部 BM25 库)。补足向量召回对精确术语、编号、专有名词的漏检——例如 Study-ASOP-0039261 这种硬 ID。

Atomic可选

先检索细粒度「原子块」,再经 source_chunk_id 回跳到所属 section,提升定位精度。

Figure可选

独立图片检索通道。针对图文混排文档,直接召回相关图表证据。

C · 多轮规划循环(round_limit 默认 5 跳)
用户问题 首轮 = 原问题 round 0 混合检索 四路召回 → 累积去重 是否继续检索? decide_continue 生成新 Query 自包含 · 不重复历史 propose_query 下一跳检索(≤ round_limit) 否 / 达上限 最终生成答案 累积证据 + 会话历史 <pike_rag_cite> 引用标签 带引用回答 + 摘要 SSE 流式下发前端 无引用标签 = 视作未找到
D · 驱动这套循环的三个真实 Prompt
1

检索决策

continue_retrieve_decision
让模型「仔细分析已有 context,判断是否还需要更多信息才能精确回答」。追求详尽——不足即请求更多。
"thinking": "已知给药方案,但
缺少剂量调整规则…",
"to_request": "Yes"
2

查询提议

query_proposal
生成下一条检索 query。强制要求自包含:带全部标识符、禁用「它/他」等代词、不得重复历史 query。
"query": "Study-A 方案中
中性粒细胞减少时的
阿卡替尼剂量调整标准"
3

带引用生成

generation_qa_with_reference
仅依据 context 作答,每条证据用 <pike_rag_cite>N</> 标注。context 为空即返回拒绝语,且不带任何引用。
"answer": "应暂停给药直至
恢复至 ≤1 级<cite>2</>",
"rationale": "依据 6.3 节…"
E · 一次真实提问的完整链路(前端 → 后端 → 前端)Verified 2026-07-08

下面这张时空图按 时间顺序 展示从用户点击"发送"到答案完整渲染的所有环节。三列并排:用户在界面看到什么 · 后端在做什么(含代码位置)· 通过 SSE 推送的事件。所有事件名、payload 字段、prompt 摘录都是从代码里 grep 出来的,不是示意。

「Study-A 方案里,出现 3 级中性粒细胞减少该如何处理?」
POST /api/v2/chat/completions · stream=true · workflow_id=qa · round_limit=5request.max_hop 未指定则默认 5,见 qa_conversation_workflow.py:1047
用户视角
后端动作
SSE 事件
1SUBMIT
用户点击「发送」
React SPA 打开 SSE 连接;输入框清空,气泡骨架屏 pending。
FastAPI 接收请求
路由 v2/conversation_router.pyChatCompletionsRequest,透传到 chat_completions usecase。
src/api/interface/http/routers/v2/conversation_router.py
(尚未开始 emit)
2PERSIST
"正在思考..."状态出现
前端根据首个 SSE 事件把骨架屏切换为"正在验证问题"字样。
用户消息立即落库
upsert_chat_session 建/更新会话(title 取 question 前 80 字)→ insert_chat_message(role=USER)。落库失败会 logger.warning不中断请求
chat_completions.py:57-71 · _run_pipeline
validation.started
phase: "validation" · visibility: "internal" · ui_channel: "thinking"
qa_conversation_workflow.py:166
3GATE
若文档缺失 → 直接得到拒绝语
若门禁判定 missing/processing,UI 显示"该文档暂未就绪"并结束;不进入检索。
三态文档检查
遍历 doc_idsavailable(参与召回)· processing(前端提示)· missing(短路返回)。通过后打埋点 span,进入 QA workflow。
validation.passed
qa_conversation_workflow.py:182
4WORKFLOW
状态:正在检索知识库
前端提示语切换为 "workflow=qa 已启动"。
装配多轮循环游标
初始化 issued_queries=[] · retrieved_docs=[] · retrieved_chunks=[] · round_traces={}round_limit = request.max_hop if request.max_hop > 0 else 5
qa_conversation_workflow.py:1047 · _answer()
workflow.started
payload: {"workflow": "qa"}
qa_conversation_workflow.py:198
FOR round_idx IN range(round_limit=5) · 下面 5 步循环执行,直至决策=停止 或 达上限
RSTART
状态:正在进入第 N 轮
round span 埋点开启
进入 observability.start_span("qa.workflow.round"),把 round_idx 写进 span attribute。首轮 query = 原问题,跳过决策/提议;非首轮走 4.1 → 4.2。
qa_conversation_workflow.py:869-885
round.started
payload: {"round": round_idx + 1}
L874
4.1DECIDE
状态:正在评估已有证据
Planner · 决策是否继续检索
调 LLM 用 continue_retrieve_decision prompt(详见下方"真实 prompt 摘录")。输入 question + chunks(累积) + queries(已发) + history;解析出 {thinking, to_request: bool}to_request=False → 立即 break 主循环,进入 STEP 5
pikerag_query_planner_adapter.py:28 · decide_continue()
planner.retrieve_decision.started planner.retrieve_decision.completed planner.decision
payload: {round, history_message_count, issued_query_count, evidence_chunk_count, continue_retrieve: bool, decision_preview: str}
L630 / L659 / L670
4.2PROPOSE
看到新 query 提示(可见)
UI 显示 "正在补充搜索:<新 query>"。此步 LLM 是流式 token 输出——你看到的 query 是一个字一个字打出来的。
Planner · 生成下一条 query
调 LLM 用 query_proposal prompt。强约束:自包含(带全部标识符)· 禁用代词(他/它/上述)· 不重复历史 query_on_query_proposal_delta 回调把每个 token 通过 llm.delta 事件推给前端。
pikerag_query_planner_adapter.py · propose_query()
planner.query_proposal.started llm.delta × N planner.query_proposal.completed query.proposed
payload: {round, query: str, proposal_preview: str}
L685 / L713 / L724
4.3RETRIEVE
状态:已召回 X 篇(可见)
UI 计数器更新。若走 Milvus 链路(V2 workflow),耗时通常 30-200 ms;若走 Chroma 链路(V1 workflow),耗时随 KB 大小线性增长。
四路混合召回
V2 workflow 走 HybridRetrieverV2(Milvus 原生 hybrid_search):Dense(metric=COSINE)+ Sparse(metric=BM25)两条 AnnSearchRequest 并发发出,RRFRanker(k=60) 融合;filter_expr 限定 kb_id + doc_id in [...],禁止 KB 级全扫。返回 Documents 去重后累积到 retrieved_docs
hybrid_retriever_v2_milvus.py:143-232 · retrieve_sections()
retrieval.started retrieval.completed retrieval.results
payload: {round, query, newly_added_count, duration_ms, result_count, results_preview: [...]}
L753 / L774 / L786
RLOOP
回到 4.1 · 用新 query 再决策一次。until to_request=Falseround_idx == round_limit
query in issued_queriesskip_reason=duplicate_query(去重硬闸);若 query is None → skip。都不消耗 round 配额。
下一轮 round.started ...
5GENERATE
打字机效果:答案一字一字出现
前端把 llm.delta 事件的 text_delta 顺序拼接、渲染。答案中的 <pike_rag_cite>N</pike_rag_cite> 标签在渲染时被解析为可点击的 [N] 引用,悬停显示原文。
Evidence 装配 → LLM 生成
evidence_assembler.to_reference_chunks(retrieved_docs, retrieved_chunks) 把累积证据按顺序编号,用 <pike_rag_cite>1</pike_rag_cite> ... 标签标注。调 LLM 用 generation_qa_with_reference prompt——严格要求"仅依据 context 作答",context 为空必须拒答且不含任何引用标签
pikerag_answer_generator_adapter.py · generate()
llm.started llm.delta × N(首字前 gpt-5 类模型可静默 30-90s) llm.completed
llm.delta.payload: {text_delta, token_index, delta_type, stage: "final_generation", round}
llm.completed.payload: {char_count}
L812 / L838 / L928
6HEARTBEAT
看不见的保命帧
reasoning 模型(gpt-5)首 token 前长静默时,客户端理论上感知不到——但实际上底层 SSE 每 N 秒推一帧 type=heartbeat 空事件。外部调用方可 filter type == "heartbeat" 忽略。
防 nginx/CDN 60s 断流
nginx / Cloudflare / API gateway 默认 60s read timeout;心跳事件保证 ≥1 帧/N 秒下行,避免被误判 SSE 中断。
chat_completions_response_content.py:59-79 · append_heartbeat()
heartbeat
SSE payload: {"type": "heartbeat", "stage", "round", "seq", "elapsed_ms"}
7FINALIZE
引用可交互 · 会话入库
前端解析 <pike_rag_cite> 映射到 references_ready.count 对应的 chunk 数组,把 [1][2] 变成可悬浮/点击的高亮引用。用户消息 + 助手消息都进 chat_messages 表。
Assistant 消息落库
_persist_assistant_messages(collector, ...):从 MessageCollector 收集到的 reasoning/tool/answer 三类事件按 message 批量入库;observe_chat_request_duration() 埋 Prometheus 指标。response.close(final_status) 关闭 SSE 队列。
chat_completions.py:130-142 · _persist_assistant_messages
answer.ready references.ready request.completed [DONE]
answer.ready: {"answer": str}
references.ready: {"count": int}
最后一帧: data: [DONE]\n\n
L259 / L270 · _build_answer_message() ; chat_completions.py:91
附录 1 · 驱动这套循环的 3 个真实 Prompt(原文摘录)

下面是 直接从代码抠出来的原文——没做任何改写,包括英文、格式化标记 {{}}、注释。三个 prompt 都在 src/pikerag/prompts/qa/ 目录下,微软 PIKE-RAG 框架的原始定义。

① continue_retrieve_decision · 决策是否需要更多信息
src/pikerag/prompts/qa/multi_round.py:14-48
# system
You are a helpful AI assistant good at context reading and question addressing.

# user template
# Task
Given the question and context, your task is to thoroughly analyze the context
and decide whether additional information is required for you to address the question.

Aim for a detailed and precise answer. If the situation warrants further details,
you should decide to request for more information.

# Output Format
Please output in following JSON format:
{
    "thinking": <A string. Your thinking for this task, should be in {response_language}.>,
    "to_request": <A string. "Yes" to request more, "No" to indicate the context is sufficient.>
}

# Context
{context_if_any}

# Question to Be Answered
{content}

# Your Output

关键 parser 逻辑:to_request 被解析成 bool——output["to_request"].strip().lower() in ["yes","y","true","1"]解析失败默认 True(保守地继续检索,而不是提前停)。

② query_proposal · 提议下一条自包含 query
src/pikerag/prompts/qa/multi_round.py:78-115
# user template(system 同上)
# Task
Given the question and context, your task is to thoroughly analyze the context
and formulate a specific query requesting additional information that is helpful
to address the question.

Here are some notes for the "query" part you should follow:
- Ensure that your query is detailed and self-contained, including all
  necessary identifiers and context.
- Avoid using ambiguous pronouns such as 'he', 'it', etc. Instead, specify
  relevant details such as the specific file, product, or other pertinent information,
  so that the query can be understood and addressed independently of any preceding dialogue.
- Never output same query if it has already been asked. If the historical query
  is important but no relevant context is found, consider asking about a different
  aspect or rephrasing the query to obtain complementary information.
- Your response "query" would be used to retrieve more information from a knowledge base
  or search engine, format it in a suitable way.

# Output Format
{
    "thinking": <String, in {response_language}>,
    "query": <String, in {response_language}>
}

# Context
{context_if_any}

# Question to Be Answered
{content}

# Queries You Already Asked
{queries_if_any}

# Your Output

关键 parser 逻辑:query.strip() == "" → NoneNone 会触发 skip_reason=query_none,跳过本轮)。去重硬闸在 workflow 层:if query in issued_queries: skip——即使 LLM 忘了"不重复"规则,代码也拦得住。

③ generation_qa_with_reference · 最终答案生成(严格引用规则)
src/pikerag/prompts/qa/generation.py:69-102
# user template
# Task
Given the question and context, your task is to read the context carefully
and give your answer to the question.
Each context chunk is labeled with a special citation tag like
<pike_rag_cite>1</pike_rag_cite>, <pike_rag_cite>2</pike_rag_cite>, etc.
When your answer uses information from a specific context, add the corresponding
citation tag, following these rules:

- Cite at most ONCE per paragraph for the same source. Do not repeat.
- If a sentence merely paraphrases already-cited material, do NOT cite it again.
- For bullet lists, place the citation at the END of the bullet.
- Do not cite for obvious facts, definitions the user already knows.
- Place the citation tag at the END of the smallest meaningful unit.
- Aim for a clean reading experience: one citation per paragraph or bullet.

Important: You must answer ONLY based on the provided context. If the context
section is empty or does not contain information relevant to the question, respond
with a short refusal message in {response_language}. Do NOT answer from your own
knowledge. Do NOT include any citation tags in the refusal message.

# Output format
{
    "answer":    <String with inline <pike_rag_cite>N</pike_rag_cite> tags>,
    "rationale": <String, in {response_language}>
}

# Context, if any
{context_if_any}

# Question
{content}{yes_or_no_limit}

Let's think step by step.

关键 encode 逻辑:每条 chunk 被自动装成 <pike_rag_cite>{idx+1}</pike_rag_cite> {content} 格式插入 context_if_anycontext_len_limit=80000 字符硬上限。输出后处理:reindex_citations()citation_utils.py)把 <pike_rag_cite> 标签转为用户友好的 [N];拒绝语因不含标签,references 数组会被自动清空。

附录 2 · 完整 SSE 事件字典(emit 出现的全部事件类型)

qa_conversation_workflow.py + chat_completions.py grep 出的全部 20 种事件。SSE 帧格式统一:data: {"type": "...", "id", "created", "model", ...}\n\n,最后一帧固定是 data: [DONE]\n\n

validation.started
开始校验请求参数与文档状态。internal · thinking
validation.passed
校验通过,进入 workflow。internal · thinking
validation.failed
文档不存在 / 未就绪 / 参数错。短路返回。
workflow.started
进入 QA workflow 主循环。payload={"workflow":"qa"}
round.started
进入第 N 轮。payload={"round": N+1}
planner.retrieve_decision.started
开始决策"是否继续检索"(含 history_message_count / issued_query_count / evidence_chunk_count)
planner.retrieve_decision.completed
决策完成,含 continue_retrieve:bool + decision_preview
planner.decision
"planner.decision" 简化广播事件(continue_retrieve:bool),供 UI 独立订阅
planner.query_proposal.started
开始生成下一条 query
planner.query_proposal.completed
query 生成完成,含 query 文本 + proposal_preview
query.proposed
"query.proposed" 简化广播(round + query),供 UI 显示"正在补充搜索"
retrieval.started
开始 Milvus/Chroma 检索。payload 含 query
retrieval.completed
检索完成,含 newly_added_count + duration_ms
retrieval.results
给 UI 的结果预览事件(含 result_count + results_preview 数组)
llm.started
开始调 LLM 生成最终答案。user · assistant · status
llm.delta
流式 token 增量。payload 含 text_delta + token_index + stage(query_proposal/final_generation)
llm.completed
LLM 输出完成。payload={"char_count": int}
heartbeat
SSE 保活帧。gpt-5 等 reasoning 模型长静默时防 nginx/CDN 断流
answer.ready
最终答案已备好(含完整 answer 字符串)。user · assistant · final
references.ready
引用集已备好(count)。internal · system
request.completed
整个请求成功结束(含 execution_code=0 + question_id + session_id)
request.failed
整个请求失败(含 error_message)

证据来源:grep -n 'event_type=' src/api/infrastructure/workflow/qa_conversation_workflow.py src/api/application/conversation/usecases/chat_completions.py src/api/infrastructure/workflow/qa_conversation_workflow_v2.py

文档门禁

document gate

检索前对文档做三态分流,仅对已完成索引的文档执行 RAG:

  • available — 索引就绪,参与召回
  • processing — 仍在处理,前端提示
  • missing — 不存在,短路返回

LLM 客户端容错

azure openai · gpt-5.4

面向 reasoning 类模型的长静默期做工程加固:

  • 心跳线程防 nginx/代理断流
  • RateLimit 解析等待时间重试
  • token usage 全链路计量

引用与忠实性

citation reindex

答案的可信度由引用标签闭环保证:

  • 流式转换 tag → [N] 角标
  • 仅保留被引用的 references
  • 无引用即判定「未找到答案」
DEEP DIVE · 03A

多轮 QA 状态机:一次问答走了多少步

前面讲了「四路混合召回 + 五轮决策」的骨架,这里把真实的 qa_conversation_workflow.py 主循环拆开来看——从用户点击"发送"到答案落地,中间有一整台状态机在运转:emit 事件流、round span、决策/提议/去重、并行观测埋点、最终生成。这是理解"为什么慢/为什么答得对"的关键结构。

阶段 0Init_resolve_round_query
问题解析 · 上下文装配

从 HTTP 请求接收 questiondoc_idsmax_hophistory_messagesround_limit = request.max_hop if request.max_hop > 0 else 5——用户可传入自定义上限,默认 5 轮。

装配 issued_queries=[]retrieved_docs=[]retrieved_chunks=[]round_traces={} 四个游标,贯穿整个循环共享状态。

SSE workflow.started
阶段 1Round Loopfor round_idx in range(round_limit)
循环入口 · OpenTelemetry span 埋点

每一轮进入 with observability.start_span("qa.workflow.round")——所有耗时、决策结果、命中数都进 span attribute,产品可以在 Grafana Tempo 上看每一轮的火焰图。

如果本轮是 round_idx == 0,query 就是原始 question;否则调用 Query Planner 两阶段决策:

round.started qa.workflow.round span
阶段 2Decidequery_planner.decide_continue()
是否继续? · LLM 决策

question、当前 chunks(已累积)、queries(已发过的)和 history_messages 打包成一条 message,调 LLM 让它自己评估证据是否充足。返回 (should_continue, decision_output)——如果 should_continue == False,跳出循环,进入生成阶段。

reasoning 模型(gpt-5 类)在这里有专门的 qa_conversation_workflow_v2 走独立分支,避免思维链干扰结构化输出。

决策=停 → break 决策=继续 → 下一步
阶段 3Proposequery_planner.propose_query()
生成下一条 query · 强约束

Query Planner 的第二个能力:基于当前证据,生成一条自包含的新查询——强制要求带全部标识符、禁用"它/他/上述"等代词、不得与 issued_queries 重复。

去重是硬闸:if query in issued_queries: skip_reason=duplicate_query, continue——防止 LLM 反复问同一个问题浪费预算。

query is None → skip duplicate → skip 新 query → 加入 issued_queries
阶段 4Retrieve_execute_retrieval_round
四路混合召回 · Milvus 原生 hybrid_search

把新 query 送进 PikeragRetrievalExecutorAdapter.retrieve()——底层最终调 HybridRetrieverV2实际是 Milvus 原生 hybrid_search 的 alias,见下一小节的复查说明)。返回的 Document 累积到 retrieved_docs,chunks 去重后加到 retrieved_chunks

retrieval_count += 1retrieval_duration_seconds += elapsed 每一轮累加,最后写入 span:qa.retrieval.countqa.retrieval.total_duration_seconds

Milvus hybrid_search RRFRanker(k=60)
阶段 5Answeranswer_generator.generate()
最终生成 · 带引用

循环结束(决策停止 或 用尽 round_limit)后,evidence_assembler.to_reference_chunks() 把累积的 chunks 装配成引用块,交给 PikeragAnswerGeneratorAdapter.generate()——LLM 基于所有累积的证据生成最终答案,通过 on_delta 回调把 token 流式推给前端。

输出 JSON 里包含 answer(含 [N] 引用标号)+ reference_chunks(编号对应的原文片段),前端把 [N] 变成可点击的高亮引用。

llm.started SSE llm.delta × N workflow.completed

证据来源:src/api/infrastructure/workflow/qa_conversation_workflow.py(V1,round loop 主循环在 L869) · qa_conversation_workflow_v2.py(v2,reasoning 模型分支) · src/api/infrastructure/retrieval/pikerag_query_planner_adapter.py(Planner 两个能力 decide_continue + propose_query

DEEP DIVE · 03B

检索器的真实分布:两条并行链路,不是"三代同堂"Verified 2026-07-08

初版报告基于设计文档写的是"三代 Retriever 演进"——这个说法不准确。经代码复查(grep new/import 追踪谁在实例化每个类,再核对 registry.py 的路由注册),真相是:只有两个真实的实现——PIKE-RAG 原生的 Chroma 版本 HybridRetriever,以及项目自研的 Milvus 版本 HybridRetrieverV2Milvus。第三个类名 HybridRetrieverV2 实际是后者的 alias——新代码用它的名字导入,但拿到的是 Milvus 类。

Correction · 2026-07-08 代码复查更正
初版判断有误:初版 SECTION-03B 描述为"三代 Retriever 并存 → 反映团队 3 个月的迭代 → 已收敛到 GEN-03"——这是只读设计文档 docs/design/001-architecture.md 得出的推论,没有交叉验证代码。

实际关系(grep -n "class Hybrid\|import Hybrid" src/ 验证):
· src/api/infrastructure/retrieval/hybrid_retriever_v2.py 只有一行有效代码:from ...hybrid_retriever_v2_milvus import HybridRetrieverV2 # noqa: F401——纯 re-export
· 所有 import HybridRetrieverV2 的调用点(QA workflow v2、invocation_retrieval_service)拿到的都是 Milvus 类
· 不存在"Chroma + Milvus 双写"的 GEN-02 过渡实现
路径 A · PIKE-RAG 原生框架
HybridRetriever
在用 · 走 Chroma + BM25
  • 基于 Chroma(Dense)+ langchain BM25Retriever(Sparse)
  • 依赖 persist_dir 本地目录,无横向扩展
  • Dense 与 Sparse 结果需手工在应用层融合去重、加权
  • PIKE-RAG 框架代码(src/pikerag/),未针对大规模优化
CALLED BY
qa_conversation_workflow.py:427(V1 主 QA workflow)
knowledge_base_vectorize_service.py:64(向量化服务)
invocation_retrieval_service.py:127(invocation test)
src/pikerag/knowledge_retrievers/
hybrid_retriever.py
路径 B · 项目自研主力
HybridRetrieverV2Milvus
在用 · 走 Milvus 原生 hybrid
  • Milvus 原生 hybrid_search 一次调用同时跑 Dense + Sparse
  • 融合器 = RRFRanker(k=60)(Reciprocal Rank Fusion)
  • Dense 走 COSINE 相似度,Sparse 走 BM25 内建评分
  • 元数据字段(DOCUMENT_ID / METADATA)随查询一次返回
CALLED BY
qa_conversation_workflow_v2.py:946(V2 QA workflow,reasoning 模型分支)
invocation_retrieval_service.py:159(invocation test)
通过 HybridRetrieverV2 alias 名导入
src/api/infrastructure/retrieval/
hybrid_retriever_v2_milvus.py
辅助 · 转发 alias(不是独立实现)
HybridRetrieverV2
  • 文件里只有一行代码from api.infrastructure.retrieval.hybrid_retriever_v2_milvus import HybridRetrieverV2 # noqa: F401
  • 作用:让新代码用短名 HybridRetrieverV2 导入,但实际类是 HybridRetrieverV2Milvus
  • 推测目的:给未来"再换存储后端"留一层间接层——但目前没有第二个后端
src/api/infrastructure/retrieval/hybrid_retriever_v2.py(1 行 re-export)
真实运行时分布 · 两条链路并存

Chroma 链路(HybridRetriever)处理 V1 QA workflow、知识库向量化、invocation test 老分支——是项目冷启动就在用的路径,未被删除。 Milvus 链路(HybridRetrieverV2Milvus)处理 V2 QA workflow(reasoning 模型专用分支)和 invocation test 新分支——是新加的路径,专门解决 reasoning 模型(gpt-5 等)的输出兼容问题

接手要问的问题:生产实际流量走 V1 workflow(Chroma)还是 V2 workflow(Milvus)?如果两者都活着——数据同步机制是什么?向量化服务把 chunk 写进 Chroma 之后,什么时候、由谁写进 Milvus?这是接手第一天必须搞清的问题,报告里其他判断都依赖这个答案。

验证方法:grep -rn "HybridRetriever(" src/(不带 V2 后缀)找 Chroma 调用点;grep -rn "HybridRetrieverV2" src/ 找 Milvus 调用点;两个 workflow 文件对比看流量入口。

RRFRanker 融合原理(Milvus 链路专有)

对每一路召回结果(Dense、Sparse),第 rank 位的文档贡献分数 1 / (k + rank),两路分数直接相加得到最终排名。k=60 是 Milvus 默认值,平衡"高排位强化"与"低排位不被淹没"。这种融合方式不需要调参加权,也不依赖不同索引分数的绝对值可比性——Dense 的 cosine 距离和 Sparse 的 BM25 分数量纲完全不同,RRF 用"排名"消除了这个问题。Chroma 链路没有这个融合器——PIKE-RAG 原生实现的融合方式(应用层排序)没有 RRF 这么好的鲁棒性。

DEEP DIVE · 03C

Agentic 平台的心脏:13 Agents · 6 Skills · Harness 模式

Section-04 讲过 PIKE-AGENT 是 .NET 侧的 Agentic 后端。这里下钻——它不是一个大 LLM prompt,而是一套 Definitions 目录:13 个专职 Agent + 6 个可复用 Skill,通过 load_skill() 递归调用,走的是 Harness 模式(LLM 动态判断走哪条路,而不是固定 workflow)。

13 个专职 Agent · 按业务域分组

每个 Agent 是一个 v1.agent.yaml,定义 instructions(系统提示词)、skills(允许调用的技能)、model.poolId(用哪个模型池)、maxTurns(agentic loop 上限)。分成三大域:

mvr-pharma
MVR 医药主 Agent · 入口
kb-workflow-qa
GKB 工作流问答 Agent
doc-classifier
文档分类打标
doc-attribute-extractor
抽 study_code / version 等业务字段
doc-summarizer
生成文档摘要
doc-relation-builder
建立文档间关联
graph-entity-extractor
GraphRAG · 抽实体
graph-entity-summarizer
GraphRAG · 实体摘要合并
graph-relationship-enricher
GraphRAG · 补关系描述
graph-claim-extractor
GraphRAG · 抽 Claim(可核查陈述)
graph-conflict-confirmer
GraphRAG · 确认矛盾观点
graph-community-reporter
GraphRAG · 社区摘要(Leiden 算法)
mvr-replan
评估证据充分性(Skill 里也叫)

路径 src/pike-agent/src/Modules/Pike.Agentic/Definitions/agents/*/v1.agent.yaml

6 个可复用 Skill · MVR 主链路

Skill 是 Agent 可以调用的子任务模板——一个 skill 有自己的 instructions(提示词)、allowedTools(限定能用的工具)、metadata(版本)。Agent 用 load_skill("xxx") 递归进入下一个 skill 上下文,形成 skill chain

MVR 知识问答mvr-knowledge-qa
把用户问题拆分为原子检索任务逐条执行。7 种检索策略(术语查询/语义搜索/英文变体/分类先验/文件定位/目录导航/上下文扩展)+ 多语言硬约束(每个核心检索必须中英各搜一次)+ 深挖规则(发现新术语/新文档就追踪)。执行完调用 mvr-replan 评估。
allowedTools: kb_search · kb_discover_documents · kb_list_profile_facets · kb_read · kb_get_outline · kb_lookup_term · context_remove · context_list
检索评估与重规划mvr-replan
评估已检索资料是否足以回答用户问题。6 个评估维度(覆盖度/完整性/术语确认/关键词但无解释/引用追踪/主题对齐)。三个决策分支:资料充分→加载 mvr-grounded-answer;主题偏离/不足有线索→回到 mvr-knowledge-qa 深挖;完全无相关→尝试更多变体。
典型 next: load_skill("mvr-grounded-answer") 或 load_skill("mvr-knowledge-qa")
可信答案编写mvr-grounded-answer
基于上游检索证据生成带引用的结构化答案。硬约束:先结论后依据关键事实必须有 [N] 引用禁止编造/常识补充/无证据推理禁止澄清反问、检索无关时用"⚠️ 知识库中未找到"标注。
工具:仅使用上游 chunks,不再调 kb_* 工具
MVR 闲聊路由mvr-chitchat
识别纯问候/闲聊或明确域外问题,礼貌引导用户提出具体业务问题。
三阶段智能召回multi-hop-retrieval
替代 mvr-knowledge-qa 的一种更激进策略:三阶段强制执行、最少 8 次工具调用。Phase 1 多角度向量检索 → Phase 2 文件名发现(kb_find_documents)→ Phase 3 命中文档定向阅读(kb_get_outline + kb_read)。设计初衷是解决"只搜文本、不找文档"的召回盲区。
硬要求:Phase 1/2 必须都跑,不能因为 Phase 1 结果好就跳 Phase 2
通用可信答案grounded-answer
mvr-grounded-answer 的非 MVR 场景版本,规则相同但用于 GKB Agent 等非 MVR-pharma 主链路。
User Query mvr-pharma Agent load_skill() mvr-knowledge-qa ✓ 检索完 mvr-replan 评估分支 grounded-answer SSE 流回前端
典型 skill chain:用户提问后,mvr-pharma 主 Agent 根据意图判断是业务问题还是闲聊——业务问题就 load_skill("mvr-knowledge-qa");后者又根据 replan 结果判断"资料够不够",够了 load_skill("mvr-grounded-answer") 生成答案,不够则回到 knowledge-qa 深挖。

maxTurns = 25mvr-pharma/v1.agent.yaml)—— 硬性上限,防止 skill 递归死循环。

为什么用 Harness 不用 Workflow?Workflow 是"先画流程图再执行"(固定路径),Harness 是"让 LLM 自己决定下一步走哪个 skill"——更灵活但也更难调试。这是团队从 v1 Workflow 迁移到 v3 Agentic 的核心动机。
05 — 系统架构

从 Python 服务到双后端 Agentic 平台

项目演进中引入了一套 .NET 重写(历史上的 pikerag-net,现为 pike-agent)。两个后端共享同一 PostgreSQL 数据库,由 Nginx 按 URL 前缀路由共存:Python 承载 v1/v2 检索问答,C# 承载 v3 Agentic 与知识平台能力。

PYTHON · /api/v1 · /api/v2

PIKE-RAG API

27,225 行 · 299 文件 · FastAPI
检索
HybridRetriever(Milvus 原生 dense + sparse)
框架
FastAPI · LangChain · 端口-适配器架构
文档
Azure Document Intelligence 解析流水线
异步
api 投递 → Redis 队列 → job 消费回写
观测
OpenTelemetry span · LGTM 开发栈
C# · .NET 10 · /api/v3

PIKE-AGENT

59,325 行 · 930 文件 · Modular Monolith
架构
Vertical Slices + CQRS(MediatR)
工作流
Elsa Workflows · Hangfire 后台任务
向量
Milvus(gRPC via Pike.Milvus SDK)
编排
.NET Aspire · Scalar OpenAPI 3.1
Pike.AgenticPike.AiGatewayPike.GraphRAGPike.IdentityPike.KnowledgePike.WorkerPike.PlatformPike.Common

Web 前端

32,564 行 · 154 文件 · React

基于 React + Ant Design 与 @ant-design/x 会话组件构建,含知识库管理、Chat Playground、工作流可视化(@xyflow/react + dagre + mermaid),支持 i18n 与流式 SSE 渲染。

部署与评测

docker · helm · ragas

Docker Compose / Helm 双形态部署,内建 RAGAS 评测框架与纯检索基准(bge-m3 vs Azure)。

05 — BUSINESS DELIVERY · 从代码到 GO LIVE

用真实交付数据说话

前四节讲了「代码里有什么」,这一节讲「作为一次真实的商业交付,它是如何跑完的」。以下 8 张卡片全部来自项目内部的 MVR_Doc/ 目录:709 条文档清单、12 份周报、UAT 材料、部署手册。所有客户标识(人名、内网主机、Study 代号、SharePoint 路径)均已脱敏处理。

CARD 1 · DELIVERY LIFECYCLE

从设计到部署 · 六段交付全景

项目按标准企业级交付六段组织,每段都有独立的产物目录。UAT 段是本次交付的最大工作量重心。

STAGE 0
项目管理
Weekly Status Update ×12 · Project Plan · internal backlog
STAGE 1
设计
PRD-P1 (OpsMate-Ask) · UIUX v8 · 需求汇总 16 章 · PIKE-RAG Enhancement
STAGE 2
开发
文档库对接设计 · Clinical trial 相关资料 · mockup V4
STAGE 3
测试 · UAT
DocumentList 709 条 · Test Case Admin · CQM QA 集 · 失败问题跟踪 · UAT 用户名单
STAGE 4
部署
安装配置指南(AZ 标注版 + MS 版)· UAT 账号发放 3 批
STAGE 5
项目交付
Project Delivery Doc(交付材料汇总)
CARD 2 · KB INGESTION RHYTHM

40 个月 · 从 4 份到 4 GB

709 份知识库文档并非一次性灌入——最早的资料来自 2022-06,但 绝大部分(约 60%)集中在 2026-04 系统冷启动期。以下柱图按月粒度展示上传数量,深色柱是三次批量导入的峰值。

2022-062023-012023-082024-042024-112025-062026-012026-06
2026-04-29
系统冷启动灌入
101 份/天
2026-05-06
UAT 培训启动
2026-05-27+
UAT 账号 · 3 批发放
3
2026-06-03
文档清单冻结
709
2026-06-15
GKB Go Live 上线
CARD 3 · KB VOLUME

单一知识库 · 全量家底

整个项目所有文档汇总到 一个 KB(kb_<id>)——不做业务分片,检索通过元数据过滤实现。

709
份文档
4.16GB
总体积
~34
名 SME · Top 1 独占 46%
5.87MB
平均文件大小

按文件大小分布(709 份):

< 100 KB
143
100 KB ~ 1 MB
343
1 ~ 5 MB
124
5 ~ 20 MB
66
> 20 MB
33
CARD 4 · BUSINESS COVERAGE

17 个业务板块 · Top 8 覆盖 92%

FilePath 一级目录归纳。General、China Local Process、Study protocol 三巨头是最核心的业务资产。

General · 通用资料
213
China Local Process · 中国本地流程
200
Study protocol · 临床研究方案
66
Communication
46
User Guide · Power Apps
37
Digital & DCT Open Day · 数字化 + 分布式试验
32
Onboard · 新员工培训
29
Shield Database for PO check
25
其余 9 个板块合计
61
CARD 5 · CLINICAL COVERAGE

10 个 Study · 3 大治疗领域

Study protocol 目录下共 66 份 CSP 相关文档,覆盖 10 个真实临床研究方案。头部方案(Top 4)占 Study 总量 64%,说明本次 POC 聚焦在 少数重点在研药物。方案代号已按客户要求脱敏,此处只披露治疗领域。

Oncology
肿瘤
Study protocol 中的主战场。CSP 通常含剂量调整、剂量限制毒性 DLT、不良反应分级、给药中断/恢复标准等复杂决策规则。
Respiratory
呼吸系统
呼吸吸入类药物的临床研究,PK/PD 与给药装置有关。
CVRM
心血管肾脏代谢
合并症与人群基线要求复杂,入排标准与访视安排是查证重点。
CSP 资产语义
CSP(Clinical Study Protocol)= 一个 Study 的宪法。它规定入排标准、随机方案、给药规则、访视流程、终点评估。每份 CSP 都对应配套的 IB(研究者手册)IND统计分析计划 等——这就是为什么 10 个 Study 会产生 66 份 CSP 相关文档。所有文档均属 商业机密级,涉及未上市在研药物,PIKE-RAG 之所以能"带出处、可核对",就是为了满足 GxP 的可追溯要求。
CARD 6 · PRODUCTION TOPOLOGY

单节点 all-in-one · 双 namespace 解耦

生产部署跑在客户内网的一台 RHEL9 GPU VM 上,用 HashiCorp Nomad + Terraform + Makefile 编排。应用层与推理层分别在两个 namespace,通过本地回环通信。2026-06-03 完成从旧 docker-compose 到 Nomad 的切换,独占标准端口 80/443。

namespace = staging 应用层 · 8 job
db-init
Python 迁移
db-init-net
.NET 迁移/seed
api
Python FastAPI :8000
api-net
.NET AI Gateway :8080
job-net
Hangfire Worker :8085
embedding
bge-m3 GPU(可选)
web
React SPA :3001
gateway
Nginx + TLS
Schema 物理隔离:三个独立 PG schema(应用 / 工作流 / 后台任务),与旧应用完全隔断。
namespace = default 推理层 · 4 job
LLM · Qwen2.5-72B AWQ
主力推理(AWQ 量化)
Embedding
bge-m3
Reranker
bge-reranker-v2-m3
Document Intelligence
离线容器(8 核 / 16 GB)
推理层先行部署,应用层通过 127.0.0.1 回环调用,无外网依赖。
EXTERNAL DEPENDENCIES · 外部依赖
Aurora PostgreSQL · 云端托管
ElastiCache Redis · TLS 传输
AWS S3(cn-north-1) · 走 IAM 实例角色,无静态密钥
集群内还自建了 Postgres · Redis · MinIO · Milvus + etcd 五组基础设施,其中 MinIO 仅供 Milvus 用(业务对象存储走外部 S3)。
CARD 7 · 12-WEEK EXECUTION TIMELINE

12 份周报 · 从「资源盘点」走到「Go Live 上线监控」

项目按周节奏推进(微软 Industry Solutions 交付方向 AZ R&D 提交的 Weekly Status Update),下方按周展示每周最关键的工作重点。深色圆点为里程碑周。

2026-04-02
项目启动 · 首次开出 5 条风险条目(OpsMate / PIKE-RAG 资源、需求变更、开发未评估)
PLAN
2026-04-07
AZ 环境资源盘点与评估 · PIKE-RAG 已知缺陷分析和修复
PLAN
2026-04-13
AZ 环境部署验证 · PIKE-RAG 缺陷修复 · GKB 测试基线建立
DEV
2026-04-20
OpsMate 已在 AZ 环境部署完成 · PIKE-RAG 新接口开发完成,已支持流式响应
DEV
2026-04-27
★ 端到端首次功能演示 · 收集反馈意见 · Test Approach 与 test case 根据反馈更新
DEV
2026-05-11
AWS OpsMate SAT · CKB(MVR/study 文档管理)UX 设计初版 · GKB UAT 测试讲解与计划
DEV
2026-05-18
★ GKB UAT 启动(第一轮)· Ask 模块新增通用知识文档来源 222 份
UAT
2026-05-25
GKB UAT 测试与问题修复 · 开发 SSO 权限、admin Source 设置与日志、PIKE-RAG 准确度优化
UAT
2026-06-01
GKB UAT 修复继续 · CKB 相关功能设计启动
UAT
2026-06-08
GKB 部署准备 · CKB 相关功能开发 · MVR 相关功能设计启动(三条战线并行)
UAT
2026-06-15
★ GKB 部署完成 · CKB 开发中
LIVE
2026-06-22
★★ GKB 已 Go Live · 进入上线监控 + Hot-fix · CKB 审计日志、安全监控开发中
LIVE
CARD 8 · RISK TRACKING

12 周风险演变 · 9 条目全谱

从周报「项目风险」页汇总合并出 9 个独立风险条目——2 条已关闭,7 条至 06-22 仍在跟踪。左侧色条表示风险级别: / / 低/已关闭

高风险
中风险
低风险 / 已关闭
R1 · 项目进度风险已关闭
级别 低 · 首现 04-07 → 末次 04-13
修订后的计划制定结合了范围内容,但执行过程中可能因实际情况引起计划变动。
关键更新:提前细化阶段计划,建立每日站会 + 每周项目组沟通机制,必要时调整并与各方达成一致。
R2 · OpsMate 资源确认已关闭
级别 高 · 首现 04-07 → 末次 04-17
需要尽快确认 OpsMate 资源,并在 AZ 环境中完成部署验证。
关键更新:0417:已完成部署验证。
R3 · PIKE-RAG 资源确认(含 GPU/DI/OpenAI)已关闭
级别 高 · 首现 04-07 → 末次 05-27
GPU VM 访问 DI 资源、Azure OpenAI 调用等一系列资源与联通性问题。
关键更新:0414 获得 GPU VM 权限 → 0416 网络通但运行异常 + OpenAI KEY 错 → 0423 全部解决 → 0527 生产 service account 3 个月过期 key 申请双 key 轮换。
R4 · MVR Writing 需求变更(早期)进行中
级别 中 · 首现 04-07 → 末次 05-11
MVR Writing PRD 内容尚未确定,存在后续变更的可能性。
关键更新:0416:已进行一次内部讨论。
R5 · PIKE-RAG 开发风险(新场景不支持)进行中
级别 高 · 首现 04-07 → 末次 06-22
尚未完全分析 PIKE 代码细节,当前工作量评估存在后续变更的可能性。
关键更新:0509 代码梳理完成 → 0520 关键发现:PIKE-RAG 不支持新场景(闲聊引导、比对文档、相似文档识别、文档搜索),需额外设计和开发 → 0529 正在评估新场景工作量。
R6 · CKB 上线延期风险(早期)进行中
级别 高 · 首现 04-27 → 末次 05-11
Clinical Trial 功能定位变化,OpsMate 不再对接 Clinical Trial,需自行开发 MVR 文档管理与用户账号系统。
关键更新:0424 开始 UX 初版设计 · 0509 完成 UX 初版,上线时间点暂时不受影响。
R7 · CKB 上线延期风险(持续跟踪版)进行中
级别 低 · 首现 05-18 → 末次 06-22
同上,需求变更导致 CKB 范围扩展。
关键更新:0529 追加「文档类查询」能力(全面检索/比对,逻辑和问答完全不同,需新增设计 + 更新 PRD 和测试用例)。
R8 · MVR Writing 需求变更(持续版)进行中
级别 低 · 首现 05-18 → 末次 06-22
MVR Writing PRD 内容仍在演化。
关键更新:0529 关键结论:部分报告内容 AI 无法从原文档提取,需引入 human-in-the-loop 流程;现有 sample 材料不完整。0617 研究 EDC 导出、揭盲表、Issue Log、SDV analysis,更新 PRD 设计。
R9 · GKB Go Live 后 Hot-fix 收敛进行中
级别 低 · 首现 06-22 → 末次 06-22
GKB 已上线后进入 Hot-fix 阶段,10 条 tickets 在 06-19 一次性交付,包括提示词优化、图表纵轴动态、user feedback 权限 bug、TMF Index excel 入库失败、chat history title 自动总结等。
关键更新:0620:10 条 tickets 全部部署 UAT · SPB update(Teams channel + SharePoint 配置 + Qwen 部署)与 GKB source 排除项功能仍 In Progress。

数据来源:本节全部内容来自项目仓库内的 MVR_Doc/ 目录—— 3-Test/DocumentList_0603.csv(709 条真实文档清单,通过 Python 聚合分析)、 0-Project Managment/AZ R&D_MVR_Weekly Status Update_*.pptx(12 份周报,通过 python-pptx 解析)、 4-Deployment/*.docx(安装配置指南)、 deploy/nomad/deploy-staging.md(生产部署手册)。 无任何外部网络访问:SharePoint、生产 VM、UAT 系统均未接触。人名、内网主机、Study 代号、SharePoint URL、上传者 UUID 已全部脱敏。

06 — 问题与后期优化

还有哪些没做完 · 未来往哪走

本节的判断依据全部来自项目内部真实材料:docs/remediation-plan.md(整改记录,2026-05-28 最后更新,列出 P0/P1/P2 全部技术债)· 12 份周报里末次仍进行中的 7 个风险 · docs/features/{new, in-progress, deferred} 三个状态目录 · Pike.GraphRAG 设计文档(v0.6,2026-05-26)。不是空穴来风的建议,是团队已经写在纸上、但还没干完的活。

CATEGORY 01 · ARCHITECTURE DEBT架构耦合债 · 5 项

代码规模已达 55,500 行 · 916 文件 · 18 个模块。当前依赖链过深(GraphRAG → Agentic → Knowledge → Platform → Common),改一处触发多处重编译。

清理已弃用的 Worker 项目
P0
Worker(GPU 推理服务)已被 src/vllm 容器 + vLLM OpenAI 兼容 API 替代。需删 Pike.Worker 模块(3,631 行)+ Worker Host(28 行)+ Dockerfile/docker-compose/Nomad 相关配置。
docs/remediation-plan.md · 待整改 #1
删除空项目 MarkItPdf.NET
P0
目录存在但完全为空、未注册在 slnx 中。残留垃圾,直接删。
docs/remediation-plan.md · 待整改 #2
Pike.Common 拆分
P1
Common 同时装了纯抽象(Domain 基类、接口)和重型基础设施(S3 / Redis / Hangfire / OTel)。所有模块都依赖它——任何变动触发全量编译。目标拆成 Pike.Common(纯抽象)+ Pike.Infrastructure(S3/Redis/Hangfire)+ Pike.ServiceDefaults(OTel + Aspire wiring)三层。
docs/remediation-plan.md · 待整改 #3
Pike.Agentic 拆出 Evaluation 模块
P1
Pike.Agentic 已达 18,011 行,装了 Agent 执行、Skill 管理、AI Workflow、Evaluation 四大领域。建议把 EvalRuns.cs(909 行)+ EvalDatasets.cs(415 行)+ Application/Eval* 拆成独立 Pike.Evaluation(约 3,000 行)。
docs/remediation-plan.md · 待整改 #4
全模块 Abstractions 解耦
P1
当前模块间直接引用完整实现项目 → 传递依赖爆炸(GraphRAG → Agentic → Knowledge → Platform → Common)。方案:每个模块提取独立 *.Abstractions csproj,只装接口 + DTO + 事件契约,模块间只引 Abstractions。收益:编译隔离 + 测试友好 + 宿主项目瘦身。
docs/remediation-plan.md · 待整改 #5

CATEGORY 02 · CODE-LEVEL TECH DEBT代码级技术债 · 5 项

已经能跑,但结构、类型安全、可维护性上有明确改进空间——大文件、Raw SQL、重复 PackageReference。

大文件拆分
P2
四个文件超过 500 行:QaWorkflowContext.cs(843)、AgentFactory.cs(808)、Endpoints/EvalRuns.cs(909,随 Evaluation 拆分解决)、Endpoints/Agents.cs(506)。建议:Context 提取 Step 逻辑为 partial class;Factory 按 Agent 类型拆多个;Endpoints 按 CRUD 拆分。
docs/remediation-plan.md · 待整改 #6
重复 PackageReference 整理
P2
MediatR、FluentValidation、Hangfire.Core、Elsa.* 在 5-7 个 csproj 中重复声明。在 Directory.Build.props 中为 src/Modules/** 加条件引入通用基础包,各模块 csproj 只声明特有包。
docs/remediation-plan.md · 待整改 #7
Raw SQL upsert 迁到 EF Core
P2
GraphMergeService.PersistMergedGraphAsync() 用原始 SQL 拼 INSERT ... ON CONFLICT DO UPDATE,无编译时检查。改用 EF Core ExecuteUpdateAsync 或 Npgsql bulk copy helper 保持类型安全。
docs/remediation-plan.md · 待整改 #8
Agent poolId SyncPolicy
P2
Agent YAML 中 poolId 每次 db-init 都覆盖 DB。已通过统一 YAML 临时修复,但需增加 SyncPolicy: CreateOnly | AlwaysSync 语义,避免 seed 覆盖运行时配置。
docs/remediation-plan.md · 待整改 #9
GraphEmbeddingService 对接 Milvus
P2
GraphRAG 阶段 6(Text Embedding)目前部分实现——Entity/Community Report Embedding 已生成但未对接 Milvus 存储。需完成到 Milvus 的写入链路,供 Graph 查询用。
docs/remediation-plan.md · 待整改 #10 + Pike.GraphRAG v0.6 设计

CATEGORY 03 · UNRESOLVED PROJECT RISKS项目未闭合风险 · 4 项

从 12 份周报里追踪出来 —— 06-22 最新一份周报里仍在跟踪、未关闭的风险条目。这些不是技术债,是"业务尚未闭环"的活。

PIKE-RAG 新场景能力缺口
2026-05-20 关键发现:PIKE-RAG 对新场景(闲聊引导 / 比对文档 / 相似文档识别 / 文档搜索)不支持,需要额外设计和开发。0529 团队仍在评估工作量。目前 multi-hop-retrieval skill 是一个尝试性补丁(强制三阶段 + 至少 8 次工具调用),但根本设计尚未落地。
周报风险 R5 · 首现 04-07 · 末次 06-22(持续 11 周未闭)
MVR Writing 需求悬空 · 需引入 Human-in-the-loop
2026-05-29 关键结论:部分 MVR 报告内容 AI 无法从原文档提取,需引入 human-in-the-loop 流程;现有 sample 材料不完整。0617 团队开始研究 EDC 导出、揭盲表、Issue Log、SDV analysis 更新 PRD 设计,但 human-loop 交互流程尚未定义。
周报风险 R9 · 首现 05-18 · 末次 06-22(持续 5 周未闭)
CKB 需求持续扩张 · 文档类查询新需求
2026-05-29 追加:CKB 需支持文档类查询(全面检索文档、比对文档等),逻辑与问答完全不同,需新增设计并更新 PRD 与测试用例。这条持续扩张会否影响 CKB 后续里程碑仍需关注。
周报风险 R7/R8 · 首现 04-27/05-18 · 末次 06-22
GKB 上线后 Hot-fix 收敛 · 2 项 In Progress
2026-06-22 最新周报显示:10 条 GKB Hot-fix 已在 6/20 全部部署 UAT,但仍有 2 条 In Progress——SPB update(Teams channel + SharePoint 配置 + Qwen 部署)和 GKB Source 排除项功能(排除某文件夹路径不入库),预计 6/25 交付。
周报 06-22 · slide 3

CATEGORY 04 · CAPABILITY ENHANCEMENT能力增强 · 4 项

来自设计文档里明确规划、正在或计划推进的能力扩展 —— 这些不是"修 bug",而是"未来路线"。

Pike.GraphRAG 全阶段实现 · 完成 Phase 6 Embedding
P1
Pike.GraphRAG(C# 版微软 GraphRAG)Phase 3-5 已核心实现(Entity/Relationship/Claim 抽取 + Leiden 社区检测 + 社区摘要),Phase 6(Text Embedding)仅部分实现。知识库级别默认关闭,需完成后开放使用。当前 4,004 行 · 66 文件。
docs/design/pike-graphrag-project-design.md v0.6 · 2026-05-26
Claim 抽取 + 溯源架构
P1
设计已定:解决三个真实需求——广泛性查询(覆盖全部文档不受 top-k 限制)、矛盾观点识别(自动识别不同文档间的矛盾并标注)、原文溯源(含 chunk_id)。三个新工具:kb_search · claim_search · claim_conflicts
docs/design/claim-extraction-architecture.md
文档发现工具收敛 · 合并 list/find
P1
当前存在两个能力重叠的工具:kb_list_documents(列表浏览)和 kb_find_documents(内容检索)——Agent 选工具成本高。设计收敛为一个新工具 kb_discover_documents,支持浏览、结构化过滤、语义召回三类检索,保留分页契约。
docs/design/303-document-discovery-tool.md
Python V2 对话端点迁到 .NET · 不是简单下线Verified 2026-07-08
P1
代码复查更正:初版判断"V2 只剩 chat/completions 端点、可以简单下线"——这个说法误读了设计文档。真实情况(src/api/interface/http/routers/registry.py):
  • Python V1 路由 已全部注释掉[V1-removed])——admin/auth/document/kb/workflow 全下
  • Python V2唯一在跑的 Python 对话端点——/api/v2/chat/* + chat history
  • 整个多轮 QA workflow、四路混合召回、Query Planner、Answer Generator 全部在 Python 侧——不是"轻量遗留",是核心引擎
  • .NET 侧只有 /api/v3/*(主力) + /api/v1/*(自己的 legacy 兼容层,跟 Python V1 完全无关)
真正的迁移代价:把整条 QA 主链路从 Python 重写到 .NET——涉及 PIKE-RAG 框架、Chroma 客户端、multi-round planner、evidence assembler 全套。不是"下线",是"重写"
代码复查:src/api/interface/http/routers/registry.py(V1 全注释)· routers/v2/conversation_router.py(V2 chat 主端点) · pike-agent/**/Endpoints/*.cs(.NET 只有 v1 + v3)
DEEP DIVE · 深度实测与真实模型接入

下面这三块内容已拆到独立子页,方便打印/分享/更新

主报告聚焦「历史 / 架构 / 交付 / Roadmap」;实测发现的 Bug 明细、真实模型接入过程、以及最新一次端到端测试报告, 都拆到了 3 个独立子页。每个子页都可以单独发给同事;主报告顶部导航条常驻,任何时候都能一键跳转。

附录一 · TEAM & FEATURE MAP
团队与功能地图
(419 commits 的故事 + 25 功能页全景)
Part I · 团队 — 基于 git log 全量分析,复原三个月时间线、五幕故事、五个开发者手艺画像、 以及 .NET 中途上场的技术判断。Part II · 功能地图 — 基于 routes.tsx(180 行单一权威源) + 实际点开每个菜单,画出 4 大板块 · 25 个功能页 · 8 个内置任务流程 · 13 个 Agent 全清单。
阅读 →
附录二 · 真实环境验证
实测记录
(冷启动 · 故障恢复 · Hub 兼容验证)
Part I · 起点 — 从 Mock 到 Real 三模型接入(223 内网 ornith-1.0-35b-mtplx + nomic-embed-text-v1.5 + bge-reranker-v2-m3)+ NMPA 官方说明书替换 Demo 语料。 Part II · 跑起来 — 端到端测试 6 环节全通 · 64 次真调用统计。 Part III · 一路的坑 — 235 冷启动 + 真模型接入过程踩到的 7 个 Bug 时间线。 Part IV · 最新运行态 — Milvus 中断根因、错误误报修复、阿司匹林问题回归,以及 Home AI Hub v0.3.0 的 tools / SSE / 768 维 / rerank 实测。
阅读 →
附录三 · MAF 运行架构
MAF 如何驱动 MVR
(Agent · Skill · Tool · Workflow 完整协作)
运行架构 — 前端、Agent API、AgentFactory、MAF Runtime、AI Gateway、知识工具与 SSE 持久化的完整分层。 MVR 链路 — 从用户提问到 Skill 动态加载、双语检索、重规划、可信答案和结构化引用的 12 步真实流程。 继续开发 — 改问答、加 Tool、建 MVR 生成/审批 Workflow、评测与发布分别应修改哪些文件和入口。
阅读 →

为什么拆:这两块附录都是「一次性深度调查 + 长期演进证据」性质,主报告如果全塞进来会超过 2600 行, 重要的架构叙事会被淹没。附录一回答「项目怎么来的 · 有些什么」,附录二回答「真的能跑吗 · 一路发现了什么」, 主报告聚焦「WHAT / WHY」,附录承载「HOW / EVIDENCE」。

SUGGESTED ROADMAP · 优先级 & 阶段建议

如果只能干三件事,按什么顺序

下面的三阶段划分是基于业务价值 × 技术风险两个维度综合排出的建议,仅供项目组参考。所有项都可在上面 4 类问题里找到对应条目。

短期 · 4 - 6 周
收敛 GKB · 稳定线上
10
先把线上系统稳住:完成 06-22 遗留 2 项 In-Progress Hot-fix · 清理已弃用 Worker 项目(P0)· 删空的 MarkItPdf.NET(P0)· 补 GraphEmbeddingService 对接 Milvus · 补文档发现工具收敛 · 完成 UAT 未闭合缺陷回归 · 修 Pike.Milvus SDK 的 REST/BM25 兼容问题(P0,7-09 实测发现)· 补 GATEWAY_MASTER_KEY 长度 fail-fast 校验(P1)· Milvus DENSE_DIM 硬编码 1024 改为 env / KB-level 可配(P1,7-09 真实模型接入时发现)· Agent KB 关联 API 加 FluentValidation NotNull 校验避免 500 NullReferenceException(P1)。

为什么优先:GKB 已 Go Live,用户在用,任何遗留缺陷都是"用户能感知到的问题"。Milvus SDK 问题会挡住所有新 KB 的全新部署;DENSE_DIM 硬编码会挡住任何客户自选 embedding 模型的场景。
中期 · 2 - 3 月
架构解耦 · CKB 落地
7
为下一阶段扩张做基础:全模块 Abstractions 解耦(P1)· Pike.Common 拆三层(P1)· Pike.Agentic 拆 Evaluation(P1)· PIKE-RAG 新场景能力(比对文档 / 相似文档 / 文档搜索)· CKB 文档类查询设计落地 · Claim 抽取 + 溯源上线 · Pike.GraphRAG Phase 6 完成。

为什么先架构再业务:CKB 会引入大量新代码,如果先在耦合架构上继续堆,之后更难拆。
长期 · 6 月 +
MVR Writing · 平台化
5
形成真正的 MVR 智能助手:设计并落地 MVR Writing human-in-the-loop 流程 · 完成 GraphRAG 全阶段并开放为默认能力 · Python V2 主对话链路迁到 .NET(重写而非下线,含 QA workflow + 4 路召回 + Planner + Generator) · 大文件拆分(AgentFactory / QaWorkflowContext / Endpoints)· 重复 PackageReference 整理 + Raw SQL 迁 EF Core。

为什么最后:MVR Writing 涉及 human-loop 交互设计,需要 CQM 团队充分参与,且依赖前两阶段的架构和能力就位。

本节全部结论的证据来源docs/remediation-plan.md(P0-P2 完整清单,2026-05-28)· docs/design/pike-graphrag-project-design.md(v0.6,2026-05-26)· docs/design/claim-extraction-architecture.md(Claim 溯源架构)· docs/design/303-document-discovery-tool.md(工具收敛设计)· docs/design/501-api.md(V3 API 设计,版本策略)· 12 份周报MVR_Doc/0-Project Managment/AZ R&D_MVR_Weekly Status Update_*.pptx 里 04-02 到 06-22 的风险追踪)。 Roadmap 三阶段是基于以上材料的综合建议,不代表项目组已有的正式决议。