从产品重构为研究原型的第一步(负责人 2026-10-15:「其其实不需要(至少目前不需要) 是一个工程产品」)。协作环境照 ClinDB-ReviewBench(负责人点名参考)。 - AGENTS.md 为唯一规则,CLAUDE.md 只引用它;硬性约束保留旧项目的答案隔离、各路隔离, 加上数字可追溯、花钱先报预估等 - WORK_LOG.md 记当前状态与负责人的关键决定 - research-wiki:README、explanation/结构.md(8 个模块、依赖边界、推进顺序)、 design/0001(为什么重构、继承与放弃什么、单一 uv 环境)、guides/开发环境.md - 三份新文档过了一轮「硕士生阅读」并按意见修订 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
16 KiB
AGENTS.md
本文件是给所有编码 Agent(Claude Code、Codex 等)的工作说明。CLAUDE.md 只引用本文件,内容以这里为准。
项目是什么
CADx-4 研究胰腺囊性肿瘤的鉴别诊断:给同一个病人的三路资料(临床信息、CEUS 超声造影视频与超声报告、MR 报告)各配一个只看自己那一路的 AI 专家,模拟多学科会诊,判断病灶属于四类胰腺囊性肿瘤中的哪一类:IPMN(导管内乳头状黏液性肿瘤)、SPN(实性假乳头状肿瘤)、MCN(黏液性囊性肿瘤)、SCN(浆液性囊性肿瘤)。前三类有恶变可能或本身是低度恶性,通常要手术或密切随访;SCN 几乎都是良性,多数不用手术。分错直接决定病人是手术、随访还是不用管。金标准是术后或穿刺取得的病理结论。最终产出是一篇医学 AI 交叉论文,重点在临床问题、与人类医生的对比和可解释性,不在方法复杂度。
几个词先解释:MR 是磁共振检查,本项目只有它的文字报告;CEUS 是往静脉打造影剂后连续录下的超声视频;ROI(感兴趣区)是视频里圈出的病灶区域;TIC(时间-强度曲线)是 ROI 平均亮度随时间的变化,造影剂进来变亮、洗出变暗,曲线形状就是灌注的量化。本仓库的术语表随模块迁移逐步建在 research-wiki/reference/术语表.md;建成前查旧项目的 reference/CADx-4-CAD/research-wiki/reference/glossary.md。
本仓库是对旧项目的重构。旧项目在远程服务器上按工程产品搭建(HTTP 服务、数据库、任务队列),后来发现目前不需要做成产品,于是在这里按研究原型重写,只保留有研究价值的算法和知识。旧项目的完整副本在 reference/CADx-4-CAD/(只读,见下文)。重构的来由和取舍见 research-wiki/design/0001-从产品重构为研究原型.md。
和临床合作方之间的议题、会议纪要、交付清单和临床口径在 Linear(团队用的议题跟踪系统,临床方也在上面)上,不在本仓库。
新会话先读什么
WORK_LOG.md:当前目标、做到哪一步、下一步、运行中的任务。research-wiki/README.md:文档体系与各目录的用途。research-wiki/explanation/结构.md,以及与当前任务相关的其他explanation/文档。
事实来源
文档之间有冲突时按下表定谁说了算,找负责人确认,不自行折中成第三种说法。
| 问题 | 看哪里 |
|---|---|
| 当前进度、下一步 | WORK_LOG.md |
| 代码分哪些模块、谁能依赖谁、各模块预期规模 | research-wiki/explanation/结构.md |
| 各环节在做什么、输入输出 | research-wiki/explanation/ 的其他文档 |
| 某个决策为什么这样定 | research-wiki/design/NNNN-*.md(写完冻结) |
| 命令怎么敲、环境怎么配 | research-wiki/guides/ |
| 医学词、参数的原始口径、临床方给的字段定义 | research-wiki/reference/(每条自带出处) |
| 配置值、字段定义 | 代码里的定义与校验;文档只解释含义,不抄数值 |
| 临床口径、双方约定、待交付清单 | Linear(临床方维护的那几份 document 是权威,仓库里的抄本以它为准) |
| 旧项目做了什么、踩过什么坑 | reference/CADx-4-CAD/(只当线索,见下文) |
reference/ 怎么用
reference/CADx-4-CAD/ 是旧项目的副本,含完整 git 历史(366 个提交)和当时没进 git 的 debugs/ 脚本。
- 只读。不在里面改文件、不在里面跑会写盘的命令。
- 算法代码可以迁移:
app/domain/ceus_roi/(除service.py)、app/providers/video_decoder.py和debugs/里的三个.py脚本是负责人自己的成果,迁移时去掉数据库、HTTP、任务队列等产品层依赖,按本文件的标准改写,并补上测试。读交付包的逻辑(app/providers/里的列白名单、病例号规范化、剔除报告结论段)只借规则,按普通函数重写。产品层代码(app/api/、persistence/、workers/、migrations/等)不搬。 - 旧文档和报告里的数字(40 例跑通 34 例、参数取值等)只是线索。要写进本仓库的文档或结论,先在原始产物或本仓库的真实运行里核到。
- 旧
research-wiki/design/0001–0049讲的是当时为什么那样定。其中与研究有关的结论(优先读哪几份见本仓库research-wiki/design/0001第 8 条的表)在本仓库重新写成 explanation 或 design。可以注明「出自旧 design 00NN」交代来历,但现行规则以本仓库的文档为准。
reference-pdf/ 放文献原文,命名 年份-第一作者-短名.pdf,索引在 reference-pdf/INDEX.md。两者都不进 git。
硬性约束
- 答案不进 AI 专家能看到的任何东西。 病理金标准、工作簿里的诊断列、按规则算出诊断倾向的公式列、医生的结构化判读列(标注工作簿是临床方另交的一份表,医生在里面逐例标了病灶位置、大小、囊壁、增强模式等三十来列,它只用于评价专家抽取得准不准)、报告的「印象 / 结论」段、真实病例号、Excel 行序,一律不能进入提示词、工具返回或专家可读的文件。行序也算:原始表格按诊断分组录入,行号本身就带着答案。这一条错了不会报错,只会让所有「系统诊断得准不准」的结论变成「系统能不能读到答案」。任何会削弱这条的改动,先停下来问。来源:旧项目 CLAUDE.md §1.2、旧 design 0026。
- 每个 AI 专家只看自己那一路资料。 性别和年龄只给临床那一路;超声和 MR 两路看不到,即使医院把这几列也复制进了报告工作簿。各路能看见什么见
research-wiki/explanation/里的会诊文档(迁移完成前看旧项目research-wiki/explanation/agent-system.md§2)。来源:同上。 - 患者资料不进 git、文档和提交说明。 原始资料只在
data/,运行产物只在runs/,两者都不进 git。文档里可以写结构和规模,不写报告原文、姓名、病例号和诊断。把病例资料发给外部模型服务之前,先确认负责人已同意该服务。超声帧上烧录的信息栏(拼音姓名、检查号、性别年龄、医院与设备参数)不做遮蔽:临床方判定拼音和不唯一的检查号不构成身份泄露(出自旧 design 0041)。 - 密钥不进 git、文档、日志和运行快照。 只放环境变量或本地
.env;仓库只提交不含真实值的.env.example。 - 写进论文和结论的数字能追到一次真跑的原始记录(
runs/下的运行目录)。不把预期、估算、旧项目报告或别人论文里的数字当成本仓库的结果;没有就写「没有」。文献里的数字核到reference-pdf/的原文。 - 错误不静默吞掉。 不吞异常,不用默认值把问题盖过去,缺关键配置直接报错。细则见「写代码的标准」。
- 花钱的批量运行先说预估成本。 调模型 API 跑全部病例这类动作,开跑前在回复里给出预估;量级明显超过以往的,先问。
怎么协作
- 用简体中文回复,先给一句话结论。
- 先讲后写。 新环节或新设计先在终端讲:旧项目怎么做的、问题在哪、打算怎么做、为什么。负责人认可后再写文件或代码。需要负责人决定时,一次列出全部问题、选项和你的倾向。
- 能自己定的直接定,按设计判断或文献先例给方案,不把问题抛回去。
- 范围要窄,改得要深。 只动这次要改的那一块;范围内要改到读起来像一开始就这么设计的,被取代的旧内容同一次删掉。范围外看到问题,记下来告诉负责人。
- 讨论会话与执行会话分开。 和负责人讨论方向的会话写任务书(
research-wiki/任务书/NN-*.md),由负责人另开会话执行、讨论会话验收。小改动不必写任务书。 - 子 Agent 交证据不交判断:带
文件:行号、命令原始输出或产物路径;prompt 里不带邮箱等个人信息和患者资料;要用浏览器只能用无头模式。收回的报告先抽查两三条原文,其中一条挑带评价性修饰的句子;子 Agent 说「通过」不算数,要自己核对实际改动和测试产物。 - 审查:改动完成后派一个新鲜上下文的子 Agent,只给 diff、验收标准和相关文档,不给实现时的推理。下文「账本级」的代码另派一个全新上下文的子 Agent 再审一轮;Codex 可用时再加一轮对抗审查(
/codex:rescue --fresh --wait)。审查意见只采纳影响正确性或明确需求的项,采纳前问:日常使用中会不会真撞上。 - 汇报时分开写已验证的事实、推断和未验证的部分。
- 新加的规则注明来源:负责人哪句话、哪份 design 文档,还是 Agent 自己提的。本文件的改动来源写在提交说明里。
- 推送、建 PR、往 Linear 写东西这类外部可见的动作先问;Linear 上有临床方在看。
写代码的标准
两档标准
- 账本级(像记账一样只追加、可核对;出错会悄悄污染结论的部分):答案隔离(各路资料怎么读、怎么分给专家)、金标准的读取、评分与统计、写进论文的定量结果。要求:
- 运行产物只追加,已有的运行目录不删、不覆盖,要删先问;
- 每条规则有独立预期值的测试,预期值来自手算、独立脚本或人工核对过的样例,不从实现里抄公式。
- 其余部分:视频解码、ROI 追踪、绘图、提示词拼装、脚本。写错了马上能看出来,按研究原型的标准写:用最直接的写法,遇到罕见情况就停下来把原因写清楚。
可复现
随机种子、模型的精确版本号(以服务商返回为准)、提示词版本、git 提交号和全部配置,写进每次运行的快照。复现不了的结果等于没有结果。每次运行一个新目录,同一个输出目录同时只允许一个进程写。
不设默认值
配置的每个字段都必填,函数参数不写默认值;可以没有的值显式写 null,不靠省略。缺了就报错。默认值会掩盖错误:字段名拼错、文档改了代码没跟上,流水线照样跑完,没人知道用的是哪个值。路径、模型名、服务地址同样不写死在代码里。
罕见情况怎么办
「可能会出现」不是写分支的理由。按顺序选:
- 改数据的表示或格式要求,让它不可能出现。
- 做不到,就在系统边界检查,不满足就报错停下。边界指交付的资料、模型的输出、负责人的配置。模块之间的调用不算边界,不互相防御。
一个抽象在出现第二个真实调用点之前不引入。不考虑向后兼容:不写兼容层,读到旧格式直接报错;发现更好的设计就按它重写,同步改文档和测试。
遇到问题先找原因
出了问题先查清为什么,再到原因所在的地方改。重试、跳过、补默认值、放宽解析、捕获异常后接着跑,只是让症状消失。动手前问:这个改动是让问题不再发生,还是让问题发生了也没人看见?
错误怎么处理
- 外部错误(模型接口超时、网络断开):只在调用外部服务的那一处做有限次重试,仍失败就把这一步记为失败并写明状态。
- 内部错误(资料不满足规则、不该有的状态组合):立刻停下,报错写清是哪条规则、哪个病例的哪个文件。模型交不出合格输出按内部错误处理。
中间层不写 try/except,除非它能把错误完整处理掉且上层不需要知道。不写 except Exception: pass 及其变体。
提示词
- 提示词引导模型怎么想,不设必填栏目:逼模型填栏目,它会为了填而编。只有程序确实要读的值才做成输出字段。
- 提示词是
prompts/下的版本化文件,不散落在代码字符串里;给模型的提示词用英文。
测试
- 真实模型链路要用真跑证明,假模型测试证明不了;需要显卡或花钱的测试用 pytest 标记
gpu、live区分,不进make check。 - 修缺陷时附一条能复现原问题的测试。
- 不为方便测试给产品代码加参数、开关或分支;不靠删断言、改预期值或只保留成功的运行让测试通过。
规模与注释
- 每个模块的预期规模写在结构文档里;超过预期一倍时停下检查:是需求本身复杂,还是专门路径在变多。
- 注释只写这段代码现在做什么、为什么这样做;修改过程写进提交说明和
WORK_LOG.md。
文档
- 代码标识符用英文;注释、docstring、文档和回复用简体中文。
- 读者是没参与过讨论的医学 AI 方向硕士生,也包括负责人:懂 Python 和深度学习,不认识 IPMN,也不知道我们讨论过什么。医学词、缩写、代号首次出现处就地解释;每个决策写清为什么。
- 同一个配置值只在一处定义,别处引用。
explanation/、guides/、reference/是常青文档:改了就重写那一段,不在后面追加「某日又改了」。- 决策的来历写进
research-wiki/design/NNNN-简短主题.md,按顺序编号,写完冻结;决策变了新写一份,开头写明取代哪份,旧的开头加一行指向新的。 WORK_LOG.md只记当前状态:目标、已完成(附证据)、未完成、关键决定(附负责人原话)、下一步。一个阶段完成后压缩成几行。没有真实证据的「可能情况」只在汇报里提一句,不写进WORK_LOG.md。- 新写或大改一份文档(新文档、重写整节、改动超过约 100 行),过一轮「硕士生阅读」:新鲜上下文的子 Agent 扮演这样的读者,只读这份文档,先列出最可能读不下去或要回头重读的段落、再复述要点,然后报哪句读不懂、哪个医学词没解释、哪个决策缺理由、哪个值两处不一致。不要让它描述自己的思考过程。格式和措辞建议不采纳。
提交规范
提交信息用英文前缀加中文正文,前缀按 Conventional Commits:feat:、fix:、docs:、test:、refactor:、chore: 等,可加作用域,例如 feat(ceus): 暗区掩膜改用奇偶规则。
本机环境的坑
grep、find、ps、diff可能被 shell 包装函数改写,静默少输出。搜索用/usr/bin/grep、/usr/bin/find;查进程用pgrep -af;比较文件用cmp或/usr/bin/diff;看 git 差异用git --no-pager diff。派出去的子 Agent 的 prompt 里也写上这一条。- 超过约一分钟的命令放进 tmux 跑,日志写到文件。
- 日常开发和实验在本机(一张 RTX 4070 Ti SUPER 16GB)。需要用远程服务器(旧项目所在的 4090 服务器)时,ssh 和 rsync 一律走
wangwenhan@202.38.247.58,不走 Tailscale 地址100.83.164.94(同一台机器,后者实测约 150KB/s,前者约 100MB/s)。它的显卡是共用的,命令必须显式设CUDA_VISIBLE_DEVICES。
开发命令
make sync:用 uv 同步项目环境和依赖。make fmt:用 ruff 自动修正并格式化。make test:运行不花钱、不需要显卡的测试。make check:依次检查 lint、格式、类型、导入边界并运行测试;失败即停止。
环境怎么搭见 research-wiki/guides/开发环境.md。