Managing agents
===============

An agent is a row in ``aidb.agents`` , created with
``aidb.create_agent()`` . Nothing runs until you call :ref:`Invoking agents <Invoking agents>`  —
creating an agent just stores its configuration.

.. code:: sql

   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:

.. code:: sql

   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 :ref:`Choosing a model <Choosing a 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 :ref:`Tool calling and structured output <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 :ref:`Tools reference <Tools reference>`  (``aidb.tools`` )
the agent is allowed to call; ``delegates`` is an array of names of
other agents it may :ref:`Delegation <Delegation>`  . 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.

.. code:: sql

   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.

.. code:: sql

   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()`` :

.. code:: sql

   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 :ref:`Budgets and limits <Budgets and limits>` 
for the full behavior, since it’s a property of how a conversation runs,
not of the agent’s static configuration.

.. code:: sql

   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:

.. code:: sql

   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
:ref:`Agents reference <Agents reference>`  for its full column list.

``aidb.delete_agent()`` removes an agent by name:

.. code:: sql

   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 :ref:`Retrieving conversations <Retrieving conversations>`  even after the agent that produced it has
been deleted.

.. code:: sql

   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.
