Managing agents v7

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');
Output
                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_completions
  • completions (any other OpenAI-Chat-Completions-compatible endpoint)
  • nim_completions
  • openrouter_chat
  • gemini
  • llama_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.