从“我想研究一下”变成一个可保存的任务

假设已经收集到一个 Agent SDK 的发布记录。用户关心的是:这次关于工具输出或运行状态的变化,对自己设计 Java Agent 有什么启发?如果只是把发布正文发给模型,得到一段回答后,后续很难说明用的是哪个版本、哪些原文、执行过哪些动作,以及刷新后还能不能读到相同结果。

本项目先把一次研究变成独立资源。用户选择一个已经入库的 Signal,提交问题;服务固定来源和用户画像版本,组织证据提取、受限 Agent Run、报告组装与可选实验提议,最后保存同一项 Research Run。

这一章的重点是材料和状态怎样连接。它解释已实现的受限流水线,不把它扩写成自动浏览全网的研究员。

先区分六个容易混淆的名字

记录 回答什么问题 为什么单独保存
SourceDocument 研究时保存的原文是什么? 同一来源后续可能更新,需要固定内容版本
Signal 哪项变化或材料值得关注? 给研究提供可选择的入口,连接来源身份与文档
Claim 材料中提出了什么主张? 便于检索、分类和围绕命题判断
Evidence 这项主张引用了哪里? 保存 quote、位置与 hash,允许回查原文
Agent Run 循环做过什么、怎样停止? 保留步骤、候选、用量、恢复与终态
Research Run / Report 这次问题最终得到什么? 连接子运行、来源与可重读研究结果

它们之间不是可以随意替换的“文本”。Signal 是研究入口,Claim 是可检索的主张,Evidence 是引用记录;Report 则将这些材料组织成面向问题的结果。

这里还有一个关键区别:研究选定的 Signal 是锚点,最终实际引用的 Claim 可以来自画像范围内的其他已有材料。 因此报告应分别保存研究锚点和引用来源,不能因为用户点开某个 Release,就假设所有结论都只引用该 Release。

请求入口为什么只接受少量字段

当前创建入口是 POST /api/v1/research-runs。下面只表示请求体形状,角括号内容需要替换为已有记录标识;它不是可直接执行的调用,也不包含鉴权配置。

{
  "signalId": "<已有 Signal UUID>",
  "question": "这项变化对工具结果进入上下文的设计有什么启发?",
  "idempotencyKey": "<本次逻辑请求的 UUID>"
}

请求没有让客户端指定任意 URL、命令、工具名或外部代码。服务先校验身份、问题、Signal 与可用画像,再捕获来源文档版本。这样输入决定“研究哪项已有材料、回答什么”,不会悄悄扩大为任意执行权限。

幂等键表示一次逻辑请求。网络超时后,要用同一键与相同请求体重试;同键换问题会被拒绝。否则,一个客户端重试可能变成第二次付费子调用,甚至留下两份看似相同、实际来源不同的研究。

沿一次研究往下走

Agent Evidence Lab一条研究请求,连接六类持久化材料

这里画的是已合并基线中的 Signal 锚定工作流;不是自动浏览整个互联网。

  1. 01选择 Signal 与问题

    校验身份、已有来源、用户画像和幂等键,固定来源快照。

  2. 02提取 Claim / Evidence

    复用或提取这份文档中的主张,校验原文引用。

  3. 03启动受限 Agent Run

    在画像范围内检索证据,解释其对问题的支持或反驳关系。

  4. 04组装研究报告

    从数据库解析引用与来源版本,不接收模型编造的引用地址。

  5. 05可选实验提议

    只判断固定模板是否适用;提议进入待审批,不在此执行实验。

  6. 06保存与读取

    研究 API 读取同一资源;完整原文复核是另一次显式保存的解读。

没有可用证据可以以 INCONCLUSIVE 结束;下游提议失败不应抹掉已经形成的证据报告。

图 02 · 工作流阅读示意。研究锚点、实际引用与可选实验提议是不同关系。

第一阶段是提取:复用或产生该文档的 Claim/Evidence。没有可用主张时,允许研究以 INCONCLUSIVE 结束。这个结果说明当前材料不足,不代表系统应该要求模型补出一个答案。

第二阶段将问题与锚点交给现有 M3 运行边界。Agent 在已有证据中搜索,模型解释候选与问题的关系,程序检查引用与完成条件。该阶段复用既有循环,不另造一套权限与恢复规则。

第三阶段是报告组装。ResearchReportAssembler 依据实际返回的 Claim ID 读回数据库记录,解析引用的文档、版本和 Evidence。模型不能自行提供一个看似权威的 URL 来替代这些关系。

