待优化清单#
📌 迁移说明(2026-06-07):本清单含早期「待优化 / 已完成」项;其中 Query Decomposition、串行多跳、Self-Reflection 相关项已于「对标主流四刀改造」移除或被 agentic 循环取代(见 04-优化迭代记录.md #22 与 20-改造实现设计.md)。多跳/拆解现改由真·LLM 工具调用 agentic 循环在运行时自驱;置信度改由结构化引用覆盖率(CitationParser)给出;CRAG 检索质量闸保留。下文历史「已完成」记录原样保留作为时间线,仅在会被误读为「当前能力」处加注。
已识别的优化方向,按优先级排列。每完成一项,移到
04-优化迭代记录.md并标记完成。
高优先级(快速见效,面试加分明显)#
H10. 离线评测框架(Naive vs Hybrid vs Rerank vs Full 对照) ✅ 已完成(2026-05-04)#
- 详见 04-优化迭代记录.md #10 与 09-评测体系建设.md
- 后续工作:
- 数据集从 10 条扩到 50+ 条(按真实 KB 内容补 expectedDocNames)
- 跑完一次完整评测后把数字回填到 #10 与 09-doc
- 加 V0(无 RAG 直答 baseline)+ V5(V4 + Query Decomposition)两档对照(注:V4=SelfReflection、V5=Query Decomposition 均已于四刀改造移除;此对照梯属四刀改造前的评测方法学,当前架构需另设对照)
H1. VectorRetriever 向量化失败降级修复 ✅ 已完成(2026-04-27)#
H2. Agent 各步骤耗时统计 ✅ 已完成(2026-04-27)#
- 升级为 Langfuse + OpenTelemetry 全链路追踪,详见 04-优化迭代记录.md #2
H3. RRF/BM25/TextChunker 核心算法单元测试#
- 问题:核心算法没有测试覆盖,边界情况(空输入、单条输入、超长输入)未验证
- 方案:参数化测试验证 RRF 融合公式、BM25 评分、文本切分逻辑
- 面试价值:展示测试驱动的工程素养
H4. Self-Reflection 置信度标记 ✅ 已完成(2026-04-27)#
- 详见 04-优化迭代记录.md #3
- 注:该能力已于四刀改造移除;置信度来源改由结构化引用覆盖率(CitationParser)给出(见 04 #22)
H5. Query Decomposition 多跳检索 ✅ 已完成(2026-04-28)#
- 复杂查询拆解为子问题并行检索,跨子问题保底分配 + 全局补齐合并
- 详见 04-优化迭代记录.md #4
- 注:自研拆解 / 串行多跳已于四刀改造移除,多焦点拆解与多跳改由 agentic 循环在运行时自驱(见 04 #22)
H6. Query-Aware Adaptive Retrieval ✅ 已完成(2026-04-28)#
- 查询感知自适应检索参数:Complexity × Specificity 二维画像 → 8 套参数预设 + RRF 加权融合
- 详见 04-优化迭代记录.md #5
H7. 双执行路径合并 + Query Decomposition 接入主路径 ✅ 已完成(2026-04-28)#
- 删除 RagPipeline 旧路径与 ChatController + Med* 实体重复映射
- Query Decomposition 升级为 DocMindAgent 主路径能力
- 详见 04-优化迭代记录.md #6
- 注:自研 Query Decomposition 主路径能力已于四刀改造移除,拆解改由 agentic 循环运行时自驱(见 04 #22)
H8. 链路成本/延迟 Tier 1 优化 ✅ 已完成(2026-04-28)#
- SelfReflection 高分短路 + 双模型策略(qwen-plus / qwen-turbo)+ 缓存阈值 3→2 + stream_complete SSE 事件
- 注:SelfReflection(含高分短路)已于四刀改造移除;双模型策略与缓存优化保留(见 04 #22)
- 预期单查询成本 -50%、p50 延迟 -25%
- 详见 04-优化迭代记录.md #7
H9. 全格式统一通过 MinerU 入库 —— PDF / PPT / 图片 / 网页 → Markdown ✅ 已完成(2026-04-28)#
- PDFBox 行交错 + 丢图丢表 + 不支持 PPT / 图片 / 网页:接 MinerU 云端 API(一个外部依赖解决多模态文档入库)
- 文件流程(PDF/PPT/图片)+ URL 流程(MinerU-HTML)共享轮询主循环,通过
stateExtractor函数抽象屏蔽 batch / task 两类响应差异 - 降级分级:PDF 失败降级 PDFBox;PPT / 图片 / URL fail-fast 不静默降级
- 双层闸门:env 静态开关 + sys_ai_config 动态开关
- 详见 04-优化迭代记录.md #8
中优先级(功能完善,体现系统性思考)#
M1. 检索结果置信度评分 ✅ 已完成(2026-05-07,Phase 5 Step 13)#
- 实际方案落点比原计划更深入:参考 CRAG 论文(Yan et al. 2024)做了三档评估器(HIGH / AMBIGUOUS / LOW)
- 不是简单的”低于阈值就标记”,而是分档触发不同后续动作:HIGH 直接生成,AMBIGUOUS 进入仲裁,LOW 让现有 fallback 接管(注:#28 起 LOW 本身不再触发兜底——有证据走 low-confidence 带证据作答,仅真·无证据才 fallback)
- #36 升级:CRAG 改为 rerank 分 × 词项覆盖率的二信号一致性裁判(不再只给 rerank 分套阈值),灰区默认仲裁
heuristic(覆盖率优先、零额外调用,从cross_encoder改默认);同时修掉「AMBIGUOUS 被强制 0-chunk 兜底丢证据」的 bug,Web 补偿改覆盖率门控(LOW 或 AMBIGUOUS 缺实体才补)并两路对齐 - 详见 04-优化迭代记录.md #16、#36
M2. Micrometer 指标监控 ✅ 已被 Langfuse 方案覆盖(Phase 6 增强 → 观测标准化 Part 1)#
- Phase 6 接入完整 OTel 集成:TracedOp 消除 span 样板代码 + ChatModelObservationFilter 采集 LLM 调用;早期靠手写 OTel SDK + RootNameFilteringSpanProcessor 白名单过滤
- 观测标准化 Part 1(已完成):删手写 OTel SDK(228 行),改用 Spring Boot 3.4 原生 OTLP tracing autoconfig + 自定义 head-based
Sampler(替代 RootNameFilteringSpanProcessor);热切换模型接ObservationRegistry→ 原生 gen_ai 埋点(删手动gen_ai.*setAttribute,激活 ChatModelObservationFilter)。详见 04-优化迭代记录.md #23 - 续修 #1 + #4 + #2(已完成):基于真实 trace 复盘——
- #1:root span 上提到
execute()顶端,找回过去被 Sampler 丢弃的 QueryUnderstanding/embedding LLM 调用(单条 trace generation 1→10)。 - #4:经 Public API 注册 DashScope 模型 ¥/token 价表(
scripts/langfuse-register-models.sh),totalCost0→非零。 - #2:降级回答标
langfuse.observation.level=WARNING;新增LangfuseScoreClient把 confidence/citation_coverage/rerank_top1/crag_grade 推成 Langfuse score(Scores API,JDK HttpClient 需强制 HTTP/1.1 绕开 Next.js 网关 EOF)。 - #3/#5/#6 经实跑核验为伪问题(当初分析基于被 strip 的 UI 导出快照;活 trace 里
rag.*属性、TTFT、llm_generationinput/output 全都在)。详见 04-优化迭代记录.md #24
- #1:root span 上提到
M6. 观测系统重构 Part 2:三套埋点统一为单一发射门面 ✅ 已完成(2026-06-08)#
- 问题:SSE 事件、
agentTrace、OTel span 三套埋点在DocMindAgent/SupervisorAgent/AgenticSearchOrchestrator里并行手写(sendSseEvent复制 3 份、addTrace/updateTrace复制 2 份),同一步要发三次、易漂移 - 方案(已实现):新增
agent/emit/StageEmitter.java发射门面——收口单一sse()/trace()/traceObs(),每个 SSE 事件 payload 形状集中定义一次,三类各处局部包装调用、旧 helper 全删。边界:OTel span 是时长(TracedOp 生命周期),仍由TracedOp收口,门面只统一 SSE+agentTrace 两类点事件。详见 04-优化迭代记录.md #25 - 验证:golden SSE 契约 diff(改前抓 6 路径基线 → 迁移后比对),主路径 9/9 全等;全量后用 global-union 契约证明逐事件零回归(per-scenario 的 cache 差异系 LLM 路由漂移 + null 值键省略的运行时非确定性)。#1/#2/#4 全部不回归
- 面试价值:「加了又删、收敛到简单可组合」延伸到可观测层;难点是 SSE 硬契约 + 用 golden diff 区分「契约漂移 vs 运行时非确定性」
M7. 观测收尾小项:environment 维度 ✅ 已完成(2026-06-08)#
- 问题:trace 的
environment恒为default,dev/prod 不分流 - 方案(已实现):
application.yml加 OTel resource 属性management.opentelemetry.resource-attributes."[deployment.environment.name]": ${LANGFUSE_TRACING_ENVIRONMENT:development}(resource 属性 → 全 span 统一归属);docker-compose.ymlbackend 注入LANGFUSE_TRACING_ENVIRONMENT=production。验证:容器 traceenvironment=production、本地 mvn 默认development - 结论:可观测路线图(Part1 + #1/#2/#4 + Part2 + #7)全部清零
M3. QueryRouter 意图识别增强 → 已被 Phase 2 SupervisorAgent 架构取代,Phase 5 进一步加上范畴判定层#
- 原方案:增强 QueryRouter 正则 or 改用 few-shot LLM 分类
- Phase 2 演进:引入
QueryClassification(intent + complexity + specificity 三维分类)+SupervisorAgent多策略编排 +RetrievalGraderCRAG 条件补偿,原 QueryRouter 的职责被拆解到更精细的组件中。详见 04-优化迭代记录.md #13 - Phase 5 补强:在三维分类之上加最外层”范畴判定层”,区分六种处理路径(知识检索 / 元对话 / 闲聊 / 超范围 / 知识库元查询 / 任务执行)。原五类 intent 都默认要查知识库,Phase 5 解决了”总结上面的对话”被错误送入检索的问题。详见 04-优化迭代记录.md #16
M5. Markdown-aware Chunker(MinerU 后续) ✅ 已完成(2026-04-30)#
- 详见 04-优化迭代记录.md #9
- 同步落地了增量索引(content_hash diff + vector_id 1:1 链接)
M4. ContextCompressor 去重粒度参数化 ✅ 基本完成(2026-06-12,见 04 #35)#
- 原问题:去重用固定的前 50 字比较,不够灵活
- 落地:
ContextCompressor已下沉进CrossEncoderReranker.compress;精确去重改用dedupeKey()(id 优先,缺失退化内容指纹,已无”前 50 字”硬编码),并新增近重复去除(包含关系 + bigram Jaccard,阈值compress.near_dup_jaccard默认 0.85、开关compress.near_dup_enabled可运行时调)。截断也升级为 query-aware 命中段居中。
低优先级(锦上添花)#
L1. MCP 工具调用统计竞态修复#
- 问题:查询+更新之间有竞态窗口
- 方案:数据库 INSERT ON DUPLICATE KEY UPDATE
L2. MemoryTool 并发写入保护(部分完成,2026-06-12 #34 ②)#
- 原问题:无乐观锁,并发写入丢失更新
- 已解决:访问计数的读-改-写竞态——SoR 下沉 MySQL 后改用 DB 原子
access_count = access_count + 1(setSql),不再读 JSON 改字段写回(旧 Redis-WATCH/版本号方案随 Redis 退为缓存而作废) - 残留(已知 LOW):并发写入相同内容仍可能各自通过去重检查、产生内容重复行(
user_memory唯一索引建在(user_id, memory_id)而非内容)。低频可忽略;若需根治可加内容 hash 唯一约束或写入串行化
L3. 超长 Query 截断处理#
- 问题:用户输入 10000 字没有限制
- 方案:入口处截断到 500 字,日志记录原始长度
L4. 文档提取编码检测#
- 问题:固定 UTF-8,旧 PDF 可能是 GBK
- 方案:使用 ICU4J 或 juniversalchardet 自动检测编码
链路成本/延迟优化 Tier 2-3(迭代 #7 后续)#
Tier 1(短路 + 双模型 + 缓存阈值)已落地。下面是延伸方向,按 ROI 排。详见 04-优化迭代记录.md #7 末尾的 Tier 2-3 列表。
T2-1. 拆解路径 Reranker 批量化(中 ROI)#
- 问题:3 子问题各调 1 次 rerank,3× 网络往返
- 方案:合并候选集 → 1 次 rerank(query 用 originalQuery)→ SubQueryMerger 用合并分数
- 预期:拆解路径 rerank API 调用 -67%
T2-2. Prompt 模板瘦身(中 ROI)#
- 问题:主回答 input ~4000 tokens 里模板 + 元信息占 ~1000+
- 方案:审
src/main/resources/prompts/,删冗余前言、用更紧凑的 YAML 风格 - 预期:主回答 input -15%
T2-3. 历史上下文摘要(中 ROI)#
- 问题:
buildConversationHistory取近 6 条原始拼接,长会话膨胀 - 方案:超过 N 条时把更早内容用小模型摘要为 1 段
- 预期:长会话主回答 input -20-40%
T2-4. WebSearch 提前并行启动(低 ROI)#
- 问题:Tavily 1-3s 阻塞主流程
- 方案:QueryProfiler 标
timeAware=true时 Decomp 阶段就异步启动,最后 join
T2-5. Reranker 候选数自适应下调(低 ROI)#
- 问题:当前固定 25-40 候选,简单查询过度
- 方案:
QueryProfiler.RetrievalParams增rerankCandidates字段
T2-6. DashScope Context Cache(Prompt Cache)接入(中-高 ROI)#
概念区分:语义答案缓存 vs Prompt Cache#
项目当前已有的 SemanticCacheService 不是 Prompt Cache,二者是不同层的优化,可叠加使用。
| 维度 | 语义答案缓存(已有) | Prompt Cache(待接入) |
|---|---|---|
| 缓存对象 | 完整 RAG 答案 + sources | LLM 输入侧的稳定前缀(系统提示、工具 schema) |
| 命中条件 | query embedding cosine ≥ 0.92 且 KB 版本一致 | 输入前缀逐字节相同(provider 侧 KV cache) |
| 节省的成本 | 整次 RAG 调用(embedding + 检索 + rerank + LLM) | 仅 LLM 输入 token 计费 + 首 token 延迟 |
| 失效粒度 | KB 任一变更全失效 | 前缀任意 token 变更即失效 |
| 实现层 | 应用层 Redis | LLM Provider(DashScope Context Cache) |
| 适用场景 | 高频重复问题 | 每次都跑的 agent 循环(工具描述、系统模板) |
关键洞察:当前命中率 ~45% 的语义缓存解决的是”重复问题”,但剩下 55% 不命中的请求每次都把 ~1000+ token 的稳定前缀(工具 schema + 系统模板)原样发给 LLM,这部分才是 Prompt Cache 的目标。
改造方案#
- 问题:每次 agent 循环都把工具 schema(~600 token)+ 系统模板(~400 token)+ 反思模板(~300 token)原样发给 qwen,前缀稳定却未复用
- 方案:
- 调用 DashScope
/services/aigc/text-generation/generation时按context-cache文档加cache: { enable: true }参数(具体字段以 DashScope 当时文档为准) - 在 PromptAssembler 把 prompt 重组为
[稳定前缀] + [动态 chunks] + [用户问题]三段式,前缀放在最前 - 工具调用路径(
ChatClient+ToolCallbackProvider)确认 Spring AI 是否透传 cache 参数;不行则降级到自建 OkHttp 客户端走原生 API AiConfigHolder加cache.prompt_cache_enabled开关,便于 A/B
- 调用 DashScope
- 预期:
- 主回答 input token 计费 -25~40%(前缀命中 50% 折扣,按 DashScope 现价)
- TTFT 延迟 -15~30%(命中前缀不重新跑 attention)
- 工具选择 / 反思的小模型路径收益更高(前缀占比更大)
- 风险:
- DashScope Context Cache 有最小前缀长度门槛(通常 ≥256 token),短模板可能不命中
- 前缀里任何动态字段(时间戳、用户 ID、随机 nonce)都会破坏命中,需审一遍模板
- 面试 talking point:
“项目里语义缓存解决的是’同一个问题不要再跑一遍’,但 agent 每次循环里工具 schema 和系统模板都是稳定的,那部分输入 token 还是每次都付钱。所以下一步是接 DashScope 的 Context Cache,把 prompt 重排成’稳定前缀 + 动态 chunks + 用户问题’三段,让 LLM provider 在 KV cache 层复用稳定前缀——和应用层语义缓存是叠加关系,一个省整次调用、一个省每次的输入 token。”