返回主报告
PIKE-RAG · AZ China MVR Smart Tool
APPENDIX TWO · LIVE VERIFICATION

实测记录 · 从冷启动到端到端跑通

本页记录 2026-07-09 到 07-10 两天的完整实测过程,分三个阶段:先在 235 服务器全新部署基础设施(踩了 7 个坑) · 再把 Mock 模型切换到 223 内网真实三模型 + 用 NMPA 官方说明书替换手写 Demo 语料(接入过程又踩 2 个坑) · 最后跑完整端到端自测(6 环节全通,新发现 2 个 P2/P3 bug)。全部 9 个 bug 都有 root cause + 证据链 + 修复建议。

📋 三阶段 · 按时间线
Part I · 起点从 Mock 到 Real 全栈接入(2026-07-09 下午 · 数据集升级 + 三模型接入)
Part II · 跑起来端到端测试 6 环节(2026-07-10 · 7/7 通过 · 64 次真调用统计)
Part III · 一路的坑全部 9 个 Bug 时间线(235 冷启动 5 坑 + 真模型接入 2 坑 + 测试新发现 2 坑)
PART I · MOCK → REAL

从 Mock 到 Real 全栈接入

起点——把 LLM/Embedding/Reranker 三件套从 Mock 切换到 223 内网真实模型 · 顺便把 3 份手写 Demo 语料换成 NMPA 官方说明书。

REAL-MODEL FULL-STACK · 2026-07-09 三模型端到端接入验证

从 Mock 到 Real:把 LLM/Embedding/Reranker 全换成内网真实模型 + 用官方说明书替换手写 Demo 语料

📋 DATASET UPGRADE · Demo 语料 → 官方说明书

为了让端到端演示真正代表 MVR 场景 · 把最早为跑通链路手写的 3 段医学常识(每段 ~600 字节)全部替换为 中国 NMPA 注册的原研/仿制说明书全文 · 每份保留 6 个规范字段(适应症/用法用量/不良反应/禁忌/注意事项/药物相互作用)· 来源可追溯:

文档 主题 大小 Chunks 数据来源
aspirin-real.md 阿司匹林肠溶片 5.0 KB 12 国药准字 H11021614(北京曙光)+ 拜耳原研(丁香园)
metformin-real.md 盐酸二甲双胍缓释片 6.3 KB 13 恒瑞制药官网 + 国家医保局申报材料 + 信谊药厂
atorvastatin-real.md 阿托伐他汀钙片(立普妥) 6.8 KB 15 晖致 Viatris 原研 v20260119 + NMPA 药物警戒

规模变化:1.8 KB / 12 chunks(手写)→ 18.1 KB / 40 chunks(NMPA 官方) · 3.3× 内容密度 · 让 dense embedding + BM25 + rerank 三通道真实分工。 Agent 端问「阿托伐他汀严重副作用 + 药物相互作用」· 答案完整命中说明书原文:横纹肌溶解 · CYP3A4 抑制剂 · 达芦那韦 AUC 3.45 倍 · 引用 atorvastatin-real.md seg:1/seg:2 可追溯。

本次演示把系统架构里所有 AI 依赖都从 mock 切换到真实内网服务,验证整条 RAG 链路(Ingestion → Search → Rerank → Generate) 在生产等价配置下完整跑通。所有模型部署在同一台 Mac Studio(192.168.1.223),通过 AI Gateway 统一路由。

LLM
ornith-1.0-35b-mtplx (LMStudio · reasoning model)
端点
http://192.168.1.223:1234/v1
Embedding
text-embedding-nomic-embed-text-v1.5 (768 维)
端点
http://192.168.1.223:1234/v1
Reranker
BAAI/bge-reranker-v2-m3 (Cross-encoder)
端点
http://192.168.1.223:7997/v1/rerank

FLOW · END-TO-ENDAgent Chat 完整调用链

用户在 Playground 输入「阿托伐他汀有什么严重副作用」,系统内部经历以下 7 步, 全程 20744 tokens in / 1044 tokens out(reasoning model 特性 · 大部分是思考过程), rerank 触发 2 次(冷 547ms · 热 139ms)。

