AI agent token usage optimization

In the previous article we’ve pointed out that the real risk of leaning on AI for any changes is not the code bloat per se, it is budget bloat. With a fixed budget, a human team would continue working on features at a slower pace, whereas AI just stops when the token cap is reached. And moreover, that may become a surprise if nobody monitors the token burn down rate day-to-day. What I personally find useful is to set a daily limit and just monitor the alerts when the daily budget exceeds that number, or use ccusage for quick automation

The Coding Loop

Most of the current AI magic comes from a pretty simple yet brilliant idea: let’s break down our feature requirements into a list of smaller tasks as a one-shot request and write that into a file and then make a small but persistent AI agent work on a task until completion.
The Plan part involves tricky analysis of the actual assignment, the project structure, its architecture, performs gap analysis. This phase consumes a lot of tokens to fetch necessary information and execute its ‘thinking’ routines.

The Implementation phase is usually more straightforward, but still involves local analysis of the coding patterns used, success criteria (type checking, compilation, unit testing).
Both phases will work more efficiently and cost-effectively, when key information is fed into the prompt.

Documentation

The first idea that comes to mind is to give an AI agent a document or a chapter book explaining your project. The challenge here is knowing when to stop – on one side you have a high-level project intention created during the project conception, on the other you have every feature, component and module annotated with the purpose, interface and examples. The latter will ultimately be as large as the project source itself, so why don’t we just let the LLM find what it is looking for right there?. We are back at square one with this, plus extra steps to produce that document. Instead, we should only document the information an AI agent is unable to identify efficiently – our project gems. Let’s think more

The project structure

Here, the less of a difference between your project and the training data the model would see, the less effort is needed to analyze the project and implement the feature.
The initial project design needs to take into account the long-term concerns:
Modular frameworks: Essential for efficient composability and smaller context window management.
Compact, self-documenting code: Directly lowers token consumption.
Easy reverse-engineering: Allows for rapid information extraction and straightforward implementations.
Extensive library documentation: Prevents the agent from needing manual code lookups.

From the token prediction statistics (and accuracy) point of view, models have better grip of TypeScript, HTML, Shell, Python, and Java and Ruby, than Erlang, Lisp or Go, since that was the code distribution at the time of training. However, the right programming language tooling assisting in debugging matters a lot for efficient coding loop execution – proper type checkers, compilers, test suites are what you want to pay attention to.

Bad tool (Typescript):

Type '{ id: string; personalInfo: { name: string; email: string; contact: { phone: string; emergencyContactName: string; }; }; preferences: { theme: "light"; notifications: { email: boolean; push: boolean; }; }; }' is not assignable to type 'UserProfile'.
Types of property 'personalInfo' are incompatible.
Type '{ name: string; email: string; contact: { phone: string; emergencyContactName: string; }; }' is not assignable to type '{ name: string; email: string; contact: { phone: string; emergencyName: string; }; }'.
Types of property 'contact' are incompatible.
Type '{ phone: string; emergencyContactName: string; }' is not assignable to type '{ phone: string; emergencyName: string; }'.
Property 'emergencyName' is missing in type '{ phone: string; emergencyContactName: string; }' but required in type '{ phone: string; emergencyName: string; }'.

A better tool (Elm):

-- UNSUPPORTED FIELD 
---------------------------------------------------
Main.elm
The contact record does not have an emergencyContactName field. 23| { phone = "555-0199" 24| , emergencyContactName = "Sam" 25| } The contact type specifies these fields: { emergencyName : String, phone : String } Hint: I see a graveyard of similar names. Did you misspell emergencyName?

Notice how the latter message pinpoints the problem in a single sentence, saving the thinking tokens every time the LLM generates faulty code.

The architecture

Where proper documentation really helps is in the form of the project gems – the abstractions that keep the entropy low. I like explaining my architectural pattern preferences for the AI agents in the form of skills, and also keep that pattern language limited. Think about it this way, when every third-party OAuth integration in a project is done the same exact way, then your LLM would need just a single example to implement a new integration. Another important type of architectural documentation comes in the form of guardrails which limit the AI agent’s options, keeps the entropy localized as much as possible . Example: “AI Agent should use a well-maintained input sanitization library for every input instead of writing an implementation. Call out any exceptions from that rule.” I’m going to dedicate the next article to managing a decent architecture sub-agent, that maintains and enforces the clean project architecture.

The most important investment

More than architecture, your project needs a robust test harness, since that’s the best way to understand the expected result in a deterministic way, and is usually the only necessary thing to get you from a V0 to a V1 version of your project. My recommendation is to have tests for behaviors, not your code, and not just any behavior, but the behaviors either make you money, or keep the money flow going – could be your partner API contract and uptime, or a shopping cart, or a CSV file – that’s usually unique and requires the utmost attention. I prefer going top to bottom here – define the business model, turn that into the end-to-end suite, then identify the most important smoke test candidates. From there you’d want to minimize the smoke test failures by writing the integration tests, and minimize those failures by covering the failure points with unit tests.

Conclusion

While AI agents are able to work autonomously and produce viable code, project owners need to still take care of the long-term sustainability by providing efficient architecture guardrails, concise project pattern language and the test harness.