Beyond the Prompt: How to Build an AI Agent That Actually Learns From Its Mistakes

We’ve all seen the standard pattern of AI development over the past couple of years. You write a system prompt, wrap it in an API call, hook up a vector database for Retrieval-Augmented Generation (RAG), and call it an “autonomous agent.”

But let’s be honest: these systems aren’t actually autonomous. They are stateless function evaluators. They receive a prompt, generate a response, and instantly forget everything that just happened. If they make a mistake on step three of a task, they are highly likely to repeat that exact same mistake on step five. They don’t learn, they don’t adapt, and they don’t grow. They are stuck in a digital version of Groundhog Day.

To build truly autonomous software, we need a profound architectural shift. We need systems that can observe their own performance, write their own journals, and build their own custom tools.

Enter Hermes: an open-source, self-learning cognitive architecture.

In this deep dive, we will break down the core theoretical architecture of Hermes. We’ll look at real production code to see how it implements persistent memory, executes a closed learning loop, and achieves continuous self-improvement through reflection.

(The concepts and code demonstrated here are drawn from my ebook Hermes Agent, The Self-Evolving AI Workforce)

The Core Theoretical Architecture: The Master Craftsman Paradigm

To understand why Hermes represents a shift in agentic AI, consider the analogy of a master craftsman.

A novice apprentice reads a manual, builds a single chair, and immediately forgets the nuances of the wood they used. A master craftsman, however, keeps a detailed journal. In this journal, they document every project: what type of joinery worked, how a specific varnish reacted to humidity, the client’s unique aesthetic preferences, and the shortcuts that saved hours of labor.

Before starting a new project, the master reviews this journal. After completing it, they write a new entry, updating their techniques. Over decades, this journal becomes an extension of their mind.

This is exactly how Hermes operates. Instead of treating memory as a cold database query, Hermes integrates a structured, self-authored “journal” directly into an LLM reasoning loop. It is built on three interlocking theoretical pillars:

  1. Persistent Memory as a Continuous Learning Journal: A living, semantic record of facts and user preferences.
  2. The Closed Learning Loop: Real-time, in-context learning driven by tool execution outcomes and guardrails.
  3. Self-Improvement through Reflection: An asynchronous meta-process where the agent analyzes its own transcripts to build, update, and prune its own codebase of “skills.”

Let’s explore how these three pillars are implemented in code.

Pillar 1: Persistent Memory as a Continuous Learning Journal

Most agent architectures rely on vector databases to handle memory. While vector search is great for retrieving raw documents, it is terrible for maintaining an agent’s identity, behavioral guidelines, or deep user relationships. If a user says, “I prefer concise code reviews with bullet points,” that shouldn’t be a vector chunk that might get retrieved if the cosine similarity is high enough. It should be a core parameter of how the agent behaves.

Hermes solves this by using a structured, LLM-authored plain-text journal split into two files:

  • MEMORY.md: Factual knowledge about the environment, tasks, and accumulated domain knowledge.
  • USER.md: A deep, evolving model of the user’s persona, work style, and expectations.

This memory is initialized directly when the agent starts:

# From run_agent.py - Memory initialization
if not skip_memory:
    mem_config = _agent_cfg.get("memory", {})
    self._memory_enabled = mem_config.get("memory_enabled", False)
    self._user_profile_enabled = mem_config.get("user_profile_enabled", False)
    self._memory_nudge_interval = int(mem_config.get("nudge_interval", 10))
    if self._memory_enabled or self._user_profile_enabled:
        from tools.memory_tool import MemoryStore
        self._memory_store = MemoryStore(
            memory_char_limit=mem_config.get("memory_char_limit", 2200),
            user_char_limit=mem_config.get("user_char_limit", 1375),
        )
        self._memory_store.load_from_disk()

Proactive, Asynchronous Introspection

Instead of waiting for the user to explicitly say “remember this,” Hermes autonomously decides what is worth preserving. It does this using a nudge interval (memory.nudge_interval). Every $N$ turns, the agent triggers a background review process guided by the following system prompt:

_MEMORY_REVIEW_PROMPT = (
    "Review the conversation above and consider saving to memory if appropriate.nn"
    "Focus on:n"
    "1. Has the user revealed things about themselves — their persona, desires, "
    "preferences, or personal details worth remembering?n"
    "2. Has the user expressed expectations about how you should behave, their work "
    "style, or ways they want you to operate?nn"
    "If something stands out, save it using the memory tool. "
    "If nothing is worth saving, just say 'Nothing to save.' and stop."
)

Crucially, this review does not block the user interface. A self-improving agent must not sacrifice real-time performance for the sake of learning. Hermes spawns this review in a background thread, cloning its state so the user never experiences latency:

