Building an AI Financial Analyst with Oracle Select AI Agent — Part 5

The Builder, and Where I Let the LLM Near the Code

Important

Disclaimer: The writing and musing of the author do not necessarily reflect the views of his employer.

Having built this stack twice by hand, the third time I wrote something to do it for me. This post is about that tool, and about one design decision inside it that runs against where most of the industry currently is.

The builder ships in the same repository, under agent-builder/.

github.com/BASoapbox/ACME-Corp-Select-AI-Agent-Project


What it does, in order

It's a Python CLI. You answer a short interview — what data, which tables, which documents, what the agent's job is — and it generates the PL/SQL that builds the whole stack, shows it to you, and runs it.

Five stages, and it is worth seeing where the model is and isn't:

# Stage Who does it
1 Provisioning pre-flight — grants, ACLs, Resource Principal Deterministic checks
2 Interview, or import a spec from Word or CSV The model, or a file
3 Scan tables, draft COMMENT ON statements Model drafts, human reviews
4 Generate the PL/SQL Deterministic — no model
5 Show it to you, then run it You

Stage 4 is the one worth arguing about, and it is most of this post. But the stages either side of it took longer to get right and are, I think, more useful:

Provisioning pre-flight. Before generating anything, it checks whether the environment can actually support an agent: is Resource Principal enabled, are the DBMS_CLOUD* grants in place, are the OML roles granted, does the EPE network ACL exist, can the agent schema actually read every source table. Each failure prints the exact statement that fixes it. Almost every item on that list is something that cost me an afternoon at some point.

NL2SQL comment management. Scan the tables, profile the column types and foreign keys, sample distinct values, draft table and column comments with the model, let a human review them, then emit COMMENT ON statements. As Part 2 argued, comments are the highest-leverage thing you can do for natural-language SQL quality — and writing them by hand for thirty columns is exactly the sort of task people skip.

Spec import. A project can be defined in a Word document or CSV rather than an interview, which means the definition of an agent becomes a reviewable artifact you can diff and version rather than a sequence of clicks.


The decision worth arguing about

Here is the part that matters beyond Oracle.

The model runs the interview. It is banned from writing the SQL.

To be precise about which SQL: the model still writes queries at run time — that is what NL2SQL is for. What it never writes is the provisioning code, and it cannot widen what it is allowed to reach. The table list, the grants and the guardrails come out of a file a human reviewed.

The generator is deterministic. It takes a spec dictionary and emits PL/SQL with no model involvement at all. Object names, table lists, comment metadata — all owned by captured facts, not by anything generated. The system prompt says so outright:

ABSOLUTE RULE — NO SQL OR CODE GENERATION

This is the opposite of the default move in 2026, which is to let the model generate the code and check it afterward. I went the other way for concrete reasons, all of which I'd hit:

  • Models rename things. Ask for a tool called ACME_SQL_TOOL and get ACME_SQL_TOOL_V2, or a helpfully "corrected" profile name that no longer matches the agent's tools array.
  • Lists get dropped. An object_list with six tables comes back with four, and nothing errors — the agent simply can't see two of your tables.
  • Output that must be executable arrives wrapped in markdown fences.
  • Long structured output gets truncated. Some fast models cut off mid-statement regardless of max_tokens, and a PL/SQL block truncated at 80% is not a syntax error you enjoy diagnosing.

None of these are model failures exactly. They're the model doing what it does — being helpful and approximate — applied to a task where approximate is the same as broken.

So the line I drew: fuzzy input from a human goes to the model; anything that becomes an executable artifact is generated deterministically. Interviewing someone about what their agent should do is genuinely hard to do with code and easy with a model. Turning a settled spec into CREATE_TOOL calls is genuinely easy with code and needlessly risky with a model.

There's a fallback path that calls the model if the deterministic generator is unavailable. It's a fallback, and it's instrumented as one — when it fires, the log says so, because output that came from a model is output you should read more carefully.


Spec as code

The second-order benefit took me a while to notice.

Because generation is deterministic, the generated PL/SQL is reproducible. The same spec produces the same script. Which means the script becomes a reviewable artifact: you can commit it, diff it between environments, and see exactly what changed when someone adjusts a routing instruction.

That's not available from a visual builder, which produces state rather than scripts, and it's not available from a model-generated script, which produces something slightly different every run.

Oracle's own AskOracle APEX application now includes a visual agent builder, and it's a genuinely nice way to explore the framework interactively. The two solve different problems: one is a console for building an agent by hand, the other is provisioning and spec management for teams who want the definition in version control. If you want a chat UI, use AskOracle. This is the layer that gets you to the point where AskOracle has something to point at.


Would I do it again

Honestly, the interview is the least valuable part. If I rewrote it tomorrow I'd keep the pre-flight checks and the comment management, and I'd think harder about whether the conversational front end earns its complexity over a well-commented spec file.

But the deterministic generator I'd keep exactly as is. Every time I've been tempted to let a model write the executable output, the failure mode has been the same: something that looks right, runs without error, and is quietly wrong in a way that surfaces three layers away.

Which, if you've read Part 4, is a familiar shape.


That's the series. The scripts, the builder, the sample knowledge base and a full implementation guide are all in the repository. If you build something similar and hit a wall somewhere I didn't, I'd genuinely like to hear about it.

Built on Oracle Autonomous Database 26ai · OCI Generative AI · OML4Py