Step 1-2 · 意图识别 + Skill 加载 ornith 35B
Agent 的系统提示词识别到"药物副作用"属于知识问答意图,加载 mvr-knowledge-qa skill。 Skill 内部产出 2 个检索 query:
  • 阿托伐他汀(短词 · 高召回)
  • Atorvastatin serious adverse effects side effects(英文长句 · 高精确)
这体现了 Query Expansion 策略:同一意图用多种表述并行检索,降低单一 query 的语义偏差。
证据 · Playground UI 显示"处理过程" · 2 次 kb_search tool call
Step 3 · Dense 检索(Embedding) nomic 768
KbSearchTool 用 KB 绑定的 lmstudio-embedding pool 对 query 生成 768 维向量, 走 AI Gateway 路由:POST /api/v3/gateway/openai/deployments/lmstudio-embedding/embeddings, Gateway 用 pk-key 鉴权后反代到 http://192.168.1.223:1234/v1/embeddingsMilvus 一次返回 initial_recall=30 条候选(通过 KbSearchTool 的 top_k=8 + initial_recall=30 配置)。

Milvus collection kb_kb_707811d90aa3426191c95_sections 里的 dense_vector 也是同一模型出的, 所以 query 向量和 doc 向量在同一语义空间。
证据 · KbSearchTool.cs:128 · docker-compose 里 EMBEDDING_DEPLOYMENT=lmstudio-embedding
Step 4 · Cross-encoder 精排(Reranker) bge-reranker-v2-m3
Dense 拉回的 30 条按 vector similarity 排序 · 但相邻候选往往分数差距很小(0.72 vs 0.69 vs 0.67)。 KbSearchTool 检测到 hits.Count(30) > topK(8)rerank_enabled=true · 触发 Reranker:

GatewayRerankService.RerankAsync(query, 30 docs, top_n=8) → 打 POST http://192.168.1.223:7997/v1/rerank · bge-reranker 用 Cross-encoder(query, doc) pair 逐对打分, 比 bi-encoder 更准 5-15% NDCG · 但成本更高(O(N)每 doc 一次前向)。

返回精排后的 top 8 · 应用 rerank_threshold=0.3 过滤低分噪音 · 然后进入下一步答案生成。这一步直接决定了引用质量 —— 没有 rerank 时经常 top-1 是"阿司匹林"(无关但语义近);有 rerank 后 top-1 精确锁定"阿托伐他汀 > 不良反应"。
证据 · pike-api-net 日志 · "Rerank completed: 8 results in 547ms" / "139ms" · 2 次调用
Step 5-6 · 证据评估 + Grounded Answer 生成 ornith 35B
Agent 的 mvr-grounded-answer skill 接收 8 个精排 chunks · 先做 coverage 评估("现有证据是否充分回答用户问题") · 然后生成答案 · 每句话必须能追溯到某个 chunk ID([1] doc3.md seg:1)。

最终答案精准命中原文关键概念:
  • "严重副作用:横纹肌溶解" ✅
  • "肌红蛋白尿 · 急性肾损伤" ✅
  • "CYP3A4 抑制剂(如克拉霉素 · 伊曲康唑)合用增加肌病风险" ✅
这正是文档 doc3.md section_3 的原文 · 完全无幻觉。
证据 · Playground UI 显示答案 + References 卡片 + tokens usage 20744 in / 1044 out
Step 7 · Post-Processor · Citation 生成 citation
Agent 定义里 postProcessors: [citation]。答案生成后 · Citation Processor 扫描文本里所有 [N] 引用标记, 匹配到对应 chunk 的 documentId + chunkId + excerpt · 以结构化 JSON 附加在回复末尾,前端渲染成"REFERENCES 1" 卡片,支持点击展开。

产出示例:{"id":"[1]", "documentId":"8b72fa57...", "chunkId":"8b72fa57..._section_3", "filename":"doc3.md", "excerpt":"罕见但严重:横纹肌溶解..."} 这就是 MVR 场景对合规性的关键要求 —— 任何答案都必须可追溯到原始文档。
证据 · Playground UI 底部 REFERENCES 1 折叠面板 · 内含结构化引用 JSON

