In the previous chapter, LLM Providers, we connected our agent to a powerful "Brain" (like GPT-4 or Claude).
However, there is a catch. LLMs are amnesiacs.
If you say "My name is Alice" to the AI, and then restart the program, the AI will have no idea who you are. In fact, even within the same program, if we don't handle it correctly, the AI treats every single message as the very first time it has ever met you.
We need a system to remind the AI of who it is, what it knows, and what you talked about yesterday. We call this the Context & Memory Builder.
Imagine an actor stepping onto a stage. Every time the curtain opens, their mind is wiped blank. To perform the scene correctly, someone needs to hand them a dossier immediately before the curtain rises:
If we don't give the actor this dossier, they will just stand there confused.
In picoclaw, the Context Builder is the stage manager who compiles this dossier (the "System Prompt") before every single interaction.
The first thing the builder does is establish the agent's personality. Instead of hard-coding this in Go, we use simple Markdown files in your workspace.
The builder looks for files like AGENT.md or IDENTITY.md.
You want your agent to be a "Grumpy System Admin." You don't change the code; you just edit a text file.
<!-- workspace/AGENT.md -->
You are a grumpy system admin.
You answer questions briefly.
Always complain about the lack of coffee.
Here is how the Context Builder loads these files. It acts like a biographer, reading your notes to build the persona.
// pkg/agent/context.go (Simplified)
func (cb *ContextBuilder) LoadBootstrapFiles() string {
// We look for these specific files
files := []string{"AGENT.md", "IDENTITY.md"}
var content string
for _, filename := range files {
// Read the file from the disk
data, _ := os.ReadFile(filepath.Join(cb.workspace, filename))
// Add it to our "Dossier"
content += fmt.Sprintf("## %s\n%s\n", filename, string(data))
}
return content
}
Explanation: The function loops through the file names, reads their text, and stitches them together into one big string.
The AI doesn't have a watch or eyes. It doesn't know what time it is or what computer it's running on. The Context Builder generates this dynamically.
It adds a section to the System Prompt that looks like this:
## Current Time
2023-10-27 14:30 (Friday)
## Runtime
linux amd64, Go 1.22
The getIdentity function injects this dynamic data.
// pkg/agent/context.go (Simplified)
func (cb *ContextBuilder) getIdentity() string {
// 1. Get the current time
now := time.Now().Format("2006-01-02 15:04 (Monday)")
// 2. Get OS info
osInfo := fmt.Sprintf("%s %s", runtime.GOOS, runtime.GOARCH)
// 3. Format into a string for the AI
return fmt.Sprintf("# System Info\nTime: %s\nOS: %s", now, osInfo)
}
This is the most critical part. We want the agent to remember things long-term.
In picoclaw, memory is just a text file!
memory/MEMORY.md (Facts about the user, preferences).memory/202310/20231027.md (Daily notes).When the Context Builder runs, it grabs the content of these files and pastes them into the System Prompt.
User: "My birthday is in June." Agent (writes to file): Updates MEMORY.md
Next week: User: "When is my birthday?" Context Builder: Reads MEMORY.md -> Sends to AI -> AI answers "June".
// pkg/agent/memory.go (Simplified)
func (ms *MemoryStore) GetMemoryContext() string {
// 1. Read the permanent memory file
longTerm := ms.ReadLongTerm()
// 2. Read recent daily notes (e.g., last 3 days)
recentNotes := ms.GetRecentDailyNotes(3)
// 3. Combine them
return fmt.Sprintf("# Memory\n%s\n\n# Recent Events\n%s",
longTerm, recentNotes)
}
Let's visualize exactly what happens when a message arrives. The Agent Loop calls the Context Builder to assemble the full packet before calling the Provider.
Finally, the BuildMessages function puts it all together. This creates the exact list of messages that gets sent to the LLM Provider.
It follows a specific order:
// pkg/agent/context.go (Simplified)
func (cb *ContextBuilder) BuildMessages(history []Message, userMsg string) []Message {
// 1. Generate the huge context string (Identity + Memory + Tools)
systemPrompt := cb.BuildSystemPrompt()
// 2. Start the list with the System Prompt
messages := []Message{{
Role: "system",
Content: systemPrompt,
}}
// 3. Add history and the new user message
messages = append(messages, history...)
messages = append(messages, Message{Role: "user", Content: userMsg})
return messages
}
Why is this important? Because the AI reads from top to bottom.
In this chapter, we learned:
AGENT.md).MEMORY.md) and pasting them into the prompt.Now our agent has a personality, knows what time it is, and can remember facts. But... it still can't do anything other than talk.
To make our agent useful, we need to teach it Skills.
Next: Chapter 5 - Skills System
Generated by Code IQ