最后,可选实验规划只判断目前支持的固定模板是否适用。适用时保存待审批计划;不适用或规划失败,也要如实表达。研究 Run 不自动批准或执行实验,提议状态不能改写证据结论。

工作流阶段与结果状态为什么分开

“代码执行到哪里”与“研究回答得怎样”是两个不同问题。当前阶段按下面顺序推进;失败或证据不足可以提前进入 DONE。

CREATED → EXTRACTING → RESEARCHING → REPORTING
                                    ↓
                           PROPOSING_EXPERIMENT → DONE

这是状态阅读示意,不是每次研究都会成功走完所有阶段。DONE 只表示父工作流不再推进,并不等于“命题成立”。

字段 示例 含义
stage REPORTING、DONE 父工作流当前处于哪个阶段
研究 status COMPLETED、INCONCLUSIVE、FAILED、BUDGET_EXHAUSTED 这次工作流的结果或停止原因
Agent stance SUPPORTED、CONTRADICTED、INCONCLUSIVE 模型相对问题命题的证据判断
实验提议状态 PENDING_APPROVAL、NOT_APPLICABLE、UNAVAILABLE 固定模板的提议是否可用,是否等待审批

例如,一个支持反命题的研究仍可能正常完成;而 INCONCLUSIVE 也可以是诚实且有价值的结果。把所有字段压成“成功/失败”会丢失这些区别。第五章进一步区分工作流完成与事实验证。

为什么保存子运行身份,而不只是保存最终回答

一个父研究可能跨过提取、模型决策与实验规划等边界。应用如果在其中一次调用后中断,需要判断那个子操作是否已经发生。如果每次重试都新建子运行,就可能重复调用、重复计费或用不同材料覆盖先前结果。

当前设计在分派前保留稳定子 ID,并利用持久化阶段、版本检查与租约继续或核对同一子操作。已经确认的结果可复用;结果不确定的调用必须保留不确定性,不能一概宣称“从来不会重复”。M3 的 Checkpoint 还负责子 Agent 内部更细的恢复。

报告中的用量也应来自实际子记录,并暴露完整性。记录到一部分 Token,不等于已记录全部费用;配置价格的预算估算也不等于供应商账单。

初次报告与完整原文解读为什么是两份记录

初次研究依赖提取和检索得到的候选。候选有数量与范围限制,可能没有覆盖问题需要的全部原文。为了检查遗漏,当前另提供显式的完整已保存原文复核。

操作 做什么 是否调用模型
GET /api/v1/research-runs/{runId} 读取父研究资源与已存报告 否
GET /api/v1/research-runs/{runId}/review 读取原问题、研究对象、已观察候选与已有解读 否
POST /api/v1/research-runs/{runId}/briefing 显式请求对绑定来源快照的补充解读 仅当前研究与提示版本首次取得生成资格时

补充结果按研究 ID 与提示版本保存。重复读取或同版本重复请求复用结果,不改写初次报告;提示版本升级后也不会因为一次 GET 自动生成新解读。超时、失败与结果不确定有各自处理,不以静默反复调用掩盖问题。

“完整原文”在这里指这项研究保存的特定来源快照。它不包含来源链接后的文档、PR、实现和测试。这个边界是理解当前能力和下一阶段深度研究的前提。

沿实现继续核查

索引以 src/main/java 下省略包前缀的路径表示;测试同样省略 src/test/java 包前缀。具体行为按本章标注的基线核对。

实现或测试 关注的契约
research/api/ResearchRunRequest.java、ResearchRunController.java 创建请求的有限字段、认证入口与 GET/POST 关系
research/application/ResearchRunApplicationService.java 来源快照、阶段推进、父子 ID 与失败处理
research/application/ResearchReportAssembler.java 从数据库解析引用,校验来源关系并决定研究终态
research/model/ResearchRunStage.java、ResearchRunStatus.java 阶段与结果的独立枚举
research/briefing/ResearchBriefingService.java、JdbcResearchBriefingStore.java 显式生成、版本唯一约束与保存复用
ResearchRunApiIT、ResearchRunRecoveryIT、ResearchBriefingApiIT 幂等冲突、恢复、私有读取与避免重复生成的验收

阅读这些文件时,可以给每条关系补三项说明:谁创建身份,谁拥有状态,谁有权推进下一步。能回答这些问题,才真正理解“一次研究”怎样从一次 HTTP 请求变成可持续保存的记录。