Просмотр исходного кода

fix(research67): 对齐结果复核工具文本与客户端操作说明

区别查询既有报告和提交新的结果复核,保持原会话并使用自然语言。工具输出同步操作类型、综合结果和原方案未重审状态,不再提示下载不存在的新执行 IR。桥接回归 83 项及包类型检查通过。
邵勃源 10 часов назад
Родитель
Сommit
0822ab974e

+ 2 - 1
.opencode/README.agentpaas.md

@@ -61,7 +61,8 @@ export AGENTPAAS_API_KEY=你的租户APIKey
 数值核对卡片同时显示原文数字片段的关联情况;未关联项的原句、路径和位置保存在 `numeric_check.artifact` 指向的检查记录中。计划值也可能被列出,因此未关联不直接判错;该计数不是科学结论正确率或语义核验覆盖率。
 后台可对遗漏位置做一次有界补查,卡片展示恢复关联、模型判断非结果和无法对应的数量。补查未完成不会清空首次检查;新关联仍须通过相同的引用和数值检查,原有错误不会被覆盖。
 `observation-ledger.json` 将原始观察与模型解释分开保存;`research-decisions.json` 提供带适用条件、检索词和影响路径的证据任务。
-只复核已有实验时可明确说“使用 reuse_observations,不重新执行或改变参数”。服务端核对已有记录、执行器版本及参数;缺少原观察或参数不一致会返回修订问题,不启动替代实验。
+查看已有报告用原任务的查询/下载;需要重新复核、纠正或形成新解释,可在同一会话直接说“只核对刚才的结果,不新增实验”。启用本体优先和结果综合的服务端会区分本次操作,保留研究问题、假设与计算记录;无需用户填写 reuse_observations 等内部参数。缺少有效父记录时保留未完成状态,不启动替代实验。
+工具文本和卡片都展示 science.operation 与原方案是否重审。复核结果通过不等于原方案问题已解决;纯复核没有新的 execution-ir.json,下载以本次实际产物清单为准。
 
 `completed` 表示执行结束;`partial` 是部分完成;`needs_clarification` 表示需要补充信息;`paused` 和等待审批均不等于失败。科研状态另行保留,模拟已执行也不代表现实结论已验证。
 

+ 6 - 4
.opencode/agent/research67.md

@@ -19,7 +19,7 @@ tools:
 
 参数:
 - agent_name: research67
-- mode: 问候、能力询问与概念澄清用 chat;明确启动研究、检索、研究计划或修订用 iterate。
+- mode: 问候、能力询问与概念澄清用 chat;明确启动研究、检索、研究计划、修订或重新复核已有结果用 iterate。iterate 是提交模式,不代表一定执行新实验。
 - conversation: 默认 continue;用户明确换到不相关的新问题时用 new。
 - question_id: 用户明确提供的 1–125 题号。没有编号时不猜编号,不从个人知识猜题目正文。
 - feedback_kind: 人工修改意见用 human;程序检查、执行结果或证据补充分别用 program_check、execution_result、evidence。
@@ -32,14 +32,16 @@ tools:
 明确配置并通过准入时可执行服务端已登记的实验适配器;以本次能力与执行凭据为准,不按题号推断能力。
 有 research.science.research_ir 时,说明 IR 状态、本轮实际执行的实验子程序和高层计划行动数。
 Lambda/CEK 的类型检查及运行步骤只描述执行程序,不能证明科学假设正确;高层行动不能因一个子程序完成而全部标为完成。
-查看细节时按产物 API 获取 execution-ir.json 及对应的 cek-receipt 凭据,不读取远端服务器路径。
+仅从实际产物清单中按 API 获取 execution-ir.json 及对应的 cek-receipt 凭据,不读取远端服务器路径;纯结果复核没有新的执行 IR,不猜测不存在的文件。
 有 observations 时分别说明已有观察、作者解释和模型审阅的数量;原始观察与解释见 observation-ledger.json。
 有 science.synthesis 时,优先通过产物 API 获取 research-synthesis.json,按子问题说明回答、实验编号、适用边界和下一步。
 它是对已有结果的综合解释,不能当成新实验;reviewed 表示模型复核通过,不改变 needs_evidence 等研究状态。
 一个综合判断可引用多个前置实验;不能因为它没有新增计算,就说整个子问题尚未分析。只转述后端明确给出的 finding。
 仅查看结果或下载总结不启动新任务,不要求用户再次运行同一实验。
 优先解释 science.next_step,按证据任务补充材料或修订原研究,不把实验记录存在说成反馈已解决。
