aida-agent — 架构可视化包

源码有据简报 · 5 个视角 · 生成于 2026-06-14 · 只读

结论

aida-agent 是一个尚未投产、单进程的 Hono 编排器;其核心骨架——双令牌认证、能力优先路由、持久化 ReAct 循环、通过 Action Proposal 的写入治理,以及阻塞式高风险审计——在代码与 ADR 中有清晰依据;其运行时风险几乎完全集中在未经验证的生产拓扑(仓库中无 Dockerfile/k8s/wrangler)以及加密密钥轮换 / 跨租户隔离执行细节。

系统地图

同一套代码库的五个视角。每张图独立溯源;合起来追踪一次请求从令牌到 Postgres 的完整路径、决定执行内容的路由/编排大脑、进程部署位置、权限边界如何维持,以及持久化状态转换如何受治理。

1 · 数据流

一次 /api/chat 请求生命周期:Aida JWT → 网关 → 会话绑定 → ReAct 循环 → SSE 事件 → Postgres + 审计。

2 · 代理编排

能力优先路由管道、共享 ReAct 循环、工具门控/风险、子运行 agent-as-tool,以及本地工作台调度。

3 · 部署

开发/本地进程拓扑与外部依赖。生产拓扑不在仓库中

4 · 权限

双令牌边界、通过 Action Proposal 的写入治理、阻塞式审计安全门、路由决策加密 + 管理员访问。

5 · 状态机

ActionProposal、PendingInteraction 与 CapabilityRoute 状态生命周期及其交叉耦合。

数据流

数据流

数据流:一次 /api/chat 请求生命周期(Aida JWT 到 SSE 到 Postgres)

sequenceDiagram
    autonumber
    actor Client as "客户端 (Aida JWT)"
    participant GW as "网关 (Hono Auth)"
    participant Bind as "会话绑定"
    participant RT as "OrchestratorRuntime"
    participant Orch as "编排器 (ReAct)"
    participant Gate as "ToolCallGate"
    participant LLM as "LLM 流 (AI SDK)"
    participant Tool as "工具后端 (Spring Boot)"
    participant Router as "能力路由"
    participant PG as "Postgres 存储"
    participant Audit as "审计管道 (WAL + Sink)"

    Note over Client,GW: auth.ts:76-194 + shared/types.ts:243-257
    Client->>GW: "Authorization: Bearer Aida-JWT"
    GW->>GW: "authMiddleware verify JWT"
    GW->>GW: "tokenProvider.resolve(tenantId,userId)"
    GW->>GW: "create TenantContext via AsyncLocalStorage"

    Note over GW,Bind: chat-routes.ts:125-280 + session-binding.ts:82-280
    GW->>GW: "validate body (message/agentId/agentKey)"
    GW->>Bind: "resolveSessionBinding (configDbReady)"
    Bind->>PG: "sessionStore.lookup or create"
    Bind->>Bind: "ConfigFacade.resolveAgent (agentId or '')"
    Bind-->>GW: "SessionBindingResult: sessionId + agentId"

    Note over GW,Client: chat-routes.ts:283-330 streamSSE
    GW->>Client: "SSE open: session_bound event"

    alt agentId === '' (统一入口)
        Note over GW,Router: chat-routes.ts:333-407 + routing/router.ts (ADR-0016)
        GW->>Router: "CapabilityRouter.route(message)"
        Router->>Client: "capability_route_decision event"
        Router->>RT: "routed: child agent run or local workbench"
    else agentId specified (代理绑定)
        Note over GW,RT: chat-routes.ts:410-459
        GW->>RT: "runtime.resolveAndRun(input, localExecutor?)"
    end

    Note over RT,Orch: orchestrator-runtime.ts:126-196 + resolve-and-orchestrate.ts:92-195
    RT->>RT: "resolveAgentPipeline: llm/tools/systemPrompt"
    RT->>RT: "assemble TurnConfig per agent (ADR-0018)"
    RT->>Orch: "orchestrate(input, deps, signal)"

    Note over Orch,PG: persistence-helpers.ts:43-100
    Orch->>PG: "check concurrent runs then runStore.create"
    Orch->>PG: "load message history"
    Orch->>Client: "run_bound event (runId/conversationId)"
    opt memoryEnabled (CONTEXT.md)
        Orch->>Orch: "memoryRecall prepended to LLM context"
    end

    Note over Orch,Tool: drive-react-loop.ts:30-100 ReAct loop
    loop ReAct Rounds (0..maxIterations)
        Orch->>LLM: "llm.stream({messages,tools,systemPrompt})"
        LLM-->>Client: "response/thinking events (streamed chunks)"
        Orch->>Orch: "ResponseGuard validate critical fields (ADR-0018)"
        Orch->>Client: "usage event (prompt/completion tokens)"

        alt model emitted tool_use
            Orch->>Client: "tool_call event"
            Orch->>Gate: "ToolCallGate: pass / confirm / reject"
            alt passed
                Gate->>Tool: "ToolBackendClient.call (backendToken injected)"
                Tool-->>Orch: "tool result"
                opt result-verifier.ts
                    Orch->>Tool: "optional 2nd-call verification"
                end
                Orch->>Client: "tool_result event"
            else confirm needed
                Gate->>PG: "create PendingInteraction"
                Orch->>Client: "interaction_pending event (pause run)"
                Note over Client,Orch: chat-routes.ts:801-891 confirm / 648-799 tool-result callback
                Client->>GW: "POST /:sessionId/confirm or /tool-result"
                GW->>PG: "resume PendingInteraction"
            end
        else no tool call (exit)
            Note over Orch,PG: persistence-helpers.ts persistRoundMessages
            Orch->>PG: "messageStore.insert + runStore.finalize"
            Orch->>Bind: "sessionStore.update(lastActiveAt)"
        end
    end

    Note over Orch,Audit: audit/pipeline.ts emit/emitAndWait
    Orch->>Audit: "emit events"
    Audit->>PG: "write JSONL + Postgres sink (ADR-0003)"
    opt context overflow / off-path (CONTEXT.md)
        Orch->>Orch: "compaction + summarization"
        Orch->>PG: "memoryExtractor mines candidates after run"
    end

    alt failure
        Orch->>Client: "error event (code/message/category)"
    else abort signal
        Orch->>Client: "cancelled event (stream close)"
    end
    Note over Orch,Client: transport/events.ts:57-68 serializeAgentEvent
    Orch->>Client: "all events serialized to SSE JSON lines"
    
