One Tool to Rule Them All: Building a Fast AI Agent for Real-Time Response
The Case for Simplicity: One Tool for Response Tasks
The speaker introduces the core argument: that stripping back agent complexity to a single tool produces faster, more effective results for response tasks. They contrast this with the industry trend of loading agents with many MCP servers and skills, explaining that in time-critical response situations, unnecessary tooling wastes precious seconds.
Optiva's Risk Team: The Problem Space
The speaker introduces Optiva's risk team, whose job is to monitor trading limits and prevent overexposure to any single asset, company, or country. When a limit breaches, the team must rapidly sift through petabytes of data using SQL to identify the cause and advise trading — all while the financial exposure clock is ticking.
The Cost of Slow Investigation and the AI Opportunity
The speaker quantifies the problem: pre-AI, human investigation of a breach took fifteen to twenty minutes at best, and could stretch to hours. The arrival of generative AI tools like Cursor and Claude Code prompted the team to explore whether an agent could dramatically accelerate this forensic work.
Lesson One: Agents Do Well with Generic Tools
The team's first iteration was a simple folder of markdown documentation covering Postgres commands, Databricks access, limit definitions, and data formats. Deployed in Cursor, this alone cut investigation times by half or more — a significant early win that proved the basic viability of the AI-assisted approach.
The Bash Bottleneck: Statelessness Slows Everything Down
Bash's lack of statefulness emerged as a key limitation. For every follow-up question, the agent would run a full new database round-trip — rewriting hundreds of lines of Python and regenerating plots — wasting time that the risk team could not afford. The speaker draws inspiration from Cloudflare's approach of converting MCP servers into TypeScript functions, and notes that LLMs have deep familiarity with the notebook paradigm of building incrementally toward an answer.
Lesson Two: A Persistent Python Sandbox Changes Everything
Switching to a single Python interpreter tool — with persistent state across turns — transformed agent performance. Without any special prompting, the agent naturally stored data in variables and reused it for follow-up queries, mirroring how a Jupyter Notebook builds toward an answer. Plot modifications and incremental data exploration became fast and natural.
Lesson Three: Back Pressure — Steering the Agent Before It Wastes Time
By studying execution traces, the team identified recurring failure patterns: timed-out queries, references to non-existent columns, and aimless schema exploration. Because the single-tool approach meant the tool was simply Python code, it was straightforward to add 'back pressure' — pre-flight checks that detect obvious mistakes, block oversized queries, and restrict table discovery to a controlled function — eliminating the most frustrating forms of agent muddling.
SQL Linting with Abstract Syntax Trees
As basic errors were eliminated, more subtle issues emerged — such as the agent confidently reporting nonsensical dollar figures from misconfigured aggregate queries. The team's solution was to parse the Python and SQL code into abstract syntax trees, then apply deterministic linting rules: for example, checking that GROUP BY clauses include the meaningful columns before allowing an aggregate query to execute. An escape hatch boolean lets the agent override checks when it has good reason to, keeping the system flexible.
AST Tricks and Takeaways
The speaker shares additional AST-based checks, including detecting when the agent uses the Python tool purely as a scratchpad without surfacing an answer to the user. They close with three key lessons: give the agent freedom with generic tools; provide a stateful scratchpad to build up answers notebook-style; design an intuitive function universe to reduce friction; and leverage code introspection tools like ASTs for advanced, scalable back pressure.
Today, I'll be talking you through, basically trying to build a story and convince you that when you're trying to build agents for response tasks, being, if you wanna make them fast and effective, if you strip back the complexity and think about just using one tool, you can accomplish some pretty impressive results. So yeah, like most of the time, like the whole industry has been pushing towards like, oh, how many MCP servers can you jam into your agent? Or like, I have like a 100, tens of thousands of skills. That can work really well to build something that's quite effective at solving a task.
But when you put it in a response situation, you really don't wanna waste time, like, going through some confluence or Jira backlog when what you really wanna do is search for logs or something. So, yeah, and I'll be showing this to you through essentially how we used this kind of design pattern for our risk team. So without further to do, I'll quickly introduce you to what Octavus risk team does. So the risk team is essentially responsible for monitoring risks across Optiva. So a bit of finance one zero one, like when you're monitoring risks, risk and trading kind of work together, and they're of like yin and yang to each other.
So trading left to their own devices would very quickly, love to punt, like, everything onto, like, Apple or something and probably lose a lot of money, but risks are there to sit and actually make sure that what Optima doing is doing with their book is seen. So, yeah, with that in mind, they monitor many, many risks. And their whole goal is to essentially make sure that we are not spending too much of our book or like exposing ourselves to too much of one thing, like too much of one company, too much of one country, etcetera, etcetera.
So, yeah, they have a big dashboard of risk that they monitor, and you can see sometimes one of these limits breach. So, when a limit breaches, essentially, all they're given is just a little bit of a graph showing, the history of that limit, and then we just get a big ding ding ding, like it's breached, and they get a time.
So now this is presented in front of them. They need to figure out very quickly what caused this breach. So Optiva for context, we generate, like, petabytes of data and gigabytes a second. So all this data will be flying past them, and they'll have to very quickly be able to filter this stuff out with SQL and essentially tell trading, like, what they have to do to scale down. And that is not an easy thing to do when you have that much data to look through.
Yeah. So and another thing that's going through at the back of their mind is that for every second that they spend on this, it is costing money. Like the time that we don't know essentially why the breach has occurred, trading will have to scale back or we could be exposed to some risk that we have no clue about and that is essentially not good. So before AI, humans would take possibly, like, twenty minutes, fifteen to twenty minutes, best case, but it can very quickly scale to very, very bad numbers, like up to hours.
So, yeah, this has been the way of things for a long time. And essentially, when these cool agents dropped off, we were thinking, Okay, once basically Genetic AI took off with Cursor and ClotCode and such, we were starting to think, Okay, maybe this could be a pretty good candidate for being able to look into these response tasks.
Like if we just dumped it into that data block and just asked it to figure out what's happening, maybe it can do a decent job. So yeah, I'll fast forward many weeks of experimentation, and I'll bring you to our first lesson. So the first lesson is that parts do pretty well with generic tools. So I kid you not, like, our first iteration was just a folder of markdown that explains, like, oh, this is the Postgres command. This is how you access Databricks to access this and that.
This is what a limit is. This is the kind of data we use. And, yeah, if we just gave that and then told it to go through its own devices and figure out what caused the breach, it actually did a pretty decent job. And we just bundled this folder and gave it to risk to run-in like cursor or whatever, and they loved it.
Like, it cut investigation times like like properly by around half or even more in some cases. So it was quite a good win. Yeah. And it was pretty great. But, of course, like, we could always do better, and we can go faster. So I'll put, like, a simple example up on the screen. Like, if we ask, like, follow-up questions, the agent was very, very keen to just, basically do another round trip to get the data that it wants.
And this is kinda like inherent to Bash. Like, Bash is a really good tool that we have. And before AI even existed, we've done a very, very good job at building up to having essentially a great text interface, which is like LLM's bread and butter, but it's not very stateful. So like the most the biggest frustrations we would have is like you might ask it to change a plot or like do some small modification and such.
The agent would be like, oh, sure. And then it just writes another thousand or 500 lines of Python or whatever to do another round trip to the database, modify the graph, and then just give it to you. And you can just feel the time being wasted while you're sitting there and trading's asking you many, many questions about why on earth is happening. So, yeah, Bash definitely didn't feel like the right medium for the end goal of what we were trying to build.
And yeah, we were pretty recently inspired by Cloudflare's code mode thing. I don't know if you guys heard about it, but they've been exploring essentially taking MCP servers and turning it into TypeScript functions. And that is quite a good idea, especially in this space as well. Like I know somewhere inside the model's brain that it is really good at handling notebooks.
Right? Notebooks essentially just build up from data to an answer at the end. So if we can somehow, like, grab into that space of the models, like, space and pull out that knowledge, then we could probably start building agents that actually follow notebook style, build up to an answer, and get things faster. So that leads up to the next lesson that we learned, which is bots do a lot better if they have some sort of persistent sandbox. So, yeah, we gave an agent basically a Python interpreter.
And spoiler, this is the one tool that we're gonna stick to. And it actually did way faster. Like, we didn't even have to spend much time teaching the agent to just save stuff in variables. It was, like, really natural to it, which probably makes sense because of how notebooks work. Right? So when we asked a follow-up, it was very easy to be like, oh, I already pulled that data.
Let me just do some quick query on it to just change that data up and give you a better answer. And, yeah, it worked really well. And the plotting issue also quickly disappeared. Like, was able to modify its plots and do a lot of really good work there. So yeah. And, like, theoretically, you could do some of this stuff in Bash, but it would have been a big waste of time.
We could tell it to, like, save stuff to files and refer to the files later. But apart from interpreter, it's just so much easier to build up this tooling, and we didn't even have to think too hard about it. It kinda just emerged out of it. So, yeah, this was great and all, and we're doing pretty fast, we still wanted to go faster.
And if you spend hours looking at the traces, which is like a great way to try and really get speed out of these things, you started to notice some patterns. So it might do these queries, and then it would just stumble around, do timed out queries, talk about columns that don't exist. And it was just incredibly frustrating to see it do this stuff and then eventually figure out the answer.
Like, you're coding, you can just go get a coffee, right, and just figure this stuff out. But when you're sitting there waiting to get an answer to trading, you really don't want to watch it do, like, basically just dumb, like, muddling around like this. And it's okay in some cases, but, yeah, like, when when you kinda just know the answer and, you just can put it in the context file, you can beg for it to just follow certain standards. But, of course, as we know, like, agents aren't always just gonna listen to you in the context file, so we wanted to do something better here.
And if you take a step back, like, all we really needed to do was to find a way to detect these timeouts early and just tell it what the schemas are. So another quick lesson that we learned is that all we have to do is to just try do some of these checks before it does the work.
Right? That's a pretty simple thing to do. But I think the breakthrough that we had with just having this one Python interpreter tool is that these things are actually really easy to do. Right? If we had the bash tool with Postgres or we just use the Postgres MCP, like, we have very little control to modify that thing to add all this back pressure.
And what I mean by back pressure is, like, steering it in the right direction before it wastes time doing something. So but when it's a Python tool, it is essentially just code. So you can just like write a function, add some quick check, and then just boom, you just have some very quick back pressure. And you can see we added some quick ones to just say like, oh, if the query is too large, like through some obvious means, then ban the query from being run and ask it to try again. And its only way to find tables was through a find table function so it couldn't waste time like playing around with Postgres information schema queries to try find tables.
And yeah, that worked pretty well to eliminate some of these issues. And we were getting to a better and better state where the agent was making, like, mistakes that I wouldn't pin my head against. But once we eliminated all of those, the mistakes that the agent was trying to make started to become very, very interesting. So we'll move on to the next thing we started seeing the agent do. So, yeah, it might do some task and it'll report that the answer is confidently, like, oh, we have $1,200,000,000,000 of cash delta. And obviously, that is nonsense.
Right? Like, I would love one day if Optiva controlled or just had a position of that much, but that's just not happening anytime soon. So, yeah, like, could go tell the agent, hey, dollars is a ridiculous number. Like, if you see that, just please think about it before telling me. But that is not scalable to, well, every sort of data source we might have. Like, some numbers might literally be one particular link, especially working with, like, nanoposics time or other random stuff.
So, yeah, we needed some sort of solution that was scalable here. But this was starting to become difficult. Like, we can't look at this stuff and then, like, run, like, checks on, like, as easily as we saw in the previous slide to just, see if the data makes sense or not. So yeah, it was very easy to maybe reach to a sub agent or something to say like, hey, can you validate this?
But sub agents, especially here, we really wanted to avoid them because that's just adding time. And having two agents having to wait for them to finish and then putting them together, just going to cost too much time. So yeah, we were assigned to hit some like, we we had a pretty good state, but if we wanted to go faster, we had to start thinking.
Like, how do we scale these kind of checks to a bigger and bigger complexity? So for context, like, we have dozen like, dozens and dozens of calculators and many, many tables. So how can we decide for each of those tables, like, how the best way to query is? And to address this problem, we took some inspiration to, like, how you generally test software.
Right? Like there's some there are many layers you can do it too. You can do like unit tests and you can just run it, of course. But another thing that we take for granted is that compilers and interpreters also do quite a lot of testing of your code. Like they're able to check if your code makes sense in the first place.
They will check for some obvious things, like you're trying to use a variable that isn't used. And if you think about it, like how does it do that? So it does that through actually parsing your code and turning it into something we call an abstract syntax tree. And if we just were able to grab that, maybe we could do some extra checks on top of it.
So the next thing that we were starting to play around with now was essentially playing around with the syntax tree. So we can take the Python code and the query that it's about to run, and we can turn it into this structure. So you can see that it eventually essentially turns into a tree that you can query and, like, look at different parts of it.
And you can see from the SQL part, you can very clearly see that the group clause just doesn't have anything. And it's not a big ask for us to like deterministically go through our tables and just document like what columns make sense to group by if you're doing an aggregate. So by doing that, we could essentially start to look at our queries and decide, Okay, we're about to do some sort of aggregate queries. Probably a good idea to make sure that there's a group by with the columns that matter.
So that was our next level of advanced back pressure. So it was almost like linting. We could link the SQL and just check that, Okay, what are the group by clauses? If they're obviously missing something, don't let the query go. It's just going to give a ridiculous number. And it was also important to us that these checks were not perfect. So sometimes the agent can make the decision in the moment that it knows what it's doing.
So we did always give it like an SK patch. Like if you wanna bypass a check, you can just pass some Boolean and we won't stop you from doing it. Yeah. And we're still experimenting with the Python area. Python can be very, very broad on what it can do. Some interesting ones that we found were that when you give the agent just one tool, it loves to use it for everything, even just noting random crap like it's its scratch pad.
So sometimes you would do that, and then it would just won't tell the users, like, oh, great. I did the answer. And then that's all they would see. They were like, where's the answer? And they would have to, like, go, like, explain it to it to the user again. So we would have simple checks like, oh, if it's just if the code is literally just print statements, which you can very easily check with an AST, then, like, which are probably into the user that you probably wanna tell the user something.
And yeah. So it is quite awesome, like, all the little things you can grab through these trees, and we're still trying to think about more and more ways we could use this to better steer our agents to, like, make the right decision at the right time. And, yeah. And we still wanna go faster, of course, but we're still trying to find, ways to use these, but this is currently where we've landed.
Yeah. So takeaways is, yeah, like, the first lesson that we learned is, if we just give freedom, which is what we started with Cursor, it is able to do some pretty impressive results just by itself. And giving it a scratch pad, so some sort of persistence to save data goes a long way to being able to build up an answer, of like how it does in a Jupyter Notebook.
And making an intuitive universe, I think, is a good way to state the third lesson. Like if you are also just in a Bash terminal or Python interpreter, like, what functions would you want to solve it? Like, if you think about the amount of pain that it has to go, we sometimes just brush it off. But if you think about, oh, it has to write, like, all this extra stuff, you just make it a quick function call, which is very easy when it's just a Python interpreter.
It's just like a quick function. It can go pretty far to making the agent perform a lot faster. And of course, if you think beyond the code and what the code actually is, there's a lot of tools already built out there that's able to take code and do introspections on it. So an example is ASTs. So if you're able to use that and then grab insights out of it, you can do some even more advanced back pressure that you might not think about.
Yeah. So that concludes this talk. If you have any deeper questions, you can, visit us at our booth. Feel free to drop by. We're giving away a Claude Max subscription for whoever can solve our puzzle. That's, like, in the booth there. And, yeah, I'll
Technologies & Tools
- Bash
- Claude Code
- Cursor
- Databricks
- Jupyter Notebook
- MCP
- PostgreSQL
- Python
- SQL
- TypeScript
Concepts & Methods
- Abstract Syntax Tree
- SQL linting
- Unit testing
Organisations & Products
- Claude Max
- Cloudflare
- Confluence
- Jira
When your goal is to make an agent fast enough to explore millions of data points and find the true cause of incidents in minutes, the way you engineer agents fundamentally changes.
Come with me as we explore how we built a fast and reliable agent for live risk limit breaches with only one tool: a Python interpreter. We’ll cover the kind of custom backpressure and speedups you gain while in an interpreter, some whacky checks you can do with ASTs and the design trade-offs you face when building agents in this manner