def _spawn_background_review(self, messages_snapshot, review_memory=False, review_skills=False):
    """Spawn a background thread to review the conversation for memory/skill saves."""
    def _run_review():
        review_agent = AIAgent(
            model=self.model,
            max_iterations=16,
            quiet_mode=True,
            platform=self.platform,
            provider=self.provider,
            api_mode=_parent_runtime.get("api_mode"),
            base_url=_parent_runtime.get("base_url"),
            api_key=_parent_runtime.get("api_key"),
            credential_pool=getattr(self, "_credential_pool", None),
            parent_session_id=self.session_id,
            enabled_toolsets=["memory", "skills"],
        )
        review_agent._memory_store = self._memory_store
        review_agent.run_conversation(user_message=prompt, conversation_history=messages_snapshot)
        # ... collect actions and print summary
    t = threading.Thread(target=_run_review, daemon=True, name="bg-review")
    t.start()

By directly inheriting the parent’s _memory_store, the background agent writes directly to disk. On the very next turn, this updated markdown is injected straight into the system prompt:

# From _build_system_prompt - Memory injection
if self._memory_store:
    if self._memory_enabled:
        mem_block = self._memory_store.format_for_system_prompt("memory")
        if mem_block:
            prompt_parts.append(mem_block)
    if self._user_profile_enabled:
        user_block = self._memory_store.format_for_system_prompt("user")
        if user_block:
            prompt_parts.append(user_block)

Pillar 2: The Closed Learning Loop

The second pillar of Hermes is the Closed Learning Loop. Traditional reinforcement learning requires complex external reward functions, and supervised learning requires massive labeled datasets. Hermes bypasses both by using its own tool execution outcomes as a real-time feedback signal.

The loop consists of four rapid-fire stages:

  1. Observation: The agent receives the user’s message and current context.
  2. Action: The agent calls a tool (e.g., writing a file, running a script).
  3. Outcome: The tool executes and returns a raw result (either data or an error trace).
  4. Refinement: The agent inspects the tool output and immediately adjusts its strategy.

This execution loop is managed inside run_conversation():

while (api_call_count < self.max_iterations and self.iteration_budget.remaining > 0) or self._budget_grace_call:
    # 1. Build API messages from current history
    # 2. Call the model (streaming or non-streaming)
    # 3. Parse tool calls from the response
    # 4. Execute tool calls, append results to messages
    # 5. Continue loop

Because the raw tool outcomes—including stack traces and bash errors—are appended directly to the conversation history, the LLM uses its in-context learning capabilities to debug itself on the fly.

Guardrails and Resource Constraints

To prevent the agent from getting stuck in infinite loops (e.g., repeatedly reading the same directory when a search fails), Hermes uses a ToolCallGuardrailController. If the model repeats a non-progressing action, the guardrail intervenes and injects a synthetic warning:

# From run_agent.py - Guardrail observation after tool execution
function_result = self._append_guardrail_observation(
    function_name, function_args, function_result, failed=is_error,
)

Additionally, Hermes enforces an IterationBudget. This class acts as a form of “learning pressure,” forcing the agent to be highly strategic with its tool usage:

class IterationBudget:
    """Thread-safe iteration counter for an agent."""
    def __init__(self, max_total: int):
        self.max_total = max_total
        self._used = 0
        self._lock = threading.Lock()
    def consume(self) -> bool:
        with self._lock:
            if self._used >= self.max_total:
                return False
            self._used += 1
            return True
    def refund(self) -> None:
        with self._lock:
            if self._used > 0:
                self._used -= 1

If an action is highly efficient (like programmatically running code), the budget can be refunded. If the agent wastes LLM calls on repetitive tasks, it quickly runs out of budget and is forced to summarize and exit. This design teaches the agent to prioritize high-leverage tools.

Context Compression: Lossy Semantic Preservation

As the conversation grows, it risks overflowing the LLM’s context window. Instead of hard-truncating old messages (which causes catastrophic forgetting), Hermes uses a ContextCompressor.

An auxiliary LLM reviews the middle turns of the conversation and summarizes them into a highly dense semantic paragraph, discarding raw execution logs but retaining the core decisions, discoveries, and outcomes:

# From _compress_context
messages, active_system_prompt = self._compress_context(
    messages, system_message, approx_tokens=approx_tokens,
    task_id=effective_task_id,
)

Pillar 3: Self-Improvement Through Reflection

While the closed learning loop handles short-term adaptation within a single conversation, reflection is how Hermes achieves long-term, structural self-improvement.

After a conversation ends, Hermes reviews its performance to extract procedural knowledge. It doesn’t just ask “What did I learn about this user?”—it asks “What did I learn about how to solve this class of problem?”

These procedural insights are compiled into Skills. Skills are structured directories stored in ~/.hermes/skills/ containing:

  • SKILL.md: A step-by-step markdown guide explaining how to perform a task.
  • references/: Session-specific details or condensed knowledge bases.
  • templates/: Starter files and boilerplates.
  • scripts/: Statically re-runnable scripts.

The background agent is pushed to actively seek out these learning opportunities via the _SKILL_REVIEW_PROMPT:

