feat(advisor): add optimization advisor module
- rag_eval/advisor/: new package with rules engine, LLM analyzer, writer - rules.py: 7-metric diagnostic rules (warning/critical thresholds, top-3 low samples) - llm_analyzer.py: Chinese optimization report via judge_model, graceful fallback - writer.py: writes optimization_advice.md + log summary - __init__.py: run_advisor() entry point (no-op when optimization_advisor=False) - Scenario.optimization_advisor: new bool field (default False) - ScenarioModel: same field added, loader.py透传 - RunArtifactPaths.advice_md: new path field - factory.py: build_models() now public; build_metric_pipeline() accepts pre-built llm/embeddings - runner.py: lifts llm, passes to pipeline and advisor; calls run_advisor() at end - siemens online YAML: optimization_advisor: true enabled - tests: 9 rules tests + 6 writer tests, all pass - docs: advisor section added to engine-flow.md and architecture.md Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -318,6 +318,10 @@ metrics:
|
||||
- answer_relevancy
|
||||
- context_recall
|
||||
- context_precision
|
||||
# 可选:鲁棒性 / 端到端指标(需数据集含 ground_truth),完整列表见 §9.4
|
||||
# - noise_sensitivity
|
||||
# - factual_correctness
|
||||
# - semantic_similarity
|
||||
output_dir: runs/legal-assistant-offline-baseline
|
||||
runtime:
|
||||
batch_size: 4
|
||||
@@ -338,7 +342,7 @@ runtime:
|
||||
- `embedding_model`
|
||||
- 负责向量相关指标的模型
|
||||
- `metrics`
|
||||
- 本次启用的指标列表
|
||||
- 本次启用的指标列表(完整可选项与依赖见 §9.4)
|
||||
- `output_dir`
|
||||
- 本次运行结果输出目录
|
||||
- `runtime.batch_size`
|
||||
@@ -399,6 +403,32 @@ app_adapter:
|
||||
- embedding model
|
||||
- 指标实例
|
||||
|
||||
当前支持的指标(`rag_eval/metrics/registry.py` 中的 `SUPPORTED_METRICS`):
|
||||
|
||||
| 指标名 | 层面 | 依赖 |
|
||||
|---|---|---|
|
||||
| `faithfulness` | 生成 | judge model |
|
||||
| `answer_relevancy` | 生成 | judge model + embedding |
|
||||
| `context_recall` | 检索 | judge model + ground_truth |
|
||||
| `context_precision` | 检索 | judge model + ground_truth |
|
||||
| `noise_sensitivity` | 鲁棒性 | judge model + ground_truth |
|
||||
| `factual_correctness` | 端到端 | judge model + ground_truth |
|
||||
| `semantic_similarity` | 端到端 | embedding + ground_truth(无 LLM 调用) |
|
||||
|
||||
后四项以 `ground_truth`(标准答案)为参照,数据集必须提供该字段。新增指标统一在 `registry.py` / `factory.py` / `pipeline.py` 三处对齐装配。
|
||||
|
||||
**Optimization Advisor(§11 优化策略落地):**
|
||||
|
||||
评测结束后,若场景配置 `optimization_advisor: true`,则自动调用 `rag_eval/advisor/` 模块:
|
||||
- 规则引擎(`rules.py`)对 7 个指标各自设阈值,识别触发项并选取 top-3 低分样本
|
||||
- LLM 分析器(`llm_analyzer.py`)结合低分样本生成中文 Markdown 优化建议(复用 judge_model,失败自动降级为纯规则报告)
|
||||
- 写出层(`writer.py`)输出 `optimization_advice.md` 并打日志摘要
|
||||
|
||||
```yaml
|
||||
# 场景配置示例
|
||||
optimization_advisor: true
|
||||
```
|
||||
|
||||
### 9.5 并发控制
|
||||
|
||||
执行层负责并发上限,不把并发策略散落到各指标实现中。
|
||||
|
||||
@@ -316,11 +316,21 @@ adapter 层的目标是:**把不同类型的目标应用,统一成同一套
|
||||
|
||||
当前支持的指标包括:
|
||||
|
||||
核心检索 / 生成指标(始终可用):
|
||||
|
||||
- `faithfulness`
|
||||
- `answer_relevancy`
|
||||
- `context_recall`
|
||||
- `context_precision`
|
||||
|
||||
鲁棒性 / 端到端指标(架构设计 §10.2,需数据集含 `ground_truth`):
|
||||
|
||||
- `noise_sensitivity` —— 鲁棒性:对检索噪声的敏感度
|
||||
- `factual_correctness` —— 端到端:回答相对标准答案的事实正确性
|
||||
- `semantic_similarity` —— 端到端:回答与标准答案的语义相似度(基于 embedding,无 LLM 调用)
|
||||
|
||||
所有指标都通过同一套装配点接入:`registry.py`(校验白名单)、`factory.py`(实例化)、`pipeline.py`(`ascore` 入参分发),新增指标只需在这三处对齐即可。
|
||||
|
||||
所以 metric pipeline 的职责可以总结为:
|
||||
|
||||
**把标准样本转换成结构化评分结果。**
|
||||
@@ -414,3 +424,39 @@ main.py
|
||||
- 可以把每次实验的资产稳定留住
|
||||
|
||||
这也是它和一次性离线脚本的根本区别。
|
||||
|
||||
---
|
||||
|
||||
## 15. Optimization Advisor 链路
|
||||
|
||||
相关代码:
|
||||
|
||||
- `rag_eval/advisor/__init__.py` — 外部入口 `run_advisor()`
|
||||
- `rag_eval/advisor/rules.py` — 规则引擎(纯函数,无 LLM),7 条指标诊断规则
|
||||
- `rag_eval/advisor/llm_analyzer.py` — LLM 分析器(复用 judge_model llm 实例,失败自动降级)
|
||||
- `rag_eval/advisor/writer.py` — 写出 `optimization_advice.md` + 日志摘要
|
||||
|
||||
Advisor 在 `write_run_artifacts()` 之后触发,仅当场景配置 `optimization_advisor: true` 时生效,默认关闭。
|
||||
|
||||
执行链路:
|
||||
|
||||
```text
|
||||
run_advisor(result, scenario, llm)
|
||||
-> rules.diagnose(score_rows, metrics) # 识别异常指标,选取 top-3 低分样本
|
||||
-> llm_analyzer.analyze(diagnoses, llm) # LLM 生成中文建议(失败自动降级为纯规则报告)
|
||||
-> writer.write_advice(...) # 写 optimization_advice.md + 日志摘要
|
||||
```
|
||||
|
||||
输出产物追加在现有 run 目录:
|
||||
|
||||
```text
|
||||
outputs/online/siemens-pdf-question-bank/<run_id>/
|
||||
scenario.snapshot.yaml
|
||||
scores.csv
|
||||
invalid.csv
|
||||
summary.md
|
||||
metadata.json
|
||||
optimization_advice.md <- 新增(optimization_advisor: true 时生成)
|
||||
```
|
||||
|
||||
规则引擎对 7 个指标各自设 warning / critical 双档阈值,`noise_sensitivity` 为"越低越好"(方向相反)。所有诊断均附带 top-3 低分样本,喂给 LLM 生成针对具体内容的中文建议。
|
||||
|
||||
1378
docs/superpowers/plans/2026-06-16-optimization-advisor.md
Normal file
1378
docs/superpowers/plans/2026-06-16-optimization-advisor.md
Normal file
File diff suppressed because it is too large
Load Diff
225
docs/superpowers/specs/2026-06-16-optimization-advisor-design.md
Normal file
225
docs/superpowers/specs/2026-06-16-optimization-advisor-design.md
Normal file
@@ -0,0 +1,225 @@
|
||||
# 优化顾问模块设计 Spec
|
||||
|
||||
- 日期:2026-06-16
|
||||
- 状态:已确认,进入实现。
|
||||
|
||||
## 1. 目标
|
||||
|
||||
在现有 RAG 评测流程结束后,新增一个**优化顾问模块**(Optimization Advisor),根据本次评测的多项指标分数与低分样本,自动诊断指标偏低的原因并给出针对性的优化建议,输出为中文 Markdown 报告 + 日志摘要。
|
||||
|
||||
对应架构设计 §11(优化策略):将"指标到动作的映射"(§11.2)从文档形式落地为代码自动执行。
|
||||
|
||||
---
|
||||
|
||||
## 2. 决策摘要
|
||||
|
||||
| 决策点 | 选择 |
|
||||
|---|---|
|
||||
| 输出形式 | `optimization_advice.md`(文件)+ 控制台/日志摘要(双输出) |
|
||||
| 生成机制 | 规则引擎定位异常指标 → LLM 结合低分样本二次解读(两层) |
|
||||
| 触发方式 | YAML 场景文件显式声明 `optimization_advisor: true`,默认关闭 |
|
||||
| LLM 实例 | 复用 `build_models()` 已创建的 `llm` 实例,不重建 client |
|
||||
| 包位置 | `rag_eval/advisor/`(独立包,对外暴露 `run_advisor()` 单一入口) |
|
||||
|
||||
---
|
||||
|
||||
## 3. 架构
|
||||
|
||||
### 3.1 执行链路
|
||||
|
||||
```
|
||||
run_scenario()
|
||||
→ load_scenario() # 读 YAML,解析 optimization_advisor 字段
|
||||
→ build_models() # 已有:创建 llm, embeddings
|
||||
→ build_metric_pipeline() # 已有
|
||||
→ Evaluator.evaluate() # 已有:打分 → EvaluationResult
|
||||
→ write_run_artifacts() # 已有:scores.csv / summary.md / ...
|
||||
→ run_advisor( # 新增(3 行)
|
||||
result, scenario, llm, artifact_paths
|
||||
)
|
||||
→ rules.diagnose(score_rows) # 规则引擎:返回 Diagnosis 列表
|
||||
→ llm_analyzer.analyze(diags, samples) # LLM:生成中文 Markdown 建议
|
||||
→ writer.write(advice, paths) # 写文件 + 打日志
|
||||
```
|
||||
|
||||
### 3.2 新增文件
|
||||
|
||||
```
|
||||
rag_eval/advisor/
|
||||
__init__.py ← 暴露 run_advisor(),外部唯一入口
|
||||
rules.py ← 纯函数规则引擎,无 LLM,可单独单测
|
||||
llm_analyzer.py ← 接收 llm 实例 + 诊断结构 → 中文 Markdown
|
||||
writer.py ← 写 optimization_advice.md,打日志摘要
|
||||
```
|
||||
|
||||
### 3.3 修改文件(最小改动)
|
||||
|
||||
| 文件 | 改动 |
|
||||
|---|---|
|
||||
| `rag_eval/shared/models.py` | `Scenario` 加 `optimization_advisor: bool = False` 字段 |
|
||||
| `rag_eval/config/schema.py` | `ScenarioModel` 加同名字段 + 透传到 `Scenario` |
|
||||
| `rag_eval/config/loader.py` | 透传 `optimization_advisor` 到 `Scenario` 构造 |
|
||||
| `rag_eval/reporting/artifacts.py` | `RunArtifactPaths` 加 `advice_md: Path` 字段 + `build_artifact_paths()` 加赋值 |
|
||||
| `rag_eval/execution/runner.py` | `run_scenario()` 末尾:`build_models` 返回 llm 传入,条件调用 `run_advisor()` |
|
||||
|
||||
### 3.4 输出产物
|
||||
|
||||
```
|
||||
outputs/online/siemens-pdf-question-bank/<run_id>/
|
||||
scenario.snapshot.yaml
|
||||
scores.csv
|
||||
invalid.csv
|
||||
summary.md
|
||||
metadata.json
|
||||
optimization_advice.md ← 新增(optimization_advisor: true 时生成)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 规则引擎(rules.py)
|
||||
|
||||
### 4.1 数据结构
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Diagnosis:
|
||||
metric: str # 指标名
|
||||
mean_score: float # 本次均值
|
||||
threshold: float # 警戒阈值
|
||||
severity: str # "warning" | "critical"
|
||||
root_causes: list[str] # 可能原因(来自架构设计 §11.2)
|
||||
suggested_actions: list[str] # 对应可调阶段
|
||||
low_samples: list[dict] # 分数最低的 N 条样本(含 question/answer/ground_truth)
|
||||
```
|
||||
|
||||
### 4.2 七条指标诊断规则
|
||||
|
||||
阈值参考 RAG 评测最佳实践,分 warning / critical 两档:
|
||||
|
||||
| 指标 | warning | critical | 根因方向 | 对应优化阶段(§11.2) |
|
||||
|---|---|---|---|---|
|
||||
| `faithfulness` | < 0.7 | < 0.5 | 生成未严格基于检索片段 / 幻觉 | 生成 prompt grounding、开启校验 |
|
||||
| `answer_relevancy` | < 0.7 | < 0.5 | 回答偏离问题 / 格式冗余 | 查询改写、生成 prompt 格式 |
|
||||
| `context_recall` | < 0.7 | < 0.5 | 检索遗漏关键信息 | 多查询、问题分解、Step-back、加大过召回 |
|
||||
| `context_precision` | < 0.6 | < 0.4 | 检索引入过多噪声 / 排序差 | 后检索重排、压缩、相关性过滤 |
|
||||
| `noise_sensitivity` | > 0.3 | > 0.5 | 回答被噪声片段干扰(越低越好) | 后检索相关性过滤、重排 |
|
||||
| `factual_correctness` | < 0.6 | < 0.4 | 回答事实与标准答案偏差大 | 检索与生成综合优化 |
|
||||
| `semantic_similarity` | < 0.7 | < 0.5 | 回答语义与标准答案差距大 | 生成 prompt、检索质量 |
|
||||
|
||||
> 注:`noise_sensitivity` 越低越好(0=完全不受噪声影响),其阈值方向与其余相反。
|
||||
|
||||
### 4.3 低分样本选取
|
||||
|
||||
每个触发诊断的指标,取该指标分数最低的 **top-3** 样本(排除 NaN)附入 `Diagnosis.low_samples`,字段包含 `sample_id / question / answer / ground_truth / <metric_score>`。
|
||||
|
||||
---
|
||||
|
||||
## 5. LLM 分析器(llm_analyzer.py)
|
||||
|
||||
### 5.1 输入
|
||||
|
||||
- `diagnoses: list[Diagnosis]` — 规则引擎输出(仅触发阈值的指标)
|
||||
- `llm` — 已有 RAGAS LLM 实例(scenario 的 judge_model)
|
||||
- `scenario_name: str` — 用于报告标题
|
||||
|
||||
### 5.2 Prompt 设计
|
||||
|
||||
使用**一次 LLM 调用**,把所有触发诊断的指标和低分样本一起发送:
|
||||
|
||||
```
|
||||
你是一个 RAG 系统优化专家,正在分析西门子医疗 CT 文档问答系统的评测结果。
|
||||
请用中文撰写一份优化建议报告,格式为 Markdown。
|
||||
|
||||
## 评测诊断摘要
|
||||
{for each diagnosis: 指标名、均值、阈值、可能原因、建议动作}
|
||||
|
||||
## 低分样本示例
|
||||
{for each diagnosis: top-3 低分样本的 question / answer / ground_truth}
|
||||
|
||||
## 要求
|
||||
1. 按指标分节(## 指标名),先解释"为什么低",再给出"具体怎么改"
|
||||
2. "具体怎么改"要结合低分样本的具体内容,而不只是泛泛建议
|
||||
3. 最后写一节 ## 优先优化次序,按性价比排序(参考:不增加调用次数的优先)
|
||||
4. 语言简洁,面向工程师,不要废话
|
||||
```
|
||||
|
||||
### 5.3 输出
|
||||
|
||||
LLM 返回的 Markdown 字符串,直接写入 `optimization_advice.md`(在报告头部追加运行元信息)。
|
||||
|
||||
### 5.4 失败降级
|
||||
|
||||
LLM 调用失败(超时/异常)时:降级为**纯规则报告**(只输出规则引擎的诊断结构,不含 LLM 解读),文件照常写出,错误信息写入报告末尾,不阻断整个评测流程。
|
||||
|
||||
---
|
||||
|
||||
## 6. 写出层(writer.py)
|
||||
|
||||
### 6.1 文件写出
|
||||
|
||||
`optimization_advice.md` 结构:
|
||||
|
||||
```markdown
|
||||
# 优化建议报告 — <scenario_name>
|
||||
|
||||
- run_id: `<run_id>`
|
||||
- 生成时间: `<timestamp>`
|
||||
- judge_model: `<model>`
|
||||
|
||||
---
|
||||
|
||||
<LLM 生成的 Markdown 正文>
|
||||
```
|
||||
|
||||
### 6.2 日志摘要
|
||||
|
||||
`run_advisor()` 完成后向 `logger.info` 打印一条精简摘要(单行,适合 `run_eval.bat` 结束后一眼扫到):
|
||||
|
||||
```
|
||||
[advisor] 触发诊断 3 项: faithfulness(0.42, critical) context_recall(0.58, warning) noise_sensitivity(0.41, critical)
|
||||
[advisor] 优化建议已写出: outputs/online/.../optimization_advice.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. YAML 配置
|
||||
|
||||
场景文件新增一个顶层字段:
|
||||
|
||||
```yaml
|
||||
optimization_advisor: true # 默认 false;true 时评测结束后自动生成优化建议
|
||||
```
|
||||
|
||||
后续若需精细配置(阈值覆盖、top-N 低分样本数),可扩展为:
|
||||
|
||||
```yaml
|
||||
optimization_advisor:
|
||||
enabled: true
|
||||
top_low_samples: 3 # 每个指标取几条低分样本(默认 3)
|
||||
# thresholds: # 可选:覆盖默认阈值
|
||||
# faithfulness: 0.65
|
||||
```
|
||||
|
||||
本轮实现仅支持 `optimization_advisor: true/false`,扩展接口预留但不实现。
|
||||
|
||||
---
|
||||
|
||||
## 8. 测试策略
|
||||
|
||||
| 测试 | 文件 | 说明 |
|
||||
|---|---|---|
|
||||
| 规则引擎单测 | `tests/test_advisor_rules.py` | 纯函数,无 LLM,覆盖每条规则的 warning/critical 触发、NaN 跳过、low_samples 选取 |
|
||||
| writer 单测 | `tests/test_advisor_writer.py` | mock Diagnosis 列表,验证 md 文件写出格式和日志输出 |
|
||||
| 集成(可选) | 现有 `tests/test_online_eval.py` | 验证 `optimization_advisor: true` 场景下 advice_md 存在 |
|
||||
|
||||
LLM 分析器不写单测(依赖网络),由集成场景覆盖。
|
||||
|
||||
---
|
||||
|
||||
## 9. 不覆盖(本轮边界)
|
||||
|
||||
- 不支持跨版本对比分析(只分析本次 run)
|
||||
- 不支持批量场景聚合建议
|
||||
- 不建设 Web UI 展示
|
||||
- LLM 分析器 prompt 本轮不做多语言适配(直接中文)
|
||||
- advisor 阈值本轮硬编码在 `rules.py`,不从 YAML 读取
|
||||
Reference in New Issue
Block a user