Building an AI Financial Analyst — Part 1
What Select AI Agent Is, and Why I Put It in the Database
Important
Disclaimer: The writing and musing of the author do not necessarily reflect the views of his employer.
If you have been asked to put a chat interface in front of your financial data, and stalled at the point of deciding what actually runs the thing, I wanted to share what I found. There are a lot of moving parts in that decision and not a great deal written about the trade-offs.
In this series I will go over what Oracle Select AI Agent is and why I chose it (this post), the foundation you have to get right (Part 2), registering your own functions as tools (Part 3), the failures that report themselves as successes (Part 4), and a tool I wrote to generate the whole thing (Part 5).
I should say up front that I am not an expert on any of this. I built one thing, it works, and these are the notes from doing it. Everything here is backed by scripts that have been run end to end against a live database:
github.com/BASoapbox/ACME-Corp-Select-AI-Agent-Project
The problem
The finance team I had in mind sits between two kinds of knowledge, and both are awkward to reach.
The first is structured: general ledger (GL) transactions, department budgets, period close status, chart of accounts. It changes daily, it lives in Autonomous Data Warehouse (ADW), and answering questions about it takes SQL that most users don't have.
The second is unstructured: reconciliation policies, Sarbanes-Oxley (SOX) compliance guides, close checklists. These are PDFs, and answering questions about them means knowing where to look, or asking someone who does.
So quite naturally, what happens today is a ticket. Someone raises one, waits for an analyst, and gets an answer hours later. For "what's the status of the March close?" or "which accounts need Level 2 approval?", that is a lot of friction for very little.
What I wanted was a chat interface where anyone could ask in plain English and get an answer immediately. Key here is the fact that the answer had to come from real data rather than the model's imagination. That one constraint drove most of what follows.
What Select AI Agent is
Select AI is Oracle's framework for connecting a large language model (LLM) to your database through SQL. It has been around a while for natural language to SQL (NL2SQL): ask a question, it writes the SQL, runs it, narrates the result.
Select AI Agent is the larger idea. It puts an agent orchestration layer inside the database, through the DBMS_CLOUD_AI_AGENT package, and that buys you several things the basic version doesn't:
- Multiple tools the agent routes between depending on the question
- A reasoning loop where the model picks a tool, calls it, looks at what came back, and either answers or calls another
- Custom tools, so you can register your own PL/SQL function and have the agent call it with the right parameters
- Multi-turn conversation through a
conversation_id - Everything in-database: orchestration, vector search, SQL generation and Python all run inside the database
The four objects
The framework has exactly four, and they stack.

The build order is bottom-up, and it is worth internalising, because it is also the debugging order. Tools first, since they are what the agent can actually do. Then the task, which is the routing instruction telling the model which tool suits which question. Then the agent, which owns a role and an explicit list of tools. Then the team, which is the thing you actually call.
Each layer only knows the one below it. When something breaks, that hierarchy tells you where to look. I will come back to this in Part 4, because each layer's errors impersonate a different layer's problems with some conviction.
Why I put it in the database
The purpose of this post isn't to position one Oracle product over another, and both of these are reasonable choices depending on what you are doing. However, for this particular problem the comparison came out fairly one-sided.
| OCI GenAI Agent | Select AI Agent | |
|---|---|---|
| Where it runs | Managed Oracle Cloud Infrastructure (OCI) service | In-database |
| Setup | OCI Console | PL/SQL packages |
| NL2SQL | Via SQL tool + DB Tools connection | Native |
| Retrieval | Managed Knowledge Base | Vector index in the database |
| Custom tools | Client-side function calling | Any PL/SQL function, run in-database |
| Cost | Separate service billing | Included with the database |
The deciding factor was custom tools. I needed more than queries and document search: moving averages, anomaly detection, regression forecasting. The GenAI Agent service can call custom functions, but your application executes them and hands back the result, so nothing runs inside the database. Select AI Agent registers a PL/SQL function as a tool and calls it in place, including Python through Oracle Machine Learning for Python (OML4Py) Embedded Python Execution, with numpy running inside the database.
The security picture helped too. Everything authenticates as the database's own Resource Principal, which means the front end holds no credentials at all. I will come back to that in Part 3, because it turns out to solve a problem I had initially written off as unsolvable.
What I built
A team called ACME_ANALYST_TEAM, whose single agent ACME_ANALYST has six tools.
Built-in: ACME_SQL_TOOL for natural language to SQL against live GL tables, and ACME_RAG_TOOL for retrieval-augmented generation (RAG) over policy PDFs.
Custom PL/SQL: ACME_TREND_TOOL for period-over-period trends using LAG(), ACME_FORECAST_TOOL for linear regression, and ACME_ANOMALY_TOOL for STDDEV()-based outlier detection.
Custom Python: ACME_PYTHON_TOOL, running numpy statistics inside the database through OML4Py.
The agent decides which one fits. Ask about August expenses and it calls the SQL tool. Ask what the reconciliation policy says and it calls RAG. Ask which department has the most volatile spending and it calls the Python tool, which runs numpy in the database and hands back computed statistics.
One thing worth mentioning: I wrote no routing logic at all. The model reads the task instruction, which maps question types to tools, and decides. It will call more than one tool in a single response when the question calls for it.
Two things worth noticing
The model never guesses at figures. Every factual answer comes from a tool returning real data, and the task instruction says so explicitly: only report data returned by tools, never invent figures. That, I think, is the difference between something useful and an expensive hallucination engine.
And everything is auditable, which I did not expect going in. USER_AI_AGENT_TOOL_HISTORY logs every tool invocation with its inputs and its output. When a user gets a wrong answer you can go and see exactly what was called and what came back. That one view does more work than any error message in this stack.
In Part 2 I will walk through the foundation: the data, the comments that make natural-language SQL work, the profiles, and the vector index. If you are building something similar and hit a wall somewhere I didn't, please let me know.
Enjoy!
Built on Oracle Autonomous Database 26ai · OCI Generative AI · OML4Py