多场景评委体系 · 技术参考
本页记录多场景评委体系的工程实现细节: 配置文件结构、关键脚本用法、OP2 输出格式与验证流程。
1. 配置文件 Schema
1.1 scene.yaml
# scenes/<scene-slug>/scene.yaml
slug: <scene-slug> # 必填: 全局唯一, a-z0-9 + 连字符
scene_type: review # 必填: review | competition
# competition 场景强制: anchor_judge=null, show_scores_publicly=true
feishu:
app_id_env: LARK_APP_ID_<SLUG> # 飞书 App ID 的 env var 名
app_secret_env: LARK_APP_SECRET_<SLUG>
access:
whitelist_env: BOSS_WHITELIST_<SLUG> # 白名单 env var (可选)
rate_limit: 5 # 每小时请求上限 (可选, 默认全局 cfg)
report:
brand_prefix: <slug> # 报告品牌前缀 (用于 reports/<brand>/)
redact_review: false # 是否脱敏回推结果
anchor_judge: tian # review 场景: 锚点 slug; competition 场景: null (必须)
show_scores_publicly: false # competition 场景: true (必须)1.2 panel.yaml
# scenes/<scene-slug>/panel.yaml
extends: panels/default.yaml # 继承基础 panel (推荐); 不填则完整定义
scoring_mode: sum_max_score # 当前仅支持此模式
output_format: op2_6section # 产出形态: op2_6section (打分报告) | workshop_ranking (排名榜)
# | meeting_summary (定性会议总结 deck, 见 §2.6)
# Panel 差异覆盖 (仅需描述与 extends 的不同之处)
judges_drop:
- org-strategy # 移除不需要的维度评委
judges_add:
- slug: custom-judge
display_name_cn: 自定义评委
judge_category: dimension # anchor | dimension
skill_path: scenes/<slug>/judges/custom-judge/SKILL.md
judges_override:
- slug: tian
judge_category: anchor
skill_path: anchors/tian/perspective/SKILL.md
always_active: true
scoring_lenses_override: # competition 场景常用: 完全替换 5 镜头
- slug: biz_value
display_name_cn: 业务价值
max_score: 3
- slug: ai_depth
display_name_cn: AI 含量
max_score: 3
- slug: completeness
display_name_cn: 完成度
max_score: 2
- slug: innovation
display_name_cn: 创新性
max_score: 2Panel 继承规则:
extends最多 1 层 (超出抛PanelError)judges_drop按 slug 移除judges_add追加judges_override按 slug 覆盖字段scoring_lenses_override完全替换镜头列表
2. 关键脚本
2.1 scene_loader.py
# 列出所有已定义场景 (含 scene_type / bot_env / anchor_judge)
python3 scripts/scene_loader.py list
# 加载单个场景 (输出 JSON)
python3 scripts/scene_loader.py load <scene-slug>
# 校验所有场景的配置合法性
python3 scripts/scene_loader.py validate主要函数:
| 函数 | 用途 |
|---|---|
load_scene(slug) | 加载并校验 scene.yaml,返回 SceneConfig |
list_scenes() | 遍历 scenes/ 目录,返回所有已定义场景 |
find_scene_by_app_id(app_id) | 按飞书 App ID 反查场景 (路由用) |
validate_scene(cfg) | 校验约束: competition 必须 anchor=null 等 |
2.2 panel_loader.py
# 解析某场景的完整 panel(含 extends 合并后)
python3 scripts/panel_loader.py resolve scenes/<slug>/panel.yaml
# 计算总分上限
python3 scripts/panel_loader.py total-max <panel-path>主要函数:
| 函数 | 用途 |
|---|---|
resolve_panel(path) | 解析 panel.yaml,执行 extends 合并,返回 Panel |
total_max_score(panel) | 所有 scoring_lenses.max_score 之和 |
grade_for_score(score, panel) | 按归一百分比返回 S/A/B/C/D 等级 |
等级标准 (与总分制无关,均按百分比):
| 等级 | 百分比区间 |
|---|---|
| S | ≥ 95% |
| A | ≥ 85% |
| B | ≥ 70% |
| C | ≥ 55% |
| D | < 55% |
2.3 op2_output.py
OP2 6 段式报告渲染器 — 纯函数,无 IO,无外部依赖。
from scripts.op2_output import render_op2_6section
report_md = render_op2_6section(
panel=panel, # resolve_panel() 的返回值
scores={ # {lens_slug: score}
"biz_value": 2.5,
"ai_depth": 2.0,
...
},
comments={ # {lens_slug: "评委点评"}
"biz_value": "方案在业务价值上...",
...
},
must_fix=["…"], # 必改项列表 (<60%)
suggestions=["…"], # 补充建议列表 (60-79%)
cuts=["…"], # 删减建议列表
)6 段输出结构:
| 段落 | 内容 | 触发条件 |
|---|---|---|
| §1 总分 + 等级 | X / Y 分 · 等级 Z | 总是输出 |
| §2 分项得分 | 各维度进度条 | 总是输出 |
| §3 必改项 | 红色警告,分项得分 < 60% | 有则输出 |
| §4 补充建议 | 分项得分 60-79% | 有则输出 |
| §5 删减建议 | 可去掉的冗余内容 | 有则输出 |
| §6 一页汇总表 | 所有维度汇总 | 总是输出 |
2.4 smoke_e2e.py
全链路静态校验 — 不调 LLM,不写磁盘,纯校验。
# 校验单个场景(推荐)
make smoke-scene SCENE=<scene-slug>
# 等价命令
python3 scripts/smoke_e2e.py smoke-scene <scene-slug>
# 校验所有场景
python3 scripts/smoke_e2e.py smoke-all校验内容:
scene_loader.load_scene(slug)不抛错panel_loader.resolve_panel(panel_path)解析成功- 所有
skill_path指向的文件实际存在 - competition 场景:
anchor_judge is None+show_scores_publicly: true - review 场景:
anchor_judge非空 + skill_path 指向锚点 SKILL.md
2.5 add_scene.py
从 scenes/_template/ 克隆新场景。
# 创建 review 类型场景
python3 scripts/add_scene.py my-new-scene --type review
# 创建 competition 类型场景(自动处理 anchor=null / 公开/输出格式)
python3 scripts/add_scene.py my-new-scene --type competition
# 演习模式(不写磁盘)
python3 scripts/add_scene.py my-new-scene --type review --dry-run2.6 meeting_summary 定性总结渲染器
output_format: meeting_summary 的场景 (会议总结评审) 不走打分 run_pipeline, 而是由 review_worker 按 output_format 分派到平行的定性流水线 meeting_summary_pipeline.py (不进 Phase 0-5 打分 / 合议)。三个脚本各司其职:
| 脚本 | 职责 | IO |
|---|---|---|
meeting_summary_output.py | 纯函数: prompt 装配 / 评委产出解析 / markdown deck 渲染 | 无 IO, 已单测 |
meeting_summary_pipeline.py | 装配层: 读会议材料 → 每评委各调一次 LLM → 渲 deck → 落 report.md + meeting-summary.md + reviews/<judge>.md | LLM + 磁盘 |
meeting_summary_pptx.py | deck → 4:1 大屏 PPTX (封面 + 每评委页) | python-pptx (可选) |
# 手动跑一场会议总结 (worker 平时按 output_format 自动分派, 此命令供联调)
python3 scripts/meeting_summary_pipeline.py --doc <会议材料> --brand <brand> --scene meeting-review
# 把已有 deck 单独渲成 4:1 大屏 PPTX (读 reports/<brand>/reviews/)
python3 scripts/meeting_summary_pptx.py <brand>
# → reports/<brand>/meeting-summary.pptx- deck 每评委四块: 判断逻辑 + 3 个亮点 + 3 项不足 + 一句话评价 (与打分场景的 5 镜头无关)。
- 大屏 PPTX 为 best-effort: 缺
python-pptx只是不出 pptx, 不影响 markdown deck 与 HTML/PDF 交付。 - 交付:
report.md/ HTML / PDF 随飞书卡片回推; 大屏 PPTX 在出站脱敏关闭的内部场景下随卡一并回推 (meeting-summary.pptx), 脱敏开启的场景则留在 VM 供内部大屏取用 (§9.2 fail-close)。
3. HTTP API
boss_server (FastAPI, port 8421) 提供以下端点:
GET /v1/healthz
无需认证,始终返回 200。
{"status": "ok", "timestamp": "2026-06-26T12:00:00Z"}GET /v1/scenes
需要 Bearer token (BOSS_API_TOKEN)。返回所有已定义场景及飞书 env 就绪状态。
{
"scenes": [
{
"slug": "op2-company",
"scene_type": "review",
"panel_path": "scenes/op2-company/panel.yaml",
"feishu": {
"app_id_env": "LARK_APP_ID_OP2_COMPANY",
"env_ready": true
}
}
],
"env_ready_count": 2
}env_ready 为 true 表示对应 env var 已在运行环境中设置(不暴露实际 App ID 值)。
POST /feishu/event
飞书事件回调入口。需要 FEISHU_VERIFICATION_TOKEN。
当前为 A3 骨架(feishu_events.py 未完整实现),返回 {"status": "ignored", "reason": "事件处理器未部署 (A3)"} 作为降级兜底。
4. 系统服务与启动顺序
三个 systemd 服务,必须按顺序启动:
boss-hermes (HTTP API + 健康检查)
↓
boss-review-worker (job queue 消费,调 LLM 跑评审)
↓
boss-feishu-ws (飞书长连接,多 bot 路由)各服务对应的 systemd 模板见 templates/ 目录:
templates/boss-hermes.service.exampletemplates/boss-review-worker.service.exampletemplates/boss-feishu-ws.service.example
5. 开发流程
新增一个场景
# 1. 克隆模板
python3 scripts/add_scene.py <new-slug> --type review
# 2. 编辑 scene.yaml(填写 feishu bot env var 名称等)
# 3. 编辑 panel.yaml(选择评委组合)
# 4. 如需专属评委,在 scenes/<slug>/judges/ 下新建 SKILL.md
# 5. 静态全链路校验
make smoke-scene SCENE=<new-slug>
# 6. 跑完整测试套件
make test添加共享评委
# 1. 在 scenes/shared-judges/<judge-slug>/ 下新建 SKILL.md
# 2. 跑 skill_lint 校验
python3 scripts/skill_lint.py scenes/shared-judges/<judge-slug>/SKILL.md
# 3. 在需要该评委的 panel.yaml 中追加引用
# 4. make smoke-scene + make test6. 常见问题
Q: 场景加载时提示 PanelError: extends depth > 1 A: panel.yaml 的 extends 目标本身也有 extends。目前只支持 1 层继承。改为直接继承 panels/default.yaml。
Q: competition 场景加载报错 anchor_judge must be null A: 竞赛场景不能设置 anchor_judge,删除或设为 null。
Q: smoke-scene 通过但 bot 没响应 A: smoke 是静态校验,不测试运行时状态。检查:① .env 中对应 LARK_APP_ID_* 是否已填;② boss-feishu-ws 服务日志是否有 "WS connected";③ GET /v1/scenes 中 env_ready: true。
Q: /v1/scenes 返回 503 A: BOSS_API_TOKEN 未在 .env 配置,或未 export 到 systemd 服务环境。