Hotdry.
ai-systems

在 PostgreSQL 行内嵌入 AI 代理:pgclaw 的架构设计、状态持久化与跨行通信

本文深入解析 pgclaw 项目如何在约400行 SQL 内实现‘每行一个 Clawdbot’,探讨其基于触发器与后台工作进程的异步架构、通过 JSONB 字段实现的状态持久化机制,以及通过频道绑定实现的跨行通信模式。

当我们在讨论 AI 代理与数据库的集成时,通常的想象是在应用层构建一个独立的服务,通过 API 调用 LLM,再将结果写回数据库。这种架构带来了额外的复杂性:需要管理服务部署、处理网络延迟、确保数据一致性,以及维护代理状态与数据库状态的同步。但开源项目 pgclaw 提出了一个截然不同的思路:为什么不让 AI 代理直接‘住’在数据库的每一行里?

pgclaw 是一个仅用约 400 行 SQL 实现的 PostgreSQL 扩展,它引入了一个名为 claw 的自定义数据类型。将一个 claw 列添加到表中,就等于为每一行数据配备了一个专属的 AI 助手(或称 “Clawdbot”)。这个想法初看有些激进,但细究其实现,却是一套精巧利用 PostgreSQL 原生能力(触发器、后台工作进程、JSONB、自定义类型)构建的异步事件驱动架构。本文将深入解析 pgclaw 的三个核心工程实现:异步处理架构状态持久化机制跨行通信模式,并给出可落地的配置参数与监控清单。

一、 架构核心:触发器、队列与后台工作进程的异步流水线

pgclaw 的运作机制可以概括为一个高效的生产者 - 消费者模型,全部在数据库内部完成。其核心流程如下:

  1. 触发器作为生产者:当用户对启用了 claw 列的表执行 INSERTUPDATE 操作后,通过执行 SELECT claw_watch('table_name'); 注册的 AFTER 行级触发器会立即被激活。根据 PostgreSQL 官方文档,AFTER 触发器在数据操作完成且约束检查通过后执行,此时可以安全地访问已被修改的行的 “新” 值。pgclaw 的触发器将相关行的标识符(如主键)和上下文信息序列化后,作为一条作业任务插入到专用的 claw.queue 表中。
  2. 后台工作进程作为消费者:PostgreSQL 的后台工作进程(Background Worker)是一个常驻进程,pgclaw 利用其持续轮询 claw.queue 表。当发现新任务时,工作进程根据任务类型进行处理:
    • 对于普通 LLM 代理:工作进程会从 claw.agents 表中读取对应的代理定义(包括系统指令 soul 和操作指令 instructions),结合当前行的数据构建提示词(Prompt),然后通过名为 rig 的轻量级 Rust 库调用配置好的 LLM 提供商(如 Anthropic Claude、OpenAI GPT、本地 Ollama 等)。
    • 对于 Claude Code 代理:如果代理定义了 workspace 字段,工作进程会为其在服务器文件系统上创建一个独立的工作空间目录,挂载必要的上下文文件(SOUL.md, AGENTS.md, context.json),然后通过 claude-agent-sdk 启动一个完整的 Claude Code 会话,该代理可以在此空间内读写文件、运行代码、执行 Shell 命令。
  3. 结果回写与历史记录:LLM 或 Claude Code 的响应被期望包含一个结构化的 JSON 对象,其中键为需要更新的列名,值为新的列值。工作进程解析此响应,并对原表发起一个 UPDATE 操作,将 AI 的 “思考结果” 直接写回对应的行。同时,完整的请求与响应上下文会被记录到 claw.history 表中,为后续的多轮对话提供记忆。

可落地参数

  • 延迟容忍度:整个异步流程(触发 -> 入队 -> 轮询 -> LLM 调用 -> 回写)通常需要 1-2 秒。这意味着此方案适用于对延迟不敏感的后台处理场景,如工单自动分类、内容摘要、数据清洗,而非实时聊天。
  • 并发与队列深度监控:需监控 claw.queue 表的记录数。如果队列持续增长,可能表明后台工作进程处理能力不足或 LLM API 响应变慢。可考虑增加 max_parallel_workers 或优化提示词以减少 Token 消耗。
  • 触发器配置要点claw_watch() 创建的触发器是 AFTER EACH ROW 类型。对于高频写入的大表,需注意触发器开销。可通过 WHEN 子句(尽管 pgclaw 当前未使用)进行条件过滤,例如仅当某些关键字段发生变化时才触发 AI 代理,以避免不必要的 LLM 调用。

二、 状态持久化:让代理拥有记忆与身份

一个没有记忆的 AI 代理只是每次对话的 “陌生人”。pgclaw 通过数据库表本身,为代理提供了优雅的状态持久化方案。

1. 代理定义表 (claw.agents):此表是代理的 “身份证” 和 “大脑蓝图”。每一行定义了一个可复用的代理。核心字段包括:

  • id:代理的唯一标识符。
  • soul:定义代理的 “灵魂” 或系统角色,例如 “你是一个严谨的客服工单分类员”。
  • instructions:具体任务指令,例如 “阅读工单内容,设置优先级为低 / 中 / 高 / 紧急,并生成摘要”。
  • memory:一个 JSONB 字段,用于存储代理的长期记忆。这是状态持久化的关键。代理在响应中可以返回一个包含 {"__memory": {...}} 键的 JSON,工作进程会将其合并更新到该字段中。例如,一个客服代理可以用它来记录常见问题模式、用户偏好或未解决的疑难杂症。
  • workspace:如果非空,则此代理为 Claude Code 代理,该字段指定工作空间目录的路径模板。