_SKILL_REVIEW_PROMPT = (
    "Review the conversation above and update the skill library. Be "
    "ACTIVE — most sessions produce at least one skill update, even if "
    "small. A pass that does nothing is a missed learning opportunity, "
    "not a neutral outcome.nn"
    # ... detailed guidance on when to update/create skills
    "If you notice two existing skills that overlap, note it in your "
    "reply — the background curator handles consolidation at scale.nn"
    "'Nothing to save.' is a real option but should NOT be the "
    "default. If the session ran smoothly with no corrections and "
    "produced no new technique, just say 'Nothing to save.' and stop. "
    "Otherwise, act."
)

This prompt programs a growth mindset directly into the agent. It treats mistakes, user corrections, and unexpected workarounds not as failures, but as first-class signals to update its procedural skills.

The Skill Lifecycle and the Curator

If an agent continuously creates skills, its library will eventually suffer from bloat, duplicate skills, and outdated APIs. To solve this, Hermes includes a background system service called the Curator (agent/curator.py).

The Curator runs periodically to analyze the skill library, merge overlapping skills, and archive stale ones:

# From AGENTS.md - Curator invariants
# Invariants:
# - Curator only touches skills with created_by: "agent" provenance —
#   bundled + hub-installed skills are off-limits.
# - Never deletes; max destructive action is archive.
# - Pinned skills are exempt from every auto-transition and from the
#   LLM review pass.

By enforcing these safety invariants, Hermes ensures that self-improvement never results in the accidental deletion of core system tools or user-pinned workflows.

The Complete Self-Learning Cycle

When you put these three pillars together, you get a continuous, self-reinforcing learning loop:

[1. Interaction] ──> User talks to Agent, executing tools in real-time.
       │
       ▼
[2. Closed-Loop Adaptation] ──> Agent debugs tool errors within the turn.
       │
       ▼
[3. Persistent Memory] ──> Background thread saves facts to MEMORY.md / USER.md.
       │
       ▼
[4. Skill Update] ──> Reflection creates/updates procedural guides in SKILL.md.
       │
       ▼
[5. Curator Maintenance] ──> Background curator merges, prunes, and archives skills.
       │
       ▼
[6. Next Turn] ──> Updated memory and skills are injected into the next prompt.

This cycle allows the agent to grow more capable, more personalized, and highly specialized the more you use it—without requiring a single gradient update, fine-tuning run, or manual code deployment.

The Shift to Truly Stateful AI

The era of the stateless AI wrapper is drawing to a close. To build applications that can truly operate as autonomous software engineers, research assistants, or personal chiefs of staff, we must build systems that remember, reflect, and adapt.

By combining plain-text semantic journals, real-time tool guardrails, and asynchronous skill curation, the Hermes architecture provides a blueprint for the next generation of agentic AI. It moves us away from static prompt engineering and toward dynamic, evolving systems that learn from their environments.

Let’s Discuss

  1. Memory Storage: Hermes uses structured Markdown files (MEMORY.md/USER.md) rather than a vector database for its core memory. What are the architectural trade-offs of this approach when scaling to millions of tokens of interaction history?
  2. Self-Writing Code: If an agent has the power to autonomously write and update its own “skills” (including executable scripts), what security protocols and sandboxing techniques would you implement to prevent catastrophic system failures or malicious exploits?

The concepts and code demonstrated here are drawn directly from the comprehensive roadmap laid out in the ebook Hermes Agent, The Self-Evolving AI Workforce: details link, you can find also my programming ebooks with AI here: Programming & AI eBooks.

How we reduced coding-agent token usage by 17.9% with an MCP server

Coding agents are powerful, but in day-to-day development they waste a lot of tokens on noisy tool output.

A typical cargo test or git status through generic shell tooling sends back a lot of text that an agent doesn’t actually need to reason with. The model still has to read it, pay for it, and carry it in context.

I built Daimonos to reduce that waste.

What Daimonos is

Daimonos is an MCP server focused on the core coding loop:

  • read / write / edit files
  • search code
  • execute commands
  • structured git, cargo, gh, and docker operations
  • batching and script execution for fewer round trips

The key idea is simple: return compact, structured output instead of terminal spam whenever possible.

Why this matters

For coding agents, token usage compounds quickly across many tool calls.

Even when the final answer is simple, the path to get there can be expensive.

We wanted to reduce:

  • total tokens consumed
  • output-token noise
  • wall-clock time per task

Benchmark snapshot

From our benchmark runs:

  • Total tokens: 41,239 -> 33,847 (7,392 saved, -17.9%)
  • Output tokens: 5,842 -> 3,198 (-45.3%)
  • Wall time: -16.4% locally
  • Remote AWS runs: -20.3% cost, -14.0% completion time

Positioning

There are lots of great MCP servers for external APIs and workflow orchestration.

Daimonos is different: it optimizes the core coding tool path itself so agents spend less context on operational noise and more on actual reasoning.

