"What if I had an AI assistant in every row of my Postgres table?"
I asked myself this a few weeks back and pgclaw is what I came up with: an open-source Postgres extension that introduces a claw data type to instantiate an AI agent - either a simple LLM or an "OpenClaw" agent - as a Postgres column.
With pgclaw, store your AI agents with the rest of your data. Supports:
- Simple LLM agents and more stateful "OpenClaw" agents
- Any LLM provider via rig: Anthropic, OpenAI, Ollama, Gemini, Groq, Together, DeepSeek, Mistral, Perplexity, Cohere, xAI, OpenRouter, and more
- A "Claude Code" in each row via claude-agent-sdk: agents that can read/write files, run code, and use tools
- ACID compliance, point-in-time recovery, JOINs, and all of the other great features of Postgres
- any language with a Postgres client
Prerequisites: Rust toolchain and PostgreSQL 17 dev headers.
cargo install cargo-pgrx
cargo pgrx init --pg17 download
git clone https://github.com/calebwin/pgclaw.git
cd pgclaw
cargo pgrx installThen in postgresql.conf:
shared_preload_libraries = 'pgclaw'
pgclaw.api_key = 'sk-ant-...'
pgclaw.database = 'mydb'
CREATE EXTENSION pgclaw;
CREATE TABLE tickets (
id serial PRIMARY KEY,
title text,
body text,
priority text,
agent claw DEFAULT claw('Set priority to low/medium/high/critical based on urgency.')
);
SELECT claw_watch('tickets');
INSERT INTO tickets (title, body) VALUES ('Login broken', '500 error on login page');
-- ~1-2s later: priority = 'critical'A claw value binds an LLM agent to a row. Two modes:
Inline — just a prompt:
claw('Summarize this ticket in one sentence.')
claw('Classify sentiment.', model => 'claude-sonnet')Agent reference — points to a reusable agent definition in claw.agents:
claw(agent => 'ticket-triage')
claw(agent => 'ticket-triage', model => 'claude-opus')Define reusable agents with identity, instructions, and memory:
INSERT INTO claw.agents (id, soul, instructions, memory) VALUES (
'ticket-triage',
'You are a meticulous support ticket classifier.',
'Read the ticket. Set priority (low/medium/high/critical) and summary.
If unsure, set medium and explain in summary.',
'{"patterns": []}'
);
CREATE TABLE tickets (
id serial PRIMARY KEY,
title text,
body text,
priority text,
summary text,
agent claw DEFAULT claw(agent => 'ticket-triage')
);
SELECT claw_watch('tickets');The agent can update its own memory by including {"__memory": {...}} in its response.
Agents with a workspace field get their own filesystem directory and run via Claude Code:
INSERT INTO claw.agents (id, soul, instructions, workspace) VALUES (
'engineer',
'You are a senior engineer. You write clean, tested code.',
'Implement the change. Run tests. Update status to pass/fail.',
'auto'
);
CREATE TABLE tasks (
id serial PRIMARY KEY, task text, status text DEFAULT 'pending', diff text,
agent claw DEFAULT claw(agent => 'engineer')
);
SELECT claw_watch('tasks');Requires Claude Code CLI (npm install -g @anthropic-ai/claude-code). Workspace paths: 'auto' uses {pgclaw.workspace_dir}/{agent}/{table}/{pk}/, or use a custom template like '/data/{agent}/{table}/{pk}'.
INSERT INTO claw.agents (id, soul, instructions) VALUES (
'support-bot', 'You are the support bot for Acme Inc.', 'Help users with orders.'
);
INSERT INTO claw.bindings (channel, agent_id) VALUES ('telegram', 'support-bot');
INSERT INTO claw.heartbeats (agent_id, every) VALUES ('support-bot', '30 minutes');
SELECT claw.route('telegram', '*', 'user123', NULL, 'My order is late');
-- Pick up responses from claw.outbox# Required
pgclaw.api_key = 'sk-ant-...' # API key for your provider
pgclaw.database = 'mydb' # Database for the background worker
# Optional
pgclaw.api_provider = 'anthropic' # see supported providers below
pgclaw.api_url = '' # override API endpoint
pgclaw.default_model = 'claude-sonnet-4-5-20250929' # default model for inline claw values
pgclaw.enabled = true # enable/disable processing
pgclaw.workspace_dir = '/tmp/pgclaw/workspaces' # base dir for Claude Code workspaces
Supported providers: anthropic (default), openai, ollama, gemini, groq, together, deepseek, perplexity, cohere, mistral, moonshot, openrouter, xai, hyperbolic, mira, galadriel
# OpenAI
pgclaw.api_provider = 'openai'
pgclaw.api_key = 'sk-...'
pgclaw.default_model = 'gpt-4o'
# Ollama (local, no API key needed)
pgclaw.api_provider = 'ollama'
pgclaw.default_model = 'llama3.1'
# OpenAI-compatible (vLLM, LM Studio, etc.)
pgclaw.api_provider = 'openai'
pgclaw.api_url = 'http://localhost:8000/v1'
- You add a
clawcolumn to a table and callclaw_watch() - On INSERT/UPDATE, a Postgres trigger enqueues the row to
claw.queue - A Postgres background worker polls the queue:
- LLM agents (no workspace): builds prompt from agent config + row data, calls LLM via rig
- Claude Code agents (with workspace): creates a workspace directory with
SOUL.md,AGENTS.md,context.json, spawns Claude Code via claude-agent-sdk
- The response is parsed for column updates (JSON
{"column": "value"}) and applied back to the row - Conversation history is preserved in
claw.historyfor multi-turn interactions
MIT of course!