Building an LLM Agent in Python
So let's build our agent. I'm not gonna use a framework, everything is just Python. As with many others I like going with direct LLM API calls. In terminal2F we're using Mistral models, you're free to pick any.
If you were going to use any type of framework, do Temporal, PydanticAI, OpenAI SDK over... lang. You may ask why not just use CrewAI, Autogen, Langchain, Pydantic AI.. on and on. But we're not here to discuss that.
Some good introductory sources to read:
- Building Effective Agents (Anthropic)
- AI Agent from Scratch in Python (Leonie Monigatti)
- Function Calling (Mistral docs)
Background / Motivating Work
There's some interesting stuff that shaped how I think about this:
- State Machines for Agents
- Are Agents Just Automata? On the Formal Equivalence Between Agentic AI and the Chomsky Hierarchy
- P2Engine: A Multi-Agent System Framework
The thesis from that paper: the memory architecture of an AI agent is the definitive feature determining its computational power, and it maps directly to classes of automaton:
- Simple reflex agents → Finite Automata
- Hierarchical task-decomposition agents → Pushdown Automata
- Agents with readable/writable memory for reflection → Turing Machines
The Debate
If the agent owns agent.messages, then the agent
already is your memory system. The runner is just a loop
that calls the model. So calling something "FSM runner" or "PDA
runner" would mostly be a label, because the real memory is still
the full chat history you keep sending.
The paper is pretty explicit that memory architecture is the thing that determines the agent's computation class:
- An FSM has "memory" only as its current state. No stack, no history buffer.
- A PDA is an FSM plus a stack.
- A TM is an FSM plus read and write memory you can revisit and modify.
So terminal2F makes a split:
- Agent = model caller (brain)
- Runner = memory + control policy (the automaton)
The Loop Runner
I follow that tutorial closely: I have an Agent class with an LLM client and a system prompt, I keep conversation memory, I expose tools with schemas plus a name-to-function map, and I run an agent loop that executes tool calls until a final answer.
The main difference is that my conversation memory and loop live
in a separate runner (runners/loop.py) instead of
inside the Agent class. This is the basic agentic loop: append
user message, call agent.step(), parse tool calls,
execute tools, append results, repeat until done.
I use the Mistral API. We use that and the help of their docs to build our agent. The function calling doc is important. You're free to adopt whatever model family you want.
What's Next: FSM, PDA, TM Runners
The loop runner keeps full chat history as memory. But if we actually want to test the automaton equivalences from the paper, we need runners with different memory architectures:
-
FSM Runner (
runners/fsm.py) : Memory is only the current state. No history buffer. Explicit state transitions. -
PDA Runner (
runners/pda.py) : FSM plus a stack. Good for hierarchical task decomposition, can push/pop context. -
TM Runner (
runners/tm.py) : FSM plus read/write memory you can revisit. Persistent, addressable memory the agent can reflect on.
These are stubbed out in the codebase. The goal is to run the same task through different runners and compare how they behave.
The Basic Structure
The Agent class has:
- An LLM client (Mistral)
- A system prompt
- Tools with schemas plus a name-to-function map
Tools: Capability vs Permission
Tools are capability on the agent, permission on the runner. The
agent can be initialized with tools=tools (everything
installed), but the runner decides what the model can actually see
and execute for that run.
The Step Function
agent.step() is the atomic unit. It takes the current
messages, makes one API call, and returns whatever the model says.
It doesn't loop. It doesn't execute tools. It just calls the model
once and hands back the response. The runner decides what to do
with that response: execute tools, append to history, loop again,
or stop.
Section B
This is the content for Section B. The flow line connects through the selected node. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent euismod ultrices ante, ac laoreet nulla vestibulum vel. Integer tincidunt felis vel eros fermentum, at tincidunt nunc tincidunt.
Nullam auctor, nisl eget ultricies tincidunt, nunc nisl aliquam nisl, eget aliquam nunc nisl eget nisl. Sed euismod, nisl eget ultricies tincidunt, nunc nisl aliquam nisl. Vivamus lacinia odio vitae vestibulum vestibulum.
Cras porttitor metus justo, ut fringilla velit fermentum a. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur tempus urna at turpis condimentum lobortis.
Section C
This is the content for Section C. Each section has its own unique content. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae.
Donec sollicitudin molestie malesuada. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Pellentesque in ipsum id orci porta dapibus.
Curabitur aliquet quam id dui posuere blandit. Nulla quis lorem ut libero malesuada feugiat. Nulla porttitor accumsan tincidunt. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui.
Section D
This is the content for Section D. The navigation shows which section is active. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Vivamus magna justo, lacinia eget consectetur sed.
Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a. Proin eget tortor risus. Curabitur aliquet quam id dui posuere blandit.
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. Pellentesque in ipsum id orci porta dapibus.
Section E
This is the content for Section E. This is the last section in the flow. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eget tortor risus. Curabitur aliquet quam id dui posuere blandit. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a.
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. Nulla quis lorem ut libero malesuada feugiat.
Sed porttitor lectus nibh. Vivamus suscipit tortor eget felis porttitor volutpat. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi.
Section F
This is the content for Section F. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce at risus eu libero venenatis facilisis. Maecenas faucibus mollis interdum.
Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Donec id elit non mi porta gravida at eget metus. Nullam quis risus eget urna mollis ornare vel eu leo.
Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Cras mattis consectetur purus sit amet fermentum.
Section G
This is the content for Section G. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam porta sem malesuada magna mollis euismod. Duis mollis, est non commodo luctus.
Nisi erat porttitor ligula, eget lacinia odio sem nec elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo cursus magna.
Vel scelerisque nisl consectetur et. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Aenean lacinia bibendum nulla sed consectetur.
Section H
This is the content for Section H. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Cras mattis consectetur purus sit amet fermentum.
Nullam id dolor id nibh ultricies vehicula ut id elit. Donec sed odio dui. Vestibulum id ligula porta felis euismod semper.
Section I
This is the content for Section I. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh.
Ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec ullamcorper nulla non metus auctor fringilla.
Maecenas faucibus mollis interdum. Nullam quis risus eget urna mollis ornare vel eu leo.
Section J
This is the content for Section J. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean lacinia bibendum nulla sed consectetur.
Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam.
Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.
Section K
This is the content for Section K. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.
Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.
Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Sed posuere consectetur est at lobortis.
Section L
This is the content for Section L. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id dolor id nibh ultricies vehicula ut id elit.
Cras mattis consectetur purus sit amet fermentum. Donec id elit non mi porta gravida at eget metus.
Maecenas sed diam eget risus varius blandit sit amet non magna. Aenean eu leo quam.
Section M
This is the content for Section M. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.
Cras mattis consectetur purus sit amet fermentum. Aenean lacinia bibendum nulla sed consectetur.
Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam.