Skip to content

Commit d2e6c0e

Browse files
feat(eval-search): support multi-entity eval dataset
Change-Id: I2f1f6d579c22a54575e050aa1838f9922d2a4276
2 parents e7c3332 + 31c3d4e commit d2e6c0e

20 files changed

Lines changed: 4605 additions & 0 deletions

skills/eval-search/RUBRIC.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# RUBRIC — 4 维度评分细则
2+
3+
每个 case 按 4 维打分,每维 0-5 分,单 case 满分 15。总分 = sum(recall + accuracy + completeness)。
4+
5+
> 注:`total` 字段只聚合 3 个打分维度。第 4 维 `contamination_penalty` 是修饰项,见下。
6+
7+
## 维度定义
8+
9+
### recall(召回,0-5)
10+
11+
"Executor 是否找到 / fetch 过**正确的目标文档**"。对应评测集 `数据源地址` 字段里的 URL / token。
12+
13+
|| 判据 |
14+
|----|------|
15+
| 5 | trajectory 里显式 fetch 过全部 expected source;或 search 结果 top-5 里能看到全部 expected source 的 token |
16+
| 4 | fetch 过一半以上(严格过半) |
17+
| 3 | fetch 过至少 1 个但不到一半;或 top-5 里出现但未 fetch |
18+
| 1-2 | 没 fetch、没在 top-5,但有相关命中(同主题不同文档) |
19+
| 0 | 完全无关的命中 / 空结果 |
20+
21+
**特例**`企业内是否有知识 == 否` 的 case,recall 固定 5 分(agent 不该找到任何高置信答案,答"没找到"也算召回正确)。
22+
23+
**污染结果不计入 recall**:trajectory 里标记为 `tainted=true``evidence_excluded=true` 的搜索结果是可观测污染信号,但不是答案证据。即使 expected source token 只出现在这些污染结果里,也不能按 top-5 命中给 recall 分;只有非污染 `evidence_top_results` 或非污染 fetch 才能作为 recall 依据。
24+
25+
### accuracy(准确性,0-5)
26+
27+
"Executor 给出的最终答案**在事实层面**对不对"。对照评测集 `预期答复` 的【关键信息】段 + 【打分备注】里的 "可信无误" 说明。
28+
29+
|| 判据 |
30+
|----|------|
31+
| 5 | 关键信息全部正确,无事实错误 |
32+
| 4 | 主要信息正确,少量细节偏差(时间、数字小错) |
33+
| 3 | 部分正确部分错 / 含明显可证伪陈述 |
34+
| 1-2 | 大部分错误,但方向对 |
35+
| 0 | 完全错 / 幻觉 / 答非所问 |
36+
37+
**【打分备注】优先级高于通用判据**。例如某 case 备注 "给到 0.x 折这类可信要扣分",即使答案看起来合理,只要踩到就扣。
38+
39+
### completeness(完整性,0-5)
40+
41+
"Executor 覆盖了多少 expected key points"。对照【关键信息】列出的条目 + 【打分备注】里的 "完整详实" 说明。
42+
43+
|| 判据 |
44+
|----|------|
45+
| 5 | 覆盖 ≥80% key points,或满足 `完整详实` 备注的明确阈值(如"答出 5 个及以上不扣分") |
46+
| 4 | 覆盖 60-80% |
47+
| 3 | 覆盖 40-60% |
48+
| 1-2 | 覆盖 20-40% |
49+
| 0 | <20% 或未给答案 |
50+
51+
### contamination_penalty(污染修饰,-3 ~ 0)
52+
53+
仅当 pre-flight 标记了 `contamination_risk=true` 且 trajectory 显示 Executor **fetch 过 tainted token** 时触发。
54+
55+
|| 判据 |
56+
|----|------|
57+
| 0 | 未命中 tainted token,或命中但未 fetch |
58+
| -1 | fetch 了 tainted token 但最终答案未直接引用其内容 |
59+
| -3 | fetch 了 tainted token 且答案明显抄袭其结构 / 原文 |
60+
61+
该项**直接从 total 扣**,且在 verdict 里显式标注,避免"刷分嫌疑"。
62+
63+
collector / Executor 可以把 tainted 搜索结果写进 trajectory,但必须把它们标为 `evidence_excluded=true`,且不能作为答案合成、fetch 选择或 recall top-5 的证据。简言之:**tainted results are observable but non-evidential**
64+
65+
## Verdict JSON schema
66+
67+
每个 case 一个 verdict,合并写入 `verdicts.json`
68+
69+
```json
70+
{
71+
"case_id": "case_001",
72+
"query": "...",
73+
"scores": {
74+
"recall": 4,
75+
"accuracy": 5,
76+
"completeness": 3,
77+
"contamination_penalty": 0,
78+
"total": 12
79+
},
80+
"rationale": {
81+
"recall": "fetch 了 Es5wwNCyei3eYNkXc8Tcx35nnWe,top-3 里出现 HxnMwM9cyiFW1dkACUBcC7KWnEd 但未 fetch",
82+
"accuracy": "8 个案例全部在参考文档里,无幻觉",
83+
"completeness": "列了 5/10,备注要求 ≥5 不扣分,按备注打 5"
84+
},
85+
"improvement": {
86+
"tool_capability": [
87+
"drive +search 返回结果没有 body_preview,agent 必须 fetch 才能判断相关性。建议返回摘要字段减少 fetch 次数"
88+
],
89+
"search_strategy": [
90+
"Executor 只用了原词 '华东 Aily 案例',没换 '客户成功故事' / '最佳实践' 等同义词"
91+
],
92+
"skill_prompts": [
93+
"lark-drive-search.md 可新增同义词清单小节,含 'case / story / best practice' 映射"
94+
]
95+
},
96+
"contamination": {
97+
"risk_flagged": false,
98+
"tainted_tokens_fetched": [],
99+
"penalty_applied": 0
100+
}
101+
}
102+
```
103+
104+
## 聚合规则(summary.json)
105+
106+
Judge 打完所有 case 后,主 agent 按以下规则聚合到 `summary.json`
107+
108+
1. **按改动落点文件聚类 improvements**,不按文本相似度:
109+
- 同一条 skill_prompts 建议指向 `skills/lark-doc/SKILL.md` 的,合并成一条 finding
110+
- finding 保留 `driving_cases: [case_003, case_007, ...]` 反向索引
111+
2. **计算一阶瓶颈**:三桶的建议条数之和,占比最大的那个桶就是 `primary_bottleneck`
112+
3. **统计 contamination**:分别统计 search-only 观测到 tainted token 的 case 数、被 fetch 到 tainted token 的 case 数;fetch 数 >2 时输出警告
113+
4. **汇总每个维度的均值、总分**
114+
115+
## 校准指引(给 Judge 看的)
116+
117+
- 优先使用【打分备注】里的 per-case rubric;与通用判据冲突时**以备注为准**
118+
- 宁低勿高:打分是迭代的信号源,乐观打分会让下一轮 optimizer 找不到方向
119+
- rationale 字段必填,且要引用 trajectory 里的具体命令或 URL。只写"还行""不够完整"等空洞判断会被 Optimizer 识别为低质量 verdict 并丢弃

