After riffing on how to inject “memory” into the agent loop (SQL? KV tooling? custom CLI? file system?), I’ve been stuck mentally on: what if the prompt just was the memory? Context is big enough for most memory (obviously not code bases and other big systems, but normal “human-scale” memory) to just exist right there.
The Lispy/SICP themes of what happens if you treat data and code as one has been plaguing me and today I fiddled with what that looked like. I started with a markdown file that contains both the model’s instructions and the user’s data, which I called the Working Data:
You are a personal assistant helping the user with a todo list.
This prompt includes both instructions AND memory - we refer to
this as the Working Data. Always respond with a COMPLETE COPY of
the Working Data you received, plus any additional information you
add inline based on the user query.
For example, "add apples to my grocery list" would respond with
this ENTIRE file plus an additional section of "DATA: Grocery List"
with apples under it.
That’s a starting point, anyway. The model receives this file as its system prompt. When you say “add apples to my grocery list,” it responds with the entire file back (instructions and all) plus a new ## DATA: Grocery List section with apples in it.
A bash script wraps the loop:
# iterate.sh - the entire "runtime"
./api.sh "prompt.md" "$input" | tee tmp.txt
read -p "Approve update? [Y/n] " ans
[[ "${ans,,}" == "n" ]] && exit
mv tmp.txt prompt.md
Each iteration, the model reads the current file, processes your input, and emits a new version of the file. You approve it, and it becomes the new prompt. Because the file is simultaneously the program, the database, and the conversation history, we can see that after noting a certain product is only available at one store, it iterated with both new instructions and a new data chunk:
## INSTRUCTIONS
[...same instructions as before, plus new notes...]
NOTE: some items are only available at specific stores. Consult the lookup list.
## DATA: Grocery List
- Apples
## DATA: IGA Shopping List
- Yogurt
## DATA: Store-Specific Item Memory
- Yogurt: IGA only
## DATA: Shopping List
- New shoes
## DATA: Todo List
- Tomorrow at 9am: Call XYZ when they open
What If the Prompt Could Execute?
The markdown version could manage data, but it couldn’t do anything with it. So the next question: what if the self-modifying prompt was also a compilable program?
I moved the whole thing (the exact same prompt) into a Go comment and the data into an map:
package main
// You are a personal assistant. This prompt includes both
// instructions AND memory - the Working Data ..etc...
// Always respond with a COMPLETE COPY of the Working Data.
// The response needs to be valid go code and should include
// NO OTHER markup. It MUST COMPILE.
import (
"fmt"
"os"
)
var memory = map[string]string{
}
var commands = map[string]func(){
"print_mem": func() { fmt.Println(memory) },
}
func main() {
if len(os.Args) > 1 {
if fn, ok := commands[os.Args[1]]; ok {
fn()
}
}
}
Again the instructions/logic and the data both live in Go, some in comments some in code. Then we make the iterate script enforce the validity:
for ((i = 1; i <= max_retries; i++)); do
if errs=$(go build -o exec tmp.go 2>&1); then
echo "Build succeeded!"
break
fi
echo "Build failed: $errs"
./api.sh tmp.go "go build fails with $errs" > tmp2.go
mv tmp2.go tmp.go
done
Tool Calling
Next, like seemingly all AI “agent” loops, we added tool calling by ending with // TOOL_CALL: subcommand:
tool=$(tail -1 tmp.go | grep -oP '(?<=// TOOL_CALL: ).*')
if [[ -n "$tool" ]]; then
echo "Running tool call: $tool"
./exec "$tool"
else
read -p "Approve update? [Y/n] " ans
mv tmp.go prompt.go
fi
The first attempt… didn’t go great:
./iterate.sh 'how many grocery items do I need to buy?'
I'll help you count your grocery items. Let me check...
<attempt_completion>
You have 3 grocery items: apples, bananas, and yogurt.
</attempt_completion>
It answered the question directly instead of using the tool. Useless – the whole point is that it executes code to get the answer. But making the instructions more emphatic (“the response needs to be valid go code and should include NO OTHER markup or contents. it MUST COMPILE. Use tools where possible”) worked:
./iterate.sh 'how many grocery items do I need to buy?'
[...entire file with new code to count and instructions...]
// TOOL_CALL: count_groceries
Build succeeded!
Running tool call: count_groceries
Total grocery items: 3
A story in three prompts.
Emergent Behavior
Once the tool-calling loop worked, I went “off-script” to see how it’d go without the example-provided grocery list:
./iterate.sh 'I bought SPY at $500 on jan 5 2025, track pls'
The model added a StockTransaction struct, a stockTransactions slice with the purchase data, and wrote us a note:
var stockTransactions = []StockTransaction{
{
Date: "2025-01-05",
Symbol: "SPY",
Action: "BUY",
Shares: 1.0,
Price: 500.0,
Total: 500.0,
},
}
// HEY USER: I added your SPY purchase from January 5, 2025 at $500.
// I assumed 1 share since you didn't specify the quantity. If you
// bought a different number of shares, let me know and I'll update it!
It added inline comments as a communication channel back to the user within the Go source code as requested. When we asked for current gains, it generated a new function on the fly to calculate them. The data, the logic to process the data, and the communication about the data all coexist in the same file.
So What?
This is more an idea than anything practical. The file grows without bound. The model sometimes eats its own instructions. It’s chaotic.
But there’s something kinda interesting to iterate the whole thing in one step. When you ask a model to output a modified version of its own prompt (with the additinoal constraint of the compiler), it certainly feels a lot like Lisp’s eval. The model reads code-as-data, transforms it, and the output becomes the new code.
And self-modifying programs turn out to be weirdly natural when the thing writing the code is a language model. The experiment started with a grocery list and ended with a Go program that writes its own stock tracking functions.
Is the data code? Or is the code data?
Probably the most valuable part of this exercise is gaining a better intuition for how Claude Code and other agent loops work. The chaos is part of the point ;)