Files
CADx-4/AGENTS.md
T
iomgaa 35e77777e3 docs: 硬性约束第 2 条改为影像专家先独立判断再听临床信息;新增 paper/ 与论文问题句、主张句
AGENTS.md 第 2 条的改写来源:负责人 2026-10-16 定「第一轮之后、影像专家提出需要才给临床信息」。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 10:25:42 -04:00

17 KiB
Raw Blame History

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(团队用的议题跟踪系统,临床方也在上面)上,不在本仓库。

新会话先读什么

  1. WORK_LOG.md:当前目标、做到哪一步、下一步、运行中的任务。
  2. research-wiki/README.md:文档体系与各目录的用途。
  3. research-wiki/explanation/结构.md,以及与当前任务相关的其他 explanation/ 文档。

事实来源

文档之间有冲突时按下表定谁说了算,找负责人确认,不自行折中成第三种说法。

问题 看哪里
当前进度、下一步 WORK_LOG.md
代码分哪些模块、谁能依赖谁、各模块预期规模 research-wiki/explanation/结构.md
各环节在做什么、输入输出 research-wiki/explanation/ 的其他文档
还没定的问题 research-wiki/待定问题/(定了就删,结论进 design/)
某个决策为什么这样定 research-wiki/design/NNNN-*.md(写完冻结)
命令怎么敲、环境怎么配 research-wiki/guides/
医学词、参数的原始口径、临床方给的字段定义 research-wiki/reference/(每条自带出处)
交付资料的格式和口径(列名、编码、时间的含义) 最新的交付包,目前是 data/交付包_20260908_40例/。旧样本(如 data/1.2-TEST/)和旧程序的口径只当线索。来源:负责人 2026-10-16「数据改过很多版,所以你们需要对齐“40例交付包”那个」
配置值、字段定义 代码里的定义与校验;文档只解释含义,不抄数值
临床口径、双方约定、待交付清单 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。

硬性约束

  1. 答案不进 AI 专家能看到的任何东西。 病理金标准、工作簿里的诊断列、按规则算出诊断倾向的公式列、医生的结构化判读列(标注工作簿是临床方另交的一份表,医生在里面逐例标了病灶位置、大小、囊壁、增强模式等三十来列,它只用于评价专家抽取得准不准)、报告的「印象 / 结论」段、真实病例号、Excel 行序,一律不能进入提示词、工具返回或专家可读的文件。行序也算:原始表格按诊断分组录入,行号本身就带着答案。这一条错了不会报错,只会让所有「系统诊断得准不准」的结论变成「系统能不能读到答案」。任何会削弱这条的改动,先停下来问。来源:旧项目 CLAUDE.md §1.2、旧 design 0026。
  2. 影像专家先独立判断,再听临床信息。 资料库里每一路只放自己那一路资料:性别和年龄只在临床那一路,超声和 MR 两路的资料里没有,即使医院把这几列也复制进了报告工作簿。会诊时,超声和 MR 两位专家先只凭本路资料各自给出判断;第一轮之后,它们需要时可以向临床专家提问,临床专家按问作答,可以答性别、年龄。先独立这一步不能省:判断有没有分歧要靠两路影像证据本身,先听到同一份人口学信息会把两位专家往同一个方向拉,分歧就看不出来了。各路能看见什么见 research-wiki/explanation/结构.md 第四部分。来源:旧项目 CLAUDE.md §1.2、旧 design 0026;临床专家只提供信息出自旧项目 HAI-62 的 2026-08-02 会议纪要;「第一轮之后、影像专家提出需要才给临床信息」与本条改写是负责人 2026-10-16 定的。
  3. 患者资料不进 git、文档和提交说明。 原始资料只在 data/,运行产物只在 runs/,两者都不进 git。文档里可以写结构和规模,不写报告原文、姓名、病例号和诊断。把病例资料发给外部模型服务之前,先确认负责人已同意该服务。超声帧上烧录的信息栏(拼音姓名、检查号、性别年龄、医院与设备参数)不做遮蔽:临床方判定拼音和不唯一的检查号不构成身份泄露(出自旧 design 0041)。
  4. 密钥不进 git、文档、日志和运行快照。 只放环境变量或本地 .env;仓库只提交不含真实值的 .env.example。
  5. 写进论文和结论的数字能追到一次真跑的原始记录(runs/ 下的运行目录)。不把预期、估算、旧项目报告或别人论文里的数字当成本仓库的结果;没有就写「没有」。文献里的数字核到 reference-pdf/ 的原文。
  6. 错误不静默吞掉。 不吞异常,不用默认值把问题盖过去,缺关键配置直接报错。细则见「写代码的标准」。
  7. 花钱的批量运行先说预估成本。 调模型 API 跑全部病例这类动作,开跑前在回复里给出预估;量级明显超过以往的,先问。

