docs: 补整体框架图与参数系统,交付资料检查改为整例跳过

- 结构.md 第二部分加四段整体框架图;第五部分新增「配置」:每段一份 YAML、
  pydantic 定义、只接收配置路径、编码解码开关与独立映射表
- 结构.md 第三、四部分:检查不过整例跳过,答案库存三表诊断、化名对照、跳过清单
- AGENTS.md「错误怎么处理」加交付资料单例问题的例外。来源:负责人 2026-10-16
  「都是遇到整个病例跳过,让医生核对去吧」
- 待定问题/03 补被跳过病例;WORK_LOG 记决定与下一步

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-16 01:02:16 -04:00
parent 9b2f842559
commit 9c636c0a4f
4 changed files with 79 additions and 7 deletions
+2 -1
View File
@@ -92,7 +92,7 @@ CADx-4 研究胰腺囊性肿瘤的鉴别诊断:给同一个病人的三路资
「可能会出现」不是写分支的理由。按顺序选:
1. 改数据的表示或格式要求,让它不可能出现。
2. 做不到,就在系统边界检查,不满足就报错停下。边界指交付的资料、模型的输出、负责人的配置。模块之间的调用不算边界,不互相防御。
2. 做不到,就在系统边界检查,不满足就报错停下。边界指交付的资料、模型的输出、负责人的配置。模块之间的调用不算边界,不互相防御。交付资料里只落在某一个病例上的问题是例外,见「错误怎么处理」。
一个抽象在出现第二个真实调用点之前不引入。不考虑向后兼容:不写兼容层,读到旧格式直接报错;发现更好的设计就按它重写,同步改文档和测试。
@@ -104,6 +104,7 @@ CADx-4 研究胰腺囊性肿瘤的鉴别诊断:给同一个病人的三路资
- **外部错误**(模型接口超时、网络断开):只在调用外部服务的那一处做有限次重试,仍失败就把这一步记为失败并写明状态。
- **内部错误**(资料不满足规则、不该有的状态组合):立刻停下,报错写清是哪条规则、哪个病例的哪个文件。模型交不出合格输出按内部错误处理。
- **交付资料里只落在某一个病例上的问题**(这一例的编号认不出、缺文件、取值不合规则、和别的表对不上等):跳过这一整例,接着处理下一例,并在标准答案库的跳过清单里写明这一例没过的是哪项检查、哪个文件哪一列。对一个病例,遇到第一项不过的检查就不再往下查,所以清单上每例只有一项,后面的检查没做过,不代表没问题。整张表读不了(工作表名或列名不对、文件缺失、配置不全)仍然立刻停下。跳过不是把问题藏起来:跳过清单交给医生核对,评价时要报告跳过了几例、各因为什么。来源:负责人 2026-10-16「都是遇到整个病例跳过,让医生核对去吧」。
中间层不写 try/except,除非它能把错误完整处理掉且上层不需要知道。不写 `except Exception: pass` 及其变体。
+5 -2
View File
@@ -4,7 +4,7 @@
## 当前目标
骨架已完成;整体架构已和负责人讨论定稿(`research-wiki/design/0002`、`explanation/结构.md`)。下一步是写第 2 步「资料整理(不含 CEUS)」或第 3 步「CEUS 定量」的任务书。
骨架已完成;整体架构已和负责人讨论定稿(`research-wiki/design/0002`、`explanation/结构.md`)。下一步是写第 2 步「装配两个库(不含 CEUS)」的任务书 `research-wiki/任务书/01-装配两个库.md`。
## 已完成
@@ -27,8 +27,11 @@
- 负责人 2026-10-16:调模型用 PolyGateway;两个库和中间文件都用文件夹(SQLite 只用于调模型账本);CEUS「直接一个个迁移」,不在本机重跑旧代码作基线;数据格式和口径以 40 例交付包为准,「推注开始时间s」是视频第 0 秒时已推注的秒数(design 0002)。
- 负责人 2026-10-16:先做第 2 步装配两个库;配置每段一份(`config/<段>.yaml`),编码解码是可选开关、映射表单独一份配置;交付资料检查一例有一项不过就整例跳过并记录,「都是遇到整个病例跳过,让医生核对去吧」;化名对照存进答案库。已写进 `结构.md`(框架图、第五部分「配置」)和 `AGENTS.md`「错误怎么处理」。
## 下一步
- 和负责人定先做第 2 步还是第 3 步,再写对应任务书;动手前先讲旧代码的结构与打算。
- 写任务书 01「装配两个库」,只引用 `结构.md` 和 `AGENTS.md` 的规则,不重复定义。
- 向临床方要编码映射表(性别、诊断、症状、既往史、化验档位)。
- 把 `待定问题/04`(6 例跨表不一致)转给医生。
- 等临床方交付 40 例的医生标注工作簿(含造影剂到达时间和注射时间)。
+71 -4
View File
@@ -7,6 +7,7 @@
- 负责人 2026-10-15 定:旧项目不再做成工程产品,在本仓库按研究原型重写。原话:「我们后面发现其其实不需要(至少目前不需要)是一个工程产品」(design 0001)。
- 负责人 2026-10-15 定:协作环境和文档体系参考 ClinDB-ReviewBench 与 dissect3;检查强度取 ClinDB 那一档;一个 uv 环境,MedSAM2 与 torch 装在里面,靠导入边界隔离(design 0001 第 4、7 条)。
- 负责人 2026-10-16 定:数据流是「医院原始资料->中间文件(可以有可以没有)->「给专家的资料库」(给agent的东西都在里面)&「标准答案库」(我们自己用来评估的金标准的标准答案等)」;两个库和中间文件都用文件夹,原话:「两个库和缓存都用文件夹」;调模型用实验室的 PolyGateway;CEUS 代码「直接一个个迁移」;数据格式和口径以 40 例交付包为准(design 0002)。
- 负责人 2026-10-16 定:配置「每段一份」;编码列的解码是一个可选的开关,映射表是独立的配置文件;交付资料的检查,一个病例只要有一项没过就整例跳过并记录,原话:「都是遇到整个病例跳过,让医生核对去吧」。
- 其余内容(模块名单、边界、依赖、规模、推进顺序)是 Agent 2026-10-16 的提议;规模在对应模块动手前由负责人逐个确认。
---
@@ -43,10 +44,48 @@
| 会诊 | 三个专家各读自己那一路,按会诊流程给出判断 | `library/` → `runs/` | 没有,只有三版设计文档 |
| 评价 | 会诊结论对金标准;专家抽取的字段对医生标注 | `runs/` + `answers/` → `runs/<运行号>/eval/` | 没有;写过一套评估骨架,因缺陷和无人使用而删除 |
整体框架:每段一个入口脚本、一份配置,段与段之间只通过磁盘上的目录交接。双线以下的会诊碰不到标准答案库,评价才把会诊结果和答案放在一起。
```
data/交付包_20260908_40例/ 医院原始资料(只读)
│ 视频 │ 三张表、关键图
▼ │
① CEUS 定量 │ config/ceus.yaml
scripts/ceus_quant.py │
ceus + segment(显卡) │
│ │
▼ ▼
work/ 中间文件 ─────────► ② 装配两个库 config/build.yaml
(可删掉重算) scripts/build.py config/code_maps.yaml
delivery → build
┌─────────┴──────────┐
▼ ▼
library/<构建号>/ answers/<构建号>/
给专家的资料库 标准答案库
化名、三路分开 金标准、化名与真实病例号对照、跳过清单
│ │
══════════════════════╪════════════════════╪═══ 会诊读不到右侧
▼ │
③ 会诊 scripts/consult.py │ config/consult.yaml
consult → llm(PolyGateway) │
│ │
▼ │
runs/<运行号>/ │
各路意见、会诊结论、账本 │
│ │
└─────────┬──────────┘
▼
④ 评价 scripts/evaluate.py config/evaluate.yaml
▼
runs/<运行号>/eval/
```
`data/`、`work/`、`library/`、`answers/`、`runs/` 都不进 git(第五部分)。每段的配置怎么写见第五部分「配置」。
几个贯穿全文的词:
- **给专家的资料库**(`library/`):AI 专家能看到的一切。每次装配是一个新目录,叫一次**构建**。
- **标准答案库**(`answers/`):病理诊断、化名与真实病例号的对照、跨表一致性报告,将来还有医生标注工作簿。只给评价用。
- **标准答案库**(`answers/`):病理诊断、化名与真实病例号的对照、跳过清单(没进资料库的病例及原因),将来还有医生标注工作簿。只给评价用。
- **中间文件**(`work/`):CEUS 定量的工作产物,可以删掉重算。
- **运行**:一次会诊或评价,产物放在 `runs/` 下一个新目录里,只追加不删。
- **账本**:一次会诊运行里每次调用模型的记录(请求、回复、用量、结局),存为 SQLite,只追加。
@@ -98,6 +137,7 @@
### 几条边界
- **专家能看见什么,只在 build 一处决定。** build 按白名单把每路允许的内容写进 `library/`,此外任何模块都不挑选给专家的材料。consult 只拿到某一次构建的路径,经 library 按路读出;它不依赖 delivery、answers、build,读原始资料和答案的代码对它来说根本不存在。报告的「印象 / 结论」段(医生写的诊断倾向)由 delivery 认出、由 build 剔除,`library/` 里只有「所见」描述。
- **交付资料的检查不过,整例跳过。** delivery 逐例检查(编号、文件、取值、跨表是否一致,具体检查项在装配两个库的任务书里列),build 把没过的病例整例跳过:资料库和答案库的病理诊断里都没有它,答案库的跳过清单写明它没过哪一项、涉及哪个文件哪一列。一例遇到第一项不过就不再往下查,清单上每例只记一项。整张表读不了时整次构建停下。规则见 `AGENTS.md`「错误怎么处理」。
- **三路互相隔开。** 每个专家的提示词只用自己那一路的对象拼。consult 里拼某一路提示词的函数只接收那一路的对象,不接收整个病例。这是研究设计本身:模拟各科医生各看各的检查结果,再在会诊时交换意见。会诊时一路能看到另一路的什么,由 consult 的会诊流程决定,在会诊的任务书里定。
- **我们自己调模型只有一个入口:consult 的「调一次模型」。** 它按固定顺序做五步:把完整请求存进账本的大块存储、写「调用开始」、经 llm 调用、把完整回复存进大块存储、写「调用结束」。llm 只管调用,不写账本,所以任何一次模型调用都绕不开登记。中断后重启时,从账本找出开始了没结束的调用,记为「状态未知」,不重做已有结果的调用。做重复运行(同一病例、同一专家调多次,测一致性)时,每一次都恰好登记一次。
- **CEUS 算法不知道分割模型是谁,也不管中间文件放哪。** ceus 接收视频路径、参数和分割结果,交出曲线、拟合、关键帧和指标;中间文件写在哪、按什么复用,由 `scripts/ceus_quant.py` 决定(目前只有它一个使用者,出现第二个使用者再收进模块)。segment 只管「给提示、出逐帧掩码」,不认识分屏和绿框。
@@ -136,7 +176,12 @@ library/<构建号>/
### 标准答案库(`answers/<构建号>/`)
与同一次构建的资料库一一对应,放:病理诊断(按 delivery 认出的诊断编码解码)、化名与真实病例号的对照、跨表一致性报告(各表之间性别、年龄、诊断对不对得上;40 例中有 6 例对不上,见 `../待定问题/04-交付数据的跨表不一致.md`),将来加医生标注工作簿。
与同一次构建的资料库一一对应(同一个构建号),放:
- 病理诊断:三张表的诊断列各存一份,用哪一份算分由评价决定;
- 化名对照:化名、真实病例号、CEUS 视频号。会诊产物只用化名,评价靠这张表接回真实病例;
- 跳过清单:没进资料库的病例、没过的那一项检查、涉及的文件和列,以及对不上的各表原值,供医生核对。40 例里已知有 6 例临床表与超声表对不上(`../待定问题/04-交付数据的跨表不一致.md`),会出现在这份清单里;
- 将来加医生标注工作簿。
### CEUS 中间文件(`work/`)
@@ -179,7 +224,7 @@ X 是超声报告工作簿的「推注开始时间s」,含义是视频第 0
| `src/cadx/` | 一个 Python 包,每个模块一个子包;别的模块只能经子包的 `__init__.py` 引用它,内部文件加 `_` 前缀 | 是 |
| `scripts/` | 入口脚本 | 是 |
| `tests/` | 不花钱、不要显卡的测试;需要显卡的标 `gpu`,花钱的标 `live`,两者不进 `make check` | 是 |
| `config/` | CEUS 参数、会诊的实验配置、模型用途清单(密钥只写环境变量名) | 是 |
| `config/` | 每段一份配置,另有编码映射表(见下文「配置」);密钥只写环境变量名 | 是 |
| `prompts/` | 版本化的提示词文件;每次运行记下所用提示词的哈希 | 是 |
| `data/` | 医院交付的原始资料 | 否 |
| `library/`、`answers/` | 两个库,每次构建一个目录 | 否 |
@@ -190,6 +235,28 @@ X 是超声报告工作簿的「推注开始时间s」,含义是视频第 0
不进 git 的目录由 `.gitignore` 和 pre-commit 的 `local-only-paths` 两道拦截,`git add -f` 也提交不了。
### 配置
所有参数都写在 `config/` 下的 YAML 文件里,每段一份:
| 文件 | 给谁用 | 内容 |
| --- | --- | --- |
| `build.yaml` | `scripts/build.py` | 交付包位置、两个库的输出位置、化名与打乱顺序的随机种子、编码解码开关与映射表位置 |
| `code_maps.yaml` | build(经 `build.yaml` 引用) | 编码映射表:交付包里每个用数字编码的列,每个数字代表什么(如性别的 1、2) |
| `ceus.yaml` | `scripts/ceus_quant.py` | CEUS 定量的全部参数,每个参数用注释写明出处 |
| `consult.yaml` | `scripts/consult.py` | 会诊的实验配置与模型用途 |
| `evaluate.yaml` | `scripts/evaluate.py` | 评价的指标与统计设置 |
每段一份,是因为四段分开运行,各自的快照只记自己的配置;改会诊参数不会改变装配配置的哈希,不会让人误以为资料库要重建。哪份文件在哪个模块落地时建,不提前写空文件。
规则:
- **入口脚本只接收配置文件路径**,不能在命令行单独改某个参数,「这次用了什么参数」只看文件就知道。
- **字段定义归使用它的模块**(第三部分),用 pydantic 写:每个字段必填,不设默认值;可以没有的值显式写 null;多写或拼错的字段名报错;读入后不能修改。
- **路径、随机种子、模型名、服务地址都写进配置**,代码里不写死。
- **每次运行把所用配置的原文复制进快照**,并记下 SHA-256 哈希;build 的快照同时复制编码映射表。
- **编码解码是一个开关。** `build.yaml` 的 `decode_codes` 必填,取 true 或 false。取 false 时,映射表位置写 null,编码列按原始数字写进资料库,并注明「编码含义未解码」;取 true 时,映射表位置必填,映射表必须覆盖交付包的全部编码列,缺一列就整次构建停下,某一例出现映射表里没有的值就跳过这一例。编码的含义要向临床方要映射表;到位之前用 false。
文件格式:数组用 npz(读取时 `allow_pickle=False`,pickle 读取时会执行任意代码),每例的标量和摘要用 json,跨病例的表用 csv,图用 png。缺失值写 null,不写 NaN 或 0:0 是合法的测量值,NaN 在 json 里不合法。
导入边界用 import-linter 检查,写进 `make check`。契约在 `pyproject.toml`,检查范围是 `src/cadx`,**不含 `scripts/` 和 `tests/`**。下面只列契约实际强制的,没有的保护不写:
@@ -233,7 +300,7 @@ Python 环境用 uv;安装与检查命令见 `../guides/开发环境.md`。
## 七、推进顺序
1. **骨架**(已完成):协作规则、文档体系、工具链。
2. **装配两个库(先不含 CEUS 结果):delivery → library、answers → runs 的快照部分 → build。** 先做它,是因为它最轻,做完就能用文字资料试会诊;而且隔离(专家看不到答案)是整个研究成立的前提,越早有代码和测试守住越好。顺带产出跨表一致性报告,给 `待定问题/04` 提供证据。
2. **装配两个库(先不含 CEUS 结果):delivery → library、answers → runs 的快照部分 → build。** 先做它,是因为它最轻,做完就能用文字资料试会诊;而且隔离(专家看不到答案)是整个研究成立的前提,越早有代码和测试守住越好。顺带产出跳过清单,交给医生核对,也给 `待定问题/04` 提供证据。参数系统(第五部分「配置」)随这一步落地。
3. **CEUS 定量:ceus 按旧模块逐个迁移(带上旧的算法单测)→ segment → `scripts/ceus_quant.py` → build 加入 CEUS 结果。** 旧算法单测约 274 个,不依赖数据库,随模块带过来,作为迁移正确性的主要依据(design 0002 决定 4)。已知问题(映射锚点写死、两条旧路径的行为差别、造影画面在右半边)在任务书里逐条定。
4. **会诊:ledger → llm → consult 的「调一次模型」→ verdicts → consult 的会诊流程。** 动手前先专门讨论定下会诊口径:后发言的看得到什么、怎样算两路一致、超声一路给哪些指标和图、模型与重复次数等(旧项目留下的未裁决问题)。
5. **评价:evaluate。** 动手前先定指标、统计方法,以及 `待定问题/02`(参数何时冻结、开发集与测试集怎么分)、`待定问题/03`(病例排除标准)。
@@ -8,6 +8,7 @@
- 旧代码 CEUS 追踪失败 6 例:3 例分屏识别失败,3 例在左半边找不到绿框(其中 1 例已确认造影画面在右半边)。来源:远程 `data/交付包_20260908_40例_result/结果清单_20261016.md`,2026-10-15 读过。
- 交付说明标明 3 例用肝实质替代正常胰腺作参照、1 例没有参照(`data/交付包_20260908_40例/交付说明_40例_20260908.md`)。
- 装配两个库时,交付资料检查没过的病例整例跳过,不进资料库(负责人 2026-10-16 定,见 `explanation/结构.md` 第三部分)。已知临床表与超声表对不上的 6 例(`04`)会因此被跳过。医生改正、重新交付后能否补回,和其他排除类别一起定。
## 为什么要紧