Try it / feedback welcome

Repo: https://github.com/beardfaceguy/daimonos

If you’re running MCP in production, I’d especially love feedback on:

  • where tool-output bloat still hurts most
  • which workflows are still too noisy
  • what would block adoption in your environment

I Ran Gemma 4 on an 8GB Laptop — Here’s What the Experience Was Actually Like

I took a screenshot of code with a SQL injection vulnerability, compressed it twice through WhatsApp, and fed it to Gemma 4 running entirely on my 8GB RAM laptop.

One minute and forty-seven seconds later, it pointed out the exact dangerous line, explained why it was vulnerable, and showed the correct way to fix it.

I’m a 19-year-old self-taught developer in Nigeria. I don’t have a high-end machine or a GPU. Just a consumer laptop, an internet connection, and four years of figuring things out alone.

When Google released Gemma 4, I skipped most of the benchmark discussions and tested it myself to see what it could actually do on limited hardware.

This is that report.

TL;DR for the skimmers:

  • Gemma 4 E2B runs on 8GB RAM without a GPU
  • It analyzed a WhatsApp-compressed screenshot and caught a real SQL injection vulnerability
  • It handled Hausa naturally, while Yoruba and Igbo showed some limitations with diacritics
  • Available RAM matters more than you think
  • It’s free, private, offline, and surprisingly capable

What Gemma 4 Actually Is:

Before I get into what I found, here’s the context you need.

Gemma 4 is Google DeepMind’s latest family of open models. Open means you can download the weights and run them locally — no API costs, no data leaving your machine. For reference: E2B downloads at 7.2GB best for 8gb RAM device, E4B at 9.6GB best for 16gb RAM.

The family comes in three variants:

E2B and E4B — The Edge Models
Built for ultra-low resource deployment. Think mobile devices, Raspberry Pi, laptops without GPUs. E2B has around 2 billion effective parameters. E4B has around 4 billion. These are the models that run on hardware most developers in the world actually own. This is what I tested.

31B Dense — The Bridge Model

31 billion parameters in a dense architecture. Sits between consumer hardware and full server deployment. Bridges the gap between what you can run locally on a powerful machine and what requires a data center.

26B MoE — The Efficient Reasoner
26 billion parameters in a Mixture-of-Experts architecture. Not all parameters activate for every token; only the relevant experts fire. This makes it highly efficient for reasoning tasks at scale without burning through compute proportionally.

I tested E2B. Here’s why that matters for developers like me.

Test 1 — Vision: Low quality Image Test

This was not a clean lab test. This was real world conditions.

I had a screenshot of an Express.js route with a SQL injection vulnerability — the classic mistake where user input goes directly into a database query without sanitization. Instead of taking a clean screenshot and uploading it properly, I sent it through WhatsApp. Then I downloaded it and sent it through WhatsApp again. Anyone who has done this knows what happens; WhatsApp compresses images aggressively. By the time I fed it to Gemma 4, the image quality had degraded significantly.

I opened Google AI Studio, loaded Gemma 4, uploaded the image, and asked it to review the code for security issues.

What happened:

One minute and forty-seven seconds later; on a fresh boot with nothing else running Gemma 4 returned a structured response that:

  • Identified the exact vulnerable line in the image
  • Named the vulnerability correctly as SQL injection
  • Explained how an attacker could exploit it
  • Provided the corrected code snippet
  • Gave step-by-step prevention advice

The output was specific. It referenced the actual code in the image, not generic advice. It did not say “make sure you validate your inputs.” It said here is the line, here is why it is dangerous, here is the fix.

Why this matters:

Most developers do not have perfect screenshots. They have photos of monitors taken in bad lighting, screenshots forwarded through three different messaging apps, images captured on a low-end phone. The documentation never tests for this. I did.

Gemma 4 processed a degraded, double-compressed image and returned accurate, actionable output. For a model running on consumer hardware, that is not nothing. That is the difference between a model that works in a lab and a model that works in the real world.

Test 2 — The Finding Nobody Else Will Write About

I asked Gemma 4 to explain JWT authentication JSON Web Tokens, a common auth mechanism in three Nigerian languages: Yoruba, Hausa, and Igbo.

This took approximately two minutes and fifty seconds. By this point I had more files open and my RAM was no longer as fresh as the first test. The model was noticeably slower.

But here is what it returned.

Hausa:
The response was accurate and natural. The model understood the request, switched languages correctly, and explained the concept in a way that read like genuine Hausa rather than a mechanical translation. For a locally running model with no internet access during inference, this was genuinely surprising.

Yoruba:
The response came through but with drift. Yoruba has tonal markers — accent marks that change the meaning of words entirely. Without those diacritics in my prompt, the output was approximate rather than precise. Writers targeting Yoruba-speaking audiences would need to verify carefully before publishing anything.