为什么这次接入很关键:之前的所有测试都用 mock(词袋 hash embedding · fake LLM), 虽然验证了系统接口能通,但没验证语义质量。 本次用真实的 nomic embedding · bge-reranker · ornith 35B 三件套,证明了架构本身能承载生产级模型 —— 换个模型只需改 Gateway pool 配置 · 不需要动业务代码。 这个"模型可插拔"的抽象是 PIKE-RAG 相比一体化 RAG 产品(如 Dify · RAGFlow)的核心差异化优势。

PART II · E2E TEST

端到端测试 · 6 环节全通

接入完 Real 三件套之后——一次完整的从 KB 关联 → 用户提问 → 检索 → rerank → LLM 生成 → 引用可追溯的端到端跑通,7/7 环节通过 · 64 次真调用统计 · 新发现 2 个 bug。

测试范围

模拟一个新用户从「打开浏览器」到「拿到带引用的医学回答」的全流程,共 6 大关键环节 + 后端日志验证。测试时间 2026-07-10 上午,登录账号 admin/admin,浏览器 Chromium via Browserbase。

7 / 7
环节全部通过
2
测试中新发现的 Bug (P2 + P3)
64
223 真实模型调用 (6 分钟)

环节明细

环节
动作
结果
关键证据
01
登录 → JWT
✅ 通过
admin/admin → JWT identity_id=1 · 跳转 /knowledge/knowledge-base
02
KB 列表 + 详情页
✅ 通过
「真实模型-Demo」显示 3 篇文档 · lake / warehouse / vector 全「已入仓+已入库」
03
召回验证 UI(hybrid RRF)
✅ 通过
查询「他汀肌肉酸痛」→ 212 ms · Top-10 全命中阿托伐他汀不良反应/相互作用章节
04
Agent 管理页
✅ 通过
mvr-pharma:4 skills / 6 tools / 1 KB / lmstudio-chat 全就绪
05a
Playground Q1 · 阿司匹林胃出血
✅ 通过
8 轮中英双语查询 · 10 引用 · 回答分「处理 + 预防」两大块
05b
Playground Q2 · 二甲双胍 + 造影剂
✅ 通过
精准答「至少 48 h + 肾功能稳定」+ 机制解释乳酸性酸中毒 · 2 引用
06
后端日志验证 · 三模型真调用
✅ 通过
44 次 embedding + 20 次 rerank + ornith 若干 · 全部 192.168.1.223

测试中新发现的 Bug

P2 · GATEWAY · 计费统计

TokenUsageCollector 主键冲突 · Gateway 层 token 计费不准

症状
并发请求时后台报 Failed to flush token usage buckets, re-queuing 3 entries,PostgreSQL 报唯一约束冲突。
日志片段
fail: Pike.AiGateway.Infrastructure.Services.TokenUsageCollector[0]
  Failed to flush token usage buckets, re-queuing 3 entries
  Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving...
   ---> Npgsql.PostgresException (0x80004005): 23505:
   duplicate key value violates unique constraint "uq_token_bucket"
根因
TokenUsageCollector.FlushAsync() (line 116) 对唯一键 uq_token_bucket(time_bucket, pool_id, backend_id?)INSERT,并发多请求命中同一时间桶时冲突。
影响
❌ Gateway 层 token 计费统计不准(数据会丢) · ✅ 不影响 Agent 回答功能,用户看到的 tokens 是 Agent 层自己算的
修复建议
把 INSERT 改为 ON CONFLICT (time_bucket, pool_id, backend_id) DO UPDATE SET prompt_tokens = uq_token_bucket.prompt_tokens + EXCLUDED.prompt_tokens, completion_tokens = ...,或在 EF Core 用 ExecuteUpdate 手写 UPSERT。
触发路径
6 分钟测试窗口内出现 > 30 次,基本每次 chat completion 都会尝试写、都会冲突。
P3 · WEB · 用户体验

前端 3 个 UX 小问题(测试中命中)

