logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·02.11.26
← Back to news

An agent in 100 lines of Lisp

Original article
thebeach.dev|218 points|68 comments|by jamiebeach|Jul 7, 2026

An Agent in 100 Lines of Lisp: A 25-Year Full Circle

Around the year 2000, I found myself in a dark computer lab at the University of Guelph, enrolled in an AI course. Back then, the curriculum didn't focus on neural networks; my final project was essentially a pathfinding algorithm dressed up to look like "AI."

However, the most lasting impression from that course was the immersion in Lisp. My professor—whose name has unfortunately slipped my mind—insisted that Lisp was the definitive language for artificial intelligence. At the time, this was the prevailing wisdom.

I found a certain artistic beauty in crafting elegant recursive functions. Aside from a strange fondness for XML stylesheets (a niche preference, I admit), no other language has ever provided that specific intellectual satisfaction.

The Rise and Fall of Symbolic AI

According to my professor, Lisp was the engine behind Symbolic AI, which focused on:

  • Expert systems
  • Theorem provers
  • The manipulation of rules and symbols

Eventually, the tide shifted. Statistical methods gained ground, and then deep learning completely obliterated overshadowed the competition. Lisp seemed to vanish along with the symbolic approach. I knew almost no one using it in production, save for mentions by Paul Graham regarding his early e-commerce ventures.


The Epiphany: Agents as Recursion

Fast forward 25 years. I've spent the last month deep in the trenches of building an AI agent platform. One morning, a thought surfaced: Could Lisp actually be the ideal language for an agent loop?

I decided to experiment with Claude to test a hypothesis: An agent is fundamentally a recursive function.

While modern frameworks (like Claude Code or OpenClaw) make things look complex, the core logic is simple:

  1. Ask the model.
  2. If it requests a tool, run the tool.
  3. Append the result to the history.
  4. Repeat.

The Implementation

In Common Lisp, this entire loop can be expressed in just 8 lines:

(defun agent-loop (messages)
  (let* ((message (ref (call-model messages) "choices" 0 "message"))
         (tool-calls (gethash "tool_calls" message)))
    (if (and tool-calls (plusp (length tool-calls)))
        (agent-loop (append messages (list message) (map 'list #'execute tool-calls)))
        (append messages (list message)))))

The Logic Flow:

  • Recursive Case: The model wants tools →\rightarrow→ execute them →\rightarrow→ recurse with an enriched message list.
  • Base Case: No tools requested →\rightarrow→ return the final conversation.

The agent's state is simply the argument being folded through the recursion. I managed to build a fully functional agent using SBCL and only two libraries: dexador (for HTTP) and shasht (for JSON).

Lisp Agent Header


The Power of Homoiconicity

Most developers build agents by "bolting on" specific tools (web search, Python interpreters, etc.). However, Lisp is homoiconic, meaning its code is written using its own data structures (lists). In Lisp, code is data and data is code.

Instead of building a library of tools, I gave the model the language itself via eval:

(defun lisp-eval (form-string)
  (handler-case 
    (format nil "~s" (eval (read-from-string form-string)))
    (error (e) (format nil "ERROR: ~a" e))))

Now, the agent doesn't just "recall" information; it writes and executes programs on the fly.

Case Study: The Fibonacci Sequence

When asked for the 30th Fibonacci number—defined mathematically as Fn=Fn−1+Fn−2F_n = F_{n-1} + F_{n-2}Fn​=Fn−1​+Fn−2​—the agent didn't guess. It wrote a program:

Transcript:

  • User: "What is the 30th Fibonacci number? Compute it, don't recall it."
  • Agent: (defun fibonacci (n) (if (= n 2) 1 (+ (fibonacci (- n 1)) (fibonacci (- n 2))))) →\rightarrow→ FIBONACCI
  • Agent: (fibonacci 30) →\rightarrow→ 832040
  • Agent: "The 30th Fibonacci number is 832040."

The model independently chose a doubly-recursive approach to solve the problem. We have essentially outsourced the symbolic reasoning to the LLM while keeping Lisp as the execution substrate.

⚠️ CRITICAL WARNING: Using eval as a tool allows the model to execute arbitrary code on your machine. Do not do this in an unsecured environment.


Persistence and Memory

To add memory, I avoided heavy dependencies like pgvector and used Lisp's native primitives to read and write the message list to a file.

ComponentLisp Implementation
StorageSimple JSON file via shasht
Recall(read-json in) →\rightarrow→ converts to list
Remember(write-json messages out) →\rightarrow→ overwrites file

The updated pipeline looks like this: Recall →\rightarrow→ Agent-Loop →\rightarrow→ Remember

Current Limitations & Future Work

The current system saves the full transcript, meaning the memory grows until it hits the context window limit.

  • Implement a compression step.
  • Use the model to summarize history between recall and the loop.

Analytics