外观
简历素材与 STAR 故事
本文档为简历编写和面试叙事提供结构化素材。 所有数据均来自项目真实评测和代码(详见 04、10),非估算。
一、项目一句话描述
30 字版
设计并实现企业级 Agentic RAG + MCP 知识问答系统,支持多格式文档智能检索与自反思纠错。
50 字版
独立设计并实现 Agentic RAG + MCP 知识问答系统(Java 全栈),涵盖 Supervisor-Worker 多 Agent 架构、混合检索、Cross-Encoder 重排、Self-Reflection 条件重写,经 52 条离线评测验证。
100 字版
独立设计并实现面向研发团队的 Agentic RAG + MCP 知识问答系统。后端 Spring Boot 3.4 + Spring AI(143 文件),前端 Vue 3 + TypeScript(35 文件)。核心架构为 Supervisor-Worker 多 Agent,6 步流程(Query Understanding → Scope Routing → Path Decision → Supervisor-Worker Retrieval → Prompt Assembly → LLM + Self-Reflection)。实现双层范畴路由(规则+LLM)、规则引擎工具选择(<1ms)、CRAG 三档检索评分、混合检索 RRF 融合、Cross-Encoder 重排、HyDE、Self-Reflection 条件重写。Langfuse OTel 全链路追踪。52 条离线评测量化每层增量价值。MCP 协议暴露 5 工具给 IDE。
English (50 words)
Designed and built an enterprise Agentic RAG + MCP knowledge Q&A system (Java full-stack). Features Supervisor-Worker multi-agent architecture, hybrid retrieval (Vector + BM25 + RRF), Cross-Encoder reranking, self-reflection with conditional rewrite, and a 52-query offline evaluation framework. Exposed tools via MCP protocol for IDE integration.
二、技术亮点 Bullet Points(简历用)
每条格式:动词 + 技术 + 量化结果,可直接复制到简历。
设计 Supervisor-Worker 多 Agent 架构,将 1400 行单体 Agent 拆分为 16 个职责单一的文件(Supervisor + 4 Worker + 状态机),新增 Worker 仅需实现接口 + 注册,改动文件数从 ≥3 降为 1
实现 Vector + BM25 混合检索 + RRF 加权融合,Recall@5 从 0.673 提升至 0.788(+0.115),事实类查询 Recall@5 从 0.733 提升至 0.933(+0.200,BM25 贡献)
集成 Cross-Encoder 语义重排(DashScope gte-rerank),MRR 从 0.594 提升至 0.731(+0.137),核心价值为将相关文档推至 top-1 位置,利用 LLM 注意力分布优势
设计 Self-Reflection 条件重写机制,反思评分不通过时注入具体 issues 触发主模型流式重写,忠实度 +0.037(0.812→0.849),8 条幻觉/遗漏被修正,三层过滤使 85% 查询零额外成本
搭建 52 条 × 4 档对照的离线评测框架,覆盖 7 类查询(事实/操作/对比/推理/对抗/超范围/多跳),产出 Recall@5、MRR、忠实度、相关性等 11 维指标报告,单次评测成本 ¥18.4
实现 Markdown-Aware 双层切块 + 增量索引,表格/代码块原子保留不切碎,SHA-256 content_hash 实现增量更新(100 页文档改 1 字仅需 ~1 次 embedding,原需 200-400 次)
集成 MinerU 多模态文档解析,统一 PDF/PPT/图片/网页为 Markdown 后入 RAG,下游检索链路零行改动;按格式分级降级(PDF 降级 PDFBox,PPT/图片 fail-fast 不静默)
实现自适应检索参数(QueryProfiler),Complexity × Specificity 二维矩阵映射 8 套检索预设,精确查询 BM25 权重 0.7、语义查询向量权重 0.6,纯规则零 LLM 成本
通过 MCP 标准协议暴露 5 个工具 / 6 端点(doc_search / keyword_search / web_search / recall_memory / store_memory / kb_meta),同一套工具代码同时服务内部 Agent 循环和外部 IDE 插件(Cursor),零代码重复
实现链路成本优化,双模型策略(主回答 qwen-plus / 决策类 qwen-turbo)+ 反思高分短路 + 语义缓存,单查询成本从 ¥0.012 降至 ¥0.005-0.007(-50%)
基于真实 bug 调研业界做法补上路由分层(Phase 5),起点是用户输入"总结上面的对话"被错误送入向量检索召回 12 条无关切片。调研 12 个生产级框架(LangGraph / Bedrock / Perplexity / LinkedIn 等)+ 3 篇论文(CRAG / Self-RAG / Adaptive-RAG)后,加四道闸:规则快路径 + 模型范畴判定 + CRAG 风格三档评估 + 自纠错切题度检查。元对话路径完全跳过检索,灰区仲裁用 reranker 单对打分(50ms)替代小模型生成(300ms)
实现 PathDecision 规则引擎替代 LLM 路由(Phase 5),工具选择从 LLM Function Calling(~300ms、不确定、不可测试)改为 RetrievalPlanner 纯规则引擎(<1ms、100% 确定性、单元测试可断言),PathDecision record 统一路径决策输出 + reason 字段写入 trace 可追溯
集成 Langfuse OTel 全链路追踪(Phase 6),Agent 7 步执行(understanding → routing → retrieval → reranking → grading → generation → reflection)通过 OpenTelemetry 协议上报 Langfuse,TracedOp 消除 span 样板代码,RootNameFilteringSpanProcessor 白名单过滤 Spring 框架噪音,ChatModelObservationFilter 自动采集 LLM prompt/completion/token 用量
三、量化成果总表
| 指标 | 数值 | 对比基线 | 来源 |
|---|---|---|---|
| Recall@5 提升 | +0.115(0.673→0.788) | V1 朴素 → V2 RRF 融合 | 评测 52 条 × 3 轮 |
| 事实类 Recall@5 | +0.200(0.733→0.933) | BM25 对事实类的贡献 | 评测分类切片 |
| MRR 提升 | +0.310(0.421→0.731) | V1 朴素 → V3 +重排 | 评测全链路 |
| 忠实度提升 | +0.037(0.812→0.849) | V3 → V4 条件重写 | 评测复测 |
| 相关性提升 | +0.027(0.834→0.861) | V3 → V4 条件重写 | 评测复测 |
| 被重写查询忠实度 | +0.230(0.510→0.740) | 8 条被修正查询平均 | 评测明细 |
| 对抗查询拒答率 | 100%(5/5) | 提示词注入变体 | 评测 |
| 单查询成本降低 | -50%(¥0.012→¥0.006) | 双模型+短路+缓存 | 成本估算 |
| 缓存命中率提升 | +50%(30%→45%) | 阈值 3→2 | 线上观测 |
| 增量索引效率 | -99%(200次→1次 embedding) | 100 页改 1 字场景 | 设计分析 |
| 路径决策延迟 | <1ms(vs LLM ~300ms) | RetrievalPlanner 规则引擎替代 LLM | Phase 5 |
| 对比类 decompose 触发率 | 100%(vs 60%) | 确定性后处理兜底 LLM 漏判 | Phase 5 |
| 代码规模 | 143 Java + 35 前端文件 | - | 源码统计 |
| 核心 RAG 组件 | 29 个文件 | - | wc -l |
| 优化迭代数 | 20 次,每次有评测数据 | - | 迭代记录 |
| 评测数据集 | 52 条 × 7 类 × 4 档 × 3 轮 | - | 评测框架 |
| 评测报告 | 624 行 Markdown | - | 评测产物 |
四、STAR 故事(8 则核心 + 3 则补充)
STAR-1:Self-Reflection 从 noop 到条件重写(最推荐讲述)
Situation:搭建离线评测框架后,跑出一个反直觉结论——V3(+重排)和 V4(+反思)在忠实度、相关性上数值完全相同(保留 3 位小数也一样)。Self-Reflection 贡献了 956ms 延迟(占总耗时 29%),对答案质量的贡献是零。
Task:必须解决这个问题——面试中讲"Self-Reflection 提升答案质量"却拿不出数据,会被直接击穿。
Action:
- 抽取 5 个样例的完整执行记录,确认反思模块确实只输出了置信度评分,不返回修改后的答案
- 定位根因:流式模式下 token 已经逐个 flush 给前端,反思在流式完成后执行,"发现问题但改不了"
- 设计 post-stream conditional rewrite 方案:流式完成后,小模型做四维评分;不通过时,将审查发现的具体 issues(如"编造了不存在的三阶段降级策略""Milvus HNSW 参数描述与来源不一致")注入 rewrite prompt,主模型重新流式生成
- 成本控制设计三层过滤:高分短路跳过 65%、评分通过跳过 50%、仅 15% 查询触发重写
- 前端新增 3 个 SSE 事件(reflection_start / reflection_token / reflection_done),实现答案无缝替换
Result:
- 忠实度 +0.037(0.812→0.849),V4 首次与 V3 拉开可量化差距
- 8 条被重写查询的忠实度平均从 0.510 提升到 0.740(+0.230)
- 修正了具体问题:Milvus HNSW 参数错误、编造不存在策略、遗漏关键步骤、幻觉修正("运行 11 个月"→"知识库中未找到")
- 成本增量仅 ¥0.004/查询,因为三层过滤使 85% 查询零额外成本
面试追问预判:
- "为什么不在生成前做反思?" → 流式体验和质量的权衡;前置反思需要先完整生成再评估,延迟更高
- "15% 的重写率够高吗?" → 这恰好说明系统在大多数情况下首次生成质量已经足够好;重写集中在中等置信度区间(rerank 0.5-0.7),正好是最需要修正的场景
STAR-2:BM25 索引未刷新导致 V2 < V1(评测暴露运维缺口)
Situation:首轮评测跑出反常数据——加入 BM25 混合检索的 V2 在事实类上 Recall@5 = 0.667,低于纯向量检索 V1 的 0.733。加了组件反而变差。
Task:定位这个反常数据的根因,确认是算法问题还是工程问题。
Action:
- 第一步怀疑 RRF 公式实现有误,写单元测试用教科书例子验证 → 公式正确
- 第二步单独调用 BM25Retriever 检索 "text-embedding-v3 维度" → 返回 0 条结果
- 第三步检查 Lucene 索引文件时间戳 → 最后修改 2026-04-12,比知识库最近一次入库(2026-04-29)早了 17 天
- 定位根因:迭代 #9 通过 MinerU 重新入库时,
processDocument()调用了MilvusService.insertChunks()但遗漏了BM25Retriever.rebuildIndex(),切片进了 Milvus 但没进 Lucene - 修复后全量重建索引,重跑评测 → V2 事实类 Recall@5 = 0.933
Result:
- 发现并修复了一个真实的生产级 bug(文档入库后 BM25 不可用)
- 如果没有评测,这个问题要等用户反馈"最近上传的内容搜不到"才能暴露
- 最终 RRF 融合在事实类上贡献了 +0.200 的 Recall 提升(0.733→0.933)
核心叙事:评测的价值不只是产出数字,更是暴露运维缺口——光为了让评测正确运行就发现了一个真实 bug。
STAR-3:从单体 Agent 到 Supervisor-Worker 重构
Situation:DocMindAgent 膨胀至 ~1400 行,检索编排决策和执行逻辑耦合在一个类中。新增检索策略(HyDE、多文档对比)需要修改核心类,且单次 ReAct 循环没有置信度反馈机制。
Task:重构为可扩展的多 Agent 架构,不破坏现有 SSE 事件协议和 API 签名。
Action:
- 设计 Supervisor-Worker 两层拓扑:SupervisorAgent 做编排决策(3 路策略:迭代 ReAct / Plan-and-Execute / 子问题拆解),Worker 做执行
- 定义统一 Worker 接口(execute + name),4 个 Worker 实现(Retrieval/Web/Memory/Analysis)
- IterationDecider 基于置信度轨迹做终止判断(够好就停 + 停滞检测 + 策略切换)
- PlanExecutor 按 dependsOn 做拓扑排序,同层步骤 CompletableFuture 并行
- ObservationEvaluator 产出 5 种精确降级动作(空召回→Web、低分→HyDE、冲突→Analysis)
Result:
- DocMindAgent 从 ~1400 行重构(当前 ~1510 行,含 Phase 5/6 新增的范畴路由 + 路径决策)
- 新增 16 个文件,每个 80-300 行,职责单一
- 新增 Worker 仅需 1 个文件(实现接口 + 注册),原来需要改 ≥3 个核心文件
- 新增 3 种执行策略(迭代/计划/拆解)和置信度反馈机制
- SSE 事件协议、API 签名完全不变,前端无感知
STAR-4:fail-silent → fail-fast(随机向量 bug)
Situation:Review 代码时发现 VectorRetriever.embed() 在 Embedding API 失败时返回 1024 维随机向量,用这个随机向量去 Milvus 检索会返回无意义的垃圾结果,但下游完全不知道——它们带着正常的相似度分数混入 RRF 融合和最终 Prompt。
Task:消除这个 fail-silent 反模式,建立正确的降级语义。
Action:将 catch 块从"返回随机向量"改为"抛出 RuntimeException",异常向上传播到 retrieve() 被外层 catch 捕获,返回空列表。空列表进入 RRF 融合不污染结果,系统自然降级为只用 BM25。
Result:
- Embedding 正常时行为不变
- Embedding 异常时向量检索被跳过,只用 BM25 结果,答案质量下降但不会出现幻觉来源
- 建立了项目的核心原则:宁可少返回结果,不返回错误的结果
STAR-5:双模型策略 + 热切换
Situation:链路成本估算显示单查询 ¥0.012,主回答占 50%、Self-Reflection 占 30%、工具选择占 15%。但反思和工具选择都是轻决策类任务,用主模型是浪费。
Task:在不引入额外运维复杂度的前提下降低成本。
Action:
- 不维护两套 ChatModel 实例,复用主模型的 OpenAiApi 连接
- 通过
OpenAiChatOptions.builder().model("qwen-turbo").build()实现 per-call 模型覆盖 - AtomicReference 实现 LLM 模型热替换,旧引用被正在进行的 SSE 流持有,自然完成后 GC 回收
- 反思高分短路(rerank top-1 ≥ 0.85 跳过),覆盖 70% 查询
- 缓存阈值从 3 调到 2,命中率从 30% 提升到 45%
Result:
- 单查询成本从 ¥0.012 降至 ¥0.005-0.007(-50%)
- 每项优化有独立的 admin 开关,可运行时回退
- 小模型 function calling 失败时自动降级到规则路由
STAR-6:Markdown-Aware 切块 + 增量索引
Situation:MinerU 输出了结构化 Markdown(带 heading、表格、代码块),但 TextChunker 还在按 \n\n 朴素分段——表格被切碎、代码块被英文句号 . 切碎、heading 层级丢失(DB 里 chapter 字段一直是空字符串)。同时文档更新只能整库重建,100 页 PDF 改 1 字要重新 embedding 200-400 次。
Task:重写切块器保留结构化语义,同时实现增量索引。
Action:
- 重写为 block-aware 装配器:逐行扫描识别 5 种 block(HEADING/TABLE/CODE/IMAGE/PARAGRAPH)
- 表格和代码块原子保留不切碎,即使超过 600 字目标值——结构完整性优先于大小均匀性
- heading 切换强制 flush buffer 防止跨章节误合并(这个 bug 被自己写的单测抓出来)
- SHA-256 content_hash 做增量 diff:同 hash → 复用 vector_id 跳过 embedding,新 hash → 才 embedding
- 打通 MySQL ↔ Milvus 的 vector_id 对齐(原来两端 ID 断开,只能整库 wipe)
Result:
- 表格完整保留、代码不被切碎、chapter 字段有值("第一章 > 第一节 > 概念定义")
- 100 页文档改 1 字:embedding 从 200-400 次降到 ~1 次
- 9 个单测覆盖各种结构化场景
补充 STAR-7:离线评测框架搭建
核心叙事:面试中"数据呢?"这个问题的根本解法。搭建 52 条 × 4 档对照 × 3 轮的评测框架,不复用 DocMindAgent(避免缓存/早停/短路污染对照),组件拆开按 variant 重新组合。最大发现不是数字,而是推翻了 3 个"我以为有用"的假设。
补充 STAR-8:MinerU 多模态文档统一入库
核心叙事:用一个外部依赖解决整个"多模态文档入库"问题。关键洞察是 MinerU 把 PDF/PPT/图片/HTML 统一输出 Markdown,下游 RAG 链路零行改动。降级按格式分级:PDF 降级 PDFBox(次优可用),PPT/图片 fail-fast(Java 生态无等价替代,静默降级比报错更危险)。
补充 STAR-9:对抗查询 100% 拒答的"虚假安全感"
核心叙事:5/5 拒答看起来完美,但查执行记录发现功劳属于 qwen-plus 的安全对齐训练,不是 SafetyGuard 拦截的。换安全对齐弱的模型可能 0/5。评测的价值不只是展示好指标,更是对漂亮数字提出质疑。
STAR-10:规则引擎替代 LLM 路由——确定性 vs 智能的取舍
Situation:Phase 2 的 SupervisorAgent 使用 LLM Function Calling 决定调用哪些工具(doc_search / keyword_search / web_search / recall_memory),延迟 ~300ms,且结果不稳定——同一个查询两次可能选出不同的工具组合。路径判定逻辑散落在 DocMindAgent 和 SupervisorAgent 中,用隐式布尔开关推断路径,出了问题不知道走了哪条路。
Task:用确定性方案替代 LLM 工具选择,同时保证覆盖度——不能因为去掉 LLM 就漏选工具。
Action:
- 分析工具选择的决策空间:本质是 4 个信号(isAmbiguous / specificity / timeAware / memoryAware)到 4 个工具子集的映射,规则空间只有十几种组合
- 设计 PathDecision record 统一封装路径决策输出:3 种模式(SELECTED_DOC / DECOMPOSED / RULE_PLANNER),
reason字段记录机器可读的判定原因 - 实现 RetrievalPlanner 纯规则引擎:4 个 if 判断 <1ms,双源时效性校验(原始+改写问题同时检查时效关键词,防止改写丢失时间词)
- ambiguous 保守策略:QueryClassification 降级时 isAmbiguous=true → 强制加 keyword_search 防向量漂移
Result:
- 路径决策延迟从 ~300ms 降到 <1ms
- 100% 确定性:同一输入永远产出相同工具组合
- 可单元测试:
assertEquals(expected, planner.plan(classification)) - 可追溯:PathDecision.reason 写入 agent trace + SSE routing 事件
核心叙事:不是所有决策都适合交给 LLM。工具选择的规则空间小且变化慢,LLM 在这上面的表现不比 if-else 好,但贵 300ms + 不确定 + 不可测试。
STAR-11:CRAG 评分机制——检索质量的最后一道闸
Situation:检索召回低质量切片(rerank 顶分 0.2-0.3)时,LLM 仍然基于这些切片生成答案——因为 LLM 不知道切片质量差,它只会"尽力回答",结果大概率是基于不相关内容编造的幻觉。Self-Reflection 能事后检查事实一致性,但它检查不了"检索到的内容跟问题不是同一主题"这种根源问题。
Task:在 generation 前加一道检索质量评估,LOW 质量时主动降级避免幻觉,AMBIGUOUS 时触发补偿检索。
Action:
- 参考 CRAG 论文(Yan et al. 2024)设计三档评分:HIGH(≥0.65 直接生成)/ AMBIGUOUS(灰区仲裁)/ LOW(≤0.25 降级 prompt)
- 灰区仲裁选 heuristic 模式(默认):利用 rerank 阶段已产出的 per-chunk 分数做分布分析——top1 强且与 top2 差距 ≥0.15 → HIGH;avg ≤0.30 → LOW;其余 AMBIGUOUS。0ms 延迟,零额外调用
- cross_encoder 模式作为可选:把 top-3 切片拼成上下文让 reranker 单对打分,~50ms,比论文里的小模型仲裁(~300ms)快 6 倍
- AMBIGUOUS + 未尝试 web → 触发 WebWorker 补偿 → 补充后重新评分
- 全部阈值放入 sys_ai_config 热配表(grader.high_threshold / grader.low_threshold / grader.arbitration_mode)
Result:
- LOW 质量切片不再进入 generation,直接走降级 prompt 明确告知"证据不足"
- AMBIGUOUS 触发 Web Search 补偿,覆盖知识库盲区
- 灰区阈值(0.65/0.25)是在 Langfuse trace 里看 rerank 分数分布后调出来的——Phase 6 可观测性的直接收益
- 前端 grader 事件展示三色徽章,用户一眼可见检索质量
核心叙事:CRAG 解决的是"检索到了但不相关"的问题——Self-Reflection 检查答案质量,CRAG 检查检索质量,两者在链路中的位置不同、解决的问题不同,是互补关系不是替代。
STAR-12:从一个真实 bug 到调研业界做法补上路由分层(推荐讲述)
Situation:一次正常使用中遇到 bug。先做了一段普通的知识问答,再问"总结上面的对话"。系统的实际处理是:
- 把"总结"、"上面"、"对话"当成检索关键词去 Milvus 查询,召回 12 条与对话内容毫无关系的切片
- 自纠错判断置信度 65% 不达标,触发重写
- 重写仍然基于同一批跑题切片,置信度降到 0%
- 0% 置信度的回答还是被推送到前端
Task:定位根因后修掉这个 bug。但更重要的是判断这是个孤立 bug,还是设计上的结构性缺陷——后续是否还有类似问题(比如"你刚才说了什么"、"翻译你的回答"等)。
Action:
第一步是确认结构性缺陷。读代码发现意图分类只有一层(factoid / procedural / comparison / opinion / chitchat),五个取值都默认要去查知识库。"总结上面的对话"被归入 factoid 或 chitchat,但下游不区分意图,照常调用 RetrievalWorker。自纠错只检查事实一致性,无法识别"检索到的内容跟问题完全不是一个主题"。所以这不是孤立 bug。
第二步是调研业界做法。看了 12 个生产级框架/产品和 3 篇核心论文:
- LangGraph Adaptive RAG 教程:用结构化输出做条件路由 + 检索后再加一道闸
- AWS Bedrock Agents:6 个内置提示词模板,Pre-processing 是独立阶段
- Perplexity 工程博客:把追问先重写为独立查询,引用历史对话的请求在重写阶段就识别
- LinkedIn 工程博客:路由 → 检索 → 生成三段式,路由用小模型决定是否在范围内
- CRAG 论文(Yan 2024):检索后用独立评估器分三档
- Self-RAG 论文(Asai 2023):在词表加反思 token 让模型自己决定是否检索(需要微调,未采用)
被弃用的方案也很明确:纯靠模型函数调用决定是否检索,LinkedIn / Airbnb v1→v2 / Microsoft Semantic Kernel 都从这个路径迁出。
第三步是设计与实现:
- 加一层范畴判定区分四种情况:要查知识库、要看历史对话、闲聊、超出范围
- 判定走两级——先用正则识别高置信度模式(命中即跳过模型调用),不命中再走模型
- 元对话路径完全不进检索流程,只基于历史对话生成回答
- 检索完成后加 CRAG 风格三档评估:rerank 顶分 ≥ 0.65 直接 HIGH,≤ 0.25 LOW,灰区做仲裁
- 仲裁的实现选择上没用论文里的小模型评估器,而是把 top-3 切片拼成上下文让 reranker 重新单对打分。一次调用 50ms,是模型生成的 1/5 时延
第四步是工程优化。范畴判定原本设计成独立组件方便排错,跑通后做了第二轮——把它合并进 QueryUnderstanding 的同一次模型调用,只在提示词里加了一个 scope 字段。知识查询路径的路由调用次数从 2 次降到 1 次,省 ~250ms。要排错时一个开关切回拆分模式即可。
整套改造严格遵循一个原则:每一道闸都做了降级路径,任何一道判错或调用失败都会回到原来的流程,不会比改造前更糟。
Result:
- 检索越界 bug 修复,复现率从 100% 降到 0
- 元对话路径调用 Milvus / BM25 / reranker 次数从各 1 次降到 0
- 知识查询路径路由调用次数从 2 次降到 1 次
- 灰区仲裁延迟从 ~300ms 降到 ~50ms
- 新增 23 条单元测试覆盖(11 条规则路径 + 12 条三档评估各种模式)
- 整个改动 ~825 行,不动 SupervisorAgent / Worker 现有逻辑、前端零改动可向后兼容
为什么推荐讲这个 STAR:
- 真实:起点是一个能复现的 bug,不是"我想做这个"
- 方法论:bug → 判断是否结构性 → 调研业界 → 决策 → 实现 → 二次优化,链条完整
- 取舍:能讲清楚为什么没用 Self-RAG(要微调)、为什么没用纯函数调用路由(业界已弃)、为什么仲裁不用小模型而用 reranker(成本/延迟权衡)
- 避坑意识:先做独立组件再合并,便于排错;每一道闸都做降级,不比改造前更糟
五、项目时间线
2026-04 初 项目启动,基础 RAG + Web UI(Spring Boot + Vue 3)
│
├── 迭代 #1 fail-silent → fail-fast(随机向量 bug)
├── 迭代 #2 Langfuse OTel 可观测性集成
├── 迭代 #3 Self-Reflection 置信度标记
├── 迭代 #4 Query Decomposition 多跳检索
├── 迭代 #5 QueryProfiler 自适应检索参数(8 套预设)
├── 迭代 #6 双执行路径合并(删 1100 行 dead code)
├── 迭代 #7 成本优化 Tier 1(双模型+短路+缓存,-50%)
├── 迭代 #8 MinerU 多模态文档统一入库
├── 迭代 #9 Markdown-Aware Chunker + 增量索引
│
2026-05-02 离线评测框架搭建 + 首轮评测
│
├── 迭代 #10 离线评测框架(52 条 × 4 档 × 3 轮)
├── 迭代 #11 Self-Reflection 条件重写(忠实度 +0.037)
├── 迭代 #12 Phase 1: 扩大候选池 + MMR + 元数据丰富化
├── 迭代 #13 Phase 2: Supervisor-Worker 多 Agent 架构
├── 迭代 #14 Phase 3: HyDE 假设文档生成
├── 迭代 #15 Phase 3: Parent Document Retrieval 双层切块
│
2026-05-06 Phase 4 完成,文档整理
│
├── 迭代 #16 Phase 5: 范畴判定层 + CRAG 三档检索置信度评估
│ (起点:用户输入"总结上面的对话"被错误送入向量检索)
├── 迭代 #17 Phase 5: PathDecision 规则引擎替代 LLM 路由
├── 迭代 #18 Phase 5: QueryUnderstanding 确定性后处理
│
2026-05-09 Phase 6 完成
│
├── 迭代 #19 Phase 6: Langfuse OTel 全链路追踪增强
├── 迭代 #20 Phase 6: 前端思考时间线重构六、技术选型对照表
| 领域 | 选了 | 放弃了 | 为什么 |
|---|---|---|---|
| 后端框架 | Spring Boot 3.4 + Spring AI 1.1.4 | LangChain4j / LangChain (Python) | Spring AI 原生支持 Function Calling + MCP Server,Java 生态成熟 |
| 向量数据库 | Milvus 2.3.4 | PgVector, Qdrant, Pinecone | Spring AI 原生支持,COSINE + 标量过滤联合查询,可扩展到亿级 |
| 全文检索 | Lucene 8.11 (SmartCN) | Elasticsearch | 内嵌式零额外部署,BM25 + 中文原生分词,项目规模不需要分布式 |
| 重排序 | DashScope gte-rerank (API) | 本地 BGE-Reranker-v2-m3 | 无 GPU 环境,API 调用简单免运维;后续可切本地部署 |
| 嵌入模型 | text-embedding-v3 (1024 维) | OpenAI ada-002, BGE-M3 | 阿里云原生 + DashScope 统一计费,中文效果好 |
| LLM | qwen-plus / qwen-turbo | GPT-4o, Claude | DashScope 统一生态 + 成本可控(双模型策略) |
| 文档解析 | MinerU 云端 API | Tabula+POI+Tess4J 拼凑 / 自部署 MinerU | 一个依赖解决 PDF/PPT/图片/网页,免 GPU,统一输出 Markdown |
| Agent 架构 | Supervisor-Worker(自建) | LangChain Plan-and-Execute | RAG 子任务同构,不需要 DAG 调度;自建轻量可控 |
| Query 拆解 | Query Decomposition | Plan-and-Execute | 学术共识:multi-hop QA 用 Decomposition,非通用 Plan-and-Execute |
| 缓存 | Redis 7(答案缓存+用户记忆) | Caffeine / Memcached | 同时承担两个角色减少组件数,支持 TTL + 跨实例共享 |
| 对象存储 | MinIO | 本地磁盘 / 阿里 OSS | S3 兼容 + 私有化部署,原件持久化 |
| 可观测性 | Langfuse + OpenTelemetry | Prometheus + Grafana | Langfuse 原生理解 LLM 概念(prompt/completion/token),Spring AI 内置 OTel 支持 |
| 前端 | Vue 3.5 + TypeScript + Element Plus | React / Ant Design | 类型安全 + 成熟 UI 组件库 + SSE 流式渲染支持好 |
七、核心技术深度(简历扩展用)
算法与公式
| 算法 | 公式 | 应用场景 |
|---|---|---|
| RRF 加权融合 | score = weight / (k + rank), k=60 | 向量+BM25 结果合并 |
| BM25 | Σ[IDF × TF(k1+1) / (TF + k1(1-b+b×dl/avgdl))], k1=1.5, b=0.75 | 关键词全文检索 |
| MMR 多样性 | MMR = λ×relevance - (1-λ)×max_sim(d, selected), λ=0.7 | 重排后多样性保证 |
| COSINE 相似度 | Milvus HNSW 索引, ef=64 | 向量近邻检索 |
| SHA-256 | content_hash 增量索引 | 文档更新 diff |
并发与线程模型
| 组件 | 并发模型 | 要点 |
|---|---|---|
| AgentToolContext | ThreadLocal + synchronizedList | 工具执行侧通道,finally 清理防泄漏 |
| SSE 流式 | DelegatingSecurityContextExecutorService | 子线程继承 Spring Security 认证信息 |
| PlanExecutor | CompletableFuture + 拓扑排序 | 同层步骤并行,跨层串行 |
| Query Decomposition | 专用线程池(core=8, max=16, CallerRunsPolicy) | IO 密集独立于 commonPool |
| ChatModel 热切换 | AtomicReference CAS | 旧引用被 SSE 流自然持有,GC 回收 |
降级策略体系
| 正常路径 | 降级路径 | 触发条件 |
|---|---|---|
| LLM Function Calling | QueryRouter 规则路由 | LLM 调用异常 |
| Cross-Encoder API | 关键词覆盖度打分 | rerank API 超时/429 |
| MinerU 云端解析 | PDFBox 文本提取 (PDF only) | MinerU 不可用 |
| 向量检索 | 跳过,只用 BM25 | embedding API 异常 |
| BM25 MySQL FULLTEXT | 应用内分词+模糊匹配 | FULLTEXT 无结果 |
| Self-Reflection LLM | 规则检查(长度/措辞) | 反思 LLM 调用异常 |
| Redis 缓存 | 跳过缓存正常执行 | Redis 连接异常 |