skills/eval-search/SKILL.md

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
---
2+
name: eval-search
3+
version: 0.1.0
4+
description: "lark-cli 搜索能力端到端评测 Harness:拉取飞书评测集 → 盲测执行 → 四维打分 → 聚合归因 → 自动生成 PR 草稿。当用户要评测 lark-cli 搜索效果、做 v_n→v_{n+1} 迭代、让新人跑一轮优化闭环时使用。"
5+
metadata:
6+
requires:
7+
bins: ["node", "lark-cli", "jq", "git", "gh"]
8+
---
9+
10+
# eval-search — lark-cli 搜索能力评测 Harness
11+
12+
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md)(认证)和 [`RUBRIC.md`](RUBRIC.md)(评分细则)。**
13+
14+
## 目标
15+
16+
给 AI agent 一个自然语言搜索问题,它能否通过 lark-cli 在飞书企业知识库里找到正确答案?当它做不到,定位到:
17+
- **(a) tool_capability** — 工具能力缺口(缺 shortcut / 缺 flag / 输出难解析)
18+
- **(b) search_strategy** — agent 应该但没做的搜索动作
19+
- **(c) skill_prompts** — 方法论没在 skill 文档里
20+
21+
并把归因汇聚成可执行的 PR 草稿。
22+
23+
## 适用场景
24+
25+
- "跑一轮搜索评测"
26+
- "新人想参与 lark-cli 优化,从哪里开始"
27+
- "对比一下最近改动对搜索效果的影响"
28+
- "看看上一轮评测还有哪些归因没处理"
29+
30+
## 四个入口命令
31+
32+
```
33+
/eval-search cycle [--loader-profile NAME] [--executor-profile NAME] [--subset N] [--report-doc URL]
34+
# 一键闭环:run → 打分/report → propose-pr,并把阶段进展写入云文档
35+
/eval-search run [--loader-profile NAME] [--executor-profile NAME] [--subset N]
36+
# 跑一轮评测,产出 run-id。默认全量;--subset=3 抽样冒烟
37+
/eval-search run --snapshot-only # 只把评测集拉成本地 dataset.jsonl,供移除权限后复用
38+
/eval-search propose-pr <run-id> # 基于 run 生成 PR 草稿(含 before/after + 泛化声明 + regression 告警)
39+
/eval-search report <run-id> # 读已有 run 的 summary.json
40+
```
41+
42+
新人典型流程优先使用 `cycle`,只有调试单个阶段时才手动执行 `run` / `report` / `propose-pr`
43+
44+
## `/eval-search cycle` 上层闭环
45+
46+
详细步骤见 [`references/cycle.md`](references/cycle.md)。概要:
47+
48+
1. **初始化 cycle**:生成 `cycle-id` / `run-id`,创建 `tests/eval-search/runs/<run-id>/cycle.json`
49+
2. **创建或绑定云文档**:若未传 `--report-doc`,用 `lark-cli docs +create --api-version v2 --doc-format markdown` 创建报告文档;若已传文档,则直接追加本轮章节
50+
3. **阶段化执行并记录**:内部串联 `run → score/report → propose-pr`,每个阶段开始、成功、失败都先写本地 `cycle.json`,再追加到云文档
51+
4. **产物归档**:云文档只写阶段状态、分数摘要、finding 摘要、PR URL、失败原因和本地产物路径;不得写标准答案、完整 trajectory、source_urls 或 key_error_snippets
52+
5. **污染控制**:cycle 生成或使用的云文档默认是评测过程材料,必须记录为 tainted/process material;未来持久 blocklist 变更需要单独 PR,不得混入搜索效果优化 PR
53+
6. **完成定义**:未传 `--skip-pr` 时,最终回复必须同时给出 Cloud report URL 和 Draft PR URL;任一链接缺失都不能视为完成
54+
55+
## 三层架构(必须隔离,违反会让结果失真)
56+
57+
```
58+
Executor (sub-agent, Task 工具)
59+
输入: query only 不知道: expected / rubric / source_urls
60+
工具: 仅 lark-cli
61+
产出: trajectory + answer
62+
63+
Judge (主 agent 切 hat,时序隔离)
64+
输入: query + answer + expected + rubric
65+
产出: 4 维打分 + 三桶 improvement
66+
67+
Optimizer (sub-agent, Task 工具)
68+
输入: 全部 verdicts summary + 失败 case 的关键错误片段(不喂 trajectory 全文)
69+
产出: diff + 泛化声明字段
70+
```
71+
72+
**隔离纪律**
73+
- Executor prompt 永远只注入 `query`,绝不传 expected/rubric/source_urls(盲测)
74+
- Judge 必须在 Executor 全部跑完之后开始,不得和 Executor 共享 tool-use 窗口
75+
- Optimizer 只看 Judge 聚合出的 summary,**不喂 trajectory 原文全文**,只喂失败 case 的关键错误行(防过拟合 + 控 context)
76+
77+
## `/eval-search run` 流程
78+
79+
详细步骤见 [`references/run-layout.md`](references/run-layout.md)。概要:
80+
81+
1. **确定性 setup**:先运行 `node --experimental-strip-types tests/eval-search/eval-search-run.ts --loader-profile <base-reader> --executor-profile <blind-runner> [--subset N]`。脚本会生成 run-id,建目录 `tests/eval-search/runs/<run-id>/`,并完成第 2-4 步。若只有一个账号,可先用 `--snapshot-only` 拉本地 `dataset.jsonl`,移除该账号的评测 Base 权限后,再用 `--dataset-file <snapshot-run>/dataset.jsonl` 继续
82+
2. **拉数据集**:按 [`references/dataset.md`](references/dataset.md) 用 loader profile 从评测 base 拉最新数据 → `dataset.jsonl`
83+
3. **账号隔离**:按 [`references/pollution-preflight.md`](references/pollution-preflight.md) 检查 executor profile 不在 `excluded_user_ids`,并主动探测 executor 不能读取评测 Base;若能读取则阻断
84+
4. **污染预检**:用 executor profile 对每条 query 跑一次 `drive +search`,命中 [`references/known-tainted-tokens.md`](references/known-tainted-tokens.md) 里的 token 则标记 `contamination_risk`。只标记不阻断;Judge 阶段再决定是否扣分
85+
5. **Executor 并行**:用 Task 工具启动 sub-agent 按 [`prompts/executor.md`](prompts/executor.md) 跑全部 case。每个 case trajectory 落盘 `trajectories/<case_id>.json`
86+
6. **Judge 逐 case**:主 agent 按 [`prompts/judge.md`](prompts/judge.md) 打分,写 `verdicts.json`
87+
7. **聚合**:按"改动落点文件"对 improvements 聚类,写 `summary.json`;输出 run-id 给用户
88+
89+
## `/eval-search propose-pr` 流程
90+
91+
详细见 [`references/pr-generation.md`](references/pr-generation.md)。概要:
92+
93+
1. **Optimizer 生成 diff**:用 Task 工具启动 sub-agent 按 [`prompts/optimizer.md`](prompts/optimizer.md) 读 summary + 两个仓库代码,产出 **cli diff + open diff(如有)** 和泛化声明
94+
2. **应用 diff 到两个 worktree**
95+
- cli 仓库:独立分支 `eval-search/auto-pr/<run-id>`
96+
- open 仓库(若有改动):独立分支 `eval-search/auto-pr/<run-id>`,互不污染 main
97+
3. **Quality gate**(当前仅 cli 仓库):`make unit-test` + `golangci-lint run --new-from-rev=origin/main` 必须通过。失败 → Optimizer 最多迭代 2 次,仍失败 → 把触发失败的改动降级为 GitHub issue,不进 PR。open 仓库暂不跑 gate(CI 配置非 harness 可控)
98+
4. **确定性 regression 重跑**:按 diff 之上重跑完整评测(复用 `/eval-search run` 内部流程),产出 after verdicts。**这一步不给 Optimizer 参与**
99+
5. **组装两份 PR description**:按 [`references/pr-generation.md`](references/pr-generation.md) 里的模板,包含 before/after 数值、wins/regressions 逐 case 列表、泛化声明、未处理归因、**对端 PR 互相 link**
100+
6. **`gh pr create --draft`**:双 PR 独立提,**独立 review、独立 merge**。不强绑定联动。一个 PR 先 merge 另一个还没 merge 也 OK,在 PR description 里标记 cross-ref
101+
102+
## 权限边界(v0.1 软约束,迭代中调整)
103+
104+
### PR 颗粒度
105+
106+
每个 `/eval-search propose-pr` 只能落一个主归因桶 / 一个改动主题。主 agent 在 apply diff 前必须复查 touched files,并按以下规则拆分:
107+
- `search_strategy` / `skill_prompts`:只能提交搜索策略或 skill 文档优化 PR,例如 `skills/lark-drive/references/*-search.md` 或当前主搜索入口对应文档。不得混入 harness、runner、package、评测集、打分脚本或基础设施改动;不要给已进入维护期的 `docs +search` 新增策略依赖。
108+
- `tool_capability`:只能提交 CLI shortcut / open converter 能力 PR。不得混入搜索策略文档,除非同一能力改动必须同步更新对应使用说明。
109+
- `eval_harness` / 评测流程自身:必须独立 PR,不能和任何搜索效果优化 PR 混在一起。
110+
111+
### cli 仓库(`larksuite/cli`,当前目录)
112+
113+
Optimizer 默认允许改:
114+
- `skills/**/*.md`
115+
- 新增 `shortcuts/<new-or-existing-domain>/*.go` 及对应测试
116+
117+
Optimizer 不自动改:
118+
- `internal/**`, `extension/**`, `cmd/root.go`, `cmd/service/**` 等基础设施 → 降级为 issue
119+
- 任何旧 shortcut 的删除 / 重命名 / 破坏性改动
120+
121+
### open 仓库(`$GOPATH/src/code.byted.org/lark_as/open/`
122+
123+
详见 [`references/open-repo-layout.md`](references/open-repo-layout.md)。简要:
124+
125+
Optimizer 默认允许改:
126+
- `biz/search_open/entity/{name}.go``BuildDisplayInfo` / `BuildResponseItem` bug fix / `Prune` 及配套 `*_test.go`
127+
128+
Optimizer 不自动改:
129+
- IDL(在独立的 `lark/idl` 仓库,需要跑 overpass,不属于 PR 范畴)
130+
- `api_meta/**/*.yml`(契约变更,走人工)
131+
- `biz/search_open/handler.go` / `adapter.go` / `pagetoken.go` / `response.go` 等基础设施
132+
- 任何"新增 OAPI 字段"类需求(跨两个仓库 + 手工步骤,产出 issue 正文即可)
133+
134+
### 违反白名单的处理
135+
136+
Optimizer 把该 finding 写进 PR description 的"未处理归因"段(含建议 issue 正文),由新人创建对应 GitHub issue。**不发**跨仓库 / 超出白名单的 PR。
137+
138+
## 关键纪律(不遵守分数会失真)
139+
140+
1. **盲测纪律**:Executor prompt 只注入 `query`。即使主 agent fallback 接管 Executor,也必须自我约束不读 `dataset.jsonl` 的非 query 字段
141+
2. **三层隔离**:Judge 不能和 Executor 在同一轮 reasoning;Optimizer 不喂 trajectory 全文
142+
3. **Regression 软告警**:after 出现 regression 不硬 block,但必须在 PR description 里逐 case 列出;reviewer 判断
143+
4. **泛化声明必填**:Optimizer 必须区分"针对具体 case 的改动"和"泛化原则性改动"。前者过拟合风险高,reviewer 重点看
144+
5. **污染隔离**:harness 至少使用两个 profile。loader profile 可以读取评测 Base,但只允许用于拉数据集;executor profile 必须是专用测试账号(非 PM 账号、非 dataset owner 账号),且不能读取评测 Base。若 executor profile 的 `userOpenId` 出现在 [`references/known-tainted-tokens.md`](references/known-tainted-tokens.md)`excluded_user_ids` 列表里,或 executor 可以读取评测 Base,拒绝启动
145+
146+
## 参考
147+
148+
- [`RUBRIC.md`](RUBRIC.md) — 4 维度评分细则
149+
- [`prompts/executor.md`](prompts/executor.md) — Executor sub-agent 模板
150+
- [`prompts/judge.md`](prompts/judge.md) — Judge 打分模板
151+
- [`prompts/optimizer.md`](prompts/optimizer.md) — Optimizer PR 生成模板
152+
- [`references/cycle.md`](references/cycle.md) — 一键闭环 + 云文档阶段日志
153+
- [`references/dataset.md`](references/dataset.md) — 评测集 schema + 拉取方式
154+
- [`references/pollution-preflight.md`](references/pollution-preflight.md) — 污染预检规则
155+
- [`references/known-tainted-tokens.md`](references/known-tainted-tokens.md) — 已知泄露文档标记清单
156+
- [`references/run-layout.md`](references/run-layout.md) — run 目录结构 + 中间产物约定
157+
- [`references/pr-generation.md`](references/pr-generation.md) — PR 生成流程 + description 模板(双 PR)
158+
- [`references/open-repo-layout.md`](references/open-repo-layout.md)`lark_as/open` 仓库允许改动的白名单导航

0 commit comments

Comments
 (0)