怎么协作

  • 用简体中文回复,先给一句话结论。
  • 先讲后写。 新环节或新设计先在终端讲:旧项目怎么做的、问题在哪、打算怎么做、为什么。负责人认可后再写文件或代码。需要负责人决定时,一次列出全部问题、选项和你的倾向。
  • 能自己定的直接定,按设计判断或文献先例给方案,不把问题抛回去。
  • 范围要窄,改得要深。 只动这次要改的那一块;范围内要改到读起来像一开始就这么设计的,被取代的旧内容同一次删掉。范围外看到问题,记下来告诉负责人。
  • 讨论会话与执行会话分开。 和负责人讨论方向的会话写任务书(research-wiki/任务书/NN-*.md),由负责人另开会话执行、讨论会话验收。小改动不必写任务书。
  • 子 Agent 交证据不交判断:带 文件:行号、命令原始输出或产物路径;prompt 里不带邮箱等个人信息和患者资料;要用浏览器只能用无头模式。收回的报告先抽查两三条原文,其中一条挑带评价性修饰的句子;子 Agent 说「通过」不算数,要自己核对实际改动和测试产物。
  • 审查:改动完成后派一个新鲜上下文的子 Agent,只给 diff、验收标准和相关文档,不给实现时的推理。下文「账本级」的代码另派一个全新上下文的子 Agent 再审一轮;Codex 可用时再加一轮对抗审查(/codex:rescue --fresh --wait)。审查意见只采纳影响正确性或明确需求的项,采纳前问:日常使用中会不会真撞上。
  • 汇报时分开写已验证的事实、推断和未验证的部分。
  • 新加的规则注明来源:负责人哪句话、哪份 design 文档,还是 Agent 自己提的。本文件的改动来源写在提交说明里。
  • 推送、建 PR、往 Linear 写东西这类外部可见的动作先问;Linear 上有临床方在看。

写代码的标准

两档标准

  • 账本级(像记账一样只追加、可核对;出错会悄悄污染结论的部分):答案隔离(各路资料怎么读、怎么分给专家)、金标准的读取、评分与统计、写进论文的定量结果。要求:
    • 运行产物只追加,已有的运行目录不删、不覆盖,要删先问;
    • 每条规则有独立预期值的测试,预期值来自手算、独立脚本或人工核对过的样例,不从实现里抄公式。
  • 其余部分:视频解码、ROI 追踪、绘图、提示词拼装、脚本。写错了马上能看出来,按研究原型的标准写:用最直接的写法,遇到罕见情况就停下来把原因写清楚。

可复现

随机种子、模型的精确版本号(以服务商返回为准)、提示词版本、git 提交号和全部配置,写进每次运行的快照。复现不了的结果等于没有结果。每次运行一个新目录,同一个输出目录同时只允许一个进程写。

不设默认值

配置的每个字段都必填,函数参数不写默认值;可以没有的值显式写 null,不靠省略。缺了就报错。默认值会掩盖错误:字段名拼错、文档改了代码没跟上,流水线照样跑完,没人知道用的是哪个值。路径、模型名、服务地址同样不写死在代码里。

罕见情况怎么办

「可能会出现」不是写分支的理由。按顺序选:

  1. 改数据的表示或格式要求,让它不可能出现。
  2. 做不到,就在系统边界检查,不满足就报错停下。边界指交付的资料、模型的输出、负责人的配置。模块之间的调用不算边界,不互相防御。交付资料里只落在某一个病例上的问题是例外,见「错误怎么处理」。

一个抽象在出现第二个真实调用点之前不引入。不考虑向后兼容:不写兼容层,读到旧格式直接报错;发现更好的设计就按它重写,同步改文档和测试。

遇到问题先找原因

出了问题先查清为什么,再到原因所在的地方改。重试、跳过、补默认值、放宽解析、捕获异常后接着跑,只是让症状消失。动手前问:这个改动是让问题不再发生,还是让问题发生了也没人看见?

错误怎么处理

  • 外部错误(模型接口超时、网络断开):只在调用外部服务的那一处做有限次重试,仍失败就把这一步记为失败并写明状态。
  • 内部错误(资料不满足规则、不该有的状态组合):立刻停下,报错写清是哪条规则、哪个病例的哪个文件。模型交不出合格输出按内部错误处理。
  • 交付资料里只落在某一个病例上的问题(这一例的编号认不出、缺文件、取值不合规则、和别的表对不上等):跳过这一整例,接着处理下一例,并在标准答案库的跳过清单里写明这一例没过的是哪项检查、哪个文件哪一列。对一个病例,遇到第一项不过的检查就不再往下查,所以清单上每例只有一项,后面的检查没做过,不代表没问题。整张表读不了(工作表名或列名不对、文件缺失、配置不全)仍然立刻停下。跳过不是把问题藏起来:跳过清单交给医生核对,评价时要报告跳过了几例、各因为什么。来源:负责人 2026-10-16「都是遇到整个病例跳过,让医生核对去吧」。

中间层不写 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。