Files
life-echo/api/docs/internal-eval.md
Kevin 309a051038 feat: 回忆录证据血缘与内部评测可追溯,顺带对齐本地评测台与 CI
数据库与模型:新增多版迁移(章节证据快照、对话血缘、记忆事实/时间线 lineage 等),把「成稿 ↔ 对话/记忆」的溯源信息落到表结构里。
业务链路:会话与 WS、回忆录/故事流水线、记忆写入与 enrichment 等跟着接上线索与快照;新增章节证据快照与评测侧 EvalTraceService 等模块,方便组评审用的证据包。
内部评测:自动化 run 与手工 memoir 评审共用可追溯证据;rubric/ judge 相关脚本与文档有配套调整。
app-eval-web:Memoir/实验详情里能展开看证据摘要与 evidence_trace(含对话轮次 id);Vite 代理与 development.sh 注入的 API 端口与当前默认内部评测端口一致,避免改端口后页面连错服务。
工程杂项:GitHub Actions / 仓库说明有更新;各适配器与支付/配额/plan 等多处为小改动或跟随主改动的收尾;新增/扩充了?
2026-04-08 15:37:09 +08:00

120 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 内部回归评测平台
与主 API`app/main.py`)隔离进程部署,避免评测候选链路透出给消费者 App。
## 启动
**推荐一条命令**`internal-eval.sh` 实际调用 `development.sh`,在同一进程树里启动主站 `main:app`**8000**)、**一份** Celery、内部评测 `internal_app`(默认 **8001**)以及 `app-eval-web`(默认 **5174**)。不需要再并行执行两份启动脚本。
| | 单一命令 `./internal-eval.sh` |
|---|-------------------------------|
| HTTP | 主站 **8000** + internal **8001** |
| Celery | 仅 **一个** worker与主站共用队列 |
| 前端 | 默认启动 `app-eval-web``START_EVAL_WEB=0` 可关) |
**主站 + Celery 已在其他终端**`./development.sh` 跑起来了,只在同一台机器上多开评测 HTTP 与前端、且 **不再起第二份 Worker**
```bash
cd api
# 确保 .env.development / .env 含 INTERNAL_EVAL_API_KEY:8000 已被主站监听
SKIP_INFRA=1 SKIP_INSTALL=1 EVAL_ATTACH_ONLY=1 ./internal-eval.sh
```
兼容旧写法:`SKIP_CELERY=1` 会映射为 `EVAL_ATTACH_ONLY=1`(仍要求 **8000 已在监听**)。
仅主业务、不要评测台时照旧:`./development.sh`(不设置 `LIFE_ECHO_WITH_INTERNAL_EVAL`)。
若你只需要 **8001**、刻意不启主站 **8000**,请用下文「手动 uvicorn」配合既有 Celery不要用 `./internal-eval.sh`(一键脚本会顺带拉起主站)。
**默认会起 `app-eval-web`,并用 Vite `--open` 尝试打开浏览器**`http://127.0.0.1:5174/`)。不要前端时设 `START_EVAL_WEB=0`;只要前端但不要弹窗时设 `OPEN_EVAL_WEB=0`
数据库与主服务共用;需配置环境变量后启动专用进程:
```bash
cd api
export INTERNAL_EVAL_API_KEY='your-long-random-secret'
export INTERNAL_EVAL_ENABLE_DOCS=1 # 可选,开 /docs
# GLM-5 评审(默认复用智谱 key也可单独配置
export EVAL_JUDGE_API_KEY='...' # 可选,默认 ZHIPU_API_KEY
export EVAL_JUDGE_MODEL='glm-5' # 与 Settings 默认一致
uv run uvicorn app.internal_main:internal_app --host 0.0.0.0 --port 8001
```
Celery worker 需已包含 `app.tasks.evaluation_tasks`(仓库 `celery_app.include` 已注册)。跑实验前:
```bash
uv run celery -A app.tasks.celery_app worker -l info
```
## 前端(`app-eval-web`
```bash
cd app-eval-web
npm install
VITE_EVAL_API_BASE=http://127.0.0.1:8001 VITE_EVAL_API_KEY=与上同 npm run dev
```
或使用仓库根目录 `npm run eval-web`(需本地已 `npm install``app-eval-web`)。
## SSE / EventSource
浏览器 `EventSource` 无法带自定义 Header流式端点支持 **query** `?key=`,与 `X-Internal-Eval-Key` 等效。
## 评测 Web两大模块
- **对话评测**:选 `api/tests/user_exports/*.md` 为基准 →「新建评测会话」或填写已有 `conversation_id` →「执行回放」→「GLM-5 评审对话」。
- **回忆录章节**:同一套 fixture 会带上导出 MD 中的 `source_user_id``memoir_sections`;「刷新库中章节/故事」拉 DB 快照 →「GLM-5 评审章节」(基线节选与当前成稿一并送评)。
## 真实链路透传回放(与 App 一致)
| 方法 | 路径 | 说明 |
|------|------|------|
| `POST` | `/internal/api/evaluation/sessions/eval-sandbox` | 无 body新建**临时用户**`eval_` 伪手机号)+ 空白 `conversation_id` |
| `POST` | `/internal/api/evaluation/sessions/replay-bootstrap` | body`{ "user_id" }`,在已有用户下返回新 `conversation_id` |
| `POST` | `/internal/api/evaluation/replay/conversation` | body`conversation_id``fixture_filename` **或** `user_utterances`;可选 `flush_memoir_after`(默认 true`skip_tts`(默认 true。响应增加 `segment_ids`(本批创建的用户 segment顺序与处理一致 |
| `GET` | `/internal/api/evaluation/sessions/{conversation_id}/memoir-phase1-ready` | query`segment_ids` 可重复(`?segment_ids=id1&segment_ids=id2`)。当所列 segment 均已写入 `topic_category`Phase1 完成)时返回 `ready: true` |
每轮等价于 WebSocket 文本路径:`create_user_segment``process_user_message`(内部可 `force_skip_tts`)→ `background_runner.queue_message`
- **TTS**:回放默认 `skip_tts: true`,不在评测台跑语音合成。
- **Memory / 回忆录管线**`queue_message` 与末尾 `flush_pending` 依赖 **Celery worker**`process_memoir_phase1` 等);仅起 internal API 未起 worker 时,对话会落库但章节异步不会推进。
- **app-eval-web Playground**:默认在**每轮**回放后轮询 `memoir-phase1-ready`,再发送下一轮;单轮等待默认最长 **10 分钟**(环境变量 `VITE_MEMOIR_PHASE1_WAIT_MAX_MS` 覆盖)。需 worker 正常消费任务。可通过「等待 Phase1」勾选关闭以做快速冒烟。中断或 Phase1 超时会将进度写入浏览器 localStorage可在同一基线下用「继续未完成重放」接续同一 `conversation_id`(含先补完未就绪的 segment
## 手动 GLM-5不写 `eval_runs` 表)
| 方法 | 路径 | 说明 |
|------|------|------|
| `POST` | `/internal/api/evaluation/judge/conversation` | body`{ "conversation_id" }`,返回轮次分 + 全文对话分 |
| `POST` | `/internal/api/evaluation/judge/memoir-chapters` | body`{ "user_id", "baseline_sections"? }`Chapter/Story 分项 |
| `GET` | `/internal/api/evaluation/users/{user_id}/memoir-snapshot` | 只读章节与故事正文快照 |
## 回忆录评审可追溯证据闭包lineage
**产品与 tier 口径strict / partial / fallback、synthetic vs library 分表、PM 对齐规则、backlog** 见同目录 **[traceable-memoir-lineage.md](./traceable-memoir-lineage.md)**。
手动 `/judge/memoir-chapters` 与自动化 `eval_runs.judge_bundle_json` 已按 **artifact 绑定证据** 组 prompt而不再默认拼接「最近 N 个会话全文」:
- **`lineage_tier`**`strict` / `partial` / `fallback`(章节:**有可解析 transcript 链 + 结构化记忆为 strict****仅有结构化记忆、无绑定 segment/transcript = partial**,与标注口径一致)。故事侧以 `StoryEvidenceLink` 与章节推导为主;`fallback` = 显式降级最近会话 transcript避免静默当 strict。
- **`evidence_trace`**bundle 完整 JSONsegment / conversation / chunk / fact / timeline / summary、`notes` 等)。内审计一般够用;若需按类型深链 UI 再排期。
- **`format_meta`**`truncated``dropped_sections``included_token_estimate`区分「prompt 裁掉」与「库中无 lineage」。
- **生产侧**:叙事流水线在每次 Story 写入后覆盖 `story_evidence_links`,并在当前 `story_versions.prompt_meta.memoir_retrieval` 写入本轮检索到的稳定 id`story_pipeline_sync._persist_story_lineage_sync`)。
- **章节快照 Phase C**`chapter_evidence_snapshots` + `chapter_evidence_links``chapters.current_evidence_snapshot_id` 指向当前版本;`evidence_bundle_json` 仍为镜像。评测读取顺序:表快照 → JSON → 现场 `source_segments`(不一致时 `notes` 提示)。刷新见 `memoir/chapter_evidence_snapshot.py`。历史库可选 `uv run python scripts/backfill_chapter_evidence_snapshots.py`(旧数据不强制)。
- **对话 memory tracePhase 八)**:访谈路由下,`conversation_messages.memory_retrieval_trace_json` 在配对 **AI** 消息上写入本轮 `HybridRetriever` 命中的 chunk/fact/timeline/summary/story 等 id`memory/retrieval_trace.py`)。
历史数据可无 link评测仍可用 partial/fallback 跑通;可选离线 backfill 须在 job 中显式打标,不冒充 strict。
## Fixture 详情扩展
`GET /internal/api/evaluation/fixtures/user-exports/{filename}` 在原有 `turns` 外增加:
- `source_user_id`:导出抬头中的 User ID
- `memoir_sections``## 回忆录章节(生成正文)` 下按标题切分的基线正文(已去掉 `{{IMAGE:...}}` 占位)
## 门禁规则v1
- 所有 case 的合成均分:候选须 **严格高于** 基线。
- `is_protected=true` 的 case合成份跌幅不得超过 `EVAL_GATE_PROTECTED_REGRESSION_THRESHOLD`(默认 2 分)。
结果写入 `eval_gate_verdicts`,不影响 `git`;后续可接 pre-commit / CI。