Technical Design 2A - jiale-li-orion/SecFusionAgent GitHub Wiki

SecFusionAgent Technical Design 2A

中文 · Application Backend & Product Interface | Technical Design 1 | Technical Design 2 | Technical Design 3

本文补齐 Technical Design 1 与 Technical Design 2 之间此前没有独立 owner 的 Application Backend / Product Interface。TD1 已冻结 M1–M3 Data Plane,TD2 已冻结 M4–M8 Task / Context / Perception / Capability / Policy / Decision Runtime;当前实现进一步证明,中间仍需要一层稳定的 application composition:用户的一次产品操作如何进入已有 domain runtime、哪些步骤同步完成、哪些步骤创建 durable TaskRun、HTTP 返回什么、前端读取什么、长任务如何继续更新。

本设计不修改 M1–M8 ownership,不重做 Agent Runtime,也不把 HTTP / UI 语义下沉到 domain package。本文只冻结 Presentation → Application → Domain Runtime → Infrastructure 四层边界,并定义第一版 Product API、Use Case、Read Model、错误、幂等、SSE、健康检查与测试 contract。

设计状态 — 2026-10-04

本文在 TD2 已进入实现后补充,优先解决已经出现的 integration friction,而不是提前设计未来产品。第一版以 FastAPI + REST + OpenAPI + SSE 为产品接口基线;WebSocket、GraphQL、BFF、多租户 IAM、复杂 API Gateway 不作为当前前置条件。内部 Task Event Plane 继续使用 PostgreSQL durable log + Redis Streams;前端产品接口不直接复用内部 TaskEvent schema。

早期实现 checkpoint(2026-09-29):Slice A / Slice B 已进入 executable 状态,Slice C 的第一版 Product QA routing 已闭环。apps/api/dependencies.py 已统一 request-scoped database session 与 RequestContext;HTTP edge 生成/回传 X-Request-ID,Product validation / Application failure 已映射为 RFC 9457 风格 ProblemDetail;/health/live、/health/ready、/health 已分离。StartInvestigationUseCase 已完成 target resolve → Case/EvidenceNeed → Task admission → Context/Budget/Execution/TaskRun → queue 的组合,InvestigationQueries 输出不泄漏内部 runtime schema 的 InvestigationView。

Slice C 当前实现为 POST /api/v1/questions → AskQuestionUseCase → shared Task admission。显式 LOOKUP / RETRIEVE 进入 read-only DecisionRole,创建真实 TaskRun / Budget / Execution coordinate,以 current Knowledge/Evidence 构造 lightweight state 后调用同一套 M6 ModelDecisionPlanner → DecisionService;confirmed claim/relation 继续携带稳定 EvidenceRef,bounded RETRIEVE 只把检索 passage 放入 tentative context,不提升为 confirmed fact。Product Question 的 canonical fact projection 已覆盖 claim 与 relation:claim 使用稳定 subject predicate = value 并在 source-specific / temporal identity 必要时保留 compact semantics;relation 使用共享 deterministic renderer 保留 state / source_semantics / version or scope / CSAF status / compact component+platform / justification / necessary NVD configuration coordinates,同时丢弃 source/vocabulary provenance 字段与巨大的 NVD root_snapshot,避免 applicability/VEX 语义在进入 M6 前被压扁。Question context 现在还支持有界 depth-2 development expansion:只对 root 的 references-development-object 目标继续读取下一跳,且只纳入 merged-as / head-commit / belongs-to-repo / has-parent-commit / release-contains-commit 等 closed development edges,并限制 target/edge 数量;不会把任意 Knowledge graph 无界展开进 prompt。对于没有显式 cve_id/object_id 的 generic RETRIEVE,检索结果中的 canonical RetrievedCandidate.object_id 会进入 bounded InvestigationState.targets,而 chunk ID、URL、passage 只保留为 retrieval/evidence coordinate;若 M6 请求 continuation,它只能从这些 durable targets 中显式选择对象,DecisionService 继续阻止 target escape,且 Application 只有在恰好选择 1 个稳定 object 时才允许复用 StartInvestigationUseCase 升级 durable Investigation,0 个或多个 target 都 fail closed。VERIFY / INVESTIGATE / WATCH 则直接走 durable async seam 并返回 202 Accepted。同步 Question 的 Recorded ModelRequest 绑定真实 task_run_id / execution_id / budget_ref,synthetic question:* lightweight state 不写入 Case FK,因此 TD3 可以直接量真实交互延迟而无需制造假 Case。

M6 另新增 immutable DecisionResultStore:它只持久化已经通过 M6 invariant 的 typed result,为没有 durable Case 的 DIRECT/RETRIEVE 决策提供稳定 decision_id 读取;M4 仍独占 durable Case 上“哪个 decision 是 current”的 authority。GET /api/v1/decisions/{decision_id} 已可读取该 store,并兼容读取此前 M4 DecisionCommit event。数据库已增加 20260929_0026_decision_results。Product follow-up 现有第一版 durable owner:question_sessions / question_session_turns 只保存 principal、turn order、user input、carried canonical targets、Knowledge/context coordinate 与 immutable decision_ref / investigation_ref,不复制 Decision/Evidence/M4 state;迁移 20260930_0027_question_sessions 已在真实 PostgreSQL 执行。follow-up 可只带 session_id,单一 stable target 自动 carry,上一轮 ContextManifest.context_id 成为下一轮 parent_context_id;旧 Decision 通过 ref 重读后只作为 conversation/co-reference context,M6 prompt 明确禁止把它当 evidence/current truth。generic RETRIEVE continuation 的 durable target binding 也已闭环。跨 turn RETRIEVE 现在还有独立 RetrievalInvocation owner:20260930_0028_retrieval_invocations 已在真实 PostgreSQL 执行;每个 RETRIEVE 记录 operator/revision、query/request digest、Knowledge revision、limit/source scope、ordered chunk refs 与 executed/reused disposition,并通过 ContextManifest.retrieval_invocation_refs 进入 Task provenance。同 session 的后续 RETRIEVE 只有在 exact request digest 与 world coordinate 完全一致时才允许重读上一轮 chunk refs 并跳过 lexical search;任一 ref stale/missing 自动回退 fresh search。Investigation-class session follow-up 也已接到 durable Case:ContinueInvestigationUseCase 只复用仍为 active/waiting 的最新 investigation_ref,在当前 Case revision 新增 EvidenceNeed 并通过现有 InvestigationTaskLauncher 启动下一 episode;若同 Case 已存在 non-terminal InvestigationRole TaskRun,则在写 need 之前 fail closed,避免 Product session 引入并发 Case writer。resolved/cancelled/closed Case 不静默 reopen。LOOKUP/RETRIEVE 现也有显式 Case-read profile:同 session 的最新 investigation_ref 仍指向 active/waiting Case、且本轮未显式重绑 cve_id/object_id 时,DecisionRole 直接读取该 Case 的 durable M4 InvestigationState;TaskRun、ContextManifest 和 ModelRequest 保留真实 Case/State revision coordinate,但 Product DecisionResult 不写回 M4.current_decision。M4 raw EvidenceLink ID 只在 ephemeral Product projection 中规范成 evidence:<id> 供 M6 citation gate 使用,不修改 durable Case state。Case-read RETRIEVE 要求 InvestigationState.last_world_revision == current Knowledge revision,禁止把 stale M4 state 与 fresh retrieval passage 混在同一个 read context;若 M6 返回 continuation,EvidenceNeed 继续追加到同一 Case,而不是新建第二个 Case。当前仍未完成的是 public idempotency、cancel/resume、ProductEvent/SSE、更完整 Product read surface、live Case-read / long-Investigation 的更广 frozen denominator,以及 retrieval failure 在 Context/TaskRun 建立前的独立 durable attempt 记录。正式 single-turn Product QA 与普通 multi-turn/session retrieval reuse denominator 已闭环,不能再与这些扩展项混写成“Product QA 尚未执行”。

