Skip to content

L2 · Agent System 需要哪些基础软件服务? - Platform Services ​

所属知识库:Agent 全栈技术地图
所属领域:03 · Agent System
所属章节:02 · Agent System 是如何构建和运行的? - Technical Architecture
文档类型:基础软件服务原理、架构设计与工程实践研究
版本:v1.1
更新日期:2026 年 10 月 28 日
核心问题:Agent System 在底层计算与网络资源之上,需要哪些可复用的软件服务来支撑任务接入、持久化、异步消息、缓存、文件与对象存储、身份凭据、服务发现及可观测性?这些服务如何通过明确的数据与接口契约连接 Agent Engine、模型服务和编排层,在任务中断、并发竞争、重复投递与外部副作用等情况下维持可解释、可恢复、可治理的系统行为?哪些能力属于最小必需,哪些应随规模和风险逐步引入?

专题导读 ​

本篇属 Technical Architecture。Agent System 运行在 CPU、进程、网络和磁盘之上,但仅有基础设施并不能让长任务自动恢复、让多个 Worker 安全竞争任务,也不能确保用户提交的研究任务和生成的报告在服务重启后仍可查询。介于底层基础设施(L1)和上层模型接入、Agent 执行、协作编排(L3 - L5)之间,需要一组可复用的软件能力:持久化存储、消息传递、缓存、对象存储、身份与密钥管理、服务间通信、配置发现和基础遥测等。本篇将其称为 L2 Platform Services(基础软件服务层)。这里的 Platform 不等同于 04 · Agent Platform 所研究的面向多用户、多 Agent 生命周期的产品化平台。

本篇回答三个相互关联的问题。第一,为什么需要某项服务,以及它究竟承诺什么、不承诺什么;第二,这些服务之间如何形成一致的任务状态、消息与产物契约;第三,如何在“个人研究助手 → 团队异步任务系统 → 多 Worker 服务”的演进过程中,以最小必要复杂度选型,并通过故障注入和验收证据证明其适用性。贯穿案例是技术研究助理:用户提交问题,系统异步检索与分析,保留证据,生成报告,并允许查询、取消和失败后继续执行。

本文将特别澄清几个常见混淆:数据库事务不能包裹外部 API 的实际副作用;消息队列的确认不等于业务操作恰好执行一次;缓存不能天然作为权威任务状态;容器自动重启不等于 Run 恢复;事件日志不自动等于完整事件溯源。研究内容以技术机制和工程契约为中心,不把 PostgreSQL、Redis、Temporal 或 Kubernetes 预设为所有 Agent System 的必选依赖。

一、研究定位:L2 在六层技术架构中的位置 ​

1.1 分层与责任边界 ​

text
用户 / 外部系统
       │
L6 Application          接口、交互、交付
       │
L5 Orchestration        任务依赖、多 Agent 协作、协调策略
       │
L4 Agent Engine         Run / Step、模型-工具循环、终止与恢复决策
       │
L3 Model Services       模型适配、路由、调用、限流与用量
       │
L2 Platform Services    数据库、队列、对象存储、缓存、身份、遥测等
       │
L1 Infrastructure       CPU、进程、容器、网络、磁盘、节点

H1 - H12 Engineering & Governance 横向约束与验证所有层

此图表达研究责任而非强制调用链。L4 可以直接调用 L3 和 L2;L6 也可能读取 L2 的只读投影。L2 不应成为一个包揽所有业务逻辑的“超级平台服务”。是否将某项能力拆成独立网络服务,属于部署选择,与逻辑分层不是一回事。

L1 与 L2 的区别:L1 提供可用的磁盘、网络、进程和节点;L2 在这些资源上提供具有特定语义的软件接口。例如磁盘并不保证应用级任务状态的事务一致性,数据库通过事务、约束与查询接口提供更高层抽象。L1 的容器重启机制也不等于 L2 的消息重投或持久化任务索引。

**L2 与 L3 - L5 的区别:**数据库保存任务记录,但不负责决定模型下一步如何推理;队列传递任务,但不负责规划 Agent 的子任务;对象存储保存报告,但不保证报告内容正确;身份服务证明调用者身份,不代替 L4/H2 对具体工具执行做授权判定。工作流引擎同时具有基础执行服务与编排语义,应根据研究问题划分:其任务队列与持久化服务属于 L2 依赖,其工作流状态机与编排策略主要在 L4/L5 展开。