-用户只要求复核已有结果时,应明确保留原参数并使用 reuse_observations;不能用重新提交同一模拟代替复核。
+用户要求重新核对、纠正或形成新的结果解释时,在原会话以 iterate 提交一次原始自然语言请求,服务端会区分结果复核与继续研究。不要把请求改写为内部的 reuse_observations 参数,也不要求用户知道执行器协议。
+有 science.operation 时,明确其类型:review_results 保留科学定义及实验记录,只复核解释;clarify 等待用户说明意图,不自动重新提交;continue_research 才进入原研究迭代。仅查询、下载、转述已完成的报告仍使用原任务,不新建运行。
+方案的 not_reassessed 表示本次没有重审,旧问题仍保留;复核通过不等于修复了研究设计。
 是否接入其他流程或仪器,必须以远端实际返回为准;不要自行介绍论文写作、实验授权或其他未确认能力。
 
 status=completed 只表示执行结束。若有 research.science.status,优先原样解释:
@@ -62,7 +64,7 @@ status=completed 只表示执行结束。若有 research.science.status,优先
 未给出题号时继续保留原始问题文本,不猜题号;后端未返回的任务卡字段或版本差异不得自行补造。
 对纯问候或能力查询,仅转述远端确认的能力,不扩写未确认流程,不自行启动研究。
 
-客户端辅助操作:查询能力用 agentpaas_agents;用户材料用 agentpaas_upload 上传并将 file_id 传给 agentpaas_run.input_file_ids;进度/暂停/恢复/审批用 agentpaas_task 操作原 run_id;明确选择历史任务继续修订时,先用 agentpaas_task 的 select 绑定该 run_id 的会话与父版本,单纯查询不切换上下文;取回产物用 agentpaas_download。辅助操作不受“每回合只执行一次 agentpaas_run”的限制。不要把查询、下载或恢复变成新的科研任务,不把服务端路径当成本机路径。只有用户明确要求新一轮修订时才再次运行。
+客户端辅助操作:查询能力用 agentpaas_agents;用户材料用 agentpaas_upload 上传并将 file_id 传给 agentpaas_run.input_file_ids;进度/暂停/恢复/审批用 agentpaas_task 操作原 run_id;明确选择历史任务继续修订时,先用 agentpaas_task 的 select 绑定该 run_id 的会话与父版本,单纯查询不切换上下文;取回产物用 agentpaas_download。辅助操作不受“每回合只执行一次 agentpaas_run”的限制。不要把查询、下载或恢复变成新的科研任务,不把服务端路径当成本机路径。用户要求新的研究、修订或结果复核时才再次提交;仅查询与下载不重新运行。
 
 长任务提交使用 agentpaas_run.background=true,受理后向用户报告任务编号与真实状态,不在同一回合无限轮询。重新打开或不知道原编号时用 agentpaas_tasks 找回;用 task/watch 观察、cancel 停止。后台任务等待确认时需用户明确决定,不能自行批准。每次新提交会自动检查已知资源故障;要验证模型或知识检索,用 agentpaas_check(check_model 会调用模型)。
 

+ 10 - 2
.opencode/lib/agentpaas.ts

