An agent is a row in aidb.agents, created with aidb.create_agent(). Nothing runs until you call aidb.agent_converse() — creating an agent just stores its configuration.
SELECT aidb.create_agent( name => 'db_helper', instructions => 'You are a helpful assistant that answers questions about this database. Be concise.', model => 'my_gpt', tools => ARRAY['run_sql_query'], -- optional; tool names from aidb.tools. Default: NULL (no tools) delegates => NULL, -- optional; names of other agents to delegate to. Default: NULL (no delegation) role => NULL, -- optional; Postgres role tool calls run as. Default: NULL (run as the calling role) output_type => NULL, -- optional; structured output schema from aidb.output_type(). Default: NULL (plain-text answer) input_token_budget => NULL, -- optional; input token cap per agent_converse call. Default: NULL (no limit) output_token_budget => NULL, -- optional; output token cap per agent_converse call. Default: NULL (no limit) max_iterations => NULL, -- optional; reasoning-round cap per call. Default: NULL (a hard ceiling of 25 always applies) timeout => NULL, -- optional; wall-clock seconds cap per call. Default: NULL (300 seconds applied at runtime) budget_strategy => 'attempt_complete', -- optional; behavior when a budget is exceeded. Default: 'attempt_complete' preset => NULL -- optional; reserved for future use, has no effect today. Default: NULL );
Returns no rows on success. On failure (for example, the name is already taken), it returns a single row with an error column set — it never raises, so a batch of create_agent calls can't abort partway through:
SELECT * FROM aidb.create_agent('db_helper', 'Be helpful.', 'my_gpt');
error ------------------------------------ agent 'db_helper' already exists (1 row)
Choosing a model
model must name an already-registered model that's capable of generating text and following instructions. Embedding, reranking, OCR, and multimodal-embedding providers (bert_local, openai_embeddings, nim_reranking, clip_local, and so on) can't back an agent at all, and t5_local is rejected outright since it has no instruction-following or system-prompt support. Every other text-generation provider works, split into two tiers by tool-calling quality:
Preferred — these send real, provider-native tool-call requests:
openai_responses(+openai_responses_azure)anthropic_messages(+anthropic_messages_azure/anthropic_messages_bedrock)llamacpp_generate
Supported — these work, but AIDB simulates tool calling by describing the available tools in the prompt text and parsing a tool call back out of the model's plain-text reply:
openai_completionscompletions(any other OpenAI-Chat-Completions-compatible endpoint)nim_completionsopenrouter_chatgeminillama_instruct_local
See Tool calling and structured output for more on the preferred/supported distinction. The model doesn't need to exist yet when you call create_agent — only agent_converse requires it to be resolvable.
Tools and delegates
tools is an array of names from the tool catalog (aidb.tools) the agent is allowed to call; delegates is an array of names of other agents it may hand off to. Both are validated against aidb.tools/aidb.agents at create_agent/update_agent time — an unknown name is rejected immediately rather than surfacing later inside agent_converse. Omit either for an agent with no tools or no delegates.
SELECT aidb.create_agent( name => 'help_desk', instructions => 'Help users with general questions, delegating data questions to a specialist.', model => 'my_gpt', tools => ARRAY['run_sql_query'], delegates => ARRAY['sql_specialist'] );
Permissions
By default, an agent's tool calls run as whichever Postgres role calls agent_converse. Pass role to pin an agent to a specific role instead — for example, to give it narrower table permissions than the calling application user has. The caller must already be a member of that role (pg_has_role(..., 'MEMBER')); create_agent/update_agent reject an agent configured with a role you don't belong to.
SELECT aidb.create_agent( name => 'readonly_reporter', instructions => 'Answer questions about sales data. Never modify data.', model => 'my_gpt', tools => ARRAY['run_sql_query'], role => 'reporting_role' );
Structured output
By default, agent_converse's answer is plain text. To get back structured JSON instead, build a schema with aidb.output_type() and aidb.output_field():
SELECT aidb.create_agent( name => 'sentiment_tagger', instructions => 'Classify the sentiment of the given text.', model => 'my_gpt', output_type => aidb.output_type( aidb.output_field('sentiment', 'TEXT', 'One of: positive, negative, neutral'), aidb.output_field('confidence', 'FLOAT') ) );
aidb.output_field()'s description argument is optional. A call-time output_type argument to agent_converse overrides the agent's configured schema for that one call only.
Budgets, iterations, and timeouts
input_token_budget, output_token_budget, max_iterations, and timeout (seconds) cap how much a single agent_converse call can cost and how long it can run; budget_strategy controls what happens when a budget is exceeded. All are optional — see Budgets and limits for the full behavior, since it's a property of how a conversation runs, not of the agent's static configuration.
SELECT aidb.create_agent( name => 'cost_capped_helper', instructions => 'Answer questions about the orders table.', model => 'my_gpt', input_token_budget => 20000, -- INTEGER; input tokens allowed per agent_converse call output_token_budget => 2000, -- INTEGER; output tokens allowed per agent_converse call max_iterations => 10, -- INTEGER; reasoning rounds allowed per call (a hard ceiling of 25 always applies) timeout => 60, -- INTEGER; wall-clock seconds allowed per call budget_strategy => 'error' -- TEXT; one of 'ignore', 'error', 'summarize', 'attempt_complete' );
Updating and deleting agents
aidb.update_agent() takes the same fields as create_agent, all optional — only the fields you pass change; everything else keeps its current value:
SELECT aidb.update_agent('db_helper', model => 'my_new_gpt', max_iterations => 15);
There's no dedicated "list agents" function — aidb.agents is a normal table, so SELECT * FROM aidb.agents lists them. See Agents reference for its full column list.
aidb.delete_agent() removes an agent by name:
SELECT aidb.delete_agent('db_helper');
If the agent has any conversation history, this fails by default (its internal task records are referenced by foreign key) — pass force => true to delete that internal history alongside the agent. The conversation transcript itself is untouched either way and remains readable with aidb.get_conversation() even after the agent that produced it has been deleted.
SELECT aidb.delete_agent('db_helper', force => true);
Note
create_agent/update_agent also accept a preset argument (for example, 'text_to_sql'). It's stored on the agent but doesn't currently change its behavior — reserved for future use.