1.2 三种不同的“必需” ​

讨论“Agent System 需要哪些基础软件服务”时,必须分清三个层次:

  1. 逻辑必需:任务和运行过程必须有足够的状态表达、可访问的资源以及错误反馈;这些能力可由进程内变量和本地文件实现。
  2. 场景必需:例如跨重启查询需要持久化;异步并发消费需要某种任务分发与协调;共享文件需要可访问的产物存储。
  3. 部署必需:只有在选择特定实现后,才可能需要独立数据库实例、Redis、消息代理、密钥管理服务等。

因此“单 Agent 原型一定需要 Redis / Kafka / Kubernetes”不成立。正确问题是:系统的任务契约和故障模型要求什么语义,最简单的可用实现是什么。

二、能力地图:服务类别、数据所有权与引入条件 ​

服务能力核心契约典型实现引入条件
事务数据库原子更新、约束、查询SQLite、PostgreSQL持久任务状态、并发修改
消息与任务队列异步交付、确认、重投DB 队列、Redis Streams、RabbitMQ后台执行、多 Worker
对象/文件存储大文件保存、寻址、生命周期本地文件、S3 兼容存储报告、附件、证据归档
缓存/协调有期限的快速读取或短期协作进程缓存、Redis热数据、限流、短期去重
身份与密钥身份校验、密钥访问与轮换环境注入、KMS、Vault多环境、多人、敏感工具
服务通信发现、超时、协议、错误传递HTTP/gRPC、DNS多进程/跨服务调用
遥测基础设施日志、指标、追踪采集与导出OpenTelemetry、日志服务跨组件诊断与运维
搜索与索引按条件定位知识与产物SQL FTS、搜索引擎、向量索引数据规模或检索需求增长

该表是能力分类,不是采购清单。一个 PostgreSQL 实例可能同时承担权威状态、任务队列、全文检索和部分协调;单机 SQLite 也可能满足早期全部持久化需求。需要明确权威数据源(source of truth),避免数据库、缓存、消息和内存分别声称自己保存“最新状态”。

2.1 推荐的数据所有权划分 ​

  • 任务记录与 Run 状态:由事务性状态存储持有权威版本;可通过只读投影加速查询。
  • 待处理工作:由队列/调度表表示待执行意图;消息不是业务最终结果的权威证明。
  • 大文件与报告:由文件/对象存储保存字节内容;数据库保存对象键、摘要、权限、版本和生命周期。
  • 短期上下文与缓存:可丢失、可重建;不应承载唯一的审批结果或已完成外部操作事实。
  • 凭据:由密钥管理或安全注入渠道保存;日志、消息体、运行轨迹原则上只引用凭据标识。
  • 审计事件:记录谁在何时以何权限触发了何动作;根据风险采用独立的保留、访问和防篡改策略。

三、事务数据库:任务状态为什么需要原子性 ​

3.1 数据库在 Agent System 中保存什么 ​

技术研究助理的最小持久化模型可包含 tasks(用户意图)、runs(一次执行实例)、run_events(事件记录)、artifacts(交付物元数据)、approvals(审批)和 outbox(待发送事件)。不要把 Agent 的所有上下文都塞进一行巨大 JSON,也不必在一开始就将每个中间 Token 单独建表。设计依据应是查询、并发、恢复与审计需求。

text
Task 1 ── N Run 1 ── N RunEvent
             │
             ├── N Artifact
             ├── N Approval
             └── N OutboxEvent(由事务提交产生)

Task 是长期业务对象,Run 是某次执行,Attempt 是一次可能重试的处理尝试。将这三个概念混为一谈,会导致重试后覆盖历史、取消操作误作用于新 Run、指标统计口径混乱。

3.2 最小数据契约(示意 SQL) ​

以下以 PostgreSQL 风格表示,属于架构示例,未在真实数据库上执行:

