适合 agent 的数据库 schema 应将任务、操作和审计事件与业务数据行分开记录。它通过约束强制执行租户范围和幂等性,协调并发 worker,并保留足够历史,让系统在超时后恢复,而不把模型对话记录当作证据。
数据库不需要理解提示词,只需尽量阻止不安全的状态转换和重复影响。
从任务与操作开始
任务是用户层面的工作流,操作则是该任务尝试执行的一项外部或内部影响。两者需要分开,因为一项任务可能多次重试同一操作,或在两项操作之间等待。
CREATE TABLE agent_tasks (
id uuid PRIMARY KEY,
tenant_id uuid NOT NULL,
workflow_version text NOT NULL,
state text NOT NULL,
state_version bigint NOT NULL DEFAULT 0,
current_step text,
wake_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (tenant_id, id)
);
CREATE TABLE agent_operations (
id uuid PRIMARY KEY,
tenant_id uuid NOT NULL,
task_id uuid NOT NULL,
idempotency_key text NOT NULL,
action text NOT NULL,
input_hash text NOT NULL,
state text NOT NULL,
provider_reference text,
result jsonb,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (tenant_id, idempotency_key),
FOREIGN KEY (tenant_id, task_id) REFERENCES agent_tasks (tenant_id, id)
);
复合外键可以防止操作指向其他租户的任务。唯一幂等键可以防止两个 worker 重复创建同一项逻辑操作。返回已有结果前,还应对比保存的输入 hash,避免同一个键悄然授权不同参数。
使用约束保证模型绝不能协商的规则
在数据库中强制执行唯一性、外键、非空所有权、合法 enum 值和版本检查。应用层校验仍能改善错误信息,但最终约束可以保护所有写入方,包括迁移脚本或新生成的工具。
对于租户隔离,应保证每张租户所属的子表都能通过限定租户范围的键完成关联。多租户 agent 基础设施指南介绍了数据行、schema 和数据库隔离方案。
同时记录当前状态与 append-only 事件
当前状态表可以简化读取和调度,append-only 事件表则说明状态如何变化。
在一个事务中写入状态更新和事件:
UPDATE agent_tasks
SET state = $1, state_version = state_version + 1, updated_at = now()
WHERE tenant_id = $2 AND id = $3 AND state_version = $4;
INSERT INTO agent_events
(tenant_id, task_id, event_type, actor_id, policy_version, payload)
VALUES ($1, $2, $3, $4, $5, $6);
提交前要求更新恰好影响一行。零行更新说明另一个 worker 已经推进任务,过期 worker 必须重新读取,不能覆盖新状态。
不要将密钥、完整提示词或不受限制的提供商响应放入事件 payload。应保存脱敏后的事实或引用,并采用独立的数据保留策略。
使用 outbox 保证下游投递
如果数据库更新需要发布事件或将工作加入队列,应在同一事务中写入 outbox 行。Relay 负责发送待处理行并标记为已投递。Relay 可能在确认响应丢失后重复发布,因此 consumer 仍需具备幂等性。
这种设计弥补了数据库已经提交,但进程在通知队列前崩溃的缺口。它还提供可查询的操作投递状态,不必依赖某一行日志。
使用 lease 或版本检查协调 worker
短暂的状态转换适合使用乐观版本控制。Worker 需要临时拥有任务时则使用 lease,其中包含所有者、取得时间和过期时间。工作期间定期续约,完成时要求调用方仍是所有者或持有更新且有效的 lease。
模型思考或等待提供商响应期间,不要一直保持数据库事务。应先预留操作并提交,再执行调用,最后核对结果并更新。持久状态指南解释了这种模式如何支持新的 worker 恢复任务。
保持 JSON 灵活,同时显式保存可查询事实
JSONB 适合模型特有元数据、提供商 payload 片段,以及因工作流而异的步骤局部值。租户所有权、状态、操作身份、调度、数据保留,以及策略或索引使用的字段则更适合列。
如果每次授权查询或调度都要使用某条 JSON 路径,应将其提升为类型明确的列。数据库 introspection 对 coding agent 很有用,但 schema 简单并不等于拥有权限。应将工具限制到已批准的语句或应用 API,而不是让生成的 SQL 使用管理员连接。
关联 trace,但不要将数据库变成遥测存储
在任务和操作上保存 trace 与 span 标识符,详细的模型和工具遥测则留在可观测性系统中。这样,调查人员可以从异常业务数据行找到生成它的运行,同时避免在 Postgres 中重复保存大段 trace。
OpenTelemetry 的 GenAI 语义约定定义了通用的 agent 和工具属性。该 schema 仍在演进,因此应固定埋点实际输出的版本。
在重试和并发场景中验证 schema
让两个 worker 同时处理一项任务。使用匹配和冲突的参数复用幂等键。在业务事务提交后、队列确认前让进程崩溃。在测试环境中删除一个租户,并验证其任务、操作、产物和凭据都遵循预期策略。
下方的提示词会将这些保证映射到当前 schema。它要求提供查询路径和约束,因为“我们会在 agent 中处理”并不是数据库不变量。