问题 1
直接输入 /knowledge/vector-search URL → 页面完全空白,SPA lazy chunk 没触发 → 应加 404 fallback 或重定向到 /knowledge/retrieval-validation
问题 2
答案生成完毕后 textarea 保持 disabled 状态约 10 秒 → 流式结束的时机与 UI 状态解锁不同步。
问题 3
Playground 顶部 clear 按钮点击后不清空对话 → 只能通过导航离开-回来才能重置会话。
影响
不影响功能,但会让用户觉得「按钮无响应/系统卡住」,演示时需要提前说明。
修复建议
三处独立: (1) 路由 fallback + 404 页; (2) 监听流式结束 event 解锁输入; (3) clear 按钮 handler 补上 state reset。

223 内网三模型调用统计(6 分钟窗口)

┌──────────────────┬───────┬─────────────────────────────────────────┐
│ 服务             │ 次数  │ 端点                                    │
├──────────────────┼───────┼─────────────────────────────────────────┤
│ nomic-embed      │  44   │ http://192.168.1.223:1234/v1/embeddings │
│ bge-reranker-v2  │  20   │ http://192.168.1.223:7997/v1/rerank     │
│ ornith-1.0-35b   │  ~15  │ (走 Gateway · 未直接暴露 URL 到日志)    │
│ Milvus           │  ~15  │ http://milvus:19530 (hybrid_search)     │
└──────────────────┴───────┴─────────────────────────────────────────┘

Rerank 延迟(本次 3 次测量):
  第 1 次: 707 ms   (冷启动)
  第 2 次: 624 ms
  第 3 次: 766 ms
  历史平均(热): 150-200 ms

结论

核心链路完全可用 —— 用户从「打开浏览器」到「拿到带引用的医学回答」全流程走通。

可以给同事演示的两个入口:

演示时建议告知的 Caveat:

PART III · 235 冷启动 + 真模型接入 · 7 个坑

一路的坑清单

7 月 9 号在 235 服务器全新部署时踩到的 7 个坑——Mock 时期基础设施 5 坑 + 真模型接入 2 坑。Part II 的端到端测试中又新发现的 P2/P3 各 1 个已在上一章的「测试中新发现的 Bug」处列出,不在此重复。

LIVE DEPLOYMENT FINDINGS · 2026-07-09 全栈实测

在 235 服务器全新部署 + 真实模型接入过程中踩到的 7 个坑

为了让"召回验证"页面能真跑通,把全套 Docker Compose 环境重新部署到一台新服务器(Ubuntu / Docker 29.3.1 / 30G RAM), 并用 Mock LLM/Embedding + Milvus standalone 全新起。整个过程从零到能在前端做三通道对比检索,共花约 4 小时。 期间踩到 5 个非文档提及的问题,全部有明确 root cause 和证据链,值得进入债务清单。

CATEGORY · LIVE-FINDING系统冷启动时暴露的隐藏问题

这些问题在生产环境(既有配置 + grpc 客户端 + 已初始化 collection)里都被"运气好"绕过了, 但只要一次全新部署 / 一次换机 / 一次新 KB 创建,就会 100% 复现。 客户内部 IT 团队做 DR(灾备)演练时必然踩到。

.NET Pike.Milvus SDK 用 REST 而非 grpc · BM25 auto-function 完全不生效 P0
MilvusService.EnsureCollectionAsync 用 SchemaBuilder 声明了 AddBm25Function("bm25_fn", "content", "sparse_vector"), 期望 Milvus 从 content 字段自动生成 sparse 向量。 但 Pike.Milvus.MilvusClient 用的是 HTTP REST v2 API, Milvus 2.4/2.5 的 REST insert 路径 不触发 BM25 function auto-fill, 而 grpc 路径会。生产环境 Python 侧用 pymilvus (grpc) 所以从未暴露, 但 .NET V3 路径首次 upsert 直接抛 Milvus error (code=1804): missing vector field: sparse_vector。 collection describe 结果里 functions: [], 说明 SchemaBuilder 的 function 声明连注册都没成功,不只是 insert 问题。

