让模型跑一份总结、一份分析、一次 code review,然后我盯着结果判断它到底在说事还是在编。这个动作一天十来次。以前每条结论都回头翻原始材料核对,烦得不行。上个月往输出契约里塞了四行,现在它编不出来会自己说编不出来。
这招不是我琢磨出来的。10 月 10 号在 DEV 上刷到一个叫 WildProof 的项目,Hacktoberfest 那场 AI 挑战第一周的参赛项目,作者 Dhruv Jani。离线优先的野外观察 PWA,前端 React + TypeScript,后端 FastAPI,分析走 Ollama 起的 Gemma 2B,考察记录扔 IndexedDB,没信号也能记,回到有网再跑。我本来是当乐子看的——这种 hackathon 项目十个里有九个是 README 比代码长。代码 MIT,仓库在 JaniDhruv/wildproof。
结果捡到一条设计决策,当场就抄了。
作者那句话我很认同:报告外观上的成功,掩盖不了分析环节的失败。换成我的说法,模型给你一份格式完美、语气笃定的东西,是最坏的情况,因为它把失败穿上了成功的外套。你多花的不是生成那几秒,是事后核对的那二十分钟。
他做了三件事。每条陈述必须归类:直接观察、推断、未知,并且挂上证据编号。没拍到叶子背面,就不许写“该植株不具绒毛”,只能写“未见叶片背面,无法判断”。后端要严格 JSON,Pydantic 校验,模型返回格式不对就修一次,再不对就明着报错,不给你一份凑出来的报告。第三条我觉得最值得抄:模型不可用的时候,采集的数据照样在本地,界面挂一个明确标注的回退状态,不假装分析跑过了——这个状态最容易被写成“加载中”,然后永远转圈。
仓库里还有个细节我顺手记下了。他们备了 TabPFN 的对比脚本,但当前那套 Torch 组合跑不起来,生产端点就老老实实标着“基线启发式”,不声称 TabPFN 在跑。这种主动说明不足的地方,在这种项目里真不多见!
我抄的是前两条。往你的 CLAUDE.md 或者 system prompt 里加这么一段:
输出必须是 JSON:summary、claims[]、missing_evidence[]、next_steps[]。
每条 claim 带 type(observation | inference | unknown)和 evidence_ids。
evidence_ids 为空的,type 不许写 observation。
最后一行是关键。前两行是格式,最后一行是态度。我以前一直以为结构化输出就是给下游解析用的,后来才反应过来,它最大的用处是让模型自己没法含糊。
再配一个“只修一次”的解析:
try:
return Report.model_validate_json(raw)
except ValidationError:
raw = repair_once(raw)
return Report.model_validate_json(raw)
修一次不行就让它炸。别在 except 里套三层保底处理,最后返回个空报告——那才是真正的失败伪装。
⚡ 加完这几行,第一个变化不是准确率,是措辞。它开始写“笔记里没有记录时间,因此无法判断这是晨昏活动还是昼行”,而不是“该物种主要在黄昏活动”。前者你扫一眼就知道该补什么,后者你得回去翻原始材料。
这周用下来的体感:以前一份编造的报告,我平均得花十几二十分钟才发现它在编;现在它自己把缺口标出来,扫三秒就定位!这几行我改了半小时,其中二十分钟在纠结字段名,但一次就回本。回不去了。
有坑,得说。
小模型会滥用 unknown。2B 那个量级尤其明显,你给它开这个口子,它会把一半结论往那儿推,报告变得没法用。我的做法是要求每条 unknown 必须跟一句“缺什么才能判断”,写不出缺什么的就不许标 unknown。它立刻就收敛了,因为“我不知道”比“我知道”更费字,模型也怕麻烦。
另一个坑:这套东西只管“编”,不管“错得有理有据”。证据本身有问题的时候,标成 observation 一样是错的。分类只告诉你这句话的来源,不替你做判断。别指望加个字段就把脑子外包出去。
还有个我没抄的。他们用 Temporal 把分析拆成证据规范化、模型分析、报告校验、质量打分几步,哪步失败就重试哪步,已经采集到的考察不用重来。思路好,但我的脚本几十秒跑完,为这点时间上工作流引擎不值。等哪天单次跑超过两三分钟,或者一次重跑要烧掉几分钟的采集成果,再回来看。频率不够的花活儿,先记着就行。
四行 prompt,五分钟成本。你手里要是也有那种“看起来像成功”的失败,先加个字段试试,比回头加校验代码快得多。有更省事的写法,教教我。