Igbo:
Similar story. Igbo has its own special characters and tonal markers. The model approximated and the nearest recognizable output came through; but it was not fully accurate Igbo. Close enough to understand, not close enough to trust without review.

What this means practically:

There are over 500 million people in West Africa. There are writers, developers right now building and writing for users who speak Hausa, Yoruba, Igbo, Twi, Amharic, Swahili. Those writers need to know exactly what these models can and cannot do in local languages before they ship something.

Here is my honest assessment:

Gemma 4 E2B handles Hausa better than I expected. Yoruba and Igbo have limitations tied directly to diacritics if your prompt does not include them, the output won’t either. For a model running entirely offline, the multilingual capability is remarkable. For production use in tonal African languages, test before you ship.

Test 3 — The 128K Context Window on 8GB RAM

The spec sheet says Gemma 4 supports a 128K context window. That number means nothing without knowing what it costs to use it on consumer hardware.

I fed it an entire README file — a long, detailed project documentation file — and asked for a structured summary.

It took five minutes to complete.

The output was accurate. It understood the document. It structured the summary well. It did not hallucinate content that was not there. It captured the main purpose, the architecture, the setup steps, and the key features correctly.

Five minutes is slow by cloud standards. By the standard of a free, private, offline model running on 8GB RAM with no GPU, five minutes to accurately process and summarize a long document is a different conversation entirely.

The 128K context window is not just a spec sheet number. It held an entire document in memory and reasoned about it correctly. For developers building tools that need to process long files — entire codebases, full documentation, lengthy configuration files — E2B can do this on hardware you already own. Just plan for the time it takes.

The RAM Reality Nobody Documents

Here is practical information that is not in the official documentation anywhere.

I noticed a clear performance pattern across my tests:

Test RAM State Time to Complete
Vision + code review Fresh boot, nothing open 1 min 47 sec
Multilingual explanation Multiple files open 2 min 50 sec
Long context summary Heavy use, many tabs ~5 minutes

The pattern is obvious once you see it. As RAM fills with other processes, Gemma 4 E2B slows down significantly. This is not a flaw. The model needs memory to run and it competes with everything else on your machine.

Practical advice for 8GB RAM users:

  • Close everything before running a local inference task
  • Restart your machine for faster result — you want fresh RAM
  • E2B is the realistic choice at 8GB, E4B will be tight
  • Do your most demanding tasks first, before RAM fragments
  • If you are building an app on top of Ollama, test your performance after extended use not just on first boot

I learned all while trying to build with it

Which Model Should You Actually Use

Stop reading benchmarks and use this decision guide instead.

You have an 8GB RAM laptop with no GPU → Gemma 4 E2B via Ollama. Nothing else is realistic.

Your project handles sensitive data and privacy is critical → Any Gemma 4 variant running locally via Ollama. Your data stays on your machine. Full stop.

You are building for multilingual users in Africa or South Asia → E2B has meaningful multilingual capability. Test your specific languages before shipping. Hausa works well. Tonal languages with special characters need careful prompting.

You need high performance for a server deployment → 31B Dense is your target.

You need efficient reasoning at high throughput → 26B MoE is built for this.

You are building for mobile or edge devices → E2B or E4B. These models were designed for exactly this hardware profile.

Your budget is zero and you need full capability → E2B via Ollama. Free to download, free to run, free forever. No API key. No subscription. No data leaving your machine.

What Running AI Locally Actually Means

Every conversation about AI accessibility focuses on API costs and internet connectivity. Those are real barriers. But there is a third barrier that nobody talks about: trust.

When a developer in Lagos pastes their production code into ChatGPT or any cloud AI tool, that code leaves their machine. If there are API keys in that code, database connection strings, auth secrets — they just went to a server somewhere. Most developers do not think about this. Most beginners definitely do not.

Running Gemma 4 locally via Ollama removes that problem entirely. Your code goes from your editor to your RAM and back to your screen. Nothing else happens. No network request. No logging. No third party.

For a self-taught developer building their first real project, that matters. For a developer in a region where cloud AI costs are prohibitive relative to local income, that matters. For anyone building tools that touch sensitive user data, that matters.

Gemma 4 E2B is not the most powerful model available. It is not trying to be. What it is — a capable, multimodal, multilingual model that runs on hardware most developers in the world actually own, for free, privately, offline — is something different from anything that existed before it.

There is a difference between a model that exists and a model that runs on hardware people actually own.

That difference is the whole thing.

How To Get Started Right Now

If you have not pulled Gemma 4 yet, here is everything you need.

Step 1 — Install Ollama

Go to ollama.com and download it for your operating system. Install it like any normal application.

Step 2 — Pull Gemma 4 E2B

ollama pull gemma4:e2b

This downloads the model to your machine. Approximately 2-3GB. You only do this once.

Step 3 — Start Ollama

ollama serve

This runs Ollama in the background on localhost port 11434. Leave this terminal open.

Step 4 — Test it immediately