通过在 claw 列定义中引用代理 ID(如 claw(agent => 'ticket-triage')),同一份定义可以被成千上万行数据共享,同时每行代理又通过各自的 memory 字段维护独立的状态。

2. 历史记录表 (claw.history):此表自动记录每一次代理调用的详细信息,包括时间戳、关联的行、原始请求、完整响应和元数据。这不仅是审计和调试的宝贵资源,更为实现多轮对话提供了可能。未来的扩展可以让代理在响应时查询其自身的历史记录,从而实现连贯的上下文对话。

可落地清单

  • memory 字段设计规范:建议将 memory 设计为结构化的 JSON 对象,而非任意文本。例如:{"learned_patterns": ["..."], "user_context": {...}, "last_actions": [...]}。这便于查询和后续处理。
  • 历史数据清理策略claw.history 表可能快速增长。需建立归档或定期清理策略(如保留最近 30 天记录),避免影响主数据库性能。可考虑使用 PostgreSQL 的分区表功能按时间分区。
  • 状态恢复与回滚:得益于 PostgreSQL 的 ACID 特性,对 memory 字段的更新是事务性的。如果整个代理调用链中的某一步失败,事务可以回滚,确保数据库状态的一致性。这是将状态内置于数据库的核心优势之一。

三、 跨行通信:从独立个体到协同网络

单个行内代理的能力有限,真正的威力在于代理之间能够通信与协作。pgclaw 设计了一套基于 “频道”(Channel)的轻量级发布 - 订阅模型,实现跨行甚至跨表的代理通信。

通信三要素

  1. 绑定 (claw.bindings):将代理与一个命名的频道关联。例如,INSERT INTO claw.bindings (channel, agent_id) VALUES ('telegram', 'support-bot'); 意味着 support-bot 代理订阅了 telegram 频道。
  2. 路由 (claw.route()):这是一个 PostgreSQL 函数,用于向特定频道发送消息。函数签名类似于 claw.route(channel, agent_id_filter, user_id, session_id, message)。消息被投递后,会触发绑定到该频道且符合过滤条件的代理进行处理。
  3. 出箱 (claw.outbox):代理处理完路由过来的消息后,生成的响应不会直接写回某个具体的行,而是插入到 claw.outbox 表中。外部系统(如一个 Telegram 机器人服务)可以轮询此表,获取响应并发送给最终用户。

此外,claw.heartbeats 表允许为代理注册定期执行的任务(心跳),例如 “每 30 分钟检查一次未处理工单”。这使代理不仅能被动响应变化,还能主动发起行动。

可落地模式

  • 客服流水线ticket-ingestion 代理接收原始用户消息并存入 tickets 表,触发行内 triage 代理进行初步分类。triage 代理根据复杂度,通过 claw.route('escalation', ...) 将棘手工单路由给 senior-support 代理频道,由更专业的代理处理。所有交互状态均持久化在各自的行和 memory 中。
  • 数据管道监控:多个数据管道任务作为行存储在 tasks 表中,每个任务有一个 claw 代理监控其状态。一个全局的 heartbeat 代理定期检查所有 pending 任务,并通过频道通知对应的处理代理,形成自愈合的数据处理网络。

四、 风险、限制与部署考量

尽管设计精巧,pgclaw 并非银弹,在采用前需审慎评估其边界。

1. 性能与延迟:LLM API 调用是主要延迟源,且受网络波动影响。后台工作进程是单点(尽管可配置并行度)。不适用于在线交易处理(OLTP)中对延迟敏感的读写操作。 2. 安全与成本:LLM API 密钥存储在 postgresql.conf 文件中,需通过严格的文件权限和可能的外部密钥管理服务(如 HashiCorp Vault)进行保护。无节制的代理触发可能导致巨大的 API 调用成本,需在触发器层面或通过代理内部的指令设计进行限流。Claude Code 代理拥有文件系统访问权限,必须在严格隔离的环境(如容器、沙盒)中运行。 3. 复杂性内聚:将业务逻辑(AI 代理行为)深度嵌入数据库,使得调试和跟踪变得复杂。需要熟悉 PostgreSQL 内部机制(触发器、后台工作进程)以及 pgclaw 自身的表结构。

部署清单

  1. 环境:PostgreSQL 17+,Rust 工具链,cargo-pgrx
  2. 配置:在 postgresql.conf 中设置 shared_preload_libraries = 'pgclaw',并提供 pgclaw.api_keypgclaw.database 等必要参数。
  3. 初始化:在目标数据库中执行 CREATE EXTENSION pgclaw;,然后创建 claw.agents 定义和业务表结构。
  4. 监控:重点关注 claw.queue(积压)、claw.history(增长速率)以及 PostgreSQL 日志中后台工作进程的错误信息。

结语

pgclaw 项目展示了一种极具创见的数据库系统设计思路:将智能体作为一等公民嵌入数据存储层。它摒弃了在数据库之外构建复杂中介层的传统做法,转而利用数据库引擎本身提供的可靠性与并发原语,构建了一个自包含、可持久化状态、并能相互通信的 AI 代理网络。虽然当前版本更适合特定的异步批处理场景,且存在延迟与安全方面的考量,但它无疑为 “AI-Native Database” 的未来形态提供了一个激动人心的原型。对于正在探索如何将 LLM 能力更深度、更有机地集成到自身数据工作流中的工程师而言,pgclaw 的架构思想值得深入研究和借鉴。


资料来源

  1. calebwin/pgclaw GitHub 仓库:https://github.com/calebwin/pgclaw
  2. PostgreSQL 17 CREATE TRIGGER 文档:https://www.postgresql.org/docs/current/sql-createtrigger.html
查看归档