From 7ea08fb16a4afb11263642422be53a85dfbf9b60 Mon Sep 17 00:00:00 2001 From: xcs Date: Thu, 27 Aug 2026 03:25:08 +0800 Subject: [PATCH] =?UTF-8?q?ACT-WORK-05:S3=20=E5=86=99=E7=9B=98=E5=89=8D?= =?UTF-8?q?=E5=85=88=E8=87=AA=E6=A3=80=E5=B7=A5=E5=85=B7=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E6=95=B0,=E5=A0=B5=E6=AD=BB=E6=BC=8F=E8=B0=83=20recommend=20?= =?UTF-8?q?=E6=AD=BB=E5=BE=AA=E7=8E=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实测(R2,两候选)在 S3_assess 熔断:doneWhen 有一半读 tool_..._recommend_calls > 0,而模型只调了 compare-parameters、没调 recommend 就直接写盘、自己拍了推荐等级(数据面看着对,机制上是它自己 定的,绕过了"能算的不问 LLM"这条设计意图)。doneWhen 永远不真、finish 必被拒;拒回文案只说"是不是该写的行/列漏了",把模型的注意力导向行列, 于是连续5拍死循环重写同样的18行明细、2行关系,直至熔断——拒回指错方向 比不给指引更糟这条设计律的又一次现形。 不动 doneWhen(那半句工具读数正是"机检只读基座数"的落点)。改动收在 guide 两处:第二拍结尾点出"推荐等级只能由工具算,不调它这一步收不了步"; 第三拍开头加一道自检——写盘前先看两个基座读数是否都>0,recommend 还是 0 就回去调它,不要往下写更不要自拍推荐等级。 包内测试钉死:第三拍段落里必须同时出现两个工具读数名与"自检/先看/必须>0" 这类先决语,第二拍段落里必须点出"推荐等级只能由它算"与"收不了步"。 版本号 1.2.1 → 1.2.2。 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh --- .../__tests__/steps.test.ts | 12 ++++++++++++ .../part-substitution-assessment/src/case-model.ts | 10 ++++++++-- .../part-substitution-assessment/src/manifest.ts | 2 +- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/activities/part-substitution-assessment/__tests__/steps.test.ts b/activities/part-substitution-assessment/__tests__/steps.test.ts index 39d2ec8..d58983a 100644 --- a/activities/part-substitution-assessment/__tests__/steps.test.ts +++ b/activities/part-substitution-assessment/__tests__/steps.test.ts @@ -154,6 +154,18 @@ describe('说明书与剧本的骨架', () => { expect(s3).toContain('工具调用后同拍其余动作作废'); }); + it('S3 第三拍写盘前先自检两枚工具的调用次数(2026-08-27 实测:漏调 recommend 会让 doneWhen 恒假、模型死循环重写同样的行)', () => { + const s3 = ASSESSMENT_STEPS.find((s) => s.item === 'S3_assess')?.guide ?? ''; + const thirdPass = s3.slice(s3.indexOf('第三拍')); + expect(thirdPass).toContain('tool_part_substitution_assessment_recommend_calls'); + expect(thirdPass).toContain('tool_part_substitution_assessment_compare_parameters_calls'); + expect(thirdPass).toMatch(/自检|先.*找这两个数|必须\s*>\s*0/); + // 第二拍那一段要点出「推荐等级只能由工具算,不调它收不了步」,别让模型自己拍推荐等级 + const secondPass = s3.slice(s3.indexOf('第二拍'), s3.indexOf('第三拍')); + expect(secondPass).toContain('推荐等级只能由它算'); + expect(secondPass).toMatch(/收不了步|收不掉/); + }); + it('S0 剧本写明都在库时数据工单两项本就不会进「当下可为」;S0d_p08_master 剧本按候选总数判断该不该问人(=1 必建、≥2 才问、原 PN 硬阻塞)', () => { const s0 = ASSESSMENT_STEPS.find((s) => s.item === 'S0_bind')?.guide ?? ''; expect(s0).toContain('当下可为'); diff --git a/activities/part-substitution-assessment/src/case-model.ts b/activities/part-substitution-assessment/src/case-model.ts index bc095c0..8ff9838 100644 --- a/activities/part-substitution-assessment/src/case-model.ts +++ b/activities/part-substitution-assessment/src/case-model.ts @@ -452,9 +452,15 @@ difference_status / compare_result / risk_level / impact_statement / unknown_dis · 每一对都带 evidenceRef —— 硬约束没有证据引用工具不判 MATCH,那正是要的。 调完这一拍结束。 -【第二拍:只调 \`${TOOL_RECOMMEND}\`】把第一拍出参里每个候选的 counts 原样传:{"candidates":[{"candidateInternalPn":"…","counts":{…}}, …]}。调完结束。 +【第二拍:只调 \`${TOOL_RECOMMEND}\`,别的什么都不做】把第一拍出参里每个候选的 counts 原样打包传进去:{"candidates":[{"candidateInternalPn":"…","counts":{…}}, …]}。调完这一拍结束。 +**推荐等级只能由它算**——你自己填一个进 \`substitution.relation.recommendation_level\`,数据面看着像对的,机制上却是你拍的; +而且这一步的收步判据数着这枚工具的调用次数,不调它这一步永远收不掉。 -【第三拍:写盘,一拍连做,一次都不再调工具】 +【第三拍:写盘前先自检,再一拍连做写盘,一次都不再调工具】 +动手写之前,先在局面的基座读数里找这两个数:\`tool_part_substitution_assessment_compare_parameters_calls\` 与 +\`tool_part_substitution_assessment_recommend_calls\`。**两个都必须 > 0 才能写盘**。若 \`recommend\` 那个还是 0, +说明第二拍没调成——**回去调它**(入参就是第一拍出参里每个候选的 counts),不要往下写、更不要自己拍推荐等级。 +这一步的收步判据里就有这两个数,**不调够,写多少行都收不了步**。 ① 逐条 \`substitution.difference_item\` 同 PK 覆盖写:difference_status / compare_result / risk_level 照第一拍出参**一字不改**; impact_statement(MATCH 写「无」;其余说清对应用 / 验证 / 报价的影响,如「输入上限 32V < 客户 40V,不能上板」); difference_status = unknown 的行**必须**写 unknown_disposition(「挂验证任务 VAL-<案号数字串>-01:实测使能阈值」或「向原厂补来源」);confidence(0~1)。 diff --git a/activities/part-substitution-assessment/src/manifest.ts b/activities/part-substitution-assessment/src/manifest.ts index 5346f86..a952602 100644 --- a/activities/part-substitution-assessment/src/manifest.ts +++ b/activities/part-substitution-assessment/src/manifest.ts @@ -89,7 +89,7 @@ export const manifest: ActivityManifest = { identity: { activityId: ACTIVITY_ID, name: 'ACT-WORK-05 · 差异评估', - packageVersion: '1.2.1', + packageVersion: '1.2.2', category: '需求分析与方案匹配', sdkVersion: '0.1.0-m5', checksum: 'dev-checksum-part-substitution-assessment',