ollama run gemma4:e2b "explain what a SQL injection attack is to a complete beginner"

If you get a response, everything is working. You are now running a capable multimodal AI model locally on your own machine at zero cost.

Step 5 — Try the vision capability

Head to aistudio.google.com, select Gemma 4, upload a screenshot of any code, and ask it to review for security issues. No setup required. See what it catches.

Final Thought

I started these tests expecting to be disappointed. Consumer hardware running open models has usually meant compromises — slow inference, shallow responses, limited context.

What I found instead was a model that analyzed a WhatsApp-compressed screenshot and caught a real security vulnerability. That explained JWT authentication in Hausa. That summarized long documents on 8GB RAM. All privately, offline, and free.

The compromises are still real. The speed is nowhere near cloud models. The tonal language limitations matter. The RAM constraints are physics.

But benchmark scores are measured in controlled environments on optimized hardware by people who are not your users.

I am the user.
8GB RAM. Nigeria. WhatsApp screenshots. Nigerian languages. Midnight deadlines.

And if Gemma 4 works in those conditions, then it works in the real world.

That is the benchmark that matters to me..

Pull it. Test it. Build with it.

ollama pull gemma4:e2b

Everything else is waiting on the other side of that command.

Tested on: 8GB RAM laptop, Windows, Ollama + Google AI Studio, May 2026
Models tested: Gemma 4 E2B
Location: Nigeria

Lean 4 101 for Python Programmers: A Gentle Introduction to Theorem Proving

Before we begin — if you’re interested in improving engineering quality and developer workflows, check out git-lrc on GitHub — a free micro AI code review tool that runs on every commit. If the idea resonates with you, consider giving the project a ⭐ on GitHub.

Python developers are used to expressive code.

You can automate workflows in minutes, prototype products quickly, manipulate huge datasets, and build entire backends with surprisingly little code.

But eventually many developers run into a frustrating problem:

“How do I know my program is actually correct?”

You can write tests.
You can add type hints.
You can run linters.

But tests only sample behavior.

They do not prove correctness.

That’s where Lean becomes fascinating.

Lean 4 is not just a programming language.

It’s also:

  • a theorem prover
  • a proof assistant
  • a functional programming language
  • and a system for mathematically verifying software

If you’re coming from Python, Lean can feel strange at first.

But once the mental model starts clicking, it genuinely changes how you think about programming.

This article is a beginner-friendly introduction to Lean 4 specifically for Python programmers.

The Biggest Difference: Lean Wants Precision

Python is flexible by design.

For example:

def add(a, b):
    return a + b

Python is happy with this.

But think carefully:

  • What types are a and b?
  • Integers?
  • Strings?
  • Lists?
  • Custom objects?

Python delays those decisions until runtime.

Lean does the opposite.

In Lean:

def add (a : Nat) (b : Nat) : Nat :=
  a + b

This explicitly says:

  • a is a natural number
  • b is a natural number
  • result is a natural number

At first this can feel verbose to Python developers.

But Lean’s philosophy is interesting:

Ambiguity is usually hidden complexity.

Lean tries to eliminate hidden assumptions.

Your First Lean Functions

Let’s compare Python and Lean side by side.

Python:

def square(x):
    return x * x

Lean:

def square (x : Int) : Int :=
  x * x

Very similar.

Now let’s try something slightly more interesting.

Python factorial:

def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n - 1)

Lean factorial:

def factorial : Nat → Nat
| 0 => 1
| n + 1 => (n + 1) * factorial n

This looks unusual initially.

What’s happening here?

Lean uses pattern matching.

Explanation:

  • if input is 0, return 1
  • otherwise treat the number as n + 1
  • recursively compute factorial

This style is extremely common in functional programming.

And importantly:

Lean can reason formally about recursive definitions.

Lean Cares Deeply About Correctness

Here’s a subtle Python issue:

def divide(a, b):
    return a / b

Looks harmless.

But:

divide(10, 0)

Runtime error.

Python lets dangerous states exist until execution.

Lean pushes you to think earlier.

For example, Lean separates different numeric systems carefully:

  • natural numbers
  • integers
  • rational numbers
  • finite values

That strictness is not accidental.

Lean assumes:

if something matters logically, it should be represented explicitly.

This is one reason theorem provers are powerful.

Immutable Thinking Feels Different

Python programmers often mutate state:

count = 0

def increment():
    global count
    count += 1

Lean strongly prefers immutable transformations:

def increment (n : Nat) : Nat :=
  n + 1

Instead of changing existing state:

  • input goes in
  • new value comes out

Why is this important?

Because mutable state becomes difficult to reason about formally.

Imagine trying to mathematically prove correctness of a large system where:

  • variables change everywhere
  • functions have hidden side effects
  • execution order matters constantly

Functional programming reduces this chaos.

That’s one reason languages like Lean heavily embrace immutability.

Types in Lean Are Much More Powerful Than Python Type Hints

Python type hints help developers.