触发条件:任何新 KB 首次 vectorize(Elsa workflow lake_to_vector_mainupsert_vector_store activity)。 影响:任何走 .NET V3 完整 Ingestion 流程的场景全部不可用。 目前生产为何"没事":Python V1 workflow 用 pymilvus grpc(同一 collection 也能正常写入)。
证据 · src/pike-agent/src/Libs/Pike.Milvus/MilvusClient.cs:190 · Modules/Pike.Knowledge/Infrastructure/Services/MilvusService.cs:78 · 2026-07-09 job-net 完整错误堆栈
GATEWAY_MASTER_KEY 长度错误导致系统 500 · 无友好校验 P1
BackendKeyEncryptor 用 AES-GCM 加密 backend API key(new AesGcm(_masterKey, 16)), AES 要求 key 长度必须严格是 16 / 24 / 32 字节.env.example 提示 "generate with openssl rand -base64 32", 但没做校验——用户如果给了 38 字节或其他长度的 base64, 不在启动时报错,而是在第一次调 AI Gateway 添加 backend 时CryptographicException: Specified key is not a valid size for this algorithm, 返回 500,前端无提示,日志埋在 stack trace 里。

修复建议:BackendKeyEncryptor 构造函数里加一行 if (_masterKey.Length is not (16 or 24 or 32)) throw new InvalidOperationException("..."), 让 API 启动就 fail-fast,配上清晰错误提示。
证据 · src/pike-agent/src/Modules/Pike.AiGateway/Infrastructure/Services/BackendKeyEncryptor.cs:15-20 · 2026-07-09 api-net 首次 add backend 报错
Collection 创建后没自动 load + sparse 索引参数硬编码错 P1
MilvusService.EnsureCollectionAsync 只调 CreateCollectionAsync没有接着调 LoadCollectionAsync。 Milvus 2.4+ 里 collection 默认 unloaded 状态时 delete/query/search 都会失败。 第一次 upsert 前手动 curl /v2/vectordb/collections/load 才能救。
另外 SchemaBuilder 的 .AddIndex("sparse_vector", "BM25") 生成的 metric type 参数在 Milvus 2.4 里被拒( 报 "only IP is the supported metric type for sparse index"), 需要显式指定 metricType=IP, index_type=SPARSE_INVERTED_INDEX

根因:Pike.Milvus SDK 是对着旧版 Milvus API 写的,2.4→2.5 之间 metric type/function 定义有变化,SDK 没跟上。 整个 Pike.Milvus 库需要针对 Milvus 2.5 做一轮完整回归测试。
证据 · src/pike-agent/src/Modules/Pike.Knowledge/Infrastructure/Services/MilvusService.cs:12-38 · 2026-07-09 Milvus REST 错误 code=65535
配置命名不一致 · MILVUS_HOST 与 Milvus__Host 混用 P2
.NET 官方约定用双下划线(Section__Key)作为环境变量名, 但 Pike.Knowledge.Infrastructure.DependencyInjection 里读的是 builder.Configuration["MILVUS_HOST"](全大写下划线)—— 这是从 Python 那边直接照搬过来的命名,跟 .NET 生态其他地方不一致。 部署时如果按 .NET 惯例传 Milvus__Host,代码读不到, 会 fallback 到默认 localhost,容器网络下直接连不上,报的还是模糊的 TaskCanceledException: The operation didn't complete within the allowed timeout of '00:00:30'

修复建议:Options pattern(MilvusClientOptions)+ services.Configure<MilvusClientOptions>(config.GetSection("Milvus")), 环境变量走标准 Milvus__Host / Milvus__Port, 同时保留旧变量作向后兼容一段时间。
证据 · src/pike-agent/src/Modules/Pike.Knowledge/Infrastructure/DependencyInjection.cs(读 MILVUS_HOST)· 2026-07-09 首次部署 30s 超时故障
前端 AntD Modal 有 deprecated 警告 · 部分弹窗打开无反应 P2
浏览器控制台可见两条持续报警: Warning: [antd: Modal] `destroyOnClose` is deprecated. Please use `destroyOnHidden` instead.Warning: [antd: message] Static function can not consume context like dynamic theme. Please use 'App' component instead.。 实测【AI Gateway → Pool 详情 → 添加后端】按钮点击后,弹窗DOM 生成但被 z-index 覆盖不可见, 用户以为按钮没响应(必须绕道直接调 REST API 才建成 backend)。

