← 工作原理 · 原文件 notes/reports/dev/2026-10-03_small_fixes.md(Markdown 源文件已渲染;链接到其他文档的会跳转,指向源码的只显示路径)
四个小项(2026-10-03)
没有 git commit,没有启动或停止 run,Spark 只读(回归用 scripts/test_on_spark.sh)。没有改任何边界内文件(开工时 git status --short 里的 M / ?? 文件,清单在会话里;agent/search/{controller,roles,...}.py、CONTRACT.md、search_spec.md、agent/notify/*、glossary.py 都没碰)。
测试(Spark):test_compliance、agent.research.test_kg_proxy、agent.research.test_service、agent.dashboard.usage_test、agent.dashboard.build_test、agent.records.llm_usage_test 全部 OK;scripts/check_knowledge.py PASS。
1. Compliance 正则误报
原文(节点 4 的 PLAN):“父节点在有两个输入阶段时(final:E8.5 + E9.5)完全不用第一阶段……”。AFTER_RE 的后缀分支 {E}{NUM}\s*(?:…|\+) 允许数字与 + 之间有空白,“E8.5 + E9.5” 被当成“E8.5 及以后”。
agent/search/compliance.py:去掉该分支里的\+,另加分支{_E}\s?{_NUM}\+(?!\s*(?:{_E}|\d)):只有+紧跟数字(“E10.5+”)且后面不是另一个阶段或数字时才算“以后”;AFTER_RE命中处取值改为group(1) or group(2) or group(3)。agent/search/tests/test_compliance.py:新增test_plus_between_stages_is_not_open_ended(误报原文、“E8.5+E9.5”、“E8.5 + then E9.5” 不命中;“E10.5+”、“E9.5 and later”、“≥E9.5”、“> E9.5”、“E9.5 之后” 仍命中)。- 备注:在 VM 上直接
python3 -m unittest会因agent/search/select.py遮蔽标准库select导致一个子进程用例失败(环境问题,与本改动无关);用test_on_spark.sh通过。
2. lit-search 触发条件
agent/prompts/researcher_spec.md(不在边界内):把“什么时候查”改成三条触发(方法族/算法在本 run 首次出现;方案引用具体算法参数或生物学机制却没有出处;父节点机制被证否需找替代):至少查 1 次并写sources,查不到就在risks里写“未找到文献支持”,不强制每个节点都查。- 无需主会话补丁。控制器若还有一份“lit-search 本次可用”的生成段落(roles.py),不改也能工作。
3. G53b kg-search(第六版:run 内只读已审索引,实时发现异步入队)
状态:封存,待主办方答复第 8 问(官网 KG 在 run 内的使用范围,11_data_use_scope.md §4)后再按答复范围接入。答复前 run 内不接 KG(controller 不写 kg_task,kg 保持 disabled)。
五审后收尾(Codex 2026-10-03_codex_kg_proxy_r5.md):(1) kg_review 关闭自动 accept:自动审核只输出 reject 与 hold,规则认为"可 accept"的候选一律停在 hold(原因写明等待显式批准);accept 只能由人工 / 主会话运行 python -m agent.research.kg_safe review-queue --approve <key> --by <谁> --reason <为什么>,批准记录在私有 kg_review_approvals.jsonl,决定日志带 approved_by;批准也不能越过规则的 reject / hold(被规则拒绝的候选批准了仍不入库)。(2) 五审 P0-2 的反例进了测试且不会被 accept:阶段范围变体 E9.5..E14.5、9.5–14.5 dpc(含 mixed 来源,另加 9.5 to 14.5 days、E9.5...E14.5)、数值型元数据(embryonic_day=10.5、cell_count=137、新实体 id 的名称 cardiomyocyte count 137);为此识别器补了 ..、dpc / days 区间,名称检查补了 count 137 形式,带数值属性的测量边一律 hold。T3 没有阶段窗口,阶段范围在 T3 不构成问题。五审其余项(P0-1 来源白名单、P0-3 别名基因型)因 accept 只能人工批准,不再有自动入库路径;规则本身仍待主办方答复后再统一复审。
演变:v1 agent Cypher → v2 模板 + 实时 API → v3 本地图 → v4 实时 + 回退 → v5 实时发现、已审准入 → v6 本版(Codex 四审 2026-10-03_codex_kg_proxy_r4.md:背景边直出可带入实测记录、基因型变体、按原始候选数清空答案、响应耗时依赖审计)。接口与输出不变:lit-search kg lineage|regulation|markers|order,输出 {name, relation, direction, source},≤ 20 行、已排序、无数值 / 证据 / 阶段、无过滤提示。
1. run 内所有答案只取已审索引(kg_proxy.py):背景与测量型一视同仁,只读 safe/<task>.tsv(随 run 进锁、sha256 对 MANIFEST)。kg_proxy.run 没有任何网络 / transport 参数,查询时不审查、不分页、不计时,所以响应内容和耗时与实时 API、被拦记录完全无关(测试:实时 API 返回正常 / 500 / 乱码 / 500 条被拦记录 / 延迟 1.5 s,run 内答案逐字相同、API 一次都没被调用、耗时 < 1 s)。查询的模板与参数由服务在后台线程里投递到宿主私有任务文件 kg_discovery_jobs.jsonl,不等待、不影响响应。私有区不可用时只关闭发现,答案照常。
2. 发现进程(新 kg_discover.py,python -m agent.research.kg_discover [--once],run 外后台):读任务文件,对同一模板查询实时 API 逐页取候选,每页分类后立即追加到 kg_review_queue.jsonl(带每个任务的预分类,仅作提示),请求失败时已收到的页保留(P2-2);相同任务只处理一次(kg_discovery_state.jsonl)。
3. 审核并入(新 kg_review.py,python -m agent.research.kg_safe review-queue):对队列里每个待审候选、每个任务按确定性规则重新分为 accept / reject / hold,accept 并入新版本目录 data/external/vekg/safe_v<N+1>/(各任务 TSV、MANIFEST.json:version、父版本与其 MANIFEST 哈希、队列哈希、输入哈希;DIFF.md 差异报告),决定写入私有 kg_review_decisions.jsonl;hold 的下次继续待审;绝不修改运行中 run 所锁定的目录(测试:旧目录字节级不变,输出目录与旧目录相同则报错)。规则本身的修改属于 D 级。规则要点:
- 背景边必须有条目级依据才直出:本体边(无 DOI、本体关系、两端本体 id、无自由文本无数值)或已审论文的文献结论(记录无自由文本、无数值属性、无测量 / 实验词、无阶段);
scRNA-seq cluster membership measured at E10.5、E10.5加cell_count=137都不会被接受(reject / hold); - 测量型边:严格审查 + DOI 在来源表且不是窗口论文 / banned 数据集;mixed / unknown 来源的测量边需要已知的窗内阶段,
TS_all、空列表、空串按未知阶段 → hold; - 实时名称是审查输入(
Hand2 (Gata4 knockout embryos)被拒),新 id 的规范名取通过审查的名称,与图里同 id 的已有名称冲突则 hold; - 基因型识别器是旧识别器(
heterozygous、fl/fl、flox、inhibited、overexpress*、tamoxifen等)与新识别器的并集,对整条记录联合判断,不依赖距离(190 字符间隔的变体全部被拦);普通 Gata4 调控背景不误伤。
4. 导出器(P1-2、P1-3):来源判断用解析后的有效阶段(stage_known):TS_all、空列表、空串、读不出的文本按未知;识别 E9.5 through E14.5、E9.0-E19.75(不再限制到 E19.5)、TS15 through TS23;mixed / unknown 的测量记录需要窗外依据;原先永远为真的测试断言改成真实判定。导出审计检查把实际的输入目录和输出目录也放进保护名单。
5. P2:服务对 KG 结果按最终外层序列化大小({"source","text"} 的 JSON,含转义)逐行删减后再序列化,order 的来源列表同样,不走文本截断(测试用含反斜杠的 DOI,外层 ≤ 6000 且可解码)。
6. 安全图行数更正:重新导出后各任务行数为 t1 782、t2_heart 662、t2_embryo 853、t3 932(上一版报告误写 925)。相对 v3 版减少的全是 MARKER_FOR(t1 26、t2_heart 14、t2_embryo 70、t3 61 条按集合比较;原因是 mixed / unknown 来源且无已知窗内阶段的测量边不再导出)。
真实连通(VM):对 lineage / markers / regulation 三个真实查询跑了发现进程(共 54 个候选逐页入队),再用审核命令生成下一版:50 个去重候选,t1 上 accept 22(21 条干净来源的测量边 + 1 条本体边)、reject 18(TS17 / TS19 阶段、定量名称)、hold 10(背景边带文本 / 数字,无法区分结论与测量),下一版 t1 新增 11 行。这些都在临时目录里演练,没有改动真实安全图或私有区。
测试:test_kg_proxy.py 重写,与 test_service 合计 61 个,VM 与 Spark 全 OK:四轮 Codex 的全部反例(Theiler 区间 / 列表、190 字符间隔的 knock out 及 heterozygous / fl/fl / inhibited 变体、mixed / unknown 来源、cardiomyocyte count 137、背景 lookalike、Hand2 (Gata4 knockout embryos)、3000 行与页数边界、含反斜杠的 DOI)、响应与实时 API 无关、发现进程逐页入队与失败保留、审核并入与旧版不变、私有区权限 / 符号链接 / 重叠检查。
需要主会话合入(controller.py,没改):start_research 写 rcfg["kg_task"]、kg_dir(指向本次要锁定的版本目录,如 data/external/vekg/safe 或某个 safe_v<N>);该版本目录的 <task>.tsv 与 MANIFEST.json 的 sha256 进锁;kg_vekg_dir 只用于保护名单(可不设);package 打包必须排除 ~/vec/audit_private/;外层循环:run 之间依次运行 python -m agent.research.kg_discover --once、python -m agent.research.kg_safe review-queue,把 DIFF.md 差异报告写入会话日志,下一次 run 才选用新版本目录。已知局限:run 内答案不含当次 run 期间才发现的新知识(以后版本才有);order 只比较各实体通过审查的最早 Theiler 阶段;规则比官方"允许的背景知识"口径略严。
4. 成本看板单价
agent/configs/pricing.yaml 原本是 llm_usage.py 的 subscriptions / metered 布局,而看板 usage.py 只认 models:,所以看板其实一直没有价格。
pricing.yaml:Token Plan、Claude 订阅(claude-cli)、新增 Codex 订阅(gpt-6-astra,usage 记录无 provider,故providers: [null])标billing: subscription、monthly_fee: 0(单价记 0,只计 token 与调用数);Opus API 与百炼按量标billing: metered、price_source: unknown,价格保持 null。仓库里没有任何可靠价格记录(grep notes/、configs 无),所以没有填任何数。各条加了plan:(Token Plan / Opus / ChatGPT / Bailian),沿用邮件里已有的额度桶名。agent/dashboard/usage.py:load_pricing把上述布局转成models:形式(_from_llm_usage_layout);新增price_state();_sum带出price_unknown/subscription_only;fmt_cost对未知单价显示“单价未知”(部分未知显示“部分模型单价未知”),today_line同样。agent/dashboard/pages.py:用量页成本列、额度磁贴对订阅显示“订阅(单价 0)”,对未知单价显示“单价未知”。- 测试:
usage_test.py新增test_llm_usage_pricing_layout;llm_usage_test.py原test_null_prices断言旧的全 null 文件,改成test_shipped_prices(订阅单价 0.0,Opus API 仍 incomplete)。