Lean types help prove things.

That’s a huge distinction.

Python:

def greet(name: str) -> str:
    return "Hello " + name

Helpful, but not enforced rigorously.

Lean types can encode logical guarantees.

For example, you can express:

  • “this list cannot be empty”
  • “this number is positive”
  • “this function always terminates”

That’s where Lean starts feeling less like programming and more like formal reasoning.

A tiny theorem:

theorem add_zero (n : Nat) : n + 0 = n := by
  rfl

This proves:

adding zero changes nothing

Breaking it down:

  • theorem declares a proposition
  • (n : Nat) means for any natural number n
  • : n + 0 = n is the claim
  • by starts proof mode
  • rfl means “this is true by direct simplification”

This may look trivial.

But Lean scales this exact mechanism to verify extremely sophisticated systems.

Lean Proofs Feel Like Interactive Debugging

One surprising thing about Lean:

Writing proofs often feels similar to debugging.

Suppose we define list length.

def length : List α → Nat
| [] => 0
| _ :: xs => 1 + length xs

Explanation:

  • empty list has length 0
  • otherwise:

    • ignore first element (_)
    • recursively compute remaining length

Now imagine proving something about lists.

Lean gives continuous feedback:

  • current assumptions
  • proof goals
  • missing logical steps

This creates a very interactive workflow.

Python developers often enjoy REPL-driven development.

Lean has a similar “conversation with the system” feeling.

Except instead of debugging runtime behavior, you debug logical reasoning.

Tactics: Step-by-Step Proof Construction

Lean proofs are often built using tactics.

Example:

theorem reverse_reverse (xs : List Nat) :
  xs.reverse.reverse = xs := by
  simp

What does this mean?

It proves:

reversing a list twice returns the original list

And simp tells Lean:

simplify using known rules

Some useful beginner tactics:

  • simp → simplify expressions
  • rw → rewrite using known equalities
  • intro → introduce assumptions
  • exact → directly provide proof
  • apply → use another theorem

Think of tactics like:

  • debugging tools
  • proof-building commands
  • interactive reasoning helpers

Over time they become surprisingly intuitive.

Lean Quietly Improves Your Software Engineering

Even if you never become a theorem proving expert, Lean changes how you think.

You start asking:

  • What assumptions exist here?
  • What invariants should always hold?
  • Can invalid states exist?
  • Is this behavior guaranteed?

This mindset transfers back into ordinary engineering.

You often begin writing:

  • cleaner APIs
  • safer abstractions
  • more predictable systems
  • more explicit contracts

Ironically, learning Lean can improve your Python.

How Python Developers Should Learn Lean

A good beginner path:

1. Learn basic functional programming ideas

Focus on:

  • recursion
  • immutability
  • pattern matching

2. Install Lean + VS Code

Using Visual Studio Code with the Lean extension gives excellent feedback.

3. Ignore advanced math initially

Many beginners get overwhelmed because online discussions quickly become abstract.

Start simple:

  • functions
  • lists
  • recursion
  • tiny proofs

4. Build tiny programs

Good practice ideas:

  • reverse a list
  • binary trees
  • arithmetic functions
  • list transformations

5. Expect gradual progress

Lean is closer to learning chess or mathematics than learning a typical framework.

The intuition develops slowly.

Conclusion

Lean 4 represents a very different philosophy of programming.

Python asks:

“Can we make development faster and more flexible?”

Lean asks:

“Can we make reasoning precise and verifiable?”

Both philosophies matter.

But as software systems become increasingly complex — especially in:

  • AI
  • infrastructure
  • cryptography
  • finance
  • distributed systems

— the ability to formally reason about correctness may become far more important.

And perhaps that’s the most interesting thing about Lean:

It doesn’t just teach syntax.

It teaches disciplined thinking.

So here’s a question to leave you with:

If AI increasingly generates code automatically, do developers become more valuable not for writing code — but for verifying whether systems are actually correct?

That future may arrive sooner than we think.

And if it does, theorem provers like Lean may eventually move from research circles into mainstream software engineering.

And again — if you’re interested in developer tooling around engineering quality, check out git-lrc on GitHub — a free micro AI code review tool that runs on commits — and consider giving the project a ⭐ if you’d like to support it.

From Assistants to Agents: My Take on Google I/O 2026

The Evolution of AI from Assistants to Agents

This is a submission for the Google I/O Writing Challenge

From Assistants to Agents: My Take on Google I/O 2026

The Evolution of AI from Assistants to Agents

Google I/O 2026 was the moment Google fully embraced agentic AI. Rather than showing incremental improvements, this year’s announcements reframed Gemini as an ecosystem of models, tools and platforms designed to act on our behalf.

In this post I’ll unpack the key releases, highlight some exceptional projects from Google’s Gemini Live Agent Challenge, and share my perspective on what these advances mean for developers.

The Evolution of Gemini: Omni, Flash 3.5 and Spark