依据:溯源自 packages/agent/src/gateway/{auth.ts:76-194, chat-routes.ts:125-891, session-binding.ts:82-280, orchestrator-runtime.ts:126-196}src/orchestrator/{agent.ts:232, drive-react-loop.ts:30-100, round.ts:82-100, tool-executor.ts:17-100, run.ts:27-90, persistence-helpers.ts:43-100}src/shared/types.ts:13-257src/audit/pipeline.ts;ADR-0003/0016/0018/0020 与 CONTEXT.md。

代理编排

代理编排

代理编排:能力优先路由与编排器循环

flowchart TB
  entry["统一入口
(Unified Entry)"] subgraph ROUTER["CapabilityRouter 管道 (ADR-0016 K2)"] direction TB rf["ruleFilter
租户启用 + 角色门控预过滤"] lc["llmClassify
单步 LLM,畸形回退"] dr["resolveCapabilityDispatchability
执行器绑定可用性"] ds["decideStatus
纯决策矩阵 (PRD R17)"] dec["CapabilityRouteDecision
routed / refused / clarifying / route_only"] store["PgRouteDecisionStore
持久化 + SSE (R-Y 加密)"] rf -->|"filtered candidates (non-empty)"| lc lc -->|"classification + candidates"| dr dr -->|"dispatchability map"| ds ds -->|"status, primaryCapabilityId, riskAssessment"| dec dec --> store end rc["Router Context
recent msgs, child-run output filtered"] subgraph LOOP["编排器循环"] direction TB react["driveReActLoop
共享轻量 ReAct(无持久化)"] gate["ToolCallGate
同轮 / 重复 / 跨轮守卫"] risk["evaluateToolRisk
metadata + riskLevel: allow / confirm / reject"] disc["discoverTools
每轮加载写入工具"] reqc["requestConfirmation
第 1 轮写入门控 (sideEffectLevel=read)"] react -->|"executeRound calls gate.decide"| gate gate -->|"def.riskDecision"| risk gate -->|"markConfirmationRequested()"| reqc end td["Tool Disclosure
读工具始终挂载,写工具经 discoverTools"] pend["PendingInteraction
confirm / ask_user / local_tool_result, once-only, expiry"] prop["Action Proposal
confirm|reject, approverTarget, executionPayload, hash"] subgraph CHILD["子运行 (agent_as_tool, ADR-0016 K1/K7)"] direction TB allow["assertChildAgentAllowed
seed_key 白名单"] resolve["Child Agent resolver
seed_key to ChildAgentRuntime"] minc["minimizeContext
recent N msgs + extracted inputs"] runc["runChildAgent
child agent as tool, parent_run_id lineage"] fwd["transparentForward
ChildRunOutput to user-visible text (no rewrite)"] allow --> resolve resolve --> runc minc -->|"MinimizedContext"| runc runc --> fwd end subgraph WORKBENCH["本地工作台 (local_workbench, K5)"] direction TB lwb["dispatchLocalWorkbench
直接桌面原语(无子运行)"] lexec["buildLocalExecutorRuntime
read/write tool defs + prompt suffix"] lexec -->|"filtered tools"| lwb end subgraph RUNTIME["启动组装 (ADR-0018)"] direction TB ort["OrchestratorRuntime
启动时组装的基础设施"] tc["TurnConfig
per-turn LLM, tools, prompt, skills"] ort -->|"resolveAgentPipeline"| tc end entry --> rf store -->|"decision status drives dispatch"| entry rc -->|"recent messages to classifier"| lc entry -->|"routed + agent_as_tool"| runc entry -->|"routed + local_workbench"| lwb runc --> react lwb --> react disc -->|"per-run discovery"| td reqc -->|"high-risk park"| pend risk -->|"confirm: high-risk write"| prop prop --> pend ort -.->|"infra container"| LOOP tc -.->|"per-turn config"| react %% ADR-0017: server-side desktop write policy risk -.->|"ADR-0017 write gating"| gate
依据:溯源自 packages/agent/src/routing/{router,rule-filter,llm-classifier,dispatchability,decide-status}.tssrc/orchestrator/{tool-call-gate,risk-policy,drive-react-loop,internal-tools,local-workbench-dispatch}.tssrc/orchestrator/agent-as-tool/{child-run-orchestrator,allowed-child-agents,context-minimization,transparent-forward}.tssrc/gateway/{unified-entry,local-executor,orchestrator-runtime}.ts,以及 docs/adr/0010/0016/0017/0018。

部署

部署

部署拓扑 — 运行时进程与外部依赖(dev/local,生产基础设施不在仓库中)

flowchart LR
  subgraph clients["客户端"]
    chat["Shared Chat Packages
(../../ai-chat)"] desktop["AIDA Desktop Host
(../aida-desktop, Hybrid Local Executor)"] web["React Admin Web
(Vite dev, port 8887)"] end subgraph agentproc["Hono Agent 进程 (port 3000)"] agent["Hono Agent
(packages/agent)"] orch["OrchestratorRuntime"] sched["Scheduler Engine
(BullMQ Worker)"] audit["Audit Pipeline
(JSONL + optional Postgres)"] toolclient["@aida/tool-client
(HTTP Tool Client)"] end subgraph datastores["数据存储"] configdb[("Config DB
(Postgres)")] redisSess[("Redis
(Session Dedup + Backend Token cache)")] redisSched[("Redis
(BullMQ Scheduler)")] end subgraph external["外部服务"] nacos["Nacos
(Service Discovery + Config)"] authsvc["caidao-auth-service
(OAuth2)"] springboot["Spring Boot Tool Backend
(caidaocloud-ai-service)"] llm["LLM Provider
(OpenAI-compatible / Anthropic)"] embed["Embedding Provider
(OpenAI-compatible)"] feishu["Feishu IM Platform"] end eval["Agent Eval Framework
(YAML scenarios)"] chat -->|"HTTP/SSE (Aida JWT auth)"| agent desktop -->|"HTTP/SSE (Aida JWT, Hybrid Local Executor)"| agent web -->|"Vite dev proxy: /api /admin /healthz /aida-work/* to :3000"| agent agent --> orch agent --> sched agent --> audit orch -->|"Tool schema + execution"| toolclient agent -->|"agent/provider/skill/entitlement config"| configdb agent -->|"Backend Token cache + channel dedup store"| redisSess agent -->|"BullMQ queue + job storage"| redisSched sched -->|"BullMQ Worker process"| redisSched sched -->|"Scheduled task via resolveAndRun"| orch agent <-->|"Service discovery + config bootstrap (optional)"| nacos agent -->|"Backend Token exchange + verification"| authsvc agent -->|"Tool schema discovery + execution (Backend Token)"| springboot toolclient -->|"HTTP REST with Backend Token"| springboot agent -->|"OpenAI-compatible API calls"| llm agent -->|"Embedding calls for memory recall"| embed agent <-->|"Webhook + WebSocket; IM message webhooks + user binding"| feishu audit -.->|"Optional Postgres audit sink"| configdb prodUNKNOWN{{"未知:生产部署拓扑
不在仓库中 — 无 Dockerfile / k8s / wrangler;项目尚未发布"}} agentproc -.->|"生产环境运行方式未经验证"| prodUNKNOWN classDef unknown fill:#3a2a00,stroke:#d99000,stroke-width:2px,color:#ffd97a,stroke-dasharray:5 4; class prodUNKNOWN unknown;
依据:溯源自 packages/agent/src/config.ts(PORT 3000、toolBackendUrl、authServiceUrl、scheduler/backendToken Redis URLs、configDbUrl、llmBaseUrl)、packages/web/vite.config.ts(port 8887 + /api /admin /healthz /aida-work 代理到 localhost:3000)、packages/agent/src/index.tsnacos.tsscheduler/engine.tsgateway/channels/feishu + dedup/redis、CONTEXT.md / AGENTS.md;已确认仓库中不存在 Dockerfile/docker-compose/k8s/wrangler。

权限

权限

权限:双令牌边界、写入治理与阻塞式审计安全

flowchart TB
  subgraph TOKENS["双令牌边界 (ADR-0001)"]
    direction TB
    EXCHANGE["Token Exchange
/api/auth/exchange
(only bootstrap path)"] AIDA["Aida JWT (HS256)
Client to Agent auth
30d expiry"] BACKEND["Backend Token (OAuth2)
Agent to Spring Boot auth"] AUTHZCACHE["Authorized Identity Cache
10 min TTL
(after /api/auth/authorize)"] EXCHANGE -->|"returns Aida JWT"| AIDA EXCHANGE -->|"caches Backend Token
for tool calls"| BACKEND AUTHZCACHE -->|"pre-caches identity"| EXCHANGE end subgraph GATE["请求级认证与传播"] direction TB AUTHMW["authMiddleware
verify Aida JWT signature"] CLAIMS["JWT Claims Validation
tenantId + userId required
empId, roles optional"] ALS["TenantContext via AsyncLocalStorage
request-scoped propagation"] PROFILE["User Profile Fetch (async, 3s)
GET /api/users/me
department enrichment"] TOKENPROV["TokenProvider
resolve/fetch Backend Token"] TOKENCACHE["Backend Token Cache
Redis (CONFIG_REDIS_URL) or In-Memory
25 min default TTL"] AUTHMW --> CLAIMS CLAIMS -->|"creates TenantContext"| ALS PROFILE -.->|"enriches department
(non-blocking)"| ALS ALS --> TOKENPROV TOKENPROV --> TOKENCACHE TOKENCACHE -->|"store/retrieve OAuth2
tokens with TTL"| BACKEND end AIDA -->|"Authorization: Bearer"| AUTHMW subgraph WRITEGOV["写入治理 — Action Proposals"] direction TB GATE_TC["ToolCallGate
write enters execution or creates Proposal"] RISKEVAL["Risk Policy Evaluator
evaluateToolRisk()"] RISKACTION["ToolRiskDecisionAction
allow | confirm | reject"] PROPOSAL["ActionProposal
persistent write record
(Postgres or In-Memory store)"] HASH["payloadHash (SHA256)
stable stringify before exec"] REDACT["Safe Args Redaction
token / password / secret"] SAVEFLAG["savePrivatePayload flag
true=confirm, false=reject"] STATEM["Status Machine
pending to (approved | rejected | expired)
to executing to (executed | failed)"] REQID["requesterUserId
who initiated the write"] DECSRC["decisionSource
policy auto-allow vs human"] GATE_TC -->|"calls"| RISKEVAL RISKEVAL --> RISKACTION RISKACTION -->|"confirm=create, reject=create rejected,
allow=skip"| PROPOSAL PROPOSAL --> HASH PROPOSAL --> REDACT SAVEFLAG -->|"controls privatePayload stored vs null"| PROPOSAL PROPOSAL --> STATEM REQID --> PROPOSAL DECSRC -->|"records transition origin"| STATEM HASH -->|"re-hash and compare
before executing"| STATEM end ALS -->|"TenantContext supplies tenantId/userId"| GATE_TC subgraph APPROVAL["审批授权"] direction TB TARGETVAL["ApproverTarget Validation
requester | user:ID
(deferred: role: dynamic: notify:)"] CONFIRMRULE["Confirm Rules
high-risk cannot use requester
requires explicit user:ID"] AUTHZ["Approver Authorization
requester=userId match
OR explicit user:ID match"] RESPOND["POST /api/interactions/:id/respond
approval decision endpoint"] PISHELL["PendingInteraction(kind=confirm)
delivery/response shell"] EXPIRY["Approval Expiry Lifecycle
created to pending(expiresAt)
to expired before/after decision"] TARGETVAL --> CONFIRMRULE TARGETVAL --> AUTHZ RESPOND -->|"calls authorizeApproverDecision()"| AUTHZ RESPOND -->|"updates status approved | rejected"| STATEM REQID -.->|"requester must match requesterUserId"| TARGETVAL EXPIRY -->|"wraps Proposal lifetime"| PISHELL end PROPOSAL --> PISHELL subgraph BLOCKING["阻塞式审计安全门"] direction TB EMIT["AuditPipeline.emit()
fire-and-forget
(sync for high-risk)"] EMITWAIT["AuditPipeline.emitAndWait()
strong sync, must succeed first"] HIGHRISK["High-Risk Auto-Sync
auth + error categories
critical/high severity"] JWTFAIL["JWT Signature Failure
blocking audit then 401"] CROSSTENANT["Session Cross-Tenant Denial
blocking audit before exception"] DENIEDERR["SessionAccessDeniedError
critical severity audit then 403"] EMITPARAMS["EmitParams
category/action/severity/detail
(tenantId auto-resolved from ALS)"] EMIT --> HIGHRISK EMITWAIT -->|"awaits WAL + primary sink"| HIGHRISK JWTFAIL -->|"emitBlockingAuthFailure()"| EMITWAIT CROSSTENANT -->|"before throwing"| EMITWAIT DENIEDERR -->|"thrown only after critical audit"| CROSSTENANT EMITPARAMS --> EMIT end subgraph AUDITSTRUCT["审计事件模型"] direction TB EVTSTRUCT["AuditEvent Structure
id, timestamp, requestId, traceId,
spanId, tenantId, userId, action,
detail, category, severity"] CATS["Categories
auth, session, tool_call,
interaction, guardrail, error"] ORPHAN["ORPHAN_TENANT_ID = __orphan__
sentinel for lost-in-async events"] TENANTREQ["TENANT_REQUIRED_CATEGORIES
tool_call, tool_result, llm_call, session,
quota, interaction, guardrail, quality"] EVTSTRUCT -->|"no tenantId logged under __orphan__"| ORPHAN CATS -->|"warn if tenantId missing"| TENANTREQ end CLAIMS -.-> JWTFAIL ALS -.-> CROSSTENANT subgraph ENCRYPT["路由决策加密与管理员访问"] direction TB AESGCM["AES-256-GCM (CONFIG_ENC_KEY)
IV=12 TAG=16, 32-byte key
route decision originals"] LAYERS["R-Y 3 Encryption Layers
(a) original in payload_* columns
(b) redacted view in DB
(c) /admin/audit/read-original decrypt"] FIELDREDACT["Field-Level Redaction (layer b)
name to hash, idcard to last4,
salary to bracket, phone to masked"] READORIG["POST /admin/audit/read-original
audit:read-original scope required
logs decryption_access before plaintext"] ADMINMW["adminAuthMiddleware
scope check: audit:read vs audit:read-original"] STATICTOK["Static Admin Access Token
(ADMIN_ACCESS_TOKEN env)
bypass JWT verification"] ADMINSUBJ["adminSubject
tracks who made admin action
(JWT sub or static token)"] AESGCM --> LAYERS LAYERS -->|"redacted copy to extracted_inputs jsonb"| FIELDREDACT FIELDREDACT --> READORIG READORIG --> ADMINMW ADMINMW -->|"allow static token or scoped JWT"| STATICTOK ADMINSUBJ -->|"recorded in audit_decryption_access row"| READORIG end
依据:溯源自 packages/agent/srcorchestrator/risk-policy.ts(allow|confirm|reject)、session/approver-target.ts(requester|user:ID + high-risk deferral)、session/action-proposal-store.ts(SHA256 hash、状态机、脱敏)、audit/pipeline.ts(emit/emitAndWait、ORPHAN_TENANT_ID、TENANT_REQUIRED_CATEGORIES)、config/crypto.ts(AES-256-GCM IV=12/TAG=16)、gateway/admin-audit-original.ts(audit:read-original scope、自审计),以及 CONTEXT.md/AGENTS.md/ADR-0001。

状态机

状态机

状态机:Action Proposal、能力路由决策、Pending Interaction

---
title: ActionProposal & PendingInteraction 状态机
---
stateDiagram-v2
    state "ActionProposal 状态机" as APTitle
    note right of APTitle
      action-proposal-store.ts:7-14
      privatePayload: cleared on reject, kept on
      approve until exec, cleared on failed
    end note

    [*] --> ap_pending
    state "pending
初始,等待决策" as ap_pending state "approved
decision=approved, ready" as ap_approved state "rejected
decisionSource policy|human
privatePayload cleared" as ap_rejected state "expired
expiresAt deadline passed" as ap_expired state "executing
payload integrity verified" as ap_executing state "executed
executionResult stored" as ap_executed state "failed
payload hash mismatch" as ap_failed ap_pending --> ap_approved : decide(status=approved) via approver ap_pending --> ap_rejected : decide(status=rejected, decisionSource=policy|human) ap_pending --> ap_expired : expire() on expiresAt deadline ap_approved --> ap_executing : markExecuting() about to execute ap_executing --> ap_executed : markExecuted(result) succeeded ap_executing --> ap_failed : markFailed(error) mid-execution ap_approved --> ap_failed : markFailed(error) post-decision integrity failure ap_executed --> [*] ap_rejected --> [*] ap_expired --> [*] ap_failed --> [*] state "PendingInteraction 状态机" as PITitle note right of PITitle shared/types.ts:122-126 kinds: confirm, ask_user, secret_ref, scheduled_task_approval, local_tool_result confirm kind syncs to ActionProposal above end note [*] --> pi_pending state "pending
初始,尚未响应" as pi_pending state "approved
user/approver approved" as pi_approved state "rejected
user/approver rejected" as pi_rejected state "expired
expiresAt passed (once-only)" as pi_expired pi_pending --> pi_approved : respond(decision=approve) pi_pending --> pi_rejected : respond(decision=reject) pi_pending --> pi_expired : expire() or respond() detects expiresAt past now pi_approved --> [*] pi_rejected --> [*] pi_expired --> [*] note left of pi_approved : confirm kind syncActionProposalDecision drives ap_approved note left of pi_rejected : confirm kind syncActionProposalDecision drives ap_rejected note left of pi_expired : confirm kind interaction expiry expires ActionProposal (once-only)
依据:来源:packages/agent/src/session/action-proposal-store.ts:7-14,278-340packages/tool-client/src/types/capability.ts:90-101packages/agent/src/shared/types.ts:115-126packages/agent/src/session/pending-interaction-store.ts:141-206packages/agent/src/routing/{decide-status.ts,route-decision-store.ts:178-189};docs/adr/0014。
说明 — 第三个状态机无法与上方单个 stateDiagram-v2 字段共同渲染。CapabilityRouteStatuscapability.ts:90-101)是一个 10 状态机。已确认状态:intent_clarifyinginput_clarifyingrefusedchitchat_refusedroutedroute_onlyexecutingawaiting_local_confirmcompletedfailed。已确认转换:routed → executing → {completed, failed, awaiting_local_confirm → {completed, failed}}(见 decide-status.tsroute-decision-store.ts:178-189)。

决策矩阵

每条 ADR 映射到其最主要管辖的视角,以及决策被强制执行的代码路径。ADR 编号与图中依据引用一致;映射关系是审阅者对说明文字的解读,并非对代码的新主张。

ADR视角代码路径(引用处)
ADR-0001 双令牌模型 权限 gateway/auth.ts/api/auth/exchange、TokenProvider + Backend Token cache
ADR-0003 审计管道 / 接收端 数据流;权限 audit/pipeline.ts(emit / emitAndWait)、JSONL + optional Postgres sink
ADR-0010 工具 / 风险策略 代理编排 orchestrator/risk-policy.tstool-call-gate.ts
ADR-0014 Action Proposal 生命周期 状态机;权限 session/action-proposal-store.ts:7-14,278-340pending-interaction-store.ts:141-206
ADR-0016 能力优先路由 代理编排;数据流 routing/{router,rule-filter,llm-classifier,dispatchability,decide-status}.ts;agent-as-tool (K1/K7)、local workbench (K5)
ADR-0017 服务端桌面写入策略 代理编排 risk-policy.ts 中的写入门控 → tool-call-gate.ts;local-executor
ADR-0018 启动组装 + TurnConfig 数据流;代理编排 orchestrator-runtime.ts:126-196resolve-and-orchestrate.ts:92-195、ResponseGuard
ADR-0020(数据流依据中引用) 数据流 chat-routes.ts / orchestrator persistence 一并引用;说明中未详述具体范围
R-Y 路由决策加密 权限;代理编排 config/crypto.ts(AES-256-GCM)、PgRouteDecisionStoregateway/admin-audit-original.ts

风险与未知项

下方完整复现源图中 unknowns 列表的全部条目,实质内容未作改动。每条标记为 未知(证据缺口 — 代码中尚未观察到)或 风险(若向不利方向落地,将威胁正确性、安全性或可运维性的未知项)。以上均非对系统实际行为的断言。

数据流 (14)

代理编排 (12)

部署 (12)

权限 (14)

状态机 (11)

下一步

  1. 优先补齐生产拓扑缺口。部署视角下的所有未知项均源于仓库中无 Dockerfile/k8s/wrangler。在这些问题演变为事故之前,先确定并固化生产运行时模型(实例数、SSE 粘性、Postgres/Redis HA)。
  2. 明确加密密钥轮换方案。三个视角独立标记 CONFIG_ENC_KEY 轮换未解决(路由决策、审计原文、已存储 provider keys)。在数据集仍为空时定义密钥版本化方案 — 投产前成本低,投产后代价高。
  3. 验证 SQL 层的租户隔离强制。确认 PgActionProposalStore.get() 与跨租户审批路径在查询中强制 tenant_id 谓词,而非仅在应用代码中 — 这是权限列表中严重程度最高的安全风险。
  4. 规范 ActionProposal / PendingInteraction 协调与过期 sweep。状态机视角显示两个生命周期隐式耦合,且缺少文档化的后台过期/清理路径;编写创建顺序契约与 sweep 作业。
  5. 重新渲染并验证。本包是对代码说明的静态阅读 — 在浏览器中打开(Mermaid CDN 需联网),确认五张图均正常渲染,然后随着上述调查落地,将已确认项从风险/未知列表中移出。