sql
CREATE TABLE runs (
  run_id UUID PRIMARY KEY,
  task_id UUID NOT NULL,
  status TEXT NOT NULL CHECK (
    status IN ('QUEUED','RUNNING','WAITING_APPROVAL',
               'SUCCEEDED','FAILED','CANCELLED')
  ),
  version BIGINT NOT NULL DEFAULT 0,
  lease_owner TEXT,
  lease_until TIMESTAMPTZ,
  updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE run_events (
  event_id UUID PRIMARY KEY,
  run_id UUID NOT NULL REFERENCES runs(run_id),
  event_type TEXT NOT NULL,
  event_time TIMESTAMPTZ NOT NULL DEFAULT now(),
  payload JSONB NOT NULL DEFAULT '{}'::jsonb
);

CREATE TABLE outbox (
  event_id UUID PRIMARY KEY,
  topic TEXT NOT NULL,
  payload JSONB NOT NULL,
  published_at TIMESTAMPTZ
);

真实系统还需定义租户/所有者字段、索引、时间分区、数据保留与迁移规则。SQL 的 CHECK 只限制状态取值,不能证明状态转换合法;例如 SUCCEEDED → RUNNING 的非法逆转仍需应用事务、数据库触发器或专门状态机契约约束。

3.3 乐观并发控制与状态转换 ​

两个 Worker 可能同时读取 RUNNING 状态并写入不同结果。可以采用版本条件更新:

sql
UPDATE runs
SET status = 'SUCCEEDED', version = version + 1, updated_at = now()
WHERE run_id = :run_id
  AND version = :expected_version
  AND status = 'RUNNING';

只有受影响行数为 1 时才视为本次转换被接受。若为 0,需要重新读取状态、判断是已取消、租约过期还是竞争更新;不能盲目重试原写入。版本条件只解决数据库记录上的并发写冲突,不能阻止已经发生的外部邮件发送或文件发布。

3.4 事务隔离与跨系统一致性 ​

PostgreSQL 文档区分 Read Committed、Repeatable Read 和 Serializable,并明确 Serializable 事务可能因序列化冲突失败,需要重试整个事务。[R1] 这意味着不能简单地说“使用数据库事务就没有并发问题”。隔离级别、唯一约束、锁顺序、重试策略和业务不变量必须共同设计。

最常见的双写问题是:数据库已经写入 QUEUED,但向队列发布失败;或者队列消息发布成功,数据库事务回滚。Transactional Outbox 模式将业务状态变更和待发布事件写入同一数据库事务,再由独立发布器异步投递;它缩小了跨系统不一致窗口,但发布器仍可能重复投递,所以消费者必须处理重复消息。

text
API 接收任务
  └─ 数据库事务:写 Task + Run + Outbox
        └─ commit
             └─ 发布器读取 Outbox
                   └─ 发布到队列
                         └─ Worker 幂等处理

反向的“处理完任务后更新数据库”也存在崩溃窗口。真正需要设计的是可观察的状态转换与可恢复的中间状态,而非假设所有系统共享单个原子事务。

四、消息队列与任务分发:交付不等于完成 ​

4.1 为什么要解耦提交与执行 ​

Agent 任务可能持续数十秒、数分钟甚至更久。若 HTTP 请求线程同步等待整个研究任务,客户端断开、网关超时和服务发布都会变成执行风险。典型异步模式是:提交接口持久化任务并返回 run_id;后台 Worker 从队列取得工作;状态服务提供查询、取消和产物索引。

text
POST /runs → Task API → DB + Outbox → Queue → Worker
    │                                         │
    └── 202 + run_id                        └→ Run 状态 / Artifact
GET /runs/{id} ← 状态查询服务 ←───────────────┘

202 表示请求被接受进入后续处理流程,不代表任务已成功。提交接口需要请求幂等键,避免用户重试 POST 导致重复创建任务。

4.2 消息投递语义与确认边界 ​

  • At-most-once delivery:可能丢消息,通常不会自动重投;适合可容忍丢失的通知。
  • At-least-once delivery:未确认的工作可重投,但消费者可能多次收到同一任务;常用于可靠任务分发。
  • Exactly-once:必须限定系统边界和被计数的操作。队列内部的某种去重承诺不等于“外部 API 调用恰好产生一次副作用”。

Redis Streams 的 Consumer Group 支持确认、Pending Entries List 与未完成消息认领;其文档同时提示复制和持久化配置影响故障时的数据安全。[R2] 因此 Redis Streams 不能不加条件地被描述为“永不丢消息的持久队列”。

4.3 Worker 租约、心跳与重投 ​

一种常见任务领取策略:Worker 获取带到期时间的租约,运行时定期续租,完成时在事务中提交结果并释放租约。若 Worker 崩溃,其他 Worker 可在租约到期后重新领取。需要处理两个竞态:

  1. 旧 Worker 网络恢复,可能在租约过期后继续执行;因此提交必须验证当前所有权或 fencing token。
  2. 新 Worker 重新执行,可能再次调用外部工具;因此工具执行需要业务幂等键、审批和副作用账本。

租约只约束对任务的逻辑所有权,不会自动杀死失联 Worker。 若任务可操作高风险外部系统,应在可信工具网关再次检查授权、Run 状态和执行代次。

4.4 重试、退避、死信与背压 ​

失败需要分类:网络超时/限流可在预算内指数退避;参数错误/权限拒绝不应无条件重试;不确定是否已执行的外部副作用需要先核对执行账本。重试应设次数、总耗时、抖动(jitter)和截止时间,防止故障时同步重试风暴。

队列积压不能只靠“增加 Worker”解决。应同时考虑模型 API 配额、数据库连接上限、工具并发限制和下游服务容量。背压机制可以包括并发令牌、按租户公平排队、队列长度门禁、任务优先级、过期任务取消及拒绝策略。优先级本身也可能造成低优先级任务长期饥饿。

4.5 数据库队列、Redis、RabbitMQ、Kafka、Temporal 怎么选 ​

方案优势主要代价常见适用范围
DB 任务表事务整合简单轮询与争用小规模可靠后台任务
Redis Streams消费组、待处理追踪持久化与故障配置中等规模任务分发
RabbitMQ路由、确认、队列机制Broker 运维多服务任务投递
Kafka分区日志、重放、吞吐消费模型与运维复杂大规模事件流
Temporal持久工作流、活动重试引擎语义与部署成本长流程、等待、恢复

此表是架构选型的简化比较,不是性能排名。Temporal 官方资料将 Workflow 的确定性重放与 Activity 的外部副作用区分,并提供 Activity 重试、超时等机制。[R3] 若选择 Temporal,应研究其事件历史、版本兼容和重放约束;不能把普通消息队列与完整工作流引擎视为同一种产品。

五、缓存与协调:性能优化不能破坏正确性 ​

5.1 缓存哪些数据 ​

适合缓存的对象包括短期模型元数据、非敏感工具描述、热点检索结果、限流计数和可重建的只读投影。不宜仅存于缓存的数据包括审批决策、Run 最终状态、不可逆工具副作用凭证和唯一报告索引。缓存应显式定义 TTL、版本、租户隔离和失效条件。

5.2 缓存一致性 ​

Cache-aside 模式中,读取先查缓存,未命中再读数据库并填充;更新数据库后删除或更新缓存。仍可能因并发出现短暂旧值。对于任务取消、权限撤销和预算耗尽等安全敏感状态,不能把“缓存尚未过期”当作继续执行的授权。关键决策应访问权威状态或使用带版本的可信令牌,并有可证明的撤销策略。

5.3 分布式锁与 fencing token ​

仅靠 SET NX 一类临时锁并不能解决所有并发问题。锁过期后,旧持有者可能继续执行。fencing token 是每次成功获取所有权后递增的代次,受保护资源拒绝旧代次请求;但只有下游实际验证 token,它才发挥作用。对外部 API 若无法传入 token,需要通过本地幂等账本、专用执行代理或人工审批降低重复副作用风险。

六、对象存储与产物管理:报告不是一段返回文本 ​

6.1 产物与状态分离 ​

研究报告、网页快照、图表、PDF 和中间数据可能远大于数据库行。推荐将字节内容放入对象存储,将 artifact_id、run_id、对象键、内容摘要、媒体类型、大小、版本、创建时间、访问策略保存在数据库。对象存储键不是用户权限凭证;下载必须通过授权检查或短期签名链接。

6.2 写入协议与失败窗口 ​

text
Worker 生成文件 → 临时对象上传 → 校验摘要
          → DB 事务写 Artifact 元数据与 Run 状态
          → 标记对象可交付 / 发布访问路径

数据库与对象存储通常没有统一事务。若上传成功但 DB 写入失败,会留下孤儿对象;若 DB 记录存在而对象丢失,会出现悬空引用。应采用临时命名空间、校验、定期对账与垃圾回收。对不可变交付物可使用内容摘要或版本化对象键,避免并发覆盖。

6.3 生命周期与数据治理 ​

需要明确:用户删除任务时是否删除产物;审计要求是否阻止立即删除;备份如何恢复引用关系;证据快照是否包含版权或敏感信息;是否需要跨租户隔离。L2 提供存储机制,保留期、数据主体请求和合规责任由 H9 Data Governance 制定并验收。

七、身份、密钥与服务通信:可信执行的基础 ​

7.1 身份验证不等于操作授权 ​

身份服务可以证明用户、Worker 或服务账户是谁,但“是否允许 Agent 调用某个工具读取某份资料”必须结合任务、租户、资源、审批状态和风险策略判断。Agent 提示词中的“不要越权”不是安全控制;工具网关应在执行前校验。L2 提供身份与密钥基础设施,H2/H12 定义安全与治理策略,L4 执行器携带正确的执行上下文。

7.2 凭据生命周期 ​

建议将模型 API Key、数据库凭据、第三方 OAuth Token 与用户数据分开管理。采用最小权限、定期轮换、撤销、环境隔离、审计访问,并避免将明文凭据写入任务消息、Prompt、日志、错误堆栈和产物。对用户授权的外部账户,需区分“代表用户操作”与“服务自身操作”,不能共用一个万能密钥。

7.3 服务间调用契约 ​

内部服务 API 至少约定:身份与租户上下文、请求/响应 Schema、超时、错误分类、幂等键、追踪标识、版本兼容和限流。跨服务超时存在“对方已执行但本方未收到响应”的不确定状态;重试前应判断操作是否幂等,或先查询操作结果。

一个适用于工具执行网关的请求信封示例:

json
{
  "run_id": "run-2026-001",
  "step_id": "step-004",
  "attempt": 2,
  "tool_id": "research.fetch@1",
  "idempotency_key": "run-2026-001:step-004:fetch:source-7",
  "actor_ref": "service-account/research-worker",
  "tenant_ref": "tenant-a",
  "deadline": "2026-10-28T15:00:00Z",
  "trace_id": "trace-abc"
}

此示例是研究建议的数据契约,不是 MCP、OpenAI Agents SDK 或某框架的官方标准。真实接口还需要鉴权凭证传输、参数校验、签名或可信信道、版本演进和错误响应结构。

八、可观测性基础服务:记录什么,如何关联 ​

8.1 L2 提供遥测管道,H1 研究观测体系 ​

OpenTelemetry 的官方文档将 traces、metrics、logs 作为主要遥测信号,并提供语言 SDK 与 Collector 等组件。[R4] L2 的责任是采集、传输、存储或导出这些信号;H1 进一步定义 Agent 专用指标、故障诊断方法、采样策略和告警规则。

每次运行建议关联 task_id、run_id、step_id、attempt_id、trace_id,记录任务入队、领取、模型请求、工具调用、状态更新、产物提交与终止事件。不能假设 trace 完整覆盖全部异步重试:跨队列的上下文传播需要明确实现,长任务也可能超过单个追踪保留窗口。

8.2 关键指标与诊断场景 ​

指标观察对象典型风险
Queue age / backlog待处理任务容量不足、饥饿
Run terminal rate任务最终状态静默卡死
Redelivery count重复投递Worker 崩溃或确认失败
DB transaction errors持久化层锁争用、连接耗尽
Artifact reconciliation产物一致性孤儿或悬空引用
Secret access audit凭据使用非预期调用

日志应采用结构化事件和必要的脱敏策略。模型输入、检索文档、第三方响应可能包含敏感信息,不应默认完整写入生产遥测系统。摘要、对象引用和受控调试样本通常更适合长期保留。

九、端到端一致性:一项研究任务的实际服务协作 ​

9.1 正常执行路径 ​

text
① 用户提交任务(附客户端幂等键)
② API 在 DB 事务中创建 Task / Run / Outbox
③ 发布器投递 RunRequested
④ Worker 领取任务并记录 attempt / lease
⑤ L4 Agent Engine 驱动 L3 模型调用与工具执行
⑥ 关键状态与证据索引写入 DB,必要文件写对象存储
⑦ 生成报告,校验对象与元数据一致性
⑧ DB 提交终止状态,并发布 RunCompleted
⑨ 用户查询状态、授权下载产物

上述流程并不要求每一步都对应独立服务。单体程序可以用 SQLite + 本地文件实现相同的逻辑职责;多服务实现则需要显式处理跨进程通信和故障窗口。

9.2 四个必须设计的失败窗口 ​

窗口 A:提交成功、消息未发出。 Outbox 发布器扫描未发布事件并补发;重复发布由消费者幂等处理。

窗口 B:Worker 已调用工具,但尚未提交完成。 任务可能重投;对于有副作用的工具,先查执行账本或外部操作状态,不能直接重放。

窗口 C:报告已上传,但 Run 尚未完成。 对象暂存与数据库对账,避免孤儿文件被错误公开。

窗口 D:任务被取消,但 Worker 仍在运行。 取消标记、租约代次、执行器取消传播和工具网关检查共同发挥作用;仅更新数据库状态不保证外部操作停止。

9.3 最重要的跨层不变量 ​

  • 每个被接受的任务都能找到权威 task_id/run_id 或明确拒绝记录。
  • 同一请求幂等键在定义的作用域内不会创建多个逻辑任务。
  • 终止状态不能被过期 Worker 任意覆盖。
  • 只有完成授权和校验的产物可以被交付。
  • 任务取消后,不应批准新的高风险副作用;已在执行中的操作需有可解释处理。
  • 失败恢复不能把“工具调用已发送”误判为“工具调用已成功”。

这些是建议的工程不变量,需要结合业务风险转化为可测试断言;不是所有 Agent System 都必须实现同样严格的外部副作用保障。

十、架构选型:从最小原型到可运维服务 ​

10.1 场景 A:个人本地技术研究助理 ​

建议先采用单进程执行器 + SQLite + 本地文件目录 + 标准日志。任务少、仅个人使用、无需多机并发时,这种方案具有部署简单、故障面小和易于调试的优势。应保留稳定的 Run ID、明确状态转换、原子文件写入和基本备份;不要因使用 SQLite 就省略数据契约。

**升级触发器:**需要多个用户同时提交任务、执行任务跨机器迁移、长任务在发布后继续、附件共享或统一权限管理。

10.2 场景 B:团队异步研究服务 ​

可以采用 API + PostgreSQL + 数据库 Outbox/任务表 + Worker + 对象存储 + 基础追踪。初期甚至不需要单独 Redis。关键设计是数据库事务、任务领取租约、请求幂等、文件交付协议和状态查询。若任务分发成为瓶颈,再引入消息代理并迁移消费者契约。

**升级触发器:**队列积压、数据库轮询争用、复杂延时调度、跨团队事件订阅或工作流长时间等待。

10.3 场景 C:长流程与多 Worker 协作 ​

当任务跨越小时/天级等待、包含多个依赖步骤、需要复杂重试与人工审批时,可以评估工作流引擎或专用任务系统。Temporal 等系统可提供持久工作流执行机制,但引入了确定性重放、事件历史、版本兼容和额外服务运维成本。[R3] 多 Agent 协作策略仍属于 L5;不能因部署了工作流引擎就假设协作质量得到保障。

10.4 选型决策步骤 ​

先写下任务量、平均/尾部运行时长、最大并发、任务数据大小、允许丢失窗口、恢复目标、是否有不可逆操作、审计要求与运维能力;再确定权威状态与一致性边界;然后选择最少的服务组合;最后通过故障注入验证。若没有明确瓶颈,不建议仅为“架构完整”而同时部署数据库、Redis、Kafka、工作流引擎和独立搜索集群。

十一、工程实践:可以实际执行的验收实验 ​

以下是可执行的测试设计,不是本篇已经完成的实测。建议在隔离测试环境中进行,记录版本、配置、操作命令、事件轨迹与结果。

11.1 测试 1:提交接口幂等 ​

同一用户以相同幂等键连续提交同一任务 10 次,再并发提交 10 次;查询权威数据库,预期只创建一个逻辑 Task(或返回相同的既有结果)。另测同一键对应不同请求体时是否拒绝冲突。断言应以唯一约束或事务结果为准,而不是只看 API 返回文本。

11.2 测试 2:Outbox 崩溃窗口 ​

在业务事务提交后、发布器投递前终止发布器;重启后应继续投递。再在“消息已发送、published_at 未写入”处终止,验证重复投递不会导致重复创建 Run 或重复执行不可逆操作。保留数据库行、消息 ID 与消费日志作为证据。

11.3 测试 3:Worker 租约与陈旧写入 ​

Worker A 领取任务后停止心跳,等待租约过期;Worker B 重新领取并提交结果;随后恢复 A,尝试以旧版本提交。预期旧写入被拒绝,并且不会覆盖 B 的终止状态。若有外部工具,进一步验证旧 Worker 的高风险调用被网关拒绝。

11.4 测试 4:消息重投与副作用去重 ​

构造工具“外部报告发布”沙箱,模拟服务端执行成功但客户端收到超时;触发队列重投。预期执行账本和幂等键使发布不重复,或进入明确的人工核验状态。不能仅断言 Worker 最终返回 SUCCESS。

11.5 测试 5:产物一致性与恢复 ​

分别在上传前、上传后、DB 元数据提交前和提交后注入崩溃。验证用户只能下载已授权、摘要正确的产物;定期对账能识别孤儿对象和悬空引用;恢复时不覆盖旧版本。

11.6 测试 6:缓存撤销与租户隔离 ​

在缓存命中时撤销某用户的资源访问权限,立即尝试读取和工具执行;预期权威授权生效。使用两个租户相同的任务标识或缓存键前缀进行交叉访问测试,验证缓存和对象存储都没有跨租户泄露。

11.7 测试 7:容量与背压 ​

固定任务分布和模型/工具配额,逐步提高到达率;记录 P50/P95 入队等待、队列积压、DB 连接、Worker 并发、错误率和任务成功率。超过容量时应触发明确的排队、降级或拒绝策略,而非无界增长内存。

11.8 测试 8:备份恢复与版本兼容 ​

从备份恢复数据库与对象元数据,验证 Run 状态、产物引用、权限记录的一致性。部署新旧两个 Worker 版本消费同一队列,检查消息 Schema 兼容与未知字段处理。将 RPO/RTO 写成可测目标,不要只写“支持备份”。

十二、反模式、故障归因与实施检查 ​

12.1 典型反模式 ​

反模式失败原因改进方向
缓存作为唯一状态过期或丢失不可恢复权威数据库 + 缓存
队列确认等于完成可能已处理但未提交幂等与结果账本
DB 和队列直接双写跨系统提交窗口Transactional Outbox
重试所有异常权限错误和副作用放大错误分类 + 有界重试
全部数据放单个 JSON查询、并发、迁移困难核心字段规范化
只用 Prompt 限制权限模型输出不构成授权可信工具网关
日志保存所有原文敏感数据泄露风险最小采集与脱敏
过早拆成微服务故障面与运维成本上升按瓶颈渐进拆分

12.2 故障定位的服务边界 ​

如果“用户提交后一直没有报告”,应依次检查:API 是否持久化任务、Outbox 是否待发布、消息是否到达、Worker 是否领取、租约是否续期、L4 是否终止、对象是否上传、DB 是否写入产物、交付接口是否授权。这条证据链能够区分平台服务故障、Agent 推理失败和交付权限问题。

12.3 最小验收清单 ​

  • [ ] 说明每类关键数据的权威所有者与保留期。
  • [ ] 明确 Task、Run、Attempt、Event、Artifact 的标识关系。
  • [ ] 规定任务提交幂等、状态转换和并发写入规则。
  • [ ] 明确消息交付语义、确认时点和重投处理。
  • [ ] 对不可逆工具操作定义副作用账本或核验路径。
  • [ ] 定义数据库与对象存储的跨系统一致性方案。
  • [ ] 将缓存失效、授权撤销和租户隔离纳入测试。
  • [ ] 为每次执行保留跨服务关联标识和终止原因。
  • [ ] 规定备份恢复、容量背压与故障演练目标。
  • [ ] 对新增服务给出明确需求、运维成本和退出条件。

十三、研究结论 ​

L2 Platform Services 的价值,不是为 Agent System 堆叠数据库、缓存、消息代理和工作流产品,而是把运行过程中的状态、工作、产物、身份和证据转换为具有明确接口、所有权、失败语义和可验证保证的软件服务。一个可持续运行的 Agent System 需要知道:任务是否被接受、谁正在执行、执行到哪里、哪些外部操作已经发生、结果保存在哪里,以及失败后什么可以安全重试。

本篇的核心工程判断有四点。第一,逻辑能力与独立部署服务应分开,最小原型可以用少量软件满足必要契约。第二,数据库、消息、对象存储之间通常不存在端到端原子事务,需要 Outbox、幂等、版本控制与对账机制。第三,可靠交付不能从单个中间件的宣传语推导,必须结合实际持久化配置、故障窗口和下游副作用验证。第四,服务选型应由任务持续时间、并发、恢复目标、安全风险和运维能力驱动,而非由架构图的复杂程度驱动。

下一专题 「L3 · Agent System 如何接入和管理 AI 模型? - Model Services」 将进一步研究模型适配、调用协议、路由、配额、失败处理与用量计量。L2 为其提供持久状态、凭据、通信和遥测基础,但模型调用的具体语义与质量控制属于 L3。

附录 A:参考文献与原始资料 ​

以下优先列出本篇使用的官方原始资料。访问日期为 2026 年 10 月 28 日。资料可能随版本更新;实施时应按部署版本核对配置、默认值和兼容性。文中 [R#] 指向下列资料。

[R1] PostgreSQL 官方文档,Transaction Isolation。 事务隔离级别、并发可见性与 Serializable 重试语义。www.postgresql.org/docs/current/transaction-iso.html

[R2] Redis 官方文档,Redis Streams。 Consumer Groups、Pending Entries List、确认、持久化与复制边界。redis.io/docs/latest/develop/data-types/streams

[R3] Temporal 官方文档与官方仓库文档,Understanding Temporal / Activity Execution / Retry Policies。 Workflow 确定性重放、Activity 外部副作用、超时与重试机制。github.com/temporalio/documentation/...nding-temporal.mdx ; github.com/temporalio/documentation/...vity-execution.mdx ; github.com/temporalio/documentation/...retry-policies.mdx

[R4] OpenTelemetry 官方文档,Documentation / Signals。 Traces、Metrics、Logs 及采集/导出框架。opentelemetry.io/docs ; opentelemetry.io/docs/concepts/signals

[R5] PostgreSQL 官方文档,Explicit Locking。 行锁、表锁与锁冲突的实现参考。www.postgresql.org/docs/current/explicit-locking.html

[R6] PostgreSQL 官方文档,SELECT。 FOR UPDATE 与 SKIP LOCKED 的数据库队列选型参考。www.postgresql.org/docs/current/sql-select.html

[R7] RabbitMQ 官方文档,Consumer Acknowledgements and Publisher Confirms。 消费确认与发布确认机制。www.rabbitmq.com/docs/confirms

[R8] Apache Kafka 官方文档,Documentation。 事件流、分区、消费与事务相关语义;具体保证依赖配置和边界。kafka.apache.org/documentation

[R9] Amazon S3 官方文档,Consistency Model。 对象存储读写一致性与适用边界;其他 S3 兼容产品不应自动假设完全一致。docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html

[R10] OWASP,Secrets Management Cheat Sheet。 密钥管理、访问控制、轮换与审计参考。cheatsheetseries.owasp.org/cheatshee...t_Cheat_Sheet.html

说明:[R1] - [R4] 对应本次直接检索并核对的官方资料;[R5] - [R10] 为延伸研究的官方资料入口,本次未逐页核验具体版本内容,不能据此声称完成了对应产品的功能实测。

附录 B:研究证据、验证声明与适用边界 ​

B.1 资料支持的技术事实。 PostgreSQL 的事务隔离和序列化冲突、Redis Streams 消费组与持久化限制、Temporal Workflow/Activity 的职责区别、OpenTelemetry 的遥测信号分类,分别依据 [R1] - [R4] 官方资料。它们支持相应技术机制的描述,但不构成对特定生产部署的可靠性保证。

B.2 本篇提出的工程参考设计。 L2 能力地图、数据所有权划分、Task/Run/Attempt 领域模型、Outbox 组合方案、状态不变量、三阶段演进路径和八组验收实验,是针对技术研究助理场景综合提出的架构建议;不是行业统一 Agent System 标准,也不意味着每个项目都必须采用相同组件。

B.3 代码与测试边界。 文中的 SQL、JSON、流程图与接口信封为说明性示例;本次未连接 PostgreSQL、Redis、RabbitMQ、Kafka、Temporal、对象存储或真实模型 API,未执行数据库迁移、集成测试、性能基准或故障注入。因此不能声称本文已经验证了并发安全、端到端 exactly-once、数据恢复能力或生产性能。第十一章列出的实验属于待执行的验收方案。

B.4 风险与版本限制。 中间件默认配置、复制/持久化策略、事务保证、部署版本、网络分区和外部 API 幂等支持都会影响真实行为。尤其要避免把队列的确认语义直接外推为业务副作用的唯一性。上线前必须结合具体版本、真实依赖和授权沙箱重新测试。

B.5 后续研究边界。 H4 将深入状态保存与恢复,H7 研究可靠性和故障恢复,H2/H12 研究安全授权与治理,H1 研究可观测性,L4/L5 研究执行与编排语义;本篇聚焦为这些能力提供基础软件服务契约。