影响:不是致命故障,但会让运维人员觉得"系统不稳定", 而且 antd 大版本升级时这两个 deprecated 用法会真的 break。 建议做一次 antd v5 全量 API 兼容性 sweep,同时用 { App } component 包住整个应用来接管 message/notification 静态调用。
证据 · 2026-07-09 浏览器 console · web/src 内多处 destroyOnClose + message.xxx() 静态调用
Milvus DENSE_DIM 硬编码 1024 · 换 embedding 模型必须改代码 P1
shared/milvus/client_factory.py:35 直接写死 DENSE_DIM = 1024 # BGE-M3 output dimension, 所有 KB 的 collection schema 都用这个常量建 FieldSchema(name=DENSE_FIELD, dtype=FLOAT_VECTOR, dim=DENSE_DIM)。 意味着**换任何 embedding 模型**(nomic 768 · text-embedding-3-small 1536 · Cohere 4096 等) 都必须**改代码 + 重建全部 KB**。

发现路径:2026-07-09 演示中把 Embedding 从 Mock (1024 hash) 换成 LMStudio nomic-embed-text-v1.5 (768) 时,首个 KB Ingestion 直接崩, 日志 Milvus: dimension mismatch, expected 1024 got 768

修复建议:三步走 —— (1) 立即把 DENSE_DIM 改成 env 可配 int(os.environ.get("MILVUS_DENSE_DIM","1024"))作为过渡; (2) 中期让每个 KB 存自己的 vector_dim(根据它绑定的 embedding pool 探测一次得出); (3) 长期 collection schema 里带 embedding_model 字段,系统自动匹配。 这个 bug 在生产环境"从未暴露"是因为一直只用 bge-m3 一个模型; CKB / 客户自选 embedding / GraphRAG 用不同 dim 模型时必踩。
证据 · src/shared/milvus/client_factory.py:35 · 2026-07-09 nomic 768 vs bge-m3 1024 部署冲突
Agent KB 关联 API body 字段命名错误 · 500 NullReferenceException 无提示 P1
PUT /api/v3/agentic/agents/{name}/knowledge-bases 的 body 结构在 OpenAPI 里没标清 —— 尝试传 {"knowledgeBaseIds":["kb_xxx"]} 会返回 500, 日志抛 System.NullReferenceException at SetAgentKnowledgeBasesByNameCommandHandler.Handle line 303, 前端只显示 "An unexpected error occurred",不告知字段名错了。 实际正确结构:{"knowledgeBases":[{"knowledgeBaseId":"kb_xxx","isDefault":true}]}

修复建议:(1) FluentValidation 加 NotNull 校验字段,让 400 而不是 500; (2) 补 OpenAPI schema 里 AgentKnowledgeBaseDto 的 required 标注; (3) Handler 加空判断兜底,返回 "Missing 'knowledgeBases' field. Expected structure: [{knowledgeBaseId, isDefault}]"
证据 · src/pike-agent/src/Modules/Pike.Agentic/Application/Agents/Commands/ByName/AgentByNameCommands.cs:303 · 2026-07-09 首次 PUT 关联 KB 崩

方法论说明:这 7 项是通过 真实全栈冷启动实测 + 真实模型端到端接入触发的,非静态代码审查得出。 测试环境:Ubuntu 24 · Docker 29.3.1 · Milvus 2.5.14 · .NET 10 SDK · 初期用 Mock AI Server(词袋 hash embedding); 后期接入内网 LMStudio(text-embedding-nomic-embed-text-v1.5 · ornith-1.0-35b-mtplx · BGE-reranker-v2-m3) 跑真实推理。最终成功状态: Agent 端到端问答通 —— MVR 医药智能助手基于 KB 检索 + Reranker 精排 + ornith 35B 推理, 回答"阿托伐他汀严重副作用"精准命中横纹肌溶解 + CYP3A4 药物相互作用,附引用来源。

返回主报告 · 附录一 · 团队与功能地图 →
附录二 · 实测记录 · 生成于 2026-07-10