Skip to content

Technical Design 2A

jiale li edited this page Oct 16, 2026 · 30 revisions

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-16

Product 使用 FastAPI/REST/OpenAPI/SSE,apps.application 组合能力 owner;领域包不依赖 Product DTO。账户、会话、多轮问答、Case 继续调查、不可变逐回合 Decision、来源与任务审计均已实现。推荐/关注与反馈、事件库、资料库和公开研报是当前正式产品读取面。

Slice A/B 已完成;Slice C 的问答、会话、exact retrieval reuse 与 pre-context failed retrieval attempt 已落地。Slice D 的幂等、cancel、revision conflict 和 activity 已完成;独立通用 /resume 尚未实现,session follow-up 与 runtime wake 不是该接口。Slice E 的 ProductEvent/SSE、Last-Event-ID 与终态通知已实现。Slice F 已有来源/系统、证据、情报、事件、资料与公开/私人 HTML 读取。Slice G 有 transport/owner/contract 回归;本轮未重新做真实 PostgreSQL/Redis 集成及收费模型端到端验收。

Question 的未校验模型草稿可以经 SSE 展示,但正式结果只能来自验证后的 Decision。事实 proposal 被拒绝时,当前 use case 可返回零事实、零引用的有效边界答复(evidence_validation_failed);真正调用失败仍返回 error。不能沿用“所有证据拒绝都会产生失败 Task”的旧描述。

Product 命令幂等为 migration 0032,独立失败检索记录为 0033,活动反馈为 0035,公开研报/自动编研为 0036/0037。当前源码 migration head 为 20261015_0037;这不表示每个部署已执行升级。

历史正式 Product/session QA 已进入冻结 CompetitionReport;当前能力验收与扩展分母见 验收台账。Workbench 旧 HTTP/静态控制台已退役,Product-safe DTO 承接必要观察面;A2A 保持独立 transport contract。

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

以下问题清单记录 2026-09-27 的设计动因;当前实现状态与 Slice 验收以本页顶部及第 29 节为准,不作为未完成列表。

1.1 已经正确形成的边界

2026-09-27 的 apps/api/main.py 曾负责 lifespan、session factory、router 和 dev/test Workbench;当前旧 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 与产品合同的区别