2026-10-03 formal closeout:同一 deployment:0e16e1b574d5b6383434e7fc80f64cc4 下,AskQuestionUseCase 的 14 个 live single-turn Product QA case 与 2 个 two-turn Product session 已全部进入 durable BenchmarkRun。single-turn 的 answer accuracy / groundedness / citation correctness / citation completeness / completion / multi-hop / unknown 均为 1.0,interactive latency 平均 2.517s、最大 4.032s;session context chain / target carry 均为 1.0,RETRIEVE reuse case 的 invocation coverage / reuse rate 均为 1.0。这证明 Slice C 的产品路径已经从“可执行”升级到“同 deployment 可测且可追溯”。当前 Alembic single head 为 20261002_0029。

当前代码已经存在三类 surface,本文首先把它们分轨:

  1. Product API:面向最终用户和比赛产品,要求稳定 DTO、Use Case、错误和版本 contract。
  2. Runtime Workbench:面向开发、测试和比赛复验的内部观察面,允许查看 TaskContract、ContextManifest、TaskEvent、M7 coordinates 等内部状态,但只在 dev/test + feature flag 下开放。
  3. A2A Adapter:面向 Agent-to-Agent interoperability,遵循 A2A 协议,不等价于 Product API。

三类 surface 可以读取同一套 TD1/TD2 runtime,但不共享 transport contract。


1. 当前实现基线与问题定位

本设计直接基于 2026-09-27 本地代码状态,而不是从空白系统推演。

1.1 已经正确形成的边界

当前 apps/api/main.py 保持较薄,只负责 FastAPI lifespan、数据库 session factory、router 注册和 dev/test 静态 Workbench 挂载。apps/worker 负责 Celery worker / scheduler;TD2 Investigation / Enrichment 的真实执行已经进入统一 TaskRun / ExecutionRun / Role Runtime,而不是由 HTTP request 持有长任务。

顶层 apps/ 下已经存在一批 composition module:

  • task_admission.py:把 Enrichment / Investigation compiler 与 Runtime Policy 组合成统一 TaskContractService;
  • investigation_runtime.py:组合 ModelProvider、ContextMaterializer、PerceptionRuntime、Delegation、Budget、Execution;
  • decision_runtime.py:组合 M6 DecisionService 与 M4 DecisionCommit / Continuation gate;
  • enrichment_runtime.py、perception_execution.py、investigation_delegation.py、watch_runtime.py:完成对应 runtime wiring。

这些模块已经承担“把多个 domain package 组合成可运行能力”的职责。2A 不重新制造另一套 Runtime,只在其上增加产品 Use Case owner。

1.2 当前 Workbench 为什么会变重

apps/api/routes/workbench.py 当前约 700 行,同时承担:

  • Pydantic request / response model;
  • 直接 SQL aggregation 和 list query;
  • Case / EvidenceNeed 创建;
  • Task admission;
  • ContextManifest 构造;
  • Budget / ExecutionEnvelope 创建;
  • TaskRun queue transition;
  • Celery enrichment trigger;
  • M6 DecisionRuntime wiring;
  • citation source assembly;
  • HTTPException 映射。

这些代码多数没有绕过 TD1/TD2 domain gate,因此不是“业务规则写错了”;问题是 Application Use Case 没有独立 owner,导致 route 被迫同时承担 transport、query composition 与 workflow orchestration。随着 Product API 增加,继续沿这个模式扩展会让 route 成为新的 god layer。

1.3 当前 Workbench 不是 Product API

现有 apps/web 明确是 operator/test frontend。它故意展示:

  • TaskRun / TaskContract / ContextManifest;
  • TaskEvent;
  • Case / InvestigationState / EvidenceNeed;
  • M7 trajectory / snapshot coordinates;
  • raw Decision runtime output。

这些信息对工程复验有价值,不应为了“产品化”全部隐藏。本文只规定:Product Frontend 不依赖这些 internal schema;Runtime Workbench 可以继续存在并直接观察内部状态,且不承诺 public compatibility。

1.4 当前 Product Read Path

GET /api/v1/vulnerabilities/{cve_id} 已经通过 packages.intelligence.knowledge.read.get_vulnerability_by_cve 读取 Evidence-rich KnowledgeObjectView,并保持 read-only。该 route 是当前最接近 Product API 的稳定 read seam,应保留,不因 2A 重构而打断。


2. 四层架构与 Ownership

系统产品路径固定为四层:

Presentation
HTTP / SSE / OpenAPI / A2A transport adapter
validation / serialization / request context / public error
        ↓
Application
Use Case / Command / Query / Read Model / ProductEvent
product workflow / sync-async boundary / idempotency
        ↓
Domain Runtime
TD1 Evidence/Knowledge + TD2 Task/State/Agent/Decision
business truth / runtime state / policy / capability
        ↓
Infrastructure
PostgreSQL / Redis / Celery / S3 / Model / External Provider
physical persistence / delivery / provider implementation

2.1 Presentation

Presentation 持有 HTTP method/path、query/path/body validation、request/response serialization、authentication bootstrap、request correlation、OpenAPI、SSE framing 与 public error mapping。

Presentation 不持有:

  • 漏洞、Incident、Evidence 的业务判断;
  • EvidenceNeed 创建/关闭条件;
  • Task state transition;
  • Capability / Policy 规则;
  • Knowledge / Case 的数据库查询策略;
  • Agent planning。

2.2 Application

Application 持有“一个稳定产品行为如何编排现有 domain runtime”。

例如 StartInvestigation 负责:

resolve target
→ build TaskIntent
→ Task admission
→ create/bind Case
→ bind ContextManifest
→ create TaskRun / Budget / Execution
→ queue run
→ return InvestigationView

其中每一步的业务 invariant 仍归原 domain owner;Application 只持有顺序、产品语义、sync/async boundary、public idempotency 和 read model projection。

2.3 Domain Runtime

TD1 / TD2 owner 不变:

  • Knowledge / Evidence:M1–M3;
  • InvestigationState / EvidenceNeed:M4;
  • InvestigationRole / Perception:M5;
  • DecisionResult:M6;
  • Replay / Evaluation:M7;
  • Capability / Policy / Budget / Sandbox / Telemetry:共享 runtime / M8。

Application 可以调用这些 port,但不能复制其规则。

2.4 Infrastructure

Infrastructure 持有 SQLAlchemy/PostgreSQL、Redis、Celery、S3、ModelProvider、external source、sandbox substrate 和网络实现。Infrastructure exception 先翻译成可归属的 failure,再进入 Application / Presentation;原始 SDK exception 不成为 public API contract。

2.5 依赖方向

Presentation
    ↓
Application
    ↓
Domain Port
    ↓
Infrastructure Port
    ↓
Infrastructure Implementation

禁止:

  • domain import FastAPI / HTTP status;
  • repository 返回 HTTP response;
  • Product route 直接写 ORM query;
  • Product frontend 依赖 ORM row / TaskEvent / StatePatch;
  • Application 重写 M4/M5/M6 invariant;
  • provider error 原样泄漏到客户端。

3. 三类 API Surface 分轨

3.1 Product API

目标:稳定、可版本化、可供正式前端和外部 client 使用。

要求:

  • stable DTO / read model;
  • OpenAPI;
  • 统一 error contract;
  • idempotency;
  • pagination;
  • authorization seam;
  • request / task trace linkage;
  • sync / async 明确。

3.2 Runtime Workbench

路径继续使用 /api/v1/workbench/*。

用途:

  • 开发调试;
  • 比赛复验;
  • 观察 TD1/TD2 内部 state;
  • 手工启动 Case / Task / Enrichment / Decision;
  • 查看 TaskEvent / ContextManifest / trajectory / snapshot。

Workbench 可以:

  • 直接查询内部 storage model 形成 diagnostic view;
  • 暴露内部 runtime field;
  • 返回 Celery task id;
  • 不承诺长期 public DTO 稳定。

Workbench 不可以:

  • 绕过 domain write gate 直接修改 canonical Knowledge / M4 state;
  • 在 prod 默认开放;
  • 成为正式 Product Frontend 的唯一 backend contract。

3.3 A2A Adapter

路径 /api/v1/a2a/* 保持协议适配职责。A2A Task / Artifact / Message / Stream semantics 与 Product Investigation / Decision UI 不共用 DTO。A2A inbound message 如果未来进入真实执行,仍必须通过 authenticated principal、TaskIntent normalization 和 shared Task admission。


4. Application Use Case 是产品行为的 Owner

Application 不建立单一 ApplicationService god object。每个稳定产品行为对应独立 command/query handler;输入是 typed request,输出是 public view 或 operation reference。

4.1 Intelligence Read

Use Case 类型 Domain Owner 输出
GetVulnerability Query TD1 Knowledge / Projection VulnerabilityView
SearchIntelligence Query TD1 Retrieval SearchResultPage
GetIncident Query TD1 Incident IncidentView
GetEvidence Query TD1 Evidence EvidenceView
GetDocument Query TD1 Document DocumentView

普通 GET 只读取当前世界,不在 miss 时隐式触发 enrichment、资产扫描或外部搜索。

4.2 Question / Decision

Use Case 类型 Domain Owner 输出
AskQuestion Command-like Query Task admission + M6 QuestionResultView
GetDecision Query M6 / M4 DecisionView

AskQuestion 是 Product API 的 routing point:

Question
→ TaskIntent / admission
→ ExecutionProfile

DIRECT
→ current Knowledge / M4
→ M6
→ synchronous Decision

RETRIEVE
→ bounded Perception
→ M6
→ synchronous Decision when interactive budget allows

VERIFY / INVESTIGATE
→ durable Case + TaskRun
→ 202 Accepted

WATCH
→ durable Case
→ 202 Accepted / waiting

4.3 Investigation Lifecycle

Use Case 类型 Domain Owner 输出
StartInvestigation Command M4/M5 + Task Runtime InvestigationView
GetInvestigation Query M4 read InvestigationView
ListInvestigations Query Application read projection InvestigationPage
CancelInvestigation Command Task lifecycle OperationView
ResumeInvestigation Command Task lifecycle OperationView
GetInvestigationActivity Query TaskEvent / Trajectory projection ActivityPage
StreamInvestigation Stream ProductEvent projection SSE

Product Web 通过 ListInvestigations(origin_scope=product, cursor) 连续读取账户可见的 Case 页面,并把“展开已载入”与“载入更早调查”分开。深链接先由 GetInvestigation 执行 owner check;若该 Case 不在当前列表页面,读取成功后仍把它放进可见索引。对应实现:2e131cc。

当前 Workbench 的 create_case + create_run 是工程复验用的两步操作;Product Use Case 可以把它们组合成 StartInvestigation,同时继续调用相同 CaseService、InvestigationStateService、TaskContractService、BudgetGovernor、ExecutionRunService 和 TaskRun storage gate。

4.4 Monitoring / Operations

Use Case 类型 Domain Owner 输出
ListSources Query M1 SourcePage
GetSourceStatus Query M1/M8 SourceStatusView
GetSystemOverview Query Application aggregate SystemOverviewView
GetHealth Query M8 / process HealthView

source enable/disable、credential mutation、policy revision 暂不进入公开 Product API;这些属于未来 admin/control plane。


5. Command / Query 分离

不要求引入完整 CQRS 基础设施,但所有 Product Use Case 在语义上区分 Command 与 Query。

Query

Query 只读取 projection / read port,不产生不可见副作用。页面刷新、详情读取、列表读取不能改变 Task / Case / Enrichment state。

Command

Command 创建或改变系统状态,包括:

  • ask 触发长调查;
  • start investigation;
  • cancel / resume;
  • 显式 refresh / re-investigate;
  • 未来 human review。

每个 Command 必须定义:

  • authorization;
  • idempotency;
  • expected revision;
  • sync / async;
  • public failure code;
  • audit / trace。

禁止隐藏副作用

禁止:

  • GET vulnerability miss 后自动启动外部采集;
  • GET investigation 因 stale 自动创建新 TaskRun;
  • 前端刷新触发 enrichment;
  • 列表查询改变 Case status。

6. HTTP Request Pipeline

所有 Product route 共用统一 edge pipeline:

HTTP Request
→ Request ID / Trace Context
→ Authentication Bootstrap
→ Rate / Payload Guard
→ Path / Query / Body Validation
→ RequestContext
→ Route Adapter
→ Application Use Case
→ Public DTO
→ HTTP Response

Exception
→ Global Error Mapper
→ ProblemDetail

6.1 RequestContext

RequestContext
  request_id
  trace_id?
  principal
  client_id?
  idempotency_key?
  request_started_at
  api_version

RequestContext 属于 HTTP/Application edge,不替代 TD2 ExecutionEnvelope。创建 TaskRun 后建立 request_id → task_run_id / execution_id trace linkage,Agent 后续 episode 不依赖原 HTTP connection。

6.2 Validation Boundary

Pydantic request model 负责 transport shape。Domain validation 仍由 domain owner 执行:

  • 字符串是否像 CVE:boundary normalization;
  • 某 TaskKind 是否允许 admission:Task Runtime;
  • EvidenceNeed transition 是否有效:M4;
  • Capability 是否允许:Policy;
  • citation 是否支持结论:M6。

7. DTO、Domain Model、Persistence Model 分离

Transport DTO

服务 HTTP / OpenAPI / SSE / frontend。

Domain Model

服务 TD1/TD2 invariant,例如 Observation、Claim、Relation、TaskContract、InvestigationState、DecisionResult。

Persistence Model

服务 SQLAlchemy / Redis / S3 / migration / index。

Public DTO
  ↕ mapper
Application / Domain
  ↕ repository mapper
Persistence Model

Product API 禁止:

  • response_model 直接绑定 ORM model;
  • 原样 dump ContextManifest / PolicyDecision / CapabilityBinding;
  • storage migration 无意改变 public JSON;
  • frontend 根据内部 event / table field 推断产品状态。

当前 Workbench 可以继续暴露内部 schema,因为它明确是 diagnostic surface。


8. Public Read Model

Read Model 只定义前端真正需要的稳定语义,不复制全部 domain object。第一版可以由现有 read port 即时装配;只有 benchmark 证明需要时再物化独立 projection。

8.1 VulnerabilityView

当前 GET /api/v1/vulnerabilities/{cve_id} 的 KnowledgeObjectView 保持兼容。Product frontend 若需要更稳定的聚合字段,在 Application 层增加 VulnerabilityView mapper,不要求立即破坏现有 route。

至少覆盖:

  • identity / canonical id;
  • summary / severity;
  • affected / fixed information;
  • exploit / incident relation(可用时);
  • conflict / unknown;
  • evidence summary;
  • revision / freshness。

8.2 EvidenceView

至少覆盖:

  • evidence id;
  • source / source class / authority;
  • published / fetched time;
  • locator;
  • excerpt / artifact media type;
  • revision。

内部 S3 URI、filesystem path、credential 不直接暴露。

8.3 InvestigationView

至少覆盖:

  • case_id / revision;
  • status / execution profile;
  • target / goal;
  • confirmed findings;
  • conflicts;
  • unknowns;
  • open EvidenceNeed summary;
  • current activity;
  • latest decision;
  • terminal reason;
  • timestamps。

Progress 不使用模型自由生成的百分比。如果没有可靠 denominator,使用 phase、open/closed EvidenceNeed 和 Task terminal state。

8.4 DecisionView

至少覆盖:

  • decision_id;
  • answer;
  • fact / inference / recommendation;
  • citation;
  • conflict / unknown / assumption;
  • stop reason;
  • created_at。

当前 DecisionView 另提供可选的 report_paragraphs,用于普通产品问答与已完成调查的自然段落研究报告。同步 Product Question 和持久调查终局的 DecisionRole 均显式启用报告提示词:在同一次结构化研判中先确定 answer_payload 与结论,再用问题的语言写出完整句子;段落只能引用该 Decision 已验证结论中的 EvidenceRef。前端优先显示段落和逐段证据,结构化字段留在可展开的审计层。正式 QA 的评分、回放及证据约束仍以结构化 DecisionResult 为准,不以报告措辞改写正确性;提示词版本从 decision-model-v2 变为 decision-model-v4,新评测须使用新的 DeploymentRevision,历史指标不能直接冒充当前版本。历史结果没有段落时保持兼容读取,以结论为主、字段可展开。

8.5 SourceStatusView

至少覆盖:

  • source id / class / role;
  • enabled / schedule state;
  • health;
  • last attempt / success;
  • next due;
  • degraded reason。

8.6 SystemOverviewView

面向 Product Overview 聚合:

  • source health summary;
  • recent intelligence;
  • active investigation;
  • recent decision;
  • runtime degraded component;
  • generated_at。

该 view 是 Application projection,不成为 canonical truth。


9. RuntimeActivityView:产品可解释性与内部 Trace 分离

比赛需要展示 Agent 自主调查过程,但 Product Frontend 不应直接 dump TaskEvent、PromptAssembly 或完整 trajectory。Application 提供脱敏 RuntimeActivityView:

RuntimeActivityView
  activity_id
  case_id
  seq
  actor_role
  activity_type
  summary
  evidence_need?
  selected_capability?
  child_task_ref?
  result_summary?
  status
  started_at?
  finished_at?

允许的 activity type:

task_started
evidence_need_selected
retrieval_planned
capability_selected
external_observation
child_task_started
child_task_completed
state_updated
decision_ready
waiting
completed
failed

不暴露:

  • 模型私有推理文本;
  • 完整 prompt;
  • secret / credential;
  • raw PolicyDecision;
  • 未经 redaction 的 tool output;
  • sandbox filesystem path。

Workbench 仍可以查看更底层的 TaskEvent / ContextManifest。Product AGENTS 的 Task 详情读取经 owner check 的 AgentTaskDetail 投影:只展示 ContextManifest 的版本、引用数量与可追溯坐标、每次 ModelAttempt 的模型/状态/提供方用量/预算结算,以及已有的预算、事件、CapabilityInvocation;证据与对象引用跳转到 INTELLIGENCE read。页面不接收完整 prompt、模型私有推理文本、secret 或 raw PolicyDecision。对应实现:d8ddfed。


10. 第一版 Product API

Public namespace 继续使用 /api/v1。

Intelligence

GET /api/v1/vulnerabilities/{cve_id}        # 保留现有 read seam
GET /api/v1/intelligence/search
GET /api/v1/incidents/{incident_id}
GET /api/v1/evidence/{evidence_id}

QA

POST /api/v1/questions
GET  /api/v1/decisions/{decision_id}

QuestionResultView:

request_id
mode = completed | accepted
execution_profile
decision?         # completed
investigation?    # accepted

DIRECT / bounded RETRIEVE 可以 200;VERIFY / INVESTIGATE / WATCH 返回 202。

Investigation

POST /api/v1/investigations
GET  /api/v1/investigations
GET  /api/v1/investigations/{case_id}
POST /api/v1/investigations/{case_id}/cancel
POST /api/v1/investigations/{case_id}/resume
GET  /api/v1/investigations/{case_id}/activity
GET  /api/v1/investigations/{case_id}/events

events 使用 SSE。

Operations

GET /api/v1/sources
GET /api/v1/sources/{source_id}
GET /api/v1/system/overview
GET /health/live
GET /health/ready
GET /health

当前 /api/v1/workbench/* 不迁移成 Product API;后续 Product Use Case 可以复用其已经验证过的 domain composition,但 transport contract 独立。


11. Sync / Async Boundary

HTTP latency contract 与 Agent execution duration 分离。

同步

优先同步:

  • exact / current projection read;
  • 普通 structured / lexical read;
  • 已有 Evidence World 上的 DIRECT;
  • 满足 interactive budget 的 bounded RETRIEVE;
  • status / evidence / read model。

Requirements 的普通 QA ≤5s 目标由这条 fast path 承担。

异步

以下 workload 默认异步:

  • 需要新 external evidence 的 VERIFY;
  • INVESTIGATE;
  • WATCH;
  • sandbox / browser / long provider chain;
  • 超过 interactive budget 的任务。

返回:

202 Accepted
Location: /api/v1/investigations/{case_id}

客户端以后通过 stable resource ID + GET/SSE 继续,不暴露 Celery task id 作为 Product contract。


12. ProductEvent 与 SSE

内部 TaskEvent 是 runtime protocol,ProductEvent 是产品协议。Application 增加投影器:

TaskEvent / M4 state change / M6 Decision
→ ProductEventProjector
→ ProductEvent
→ SSE

ProductEvent:

event_id
stream_id
seq
type
resource_revision?
occurred_at
data

第一版事件:

  • investigation.started
  • investigation.status_changed
  • investigation.progress
  • investigation.finding_added
  • investigation.conflict_changed
  • investigation.evidence_need_changed
  • investigation.decision_ready
  • investigation.waiting
  • investigation.completed
  • investigation.failed
  • investigation.canceled

SSE:

GET /api/v1/investigations/{case_id}/events
Accept: text/event-stream
Last-Event-ID: ...

Redis Streams 只承担内部实时 delivery;SSE reconnect 不能依赖 Redis 尚未淘汰的 transient message,必要状态从 durable TaskEvent / current read model 恢复。

第一版选择 SSE,因为主要是 server → client 状态更新。只有未来 computer-use、协同操作或高频双向控制出现后才重新评估 WebSocket。

2026-10-09 Product Question 补充:POST /api/v1/questions/stream 在同一请求内发送真实模型 model_delta,可选发送 provider reasoning;这些字节只是未验证草稿。React 会把已经收到的报告自然段按字符呈现,正式答案仍只来自通过 M6 证据不变量并持久化的 DecisionResult,到达 result 时立即替换草稿。完整会话默认请求 90 秒交互预算,服务端允许 1–120 秒;这只是执行上界,不人为延迟答案。终态 error 结束该幂等命令,重试生成新键。若模型 Decision 被证据门拒绝,服务端返回 decision_validation_failed 与失败 TaskRun 坐标,不持久化未通过校验的 Decision;前端保留明确标注“未完成、未校验”的部分报告,并按账户权限读取失败运行的模型/上下文/停止原因。未知异常只返回通用错误并在服务端按 request_id 记日志,不把内部异常或原始模型输出暴露给客户端。


13. 分页、过滤与排序

可能无界增长的列表默认 cursor pagination:

  • intelligence search;
  • incidents;
  • investigations;
  • activity;
  • source runs;
  • evidence list。
GET ...?limit=50&cursor=...

Page[T]
  items[]
  next_cursor?
  has_more

Cursor 是 opaque token。Filter 和 sort 使用 allowlist,不把任意数据库 column 暴露成 public query language。

Workbench 当前 limit-only diagnostic list 可以保留;Product API 新增时按本 contract 实现。


14. Public Error Contract

当前 Product path 仍有简单 HTTPException(detail=...);2A 将正式 Product API 错误统一为 RFC 9457 风格 ProblemDetail,并增加稳定 code、request_id、retryable。

ProblemDetail
  type
  title
  status
  detail
  code
  instance?
  request_id
  retryable
  context?

第一版 public code:

  • validation_error
  • authentication_required
  • permission_denied
  • resource_not_found
  • revision_conflict
  • idempotency_conflict
  • task_not_cancelable
  • task_not_resumable
  • provider_unavailable
  • capability_unavailable
  • budget_exhausted
  • deadline_exceeded
  • rate_limited
  • dependency_unavailable
  • internal_error

HTTP mapping:

Failure HTTP
transport validation 422
unauthenticated 401
unauthorized 403
missing resource 404
revision / lifecycle / idempotency conflict 409
rate limit 429
async accepted 202
temporary dependency/provider unavailable 503
unknown server failure 500

Evidence unknown 与 provider unavailable 必须区分。Public body 不返回 traceback、SQL、internal path、secret、raw SDK exception。

Workbench 可以暂时保留简单 HTTPException;Product route 使用统一 mapper。


15. Public Command Idempotency

当前 Workbench 创建 run 时使用内部随机 trigger_ref,适合人工复验,但正式 Product Command 必须支持 client retry。

以下 endpoint 第一版支持 Idempotency-Key:

  • POST /api/v1/questions
  • POST /api/v1/investigations
  • POST /api/v1/investigations/{case_id}/cancel
  • POST /api/v1/investigations/{case_id}/resume

Application 持有 IdempotencyRecord:

principal_scope
operation
key
request_digest
response_ref
status
created_at
expires_at?

相同 scope + key + 相同 request digest 返回既有结果;相同 key + 不同 request digest 返回 409 idempotency_conflict。

这不替代 TD1/TD2 内部 Observation/Event/Task idempotency,只处理 public command retry。

2026-10-09 实现进度:product_command_records(migration 20261009_0032)现在按账户、操作、客户端键保存归一化请求摘要与既有结果坐标。POST /questions、POST /questions/stream、POST /investigations 和 POST /investigations/{case_id}/cancel 已接入;同键同请求不重复调用模型、写会话回合或建立 Case,同键不同请求返回 409 idempotency_conflict。Question SSE 重放只返回既有终态结果,不伪造先前流式 token。Case/会话回合/最终响应与命令记录在一次事务中提交;同步模型调用之前的 TaskRun 是独立 durable checkpoint,进程在中途退出后 pending 键 fail closed,避免再次执行。失败模型调用把键标为 failed,下一次明确重试需使用新键。Web 对同一未完成表单请求复用键,并在成功后清理。独立 /resume Product route 仍未建立:现在的用户追问通过 Question session/ContinueInvestigationUseCase,运行时等待恢复由 Task Runtime 持有,不能把它们描述成已有的通用手工 resume。


16. Revision 与并发控制

需要基于当前状态发出的 command 携带 expected revision。Transport 可以使用 If-Match 或 body expected_revision,Application 转换成 domain revision check。

适用:

  • cancel / resume;
  • future human review;
  • 基于 Case state 的修改动作。

revision mismatch 返回 409 revision_conflict,不静默覆盖新状态。

2026-10-09 实现进度:Product Investigation GET/POST 返回 quoted revision ETag;取消命令必须携带 If-Match(quoted 或整数形式),Application 在锁定 Case 后与 current_revision 对照。浏览器从所展示的 Case revision 发送条件写;失败返回 409 revision_conflict,旧页面不能覆盖较新的 Case 状态。会话追问的继续调查路径仍由现有 active-run 与 M4 revision gate 控制,独立 Product /resume 尚未提供。


17. Frontend State Ownership

正式 Product Frontend 把状态分成三类。

Server State

API / SSE 持有权威版本:

  • vulnerability / incident / evidence;
  • investigation;
  • decision;
  • source / overview。

UI-local State

只属于前端:

  • 当前 tab;
  • filter / sort;
  • panel 展开状态;
  • draft question;
  • pagination cursor history;
  • transient notification。

Durable Workflow State

只属于后端:

  • TaskRun;
  • InvestigationState;
  • EvidenceNeed;
  • ContextManifest;
  • PolicyDecision;
  • Budget;
  • CapabilityInvocation;
  • Trajectory。

Product Frontend 页面刷新后通过 case_id / decision_id 恢复,不把 Agent durable state 存到 localStorage。

当前 apps/web 是 Runtime Workbench,不受“不能展示内部 Task/Context”这一限制;它只是不能成为正式 Product API contract 的依赖依据。


18. OpenAPI 是 Product Contract

FastAPI 生成的 OpenAPI 作为 Product API 的机器可读 contract,至少覆盖:

  • path / method;
  • request / response schema;
  • error schema;
  • auth scheme;
  • pagination;
  • status code;
  • example;
  • SSE endpoint 说明。

前端类型优先从 OpenAPI 生成或校验,不维护平行手写 DTO。

CI 增加 Product API contract gate:

  • OpenAPI 可以生成;
  • schema ref 可解析;
  • public response 不泄漏 persistence/internal runtime model;
  • breaking change 必须显式进入 API version review。

Workbench route 可以留在同一 OpenAPI document,但标记 internal/workbench tag;如果后续需要对外发布 spec,则生成 product-only filtered spec。


19. Authentication / Authorization Seam

2026-10-08 的 Product 实现已将 principal 绑定到真实账户会话;完整契约见产品账户、会话与个性化。旧的固定 local principal 仅属于历史数据,不自动归属新账户。

Presentation:

  • 从 HttpOnly Cookie 对应的服务端会话解析身份;
  • 构造 RequestContext principal;
  • 处理 401、Origin/CSRF 与跨标签账户变化。

Application / Domain:

  • 资源级 access;
  • command permission;
  • TD2 Policy / Capability execution authorization。

前端隐藏按钮不能替代 authorization。X-Principal 不能作为 Product 身份来源;会话、Case、Decision、Task、SSE 和 A2A 读取必须在服务端按 durable owner 判定。公共 Evidence/Knowledge 与明确纯系统来源的只读 Case 引用另设边界,不泄漏私有 Task 或会话。

退役的 Workbench 不进入 Product 路由或导航。Nginx Basic Auth 可作为外围保护,但不能代替应用账户。生产 Cookie 需要 HTTPS、精确允许的浏览器 Origin 和可撤销的服务端 session。


20. Health、Readiness 与 Degraded State

当前只有 /health/live。第一版补:

/health/live

只表示 process 活着。外部 provider 失败不影响 liveness。

/health/ready

表示 instance 是否能承接核心请求。至少检查:

  • PostgreSQL;
  • 必要 migration;
  • required runtime configuration。

optional source / asset / model provider 不应默认让整个 API unready。

/health

聚合:

overall = healthy | degraded | unhealthy
components[]
  component
  status
  checked_at
  detail_code?

典型语义:

  • PostgreSQL down → unhealthy;
  • Redis Task Event delivery degraded,但 direct read 可用 → degraded;
  • 单个 source 403 → degraded;
  • optional model provider 未配置且系统可 lexical-only → degraded / disabled;
  • optional provider 无 credential → disabled,不伪装成核心系统故障。

21. Logging、Trace 与 Audit

HTTP edge 创建 request_id / trace context。创建 TaskRun 后形成:

request_id
→ task_run_id
→ execution_id
→ capability_invocation_id
→ evidence / decision

Structured log 至少包含:

  • request_id;
  • route / use_case;
  • case_id / task_run_id;
  • status;
  • latency;
  • public error code;
  • trace_id;
  • 可记录时的 principal_id。

Audit 与 debug log 分离。Cancel / Resume / future human review 记录谁对哪个 revision 做了什么动作。Secret / credential 不进入日志。

跨运行的 ModelAttempt、token / context 评测归因留给 TD3;单次 Task 的已持久化用量与 context 坐标由 Product read projection 呈现。


22. Transaction Boundary

Application 表达 workflow intent,但不持有跨所有 subsystem 的超级事务。

保持:

  • TD1 KnowledgeChange + outbox 的既有 transaction;
  • TaskRun / TaskEvent 的既有 durable boundary;
  • M4 State transition / DecisionCommit 的既有 transaction;
  • S3 / Redis / Celery 使用各自可靠性 contract。

跨 subsystem workflow 依靠 durable state + idempotency + event / retry / compensation,不引入分布式数据库事务。

StartInvestigation 的产品逻辑顺序:

admit TaskContract
→ create/bind Case
→ open/bind EvidenceNeed when required
→ bind Context
→ create TaskRun / Budget / Execution
→ transition queued
→ dispatch
→ return InvestigationView

当前 workbench create_investigation_run 已经验证了其中大部分步骤;2A 的任务是把这段编排从 route-level implementation 提升为 reusable Product Use Case。


23. Repository / Query Ownership

Product route 不直接查询 ORM。Application Query 调用:

1.已有 domain read service;或 2.明确的 Application read repository / query service。

当前 workbench 的 overview、task list、case list 直接 SQL 可以保留,因为它是 diagnostic surface;同样 SQL 不复制到 Product route。

Repository 持有:

  • persistence query;
  • mapping;
  • pagination;
  • transaction-local data access。

Repository 不持有:

  • 产品策略;
  • task admission;
  • policy;
  • “CVSS 高就自动调查”一类业务动作;
  • provider fallback policy。

24. 代码映射:尽量补 seam,不搬家

现状已经存在 apps/*.py composition module,不进行大规模移动。第一阶段只新增 Product Application 子层:

apps/
├── api/
│   ├── main.py
│   ├── routes/
│   │   ├── knowledge.py       # existing compatibility read
│   │   ├── workbench.py       # dev/test diagnostic surface
│   │   ├── a2a.py             # interoperability
│   │   ├── questions.py       # Product API
│   │   ├── investigations.py  # Product API
│   │   ├── sources.py         # Product API
│   │   └── health.py
│   ├── dependencies.py
│   └── errors.py
│
├── application/
│   ├── commands/
│   │   ├── ask_question.py
│   │   ├── start_investigation.py
│   │   ├── cancel_investigation.py
│   │   └── resume_investigation.py
│   ├── queries/
│   │   ├── intelligence.py
│   │   ├── investigations.py
│   │   ├── decisions.py
│   │   ├── sources.py
│   │   └── overview.py
│   ├── views/
│   ├── events/
│   ├── idempotency/
│   └── errors.py
│
├── task_admission.py          # existing composition
├── decision_runtime.py        # existing composition
├── investigation_runtime.py   # existing composition
├── enrichment_runtime.py
├── perception_execution.py
├── investigation_delegation.py
└── watch_runtime.py

Application Use Case 可以依赖现有 apps-level composition module;后续如果这些 module 自然形成稳定 application runtime package,再统一移动。当前不为了目录纯度制造大范围 churn。

packages/ 下的 TD1/TD2 owner 不因 2A 重排。

现有 tests/test_architecture_dependencies.py 已把 packages 之间的依赖方向变成 executable invariant,并明确禁止 package code 反向 import apps。将 Product Application 放在 apps/application 下可以保持这一约束:Application 作为顶层 composition 层允许组合多个 package,而任何 domain package 都不需要认识 Product Use Case。若未来把 Application 下沉到 packages/application,则必须重新设计依赖矩阵,不能仅为目录命名修改 architecture test。


25. Workbench 重构边界

workbench.py 当前变重,但不要求立即重写为 Product API。收敛原则:

保留

  • internal DTO;
  • direct diagnostic SQL;
  • TaskContract / Context / Event inspection;
  • source coverage;
  • M7 coordinates;
  • dev/test mutation;
  • raw runtime detail。

逐步抽取

凡 Product API 也需要的 workflow,优先抽成 Application Use Case:

  • 创建 Investigation;
  • 启动 Investigation Task;
  • 运行 Question / Decision;
  • source / overview product projection;
  • 可复用 citation/evidence mapping。

之后:

Workbench Route
→ Application Use Case / Domain diagnostic port

Product Route
→ same Application Use Case
→ Product DTO

两类 route 可共用 Use Case,但 response model 可以不同。

不追求

不为了把 workbench.py 变短而把所有 SQL 包一层无语义 service。只有可复用的产品行为和稳定 query 才获得独立 owner。


26. Frontend Contract Mapping

当前 apps/web 继续作为 Runtime Workbench。正式 Product Frontend 至少需要以下 surface:

Product View Backend View
Overview SystemOverviewView
Intelligence Search SearchResultPage
Vulnerability Detail VulnerabilityView + EvidenceView
Incident Detail IncidentView
Investigation InvestigationView + Activity + SSE
Ask / QA QuestionResultView + DecisionView
Operations SourceStatusView + HealthView

Product Frontend 不直接消费 TaskContract、原始 ContextManifest、StatePatch、PolicyDecision、CapabilityBinding 或 Budget row;AGENTS/QUESTIONS 使用 Application 层经过授权和裁剪的审计投影展示 context refs、model attempts 与预算状态。

比赛如果需要“展示 Agent 在干什么”,使用 RuntimeActivityView;需要专家调试时进入 Workbench。


27. Testing Strategy

现有 apps/api/tests 已经在做 FastAPI component test,并验证 Workbench 创建真实 Case / EvidenceNeed / policy-admitted TaskRun。这一层保留。

2A 增加四类测试。

27.1 Application Use Case Test

用 fake / test domain port 验证编排:

  • DIRECT 不创建 TaskRun;
  • VERIFY / INVESTIGATE 创建 durable Investigation;
  • duplicate Idempotency-Key 不重复创建;
  • stale revision 返回 conflict;
  • cancel/resume 只通过 Task lifecycle owner。

27.2 Product API Component Test

通过 ASGITransport 验证:

  • validation;
  • status code;
  • ProblemDetail;
  • request_id;
  • DTO serialization;
  • sync / async response。

27.3 Infrastructure Integration Test

使用真实 PostgreSQL / Redis / object-store test profile 验证:

  • StartInvestigation durable path;
  • idempotency;
  • cursor;
  • SSE reconnect;
  • health degraded state。

SQLite component test 可以继续承担快速 HTTP contract 检查,但不替代依赖 PostgreSQL transaction / locking 语义的 integration test。

27.4 Product E2E

第一版固定:

Lookup vulnerability
→ Ask direct question
→ synchronous Decision
→ Ask verification question
→ 202 Investigation
→ observe Activity / SSE
→ terminal Decision
→ open Citation / Evidence

以及一个 degraded flow:

Start Investigation
→ provider unavailable
→ explicit degraded / partial state
→ retry or fallback
→ terminal result

E2E 验证产品 contract,不重复 M7 的模型质量 benchmark。


28. Health / Product / Workbench 的测试分层

  • /health/live:进程级 smoke;
  • /health/ready:必要 dependency readiness;
  • /health:聚合 degraded semantics;
  • Product API:稳定 contract;
  • Workbench:内部 runtime observability;
  • M7:Task / Agent replay 与质量评测。

不使用 Workbench test 数量代替 Product API completeness,也不使用 Product E2E 代替 M7 benchmark。


29. 第一版实现切片

2A 以减少当前后端 coding friction 为目标,不要求一次重构完。

Slice A — Edge Baseline

  • shared database/session dependency;
  • RequestContext / request_id;
  • global Product error mapper;
  • health/live/ready;
  • Product OpenAPI tag / schema boundary。

Slice B — Application Skeleton

  • apps/application;
  • StartInvestigationUseCase;
  • Get/List Investigation Query;
  • Product View mapper;
  • 复用现有 task_admission / CaseService / StateService / Budget / Execution。

Slice C — QA Routing

  • AskQuestionUseCase;
  • DIRECT / RETRIEVE sync seam;
  • VERIFY / INVESTIGATE / WATCH async seam;
  • QuestionResultView / DecisionView;
  • immutable DecisionResult identity + GET /api/v1/decisions/{decision_id};
  • generic RETRIEVE continuation 的 bounded canonical target resolution;
  • Product multi-turn session / follow-up target + context binding;
  • TD3 QASessionCase evaluation(per-turn QA + durable context/target trace);
  • cross-turn retrieval chunk-overlap baseline metric;
  • retrieval invocation provenance + exact-query/session result reuse;
  • pre-context failed retrieval attempt 独立 durability(2026-10-09);
  • semantic/non-identical-query reuse policy;
  • serialized Investigation-class follow-up on the same durable Case;
  • LOOKUP / RETRIEVE 直接读取 active Case M4 state。

Slice D — Investigation Lifecycle

  • cancel / resume;
  • idempotency;
  • revision conflict;
  • activity projection。

Slice E — Product Event / SSE

  • TaskEvent / State → ProductEvent;
  • SSE;
  • Last-Event-ID reconnect;
  • terminal decision notification。

Slice F — Product Read Surface

  • source / system overview;
  • evidence;
  • intelligence search;
  • 必要的 Vulnerability product mapper。

Slice G — Contract Gate

  • OpenAPI validation;
  • component tests;
  • real DB integration;
  • canonical Product E2E。

Workbench 在上述过程中继续可用,不作为重构 blocker。


30. API Review Checklist

每个新增 Product endpoint 必须回答:

  1. Query 还是 Command?
  2. Use Case owner 在哪里?
  3. 是否有隐藏副作用?
  4. sync 还是 async?
  5. 是否需要 Idempotency-Key?
  6. 是否需要 expected revision?
  7. DTO 是否泄漏 ORM / TaskEvent / ContextManifest?
  8. collection 是否有 pagination / bound?
  9. failure 是否有稳定 public code?
  10. request_id 是否能追到 TaskRun / Execution? 11.长任务为什么用 SSE / 为什么不需要 SSE? 12.页面刷新后是否只凭 stable resource ID 恢复? 13.是否存在 component / integration test?
  11. OpenAPI 是否同步?

31. 与 TD1 / TD2 的边界

TD1 不变

2A 不重新拥有 Source、Acquisition、Evidence、Knowledge、Incident、Projection、Retrieval、Artifact、M1–M3 reliability。

TD2 不变

2A 不重新拥有 TaskIntent、TaskContract、TaskRun、ContextManifest、InvestigationState、EvidenceNeed、Perception、Skill、Capability、Policy、Budget、Sandbox、Agent loop、DecisionResult、Replay、Telemetry seam。

2A 新增稳定 owner

仅新增:

Product Use Case
Product Command / Query
Public DTO / Read Model
Product Error Contract
Public Idempotency
ProductEvent / SSE
Product Health / Overview
Frontend server-state boundary

32. 设计原则摘要

2A 的目标不是“再加一层抽象”,而是消除三类已经出现的耦合。

HTTP 不侵入 Domain

Route 负责 transport,Use Case 负责产品编排,TD1/TD2 Runtime 持有业务 invariant。

Domain 不直接泄漏 Product Frontend

正式前端读取 Product View;内部 Task / Context / Event 留在 Workbench 或脱敏 RuntimeActivityView。

Product 行为必须有 owner

跨多个 domain service 的稳定产品动作不能长期散落在 route、worker 和前端。

最终稳定链路:

HTTP / SSE
→ Application Use Case
→ TD1 / TD2 Domain Port
→ Infrastructure

Domain State / Result
→ Application Read Model
→ Public DTO
→ Product Frontend

长任务:

Product Command
→ durable Case / TaskRun
→ 202 Accepted
→ internal TaskEvent / State Change
→ ProductEvent projection
→ SSE
→ frontend refreshes stable Read Model

Runtime Workbench 则保留另一条明确的工程观察链:

Workbench
→ diagnostic query / existing domain gate
→ internal Task / Context / State / Replay detail

这两条链共享真实系统,但不共享产品 contract。


33. 参考

以下资料用于补充 application/backend engineering practice,不覆盖 SecFusionAgent 自身 ownership:

34. WORLD heterogeneous read projection — 2026-10-08

WORLD 的首轮重构采用以下读取边界。GET /api/v1/world/stories?limit=4..18 由 Application Query 组合既有 Document/Revision/Chunk、带 Observation 的开发对象/InternetAsset 和 durable Incident;不写 Knowledge、不启动 M4–M6、不产生新的研判。

DTO 返回原始 headline/excerpt、excerpt_origin、结构化 facts、source、对象/事件引用、published_at / happened_at / observed_at,以及 Observation / DocumentRevision / Chunk / locator。generated_at 是读取快照的测量时间。删除 prototype 的 narrative/why_it_matters、标题特判、AI 关键词评分、类别加权与 regex 摘要。类别来自 source inventory;宽泛 independent/incidents 源需要 accepted、未 supersede 的 Repo/ResearchWork 关系,或 registry 中已有 AI/model/Agent authority scope。source abstract 优先于普通 body,来源与类别交错是覆盖规则,不代表安全重要性评分。

每个文档只读最新 Revision;SQL 按来源限量,正文只读最终选中的文档。opaque SHA 和单纯身份不会伪装成有内容的变化。来源文字保持原语言,界面与类型表达由 Web 的中文/英文 presentation 持有;不把固定英文判断模板写进 Query。

WorldStoryReader 是进程内、可替换的 15 秒 read cache。冷读取由锁合并,响应深复制,测量时间保持不变。已有快照过期后立即返回原测量并用独立 session 后台更新;刷新失败保留原测量,shutdown 取消后台读取。API bootstrap 最多预热 5 秒;读取失败/超时会记录日志,其他 Product 路由继续可用。它不替代 durable Evidence authority,也不承担历史 WorldSnapshot/replay。

首屏只依赖 stories。Hot working set 后续独立读取,不能被当作已经晋升的 Knowledge;overview 与 formation 在故事首次读取后独立读取,overview 提供近 24h 的真实采集/变化/写入摘要;完整来源 health 仍在方向 inspect 中展开。DOM 叙事 + SVG 空间对象支持 reduced motion,不依赖 WebGL;邻近位置只表示共同处于观察世界,不虚构对象间关系。对象操作带既有 object/incident/CVE/question 坐标进入 dossier 或 START。技术坐标下沉至 inspect。

35. Product WORLD / START / dossier 联调收口 — 2026-10-08

WORLD 的 Hot read 保留排名窗口(64 条)并单独返回 resident_total,计数仅包含尚有 Redis payload 的驻留对象,排除排序/固定/访问等索引与过期成员。Web 每组展示八个真实对象,搜索明确限于已载入窗口;数量不是 Knowledge 留存数。来源方向、Hot 和阅读模式支持 URL 进入。

GET /api/v1/world/formation 从已有 ProcessingRun、cause Observation 与 KnowledgeRevision 读取最近富化处理;活动处理另行保留。只有存在 cause_processing_run_id 对应的 Knowledge 提交才显示“已写入知识”,success 本身不等于 canonical commit。字段属于 Application View,不新增业务 authority,不调用调查链。

Product Document read 新增最新修订第一个原始 Chunk 的 source_excerpt / excerpt_chunk_id / excerpt_locator。前端直接阅读节选,并保留原文入口;修订/索引/Observation 坐标折叠。世界来源材料可进入对象档案,再携带真实对象与可读标题进入 START。裸情报入口不再默认选择一个 CVE。

同步 QA 的逻辑模型超时在既有 Task/Execution 失败记录之后转为可重试 deadline_exceeded ProblemDetail,携带实际 run_id / timeout_seconds。不生成回答或异步成功状态。无目标检索若模型要求继续调查,则呈现选择目标的明确提示。

M5 对非 Vulnerability 对象采用 SourceInvestigationPlannerDecision(perceive / patch / wait / stop),不向模型开放只支持 CVE 的 EnrichmentRole v1 委派。实际能力限制同时进入受信 runtime invariant;Vulnerability 仍沿原委派协议运行。模型自行选择读取与 StatePatch,既有 M4 证据 gate 保持不变。此修复来自真实 ResearchWork 调查被误委派、因空 CVE 失败的联调记录。

Investigation Product View 另行返回 case_lifecycle(既有 durable Case owner 的状态),status 继续表示当前运行投影。回合 blocked/failed 不等于 Case 已关闭;开放 Case 的追问沿同一 Case 继续,取消权限使用真实 Case lifecycle + owner principal。真正 resolved/cancelled/closed 后的后续问题仍按原 session continuation 协议处理。这一差异来自真实 follow-up 联调,前端不再依据上一回合 failed 推断“创建新 Case”。

真实 VERIFY 联调还发现重复委派同一 EnrichmentRole 子任务时,子任务可能已经 completed,而父 InvestigationRole 再次进入 waiting_dependency,因此不会再收到可唤醒的终态事件。运行时现在读取委派结果中的子任务状态:已完成则立即读取当前 canonical Evidence 并继续本回合;失败、阻塞或取消则给出明确阻塞原因;只有仍在运行的子任务才进入事件驱动等待。子任务输出本身不被提升为事实。

2026-10-12 逻辑修正:上述失败、阻塞、取消或超时此前会直接阻塞父 TaskRun,使“工具失败改变后续动作”无法执行。现在子任务状态与停止原因作为无证据的 Percept 反馈给父规划器;可改查既有证据或其他已授权来源,重复无进展仍按上限停止。子任务失败不等于原问题已被否定,也不自动满足 EvidenceNeed。 执行环境缺少富化委派端口时也返回能力不可用的 Percept,允许同样的有界替代决策;目标越界仍是执行契约错误,不降级为可忽略的材料缺口。

同日收紧委派选择:规划器仅用调查绑定的目标对象类型决定是否开放 CVE 专用委派,关联上下文的漏洞对象不改变论文或仓库任务的 schema;混合目标任务使用不含 CVE 委派的安全路由。委派适配器再次核对目标是绑定的 Vulnerability 且 CVE 标识一致,拒绝伪造或错绑目标。

后续追查确认,旧委派 ID 包含 planner iteration 和 assembly hash,同一父任务在富化后刷新 Context 时会创建另一个语义相同的子任务。现将委派身份固定为父 TaskRun、目标对象/CVE 与标准维度集合,重放时复用已完成子任务;子任务保留创建时的 Knowledge 快照,父子 Context 修订变化不构成身份冲突。新的追问 TaskRun 仍可独立发起新一轮富化。

另一次真实 VERIFY 中,模型将 inferred reasoning_relation 的命题直接写入 confirmed,M4 State Gate 按既有规则拒绝。InvestigationRole 现在把拒绝原因作为下一轮规划反馈,受 no-progress 上限约束;模型可改写为有直接证据支撑的确认或保留为 tentative/hypothesis。拒绝本身不产生 Case 事实,也不再直接让整次 TaskRun 发生意外失败。

本轮真实论文联调进一步定位到 INSPECT 仅返回结构化投影、不返回已收录正文。已补充 document 物理读取算子:按精确 object_id 读取各文档最新 revision 的有序片段,最多 100 条,并携带该版本 Observation 对应的对象 EvidenceLink。缺失新版本片段时返回空结果,不混入旧正文;不创建新事实,也不绕过 StatePatch。真实 ResearchWork 感知已读到 20 个正文片段及 1 个持久证据引用。

2026-10-09 多轮 Bitget 调查暴露另一边界:模型把解释性长句放进 WAIT/STOP reason,超过 TaskRun stop_reason 的 128 字符存储契约,使已经确认事实的 Case 意外失败。Planner 在模型输出边界保留短机器码,对自由文本或过长原因使用有界停止/等待码;原有 Case 事实、未知项与模型调用记录仍按各自 owner 保留,终结与 M6 研判不再被存储长度错误截断。

同一会话在外部 Knowledge 已前进后继续 RETRIEVE,原路径将新检索结果与旧 M4 世界版本比较并报冲突,用户无法继续。现在 Product 在 Case-read 前检查 revision;若已过期,直接在同一 active Case 接受新的 InvestigationRole 回合,让 Perception 刷新 M4 后再做研判。查询意图规划期间发生版本变化时也落到同一续接路径。无显式目标的会话继续保留原 Case 和会话顺序;显式重绑仍走独立轻量读取。前端提交失败会清掉未保存的流式草稿,保留输入文字供重试。

该续接路径的真实运行又暴露 M6 交付缺口:M4 已确认 Chainalysis 的行动并记录 Bitget 自身措施未知,但 DecisionRole 仍返回 EvidenceNeed,用户没有报告。现在调查回合结束时,只要 M4 仍有未知项或开放 EvidenceNeed,就要求 M6 交付“已知事实 + 未知边界”的阶段性自然语言报告,并让 Case 保持 waiting 供后续追问;如果模型仍返回继续调查请求,使用仅含已确认事实和对应引用的有界报告兜底。QA 评测和权威结论仍以结构化 Decision 字段为准。

真实多轮会话还暴露历史渲染串位:所有共享同一 Case 的旧回合都从 Case 的 mutable latest_decision 读取最新报告。现在 M6 收口时通过父 InvestigationRole 的 request_id 将 Decision ID 回填至该会话回合的 decision_ref;问答记录只按此稳定引用展示对应报告和引用,Case 页面仍展示当前最新研判。旧回合无稳定引用时明确显示未单独归档,不把新报告冒充旧回答。

问答右侧审计轨迹同样按请求坐标收窄:TaskRun 摘要公开本次 Product request_id,前端将相同 request_id 的运行及其父子/前驱 DecisionRole 归入选中回合,事件随 TaskRun 过滤。Case 完整页仍保留所有回合的整体时间线。本地既有数据中可精确匹配的 9 个历史 Decision 已按父运行 trace_context 回填至所属会话回合。

36. 调查意图、关联取证与回合报告 — 2026-10-11

真实产品反馈暴露:从 Repo 档案进入问答并明确要求“调查关联证据、冲突、未知与外部上下文”,原自动路由因存在 object_id 而直接 LOOKUP;M6 将仓库语言、许可证和热度计数包装成“调查完成”。现由 Application 在已有稳定 object/CVE 或会话目标时识别明确的调查请求,携完整原问题进入 InvestigationRole。普通属性问题和“调查报告是什么”仍是读取;无稳定目标的自然语言问题仍先 RETRIEVE。正文重复当前 Case 的 CVE 可以继续同一 Case,真实换目标以及显式 API 重绑仍受既有生命周期约束。

M5 非漏洞规划指导升级为 investigation-model-v4-source:按问题需要追查双向关系、读取相关对象、检索简短项目名,批量写入相关事实,未覆盖维度写入 unknown,不能用元数据完成关系或安全调查。perception-planner-v2 在 TRACE/EXPAND 的 graph 步骤后追加依赖式证据读取;只沿此次返回的至多 20 条关系读取真实 EvidenceLink。读取额度按关系分配,在 SQL 限制候选行数并报告截断及实际读取次数;Percept 保留关联端点供后续导航。该动作不创建事实、不晋升候选、不绕开 M4 StatePatch gate。SEARCH 仍是本地索引搜索,不能被描述为实时外网检索。

M6 使用 decision-model-v6(不要求报告的调用使用 decision-model-v6-core),按完整问题判断证据充分性;报告首段说明重大覆盖缺口,空 conflicts/unknowns 列表不是不存在风险的证明,公开/未归档/stars 不能推导安全或维护质量。段落仍只能引用经过结构化结论验证的证据,旧 DecisionResult 保持不可变。

所有正常完成或受控 BLOCKED 的调查回合都可进入既有独立 DecisionRole 收口;FAILED/CANCELLED 不被改成成功。即使 M5 曾认为 EvidenceNeed 已解决,M6 新发现缺口时也先通过 M4 ContinuationGate 保留 Need,再以新 revision 提交可读阶段报告,两个操作在同一事务内完成。本轮问答绑定该不可变 Decision,Case 保持 waiting。非法 continuation target 仍被拒绝,并进入安全边界报告回退;任务重投不重复创建 Need/Decision。新报告包含 unknowns 时也保持 waiting,不因模型给出 final 就误报调查完成。

同一调查继续暴露 SEARCH 回传正文却遗漏已有 EvidenceLink 的问题。现在由 M3 公开读取接口按 chunk、DocumentRevision、object 和该 revision 的 Observation 精确绑定已存在引用,M5 的 lexical/dense Percept 同时携带段落与证据;不存在匹配 link 时仍留空,不借用新版本。文献支撑的发现必须归因原文,并以文档对象为 StatePatch target,不把论文提到仓库视为已经独立验证仓库安全状态。该链路经过原有 M4 支撑对象校验;不修改来源或自动晋升 canonical Knowledge。

真实运行进一步发现 StatePatch 拒绝反馈被 recent_percepts 遮蔽,模型看不到 target_ref 校验失败并重复同一写入直至 no_progress。Planner 现在合并最近来源 Percepts 和未重复的 last_percept;拒绝原因与可引用材料共同进入下一次 PromptAssembly。仅从已展示对象补全稳定 object 引用,relation ID 不会被自动改绑;结构化 schema 明示 target_ref 的对象/事件命名空间。完整 Role-loop 回归覆盖先拒绝、再纠正、最终只接受一个事实,M4 gate 保持原有严格性。

报告质量复查又发现模型选择 continuation 后,旧回退仅取最近三条事实,丢失更早且更相关的安全发现。Product 的已结束调查回合现使用 final-only InvestigationReportResponse,由同一次 M6 调用从全量 confirmed state 选择相关结论并组织段落;未检查维度写入 Decision unknowns,Case 保持 waiting。“交付报告”不等于“证据充分”。普通 QA/M6 调用保留 continuation,旧 continuation → Need → report 分支保留为非标准 provider 的防御兼容。新增 schema 与生命周期回归覆盖 M5 已解决原 Need、M6 仍发现覆盖缺口的情形。

本地真实问题复核已输出关联 PR、研究论文中的安全线索和清晰的待核实边界,报告自然段、固定来源版本、原文入口与会话回填均已走通;Case 保持 waiting。该检查是产品功能联调,不作为正式 QA 准确率或安全覆盖率结论;CVE/NVD、官方通告和版本修复状态仍需进一步取证。实现分批推送于 8c45072、60354f5、9396e24;详情阅读组件迭代为 831b8cb。