Invoking agents v7

aidb.agent_converse() sends a prompt to an agent and runs its reasoning loop — think, optionally call tools, observe, repeat — until it produces an answer or hits a limit:

SELECT * FROM aidb.agent_converse(
    agent_name      => 'db_helper',
    prompt          => 'How many rows are in the orders table?',
    conversation_id => NULL,  -- optional; continue an existing conversation by id. Default: NULL (starts a new conversation)
    read_only       => NULL,  -- optional; force read-only mode on/off. Default: NULL (auto: on for a read replica, off otherwise)
    output_type     => NULL,  -- optional; override the agent's structured output schema for this call. Default: NULL (use the agent's own configuration)
    debug           => NULL   -- optional; also emit every action as a NOTICE. Default: NULL (false)
);
Output
              message               |           conversation_id           | error
-------------------------------------+--------------------------------------+-------
 The orders table has 4,213 rows.   | 8f14e45f-ceea-467e-9575-a3d1a2c1a123 |
(1 row)

Like create_agent, this never raises for an agent-logic failure (an unknown agent, a mid-loop reasoning failure, a budget that was exceeded) — error is populated on the returned row instead, so the call's transaction (and everything already logged for that attempt) still commits.

Pass output_type to override the agent's configured output schema for just this call — see Structured output for how to build one.

Conversations

Every agent_converse call belongs to a conversation, identified by conversation_id. Omit it on the first call and AIDB generates one; pass that same id back in on later calls to continue the conversation, with the agent's own prior history as context:

SELECT * FROM aidb.agent_converse('db_helper', 'And how many are still pending?',
    conversation_id => '8f14e45f-ceea-467e-9575-a3d1a2c1a123');

If you'd rather have the id in hand before sending a first prompt (for example, to log it alongside a UI session), mint one upfront with aidb.start_agent_session():

SELECT * FROM aidb.start_agent_session('db_helper');
SELECT * FROM aidb.agent_converse('db_helper', 'How many rows are in the orders table?',
    conversation_id => '<the id start_agent_session returned>');

start_agent_session only validates the agent name and hands back a fresh id. A conversation doesn't actually exist until the first agent_converse call that uses it.

Retrieving conversations

aidb.get_conversation() returns a conversation's user prompts and final answers, in order. It does not return tool calls or intermediate reasoning steps, which are activity details rather than "the conversation":

SELECT * FROM aidb.get_conversation('8f14e45f-ceea-467e-9575-a3d1a2c1a123');
Output
 message_id | task_id | action_type |  role | sender_id |               contents
------------+---------+--------------+-------+-----------+---------------------------------------
 ...        | ...     | user_prompt  | user  | postgres  | How many rows are in the orders table?
 ...        | ...     | answer       | agent | db_helper | The orders table has 4,213 rows.
(2 rows)

aidb.get_message() retrieves one message's plain-text contents by its message_id. Both are backed by the aidb.conversation_log view, which you can also query directly; aidb.conversations aggregates it into one row per conversation (message count, status, last message). See Agents reference for their full column lists.

Read-only mode

Pass read_only => true to run an agent without persisting anything — no conversation history is written, and conversation_id always comes back NULL, so a read-only run can't be resumed later. It's automatic (no need to pass it explicitly) when the database is a read replica.

A read-only run enforces two things at once: every tool call executes under SET LOCAL transaction_read_only = on for that statement, and any tool that isn't provably read-only is excluded from what the agent can even see or call in the first place. MCP tools are always excluded in read-only mode, regardless of how they're classified — an external server's own read/write behavior isn't something AIDB can verify. If a delegation happens during a read-only run, the delegate's own sub-call is also forced read-only.

Debug mode

Pass debug => true to also emit every action — model requests/responses, tool calls and their results, reasoning steps — as a Postgres NOTICE as it happens, in addition to (not instead of) the normal history written to aidb_internal.action_log. This is useful for watching an agent work interactively (psql prints NOTICE to the terminal by default) or for debugging why it took the path it did.

SELECT * FROM aidb.agent_converse('db_helper', 'How many rows are in the orders table?', debug => true);

NOTICE:  [tool_call] run_sql_query({"query": "SELECT count(*) FROM orders"})
NOTICE:  [tool_response] run_sql_query -> [{"count": 4213}]
NOTICE:  [answer] The orders table has 4,213 rows.

Each NOTICE includes that action's request/response payload — prompts, tool arguments, and results — truncated but not redacted. Avoid debug => true in contexts where those details (which may include data returned by your own tools) shouldn't be visible to whoever can see the client output or server log. It applies to any delegate sub-calls the run makes, too.

Budgets and limits

Each agent_converse call is capped by four budgets, all optional and set on create_agent/update_agent:

ParameterWhat it caps
input_token_budgetInput tokens for the call.
output_token_budgetOutput tokens for the call.
max_iterationsReasoning rounds — one model call plus any tools it calls in that round, not individual tool calls. A hard ceiling of 25 always applies, regardless of configuration, as a backstop against a runaway agent.
timeoutWall-clock seconds for the call. Defaults to 300 seconds if unset.

What happens when a budget is exceeded depends on budget_strategy:

StrategyBehavior
attempt_complete (default)Grants 3 extra reasoning rounds once a budget is first exceeded, telling the model its time is running out so it can wrap up — then halts with an error if it still hasn't finished.
errorHalts immediately with an error.
summarizeStops immediately and asks the model for a brief status report of what it had done so far, returning that as the answer instead of an error.
ignoreLogs a one-time warning (as a Postgres WARNING) and keeps going with no limit.

Context length

Long conversations are bounded automatically: once a task's assembled prompt would exceed an internally tracked size estimate, AIDB asks the model to summarize the older part of the history and continues with that summary in place of the original turns. This is basic, automatic bounding to keep long-running conversations from blowing past a model's context window — there's nothing to configure. A compaction entry appears in the activity log when this happens (see debug mode, or query aidb_internal.action_log directly for the full activity history behind a conversation — every model request/response and tool call/response, not just the user-facing messages aidb.get_conversation() returns).