6.8 KiB
接诊标记与必需附件流程:只读审查
日期:2026-10-27。仅审查源码、无患者内容的 Dify 提示模板备份,并用纯合成资料执行传输替身。没有读取真实请求全文、患者数据或凭据,没有真实网络、模型、数据库调用,没有修改生产代码或 Dify。
结论
未复现“必需附件/分批流程把最终接诊标记挤离消息开头”的服务端缺陷。 当前 PHP 代码和真实构造函数的合成复现均表明,直接临床回答及分批后的最终综合回答,其 query 都以以下标记开头,偏移为 0:
[TCM_CLINICAL_RESPONSE:tcm-consultation-task-analysis-r37]
分批附件的中间提取阶段确实没有该标记,这是有意的操作隔离;它没有携带原接诊问题,并只要求附件证据 JSON,其 delta 也不会交给临床输出校验/UI。因此,“中间提取没有接诊前缀”不能证明最终回答丢失了前缀,也不能直接解释最终可见回答的 PULSE_EXCLUDED_FROM_ASSESSMENT。
主代理提供的在线事实是:Qwen/OpenAI 已发布所审 SYSTEM,当前工作站仍在约 10:35:54→10:36:05 的 5 附件接诊请求中触发脉象规则失败,且远端持久状态仍 running。这些在线事实由主代理取得,本分工没有重新发在线请求。此次只读结果更支持继续核对 Dify 组装后的提示条件是否明确、以及相互冲突的通用脉象条款是否仍影响模型,而不是先改 PHP 标记位置。
源码链路
DiagnosisAiLogic.php:1633的assistantReferenceScope()返回consultation;:692–711的正式 streaming 调用显式设置reference_scope和knowledge_mode=clinical。DifyChatService.php:307–324在发现 required_files 时先进入协调器;内部物理调用带_required_batch_internal=true,随后才执行实际的ClinicalKnowledgeReference::augmentCaseQuery($query, true)。blocking 分支在:100–118具有同一顺序。ClinicalKnowledgeReference.php:11–20从TcmClinicalResponsePolicy::consultationReference()构造完整前缀,并将前缀放在原 query 之前;已有完整前缀时保持幂等。DifyChatService.php:594–608单独构造【附件分批证据提取】,将knowledge_mode改为evidence,因此主动跳过临床规则注入。原 reference_scope 并没有被改写成其他流程;临床规则跳过的原因是 evidence 操作。:606–607将该阶段输出发送给只返回 true 的内部回调,不交给原临床 delta 回调。DifyChatService.php:656–659构造$finalQuery = $query . $supplement['text'],继承原$internal临床选项,再调用内部 chat/streamChat。最终补充只追加到原问题后,随后由第 2 步在最前方加接诊前缀。- 缺附件说明、实际附件映射和 OCR 证据(
:1108、:1117、:1164)都追加到既有 query;withAttachmentManifest()(:1659)同样追加。Dify 的 request spec(:1330–1340)将 query 作为原样字符串发送;没有发现另一个本地包装器在前面加标题。
TcmClinicalResponsePolicy.php:27–42 的接诊前缀自身包含“不采集脉象、不设栏目、不补采、不把缺脉作为限制”的实际规则,不只是版本名称。
合成复现
在临时 PHP 文件中复用 server/tests/DifyClinicalAttachmentBatchTest.php 的 cURL、多路传输与附件上传替身,删除该测试的 ClinicalKnowledgeReference 替身,改为加载实际 ClinicalKnowledgeReference.php 与 TcmClinicalResponsePolicy.php;实际 DifyChatService 源码没有改写。构造合成接诊问题,分别运行两种 profile、blocking/streaming、0/2/5/7 个合成附件(批上限 3)。只输出阶段布尔值、附件计数,未输出 query 全文。临时文件执行后清理。
36 条断言通过,真实网络/模型调用均为 0。 两种 profile、两种模式结果一致:
| 原附件数 | 实际阶段及每阶段附件数 | 提取阶段 marker | 最终临床 query marker_at_start | 原问题保留 |
|---|---|---|---|---|
| 0 | 直接回答 0 | 不适用 | true | true |
| 2 | 直接回答 2 | 不适用 | true | true |
| 5 | 提取 3 → 提取 2 → 最终综合 0 | false / false | true | true |
| 7 | 提取 3 → 提取 3 → 提取 1 → 最终综合 0 | false / false / false | true | true |
还执行了原有离线测试:
php server/tests/DifyClinicalAttachmentBatchTest.php:通过。php server/tests/TcmTaskScopedTransportR37Test.php:136 条检查通过,24 次替身传输,0 次网络调用。
旧 batching 测试主要断言原 query 保留且只给最终阶段加参考规则;它使用 ClinicalKnowledgeReference 替身,没有覆盖真实 consultation marker。此次临时组合补上了这个审查空隙,但没有将审查替身代码写入生产或既有测试。
Dify 模板检查与最小修复建议
安全模板备份 artifacts/ai_no_answer_fix_20261027/dify_qwen_final_draft_node.json 和 OpenAI 同名备份的 SYSTEM 都含“仅当消息开头的服务端前缀”为该标记的条件。其 USER 编辑器第一项是 query 变量组件,后面才追加文档提取说明;编辑器纯文本导出显示的“用户输入 / query”是变量组件的 UI 标签,不能据此声称模型实际收到这个字面前缀。未发现模板备份在 query 变量前另写静态文字。
建议主代理先处理 提示作用域及相互冲突条款,不要为尚未复现的 PHP 丢标记再加重复标签:
- 将“消息开头”明确为“服务端注入的 query 内容开头”,以免被解释为 SYSTEM 本身开头、整个聊天历史开头或拼接模板外壳开头。仅以服务端 query 前缀选择接诊分支;病历/附件中的同名字串不得切换规则。
- 对通用模板中仍写着“脉象独立成行”“舌或脉缺失时限制新增方药”“长报告脉象独立成段”的具体位置加上仅适用于非接诊流程的作用域,或将这三条合并成明确互斥的接诊/非接诊分支。不要只在 SYSTEM 顶端叠加例外,而在后半段继续给出同一流程的相反指令。实际接诊段仍应禁止脉象栏目、缺脉限制和补采建议。
- 保留纯附件提取/JSON 操作的明确豁免。给 extraction 强塞整段接诊临床规则并不能修复最终回答,反而可能破坏只输出证据 JSON 的契约。
本次证明的是当前 PHP 构造出的 wire query 边界,没有读取 Dify 对本次真实请求最终组装的完整 LLM 消息,因而不能证明模型端一定按同一条件理解或执行。若需进一步区分,可由主代理在既有授权只读诊断中只取“实际 query/LLM USER 起始标记是否匹配”的布尔结果和阶段枚举,避免输出正文。当前证据不足以将失败归因于 batching 改写了最终标记,也不能将离线构造测试当作真实临床回答通过。