@yibie: Recommend this article that gave me chills. A developer recalls a professor 25 years ago saying "Lisp is the language of AI," and then he wrote a complete agent in 100 lines of Common Lisp—8 lines of recursive agent loop, one tool being e…
Summary
A developer built an AI agent in 100 lines of Common Lisp, with the only tool being eval. The model executes code through a recursive agent loop and restores skills via a persisted transcript, showcasing Lisp's unique advantage as an AI language.
View Cached Full Text
Cached at: 07/13/26, 07:51 AM
Here is a mind-blowing article I want to recommend. A developer recalls an AI class 25 years ago where the professor said “Lisp is the language of AI.” Then he wrote a complete agent in 100 lines of Common Lisp—an 8-line recursive agent loop, a single tool eval (the model writes Lisp code, the agent executes it on the spot), and 20 lines of persistent memory. The most shocking part isn’t the brevity—it’s that the agent used its own eval to write itself a Brave Search function. When the process died, the function vanished but the transcript remained, and on the next startup the agent read the history and defun’d it back into existence. The author says: “An agent’s capability is the story it tells itself.”
100 Lines of Lisp Agent — or Why My Professor Was Right, Just 25 Years Early
Around 2000 I took an AI course at the University of Guelph. I don’t remember what I learned—no neural networks, no transformers, no CUDA. But I remember what I did: writing a lot of Lisp code in a dark computer lab. The professor called it “the language of AI.”
25 years later, I’ve spent the last month buried in building an AI agent platform. This morning, a thought tugged at the base of my brainstem: “Could Lisp be a useful language for an agent loop?”
An Agent is a Recursive Function
Don’t be fooled by the power of Claude Code, OpenClaw, or any other AI agent tool—strip away the frameworks, and the agent loop is dead simple. You have a list of messages. You send it to the model. The model either responds with text or requests to use a tool. If it requests, you run the tool, append the result, and go again.
Some agents might implement this as a stateful while loop. But maybe it’s better suited to a recursive implementation with a base case.
(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 entire agent, 8 lines of Common Lisp. Base case: the model responds, return history. Recursive case: it asks for a tool, execute the tools, recur with the enriched message list. No joke.
No frameworks. No state machines. The agent’s state is simply the argument folded through recursion.
With help from Claude, I assembled a complete AI agent in about 100 lines of Common Lisp, running on OpenRouter. SBCL, two libraries (dexador for HTTP, shasht for JSON), nothing else.
The Only Tool is eval
When you build an agent, you typically start mounting tools—web search and scraping, spreadsheet and file tools, Python execution, etc. In fact, most of an agent’s code is a toolkit directory.
Lisp lets you cheat. Lisp is what language nerds call homoiconic—a fancy word for a simple idea: Lisp programs are written in Lisp’s own data structures (lists), so code is data and data is code. A program can construct another program the same way it builds a shopping list.
This means you don’t need to build tools—you hand the language itself to the model:
(defun lisp-eval (form-string)
(handler-case
(format nil "~s" (eval (read-from-string form-string)))
(error (e) (format nil "ERROR: ~a" e))))
One tool. The model writes a Common Lisp expression as a string. The agent reads it, evaluates it, and sends back whatever printed result.
Ask it for the 30th Fibonacci number—it doesn’t recall the answer, it writes a loop and runs it:
• (agent:run "What is the 30th Fibonacci number? Compute, don't recall.")
⤷ (defun fibonacci (n) ...) => FIBONACCI
⤷ (fibonacci 30) => 832040
The 30th Fibonacci number is 832040.
I didn’t tell it to use recursion, but in a blog post about a recursive agent loop, the model itself reached for the textbook double-recursive Fibonacci. Its first eval defined a function into the live image, and its second eval called it.
Memory is 20 Lines
Once the loop worked, I wanted persistence—conversations that survive across sessions. In Agent Foundry, I implemented memory with pgvector, but in this experiment, messages were already lists of hash tables—in other words, they were already JSON. Memory is just writing it down and reading it back:
(defun remember (messages)
(with-open-file (out memory-file :direction :output :if-exists :supersede)
(shasht:write-json (coerce messages 'vector) out))
messages)
(defun recall ()
(if (probe-file memory-file)
(coerce (with-open-file (in memory-file) (shasht:read-json in)) 'list)
(list system-message)))
The loop itself didn’t change at all. The entry point became a pipeline:
(remember (agent-loop (append (recall) (list new-user-message))))
Recall. Recur. Record. No schema. No migrations. No storage abstraction. The serialization format is the runtime format. Tell it your name today, ask it in a new process tomorrow—it answers.
Then It Wrote Its Own Web Search
The Fibonacci trick was neat, but then things got wild. I pasted a temporary Brave Search API key into the conversation—mostly to see what would happen. And what happened caught me off guard.
The agent used its eval to defun a brave-search function into the live image. It inspected what it had available, wrote the HTTP call, parsed the JSON response itself—and then started answering questions with live web results. I never built a web search tool.
Pause for a second. This flips the way we usually think about an agent’s capabilities. In Agent Foundry, in Claude Code, in every agent platform I know, the toolkit is fixed at design time. Someone like me decides what the agent can do, writes the tools, and ships them. Here, the toolkit is open. The model decides what it needs, writes it in its own substrate language, and evaluates it into existence.
There’s another twist that takes a minute to sink in. The defun itself dies when the process exits—the function lives in the running Lisp image, not on disk. But the transcript persists. The source code of the Brave Search function, the process of the model writing it—all of it exists as plain text in memory.json. So in a brand new session, the function doesn’t exist—until the agent reads its own history and evaluates it back into existence.
An agent’s skill—is the story it tells itself.
Skills Are Just Memory
Every serious agent platform today treats the toolkit as a design-time decision. MCP is essentially a formalization of design-time assumptions—capabilities as contracts, negotiated, versioned, and approved in advance. This experiment sits at the opposite pole: capabilities are determined at runtime, by the agent, in response to whatever the conversation needs.
Maybe the whole industry has been sliding along this continuum without naming it. Code interpreter was the first step. Skills (the SKILL.md pattern) is the second—because a skill is nothing more than a capability stored as text and loaded at runtime. This 100-line Lisp agent just collapses the remaining distance. Its skills are stored in its own transcript.
Thinking about it further, this is basically how we operate. Almost nothing you do is something that was design-time installed. You learned it, stored it, and now reactivate it on demand—often by writing it into your own memory, like remembering the summer you learned to ride a bike.
He Wasn’t Wrong, Just Early
Symbolic AI lost. Nobody’s expert systems survived against backpropagation and gradient descent. But what Lisp was actually built for—code as data, computation that can be inspected, transformed, and fed back into itself—turns out to be a very good description of what an agent is.
Models do the reasoning now. But the loop around the model—the part we actually engineer—is exactly what Lisp has always been good at.
25 years is a long time to stand too far away. Somewhere, maybe that old AI professor of mine is still ahead on something else.
Full source + Dockerfile: https://github.com/jamiebeach/lisp-agent… Original article: https://thebeach.dev/posts/lisp-agent/…
#Lisp #AgentLoop #AIArchaeology
jamiebeach/lisp-agent
Source: https://github.com/jamiebeach/lisp-agent
lisp-agent
“LISP is the language for AI.” — my professor, circa 2000
He was right. He was just 25 years early.
This is a complete AI agent in about 100 lines of Common Lisp. Recursive agent loop, one tool (the Lisp REPL itself), and persistent memory in 20 lines. It talks to any model on OpenRouter (https://openrouter.ai).
No framework. No state machine. No vector database. Just eval, append, and recursion.
The whole agent
(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)))))
Base case: the model answers in words. Recursive case: it asks for tools, we run them and recur with the enriched history. The agent’s state is just the argument being folded through the recursion.
The only tool is eval
Code is data, data is code. So instead of building a bunch of tools, the agent gets one tool: a live Common Lisp REPL.
(defun lisp-eval (form-string)
(handler-case
(format nil "~s" (eval (read-from-string form-string)))
(error (e) (format nil "ERROR: ~a" e))))
Ask it for the 30th Fibonacci number and it doesn’t recall the answer. It writes the loop and runs it.
Quick start (Docker)
docker build -t lisp-agent .
docker run -it --rm \
-e OPENROUTER_API_KEY=sk-or-... \
-v "$(pwd)/data:/agent/data" \
lisp-agent
You land in a live SBCL REPL with the agent loaded:
(agent:run "What is the 30th Fibonacci number? Compute it, don't recall it.")
(agent:run "My name is Jamie.")
Exit the container. Come back tomorrow.
(agent:run "What's my name?") ; it remembers
(agent:forget) ; wipe the slate
Quick start (bare metal)
Requires SBCL (https://www.sbcl.org/) and Quicklisp (https://www.quicklisp.org/).
export OPENROUTER_API_KEY=sk-or-...
sbcl --load agent.lisp
Dependencies (fetched automatically via Quicklisp): dexador (https://github.com/fukamachi/dexador) for HTTP, shasht (https://github.com/yitzchak/shasht) for JSON. That’s the whole list.
How memory works
Messages are already a list of hash tables, which is to say, already JSON in spirit. So memory is just writing the list down and reading it back:
(remember (agent-loop (append (recall) (list new-user-message))))
Recall, recur, remember. No schema, no migrations, no store abstraction. The full transcript (tool calls included) lands in memory.json, or wherever AGENT_MEMORY points.
Known limitation: it never forgets on its own, so a long-lived conversation will eventually hit the model’s context window. The natural fix is a compress step between recall and the loop, where the agent summarizes its own past. PRs welcome.
Configuration
| What | Where | Default |
|---|---|---|
| API key | OPENROUTER_API_KEY env var | required |
| Model | *model* in agent.lisp | anthropic/claude-sonnet-4.5 |
| Memory file | AGENT_MEMORY env var | memory.json |
Any OpenRouter model that supports tool calling works. Swap *model* and nothing else changes.
⚠️ Read this before you get clever
eval as a tool means the model executes arbitrary code wherever the agent runs. That is the entire point, and also the entire risk. Run it in the container, mount nothing you care about, and treat the host as off limits. This is a toy for a sandbox, not a pattern for production.
Why
Symbolic AI lost. But the thing LISP was actually built for, programs as data, computation that inspects and transforms itself, turned out to be a pretty good description of what an agent is. The models do the reasoning now. The loop around them is the part LISP was always best at.
Longer version on the blog: My Prof Was Right About LISP. He Was Just 25 Years Early. (https://thebeach.dev/posts/lisp-agent)
Files
agent.lisp the agent: loop, tool, memory (~100 lines)
Dockerfile SBCL + Quicklisp + deps, drops you at a REPL
LICENSE MIT
License
MIT. Go play.
Similar Articles
An agent in 100 lines of Lisp
The author reflects on learning Lisp for symbolic AI 25 years ago and demonstrates how a modern AI agent loop can be implemented in just 100 lines of Common Lisp, emphasizing the elegance of recursion.
@yibie: Every programmer should hand-write an agent. It only takes about 50 lines of code, it's fun, and it will surprise you. But to get the most out of it, do these two things: First, write it from scratch. Open a blank text file and type every line yourself. Don't use any AI, not even autocomplete. Second, rely only on the standard library documentation…
This article encourages programmers to manually write a simple AI agent (around 50 lines of code) to deepen their understanding of how agents work, and suggests writing it from scratch while relying only on standard library and API documentation.
@cellinlab: https://x.com/cellinlab/status/2064144608242679822
This article introduces the concept of Loop Engineering — instead of directly writing prompts for AI agents, it designs a system (loop) that recursively lets the agent iterate on tasks until completion. The article provides a detailed comparison of how Claude Code and Codex implement five building blocks: automations, worktrees, skills, sub-agents, etc. It suggests this could be the future trend of collaborating with coding agents, but also warns about token costs and AI slop issues.
This article systematically reviews AI Agent architecture and engineering practices, covering control flow, context engineering, tool design, memory, multi-agent organization, evaluation, tracing, and security. It is based on the OpenClaw implementation and emphasizes the critical role of Harness (testing and validation infrastructure) for system stability.
This article systematically reviews AI Agent architecture and engineering practices, covering control flow, context engineering, tool design, memory, multi-agent organization, evaluation, tracing, and security. It is based on the OpenClaw implementation and emphasizes the critical role of Harness (testing and validation infrastructure) for system stability.
@yibie: Recommended article: Eugene Yan from Anthropic (former Amazon/Alibaba ML team lead) writes a practical guide to his personal AI workflow. Not abstract ideas, but concrete methods you can replicate tomorrow: how to organize directories for easier model retrieval, how to write C…
This is a tweet recommending Eugene Yan's AI workflow guide, detailing how to efficiently collaborate with AI and achieve compound work gains by organizing context, configuring CLAUDE.md, creating skills, and more.