Gemini Ecosystem

Gemini 3.5 Flash

Gemini 3.5 Flash represents a major leap in performance and efficiency.

Google built it as a high-throughput model capable of handling long-horizon reasoning, planning and agentic workflows much faster than previous generations.

What stood out to me most was that Google focused less on “AI hype” and more on practical developer productivity.

This model is designed for:

  • Fast reasoning
  • Tool usage
  • Long context understanding
  • Agent orchestration
  • Real-time interactions

For developers, this matters because modern AI systems are no longer just chatbots. They are becoming autonomous systems capable of executing workflows.

Gemini Omni

Gemini Omni was one of the most impressive announcements.

It combines:

  • Video generation
  • Physical world understanding
  • Image editing
  • Audio interactions
  • Realistic scene creation

The ability to generate and edit multimodal content from prompts feels like Google entering full-stack creative AI territory.

This also signals that future applications will not rely only on text interfaces anymore.

AI is becoming visual, interactive and context-aware.

Gemini Spark

Gemini Spark may be the clearest preview of where AI is heading.

Spark acts like a persistent personal AI agent that can:

  • Read emails
  • Summarize conversations
  • Schedule appointments
  • Monitor tasks
  • Automate workflows

Unlike traditional assistants, Spark is designed to proactively help users rather than waiting for commands.

This changes the role of AI from “tool” to “digital operator.”

AI Search Is Becoming Agentic

Google Search also underwent a massive transformation.

The new AI-powered search experience introduces:

  • Persistent information agents
  • Cross-modal search
  • Continuous monitoring
  • Personalized summaries

Instead of manually searching repeatedly, users can now ask AI agents to monitor topics continuously.

For example:

  • “Watch for Chromium security updates”
  • “Track flights from Islamabad to Dubai”
  • “Monitor GPU price drops”

This turns search into an active system instead of a passive query engine.

Antigravity 2.0 and Developer Ecosystem

Multi-Agent Architecture

One of the most underrated announcements was Antigravity 2.0.

Google is clearly preparing infrastructure for multi-agent applications.

Antigravity introduces:

  • Long-running agent sessions
  • Sub-agent orchestration
  • Async task execution
  • Agent SDKs
  • Terminal-based AI workflows

This feels like the beginning of operating systems designed specifically for AI agents.

As developers, we may soon build applications where dozens of AI agents collaborate simultaneously.

Gemini Live Agent Challenge Winners

One of my favorite parts of Google I/O 2026 was seeing real-world projects from developers.

These projects proved that agentic AI is not theoretical anymore.

Category Project What It Does
Grand Prize ORION Surgical AI copilot for robotic surgery
Best Live Agent drone-copilot Voice-controlled drone assistant
Best Storytelling Sankofa AI heritage storyteller
Best UI Navigator Moonwalk Voice-controlled desktop AI
Best Multimodal Wand Gesture + voice browser agent
Best Innovation Rayan Memory 3D memory palace AI
Best Technical Execution JohnKeats.AI Emotional conversational companion

What impressed me most was the consistent design pattern across all winners:

  • Persistent sessions
  • Tool calling
  • Multimodal reasoning
  • Streaming interactions
  • Memory systems
  • Safety layers

This is clearly becoming the standard architecture for next-generation AI systems.

What This Means for Developers

Google I/O 2026 changed how developers should think about AI systems.

Previously:

  • AI answered questions

Now:

  • AI plans
  • AI remembers
  • AI acts
  • AI monitors
  • AI collaborates

That shift is huge.

Developers now need to focus on:

  • State management
  • Long-running sessions
  • Safety verification
  • Tool interfaces
  • Agent collaboration
  • Ethical safeguards

Prompt engineering alone is no longer enough.

We are entering the era of AI system engineering.

My Biggest Takeaway

The biggest realization I had after watching Google I/O 2026 is this:

AI is no longer becoming a feature inside applications.

Applications themselves are becoming AI-native.

The interface, logic, workflows and automation layers are all merging together into intelligent systems.

That is both exciting and slightly terrifying.

One Concern: Hype vs Reality

While the demos looked impressive, real-world deployment will still be difficult.

Challenges like:

  • Latency
  • Reliability
  • Memory consistency
  • Hallucinations
  • Safety verification
  • Tool failures

remain major problems.

Building truly reliable AI agents is significantly harder than creating impressive demos.

I think the next few years will determine whether agentic AI becomes genuinely useful or simply another hype cycle.

Final Thoughts

Google I/O 2026 felt like a turning point.

This year was not about slightly better chatbots.

It was about creating autonomous AI ecosystems capable of reasoning, planning and acting independently.

Gemini, Spark, Omni and Antigravity together show that Google is betting heavily on an agentic future.

For developers, this creates massive opportunities.

But it also creates massive responsibility.

Because once software begins acting on behalf of humans, trust becomes more important than ever.

Helpful Resources

Thanks for reading 🚀