@@ -392,10 +392,14 @@ export function formatRunResult(agentName: string, run: Record<string, unknown>,
   const artifacts = Array.isArray(run.artifacts) ? run.artifacts : []
   const metadata: Record<string, unknown> = { ...run, ...extra, status, artifacts }
   const science = asRecord(asRecord(run.research)?.science)
+  const operation = asRecord(science?.operation)
+  const resultOnly = ["review_results", "clarify"].includes(getString(operation?.operation))
+  const acceptance = asRecord(asRecord(science?.acceptance)?.dimensions)
+  const synthesis = asRecord(science?.synthesis)
   const researchIR = asRecord(science?.research_ir)
   const observations = asRecord(science?.observations)
   const irLabels: Record<string, string> = {
-    not_compiled: "尚未编译", plans_only: "仅规划", blocked: "暂不能执行", ready: "可执行",
+    not_executed: "本轮未执行", not_compiled: "尚未编译", plans_only: "仅规划", blocked: "暂不能执行", ready: "可执行",
     executed: "实验子程序已执行", reused_observations: "复用观察", failed: "执行失败", cancelled: "已取消",
   }
   const hints: Record<string, string> = {
@@ -424,11 +428,15 @@ export function formatRunResult(agentName: string, run: Record<string, unknown>,
         : `AgentPaaS ${agentName}(${labels[status] ?? "状态待核查"})`,
     output: [
       ...(science ? [`研究状态:${scientificLabel(science.status)}。执行结束不代表科学结论成立。`] : []),
+      ...(operation?.operation === "review_results" ? ["本次操作:复核已有结果;保留研究定义与实验记录,未重新规划或执行实验。"] : []),
+      ...(operation?.operation === "clarify" ? ["本次操作:等待说明研究意图;未启动研究或实验。"] : []),
+      ...(asRecord(acceptance?.plan_review)?.status === "not_reassessed" ? ["原方案:本次未重审,原有待修订项继续保留;review-parent-gate.json。"] : []),
+      ...(synthesis ? [`结果综合:${({ reviewed: "模型复核通过", needs_revision: "解释待修订", review_pending: "待复核", uncertain: "存在不确定项" } as Record<string, string>)[getString(synthesis.status)] || "状态待核查"};${synthesis.answered ?? "未报告"}/${synthesis.total ?? "未报告"} 项限定范围回答;不是独立科学验收。详情见 research-synthesis.json。`] : []),
       ...(getString(science?.next_step) ? [`下一步研究:${getString(science?.next_step)}`] : []),
       ...(observations ? [`观察记录:${observations.recorded ?? "未报告"} 份;已解释 ${observations.interpreted ?? "未报告"} 份;模型审阅认可 ${observations.reviewed ?? "未报告"} 份。详情见 observation-ledger.json。`] : []),
       ...(researchIR ? [
         `科研 IR:${irLabels[getString(researchIR.status)] || "状态待核查"};本轮已执行 ${researchIR.executed_kernels ?? "未报告"} 个实验子程序。`,
-        `高层研究计划含 ${researchIR.planned_actions ?? "未报告"} 项行动,不代表全部已执行;详情见 execution-ir.json。`,
+        ...(!resultOnly ? [`高层研究计划含 ${researchIR.planned_actions ?? "未报告"} 项行动,不代表全部已执行;详情见 execution-ir.json。`] : []),
         ...(researchIR.engine === "lambda_cek" ? ["执行引擎:Lambda / CEK;程序凭据不代表科学结论成立。"] : []),
       ] : []),
       ...(science?.execution_status === "conditional_diagnostic_completed"

+ 25 - 0
packages/opencode/test/tool/agentpaas-research-ir.test.ts

@@ -36,3 +36,28 @@ test("legacy research result does not invent an IR or execution engine", () => {
   expect(result.output).not.toContain("科研 IR:")
   expect(result.output).not.toContain("Lambda / CEK")
 })
+
+for (const operation of ["review_results", "clarify"]) {
+  test(`research tool output exposes ${operation} without inventing new executable artifacts`, () => {
+    const science = {status:"review_pending", operation:{operation},
+      next_step: "说明本次想复核的内容", research_ir:{status:"not_executed",planned_actions:0,executed_kernels:0},
+      acceptance:{dimensions:{plan_review:{status:"not_reassessed"}}}}
+    const result = formatRunResult("Research67", {status:"completed", research:{science}})
+    expect(result.output).toContain(operation==="review_results" ? "复核已有结果" : "等待说明研究意图")
+    expect(result.output).toContain("原有待修订项继续保留")
+    expect(result.output).toContain("科研 IR:本轮未执行")
+    expect(result.output).not.toContain("execution-ir.json")
+    expect(result.metadata.research).toEqual({science})
+  })
+}
+
+test("reviewed interpretation remains distinct from unresolved plan checks in tool text", () => {
+  const result = formatRunResult("Research67", {status:"completed",research:{science:{
+    status:"needs_review", operation:{operation:"review_results"},
+    synthesis:{status:"reviewed",answered:1,total:2},
+    acceptance:{dimensions:{plan_review:{status:"not_reassessed"}}},
+  }}})
+  expect(result.output).toContain("模型复核通过;1/2 项限定范围回答;不是独立科学验收")
+  expect(result.output).toContain("原方案:本次未重审")
+  expect(result.output).toContain("research-synthesis.json")
+})