现有 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/* 和静态 operator console 不再注册。必要的来源、任务、Case、上下文和模型观察由账户校验后的 Product read models 承接;开发诊断使用能力 owner、脚本与测试。退役 transport 未带走任何 domain authority,不再规划其逐步抽取或长期公开支持。

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 推断产品状态。

旧 diagnostic schema 不作为 Product DTO;退役的 Workbench 不再提供该读取面。


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。

内部 TaskEvent / ContextManifest 保留在 owner storage 和授权审计投影中。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 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。

Product Question 流式合同:POST /api/v1/questions/stream 在同一请求内发送真实模型 model_delta,可选发送 provider reasoning;这些字节只是未验证草稿。React 会把已经收到的报告自然段按字符呈现,正式答案仍只来自通过 M6 证据不变量并持久化的 DecisionResult,到达 result 时立即替换草稿。完整会话默认请求 90 秒交互预算,服务端允许 1–120 秒;这只是执行上界,不人为延迟答案。终态 error 结束该幂等命令,重试生成新键。若事实 proposal 被证据门拒绝,当前 Question use case 提交零事实、零引用的边界 Decision,标记 evidence_validation_failed,并替换未校验草稿;真正执行失败仍返回 error 和可用的 TaskRun 坐标。不持久化被拒绝 proposal 为事实。未知异常只返回通用错误并在服务端按 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。

Product 分页按本 contract 实现;旧 diagnostic limit-only list 不作为当前产品契约。


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。

Product route 使用统一 mapper;已退役的 Workbench 不保留第二套错误合同。


15. Public Command Idempotency

旧 Workbench 的随机 trigger_ref 不适合作为客户端重试合同;Product Command 已使用 client idempotency key。

以下 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 是正式 Product Web;Task/Context 通过授权和裁剪后的审计 DTO 呈现。


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。

OpenAPI 记录现有 Product 与独立 A2A routes;旧 Workbench routes 不再注册。


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

该编排已由 Application Use Case 持有,routes 只负责 transport。


23. Repository / Query Ownership

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

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

当前 overview、task、case 查询由 Application read projection 持有;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
│   │   ├── 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 迁移验收

迁移已完成:Product routes 调用 Application use cases/read models,旧 diagnostic routes 已撤下。Task/Context/Model/Evidence 通过授权后的产品 DTO 可读;内部 raw schema 不成为产品合同。当前不存在等待继续重构的 workbench.py。


26. Frontend Contract Mapping

当前 apps/web 已是四入口 Product Web,现有 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 与预算状态。

任务执行展示使用 Product-safe RuntimeActivityView 与授权审计;更深调试使用 owner 脚本和日志。


27. Testing Strategy

apps/api/tests 验证 Product routes 的权限、Case/EvidenceNeed、policy-admitted TaskRun、幂等和 SSE;不依赖旧 Workbench。

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 / 内部诊断的测试分层

  • /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:已实现

  • RequestContext/request ID、共享 session、错误映射、health/live/ready、OpenAPI。

Slice B — Application Skeleton:已实现

  • Investigation use cases/read models、统一 admission 与 domain owner 组合。

Slice C — QA Routing:主体已实现

  • 同步 Question / 异步 Investigation、不可变 Decision、会话/目标/context carry。
  • exact-query/session reuse、durable RetrievalInvocation、pre-context failed attempt。
  • 同 Case 串行追问、active-Case read、Product/session evaluation runner。
  • 暂缓:semantic/non-identical-query reuse;不属于已交付 exact reuse,见验收台账 O09。

Slice D — Investigation Lifecycle:部分验收

  • Cancel、Idempotency-Key、revision conflict、Activity。
  • session continuation 与 runtime wait/wake 各有 owner。
  • 未实现:通用独立 Product /resume,见 O08;不能用上述 continuation 代替。

Slice E — Product Event / SSE:已实现

  • Task/State → ProductEvent、SSE、Last-Event-ID reconnect、终态 Decision 通知。
  • Question draft stream 与正式验证结果分离;重放仅返回持久终态。

Slice F — Product Read Surface:已实现

  • 来源/系统、证据、对象/漏洞、事件、资料库、关注/推荐、账户任务审计。
  • 公开摘要/HTML 和账户所属私人 HTML;读取时重新校验可见性。

Slice G — Contract Gate:工程回归通过,在线范围保留

  • API/访问控制/幂等/报告/前端 contract 回归。
  • 待当前部署验收:真实 DB/队列集成、收费模型及更广自然问题端到端效果,见 O01/O03。

逐项测试入口、限制与下一步统一见 当前验收台账。旧 Workbench 已退役,不作为当前交付入口。


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

内部工程诊断通过 owner 脚本、测试与授权审计读取 Task/Context/State/Replay;退役 Workbench 不再是一条可访问的产品入口。


33. 参考

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

34. 当前 World、资料与事件读取

World 异构材料投影保留各来源的真实标题、日期、正文、身份与 Evidence 坐标;首屏 story 与 Hot/健康/formation 分开读取。Hot 每页 24 条且 offset 有界,resident_total 独立报告;完整 CVE 可查整个驻留池,不能把缓存视为持久事实。

资料库按公开 Source/Artifact、材料质量和当前修订筛选,保留 as_of 页坐标和词法多标签分类。事件库读取 durable incident 与证据绑定的编目时间线;候选信号与确认、工具里程碑与攻击事件分别表达。

Product Document excerpt 只在 Source 明确 public、选中 Observation 至少有一个 Artifact 且所有 Artifact 明确 public 时返回,最多 1,200 字符;否则连同 chunk/locator 隐藏。Evidence 详情的来源片段与对象级 EvidenceLink 不冒充逐句事实证明。

35. 当前调查与逐回合报告

自然调查请求根据稳定目标或会话上下文进入 InvestigationRole;元数据不满足安全调查正文需求。Perception 的 lexical/dense/inspect 读取绑定确切 DocumentRevision Observation 的现有 EvidenceLink,不借用新版引用。已支持但未满足完整 EvidenceNeed 的发现可经 StatePatch Gate 保留,Need 继续开放。

M5 规划同时看来源 Percepts 与未重复的状态拒绝反馈。委派子任务失败/阻塞/取消后父任务得到明确反馈,可以转向其他授权来源或有界停止;不把失败委派当成功证据,也不等待已经终态的子任务。

正常完成或受控 BLOCKED episode 通过独立 DecisionRole 形成阶段报告;FAILED/CANCELLED 不伪装为成功。M6 新发现缺口时先经 M4 接受 continuation,再以新 revision 提交报告;未知或开放 Need 保持 Case waiting。每个会话回合绑定自己的 immutable Decision,后续 Case current decision 不覆盖旧答案。

36. 验收与历史说明

当前各项实现、测试和未完成范围见 当前验收台账。原 2026-10-08/11 的在线调试细节已移至 联调历史,不再和当前操作合同混排。

37. 信息质量与公开 HTML 编研 — 2026-10-15

信息质量与 HTML 情报报告 冻结本轮补充合同:WORLD 仍为无模型读取,但过滤错误页、正文污染并保留真实日期语义;公开编研使用独立 M4 tentative → M6 Decision → 审校 → publication 写入流程。私人 HTML 沿用账户权限,公开目录只读通过审校且来源当前仍公开、修订未变化的发布记录。模型输出自然段与有界章节,受控 renderer 生成 HTML;生成不改变权限。具体 prompt、幂等、UTC 窗口、失效及验证边界见该页。实现迁移为 20261015_0036(公开研报)与 20261015_0037(自动编研任务),代码已合入默认 main。

Clone this wiki locally