personal memory agent
1# Prompt Template System
2
3This document describes solstone's template variable system for personalizing prompts used in generators and agents. Templates enable dynamic substitution of owner identity, contextual information, and reusable prompt fragments.
4
5## Overview
6
7Prompts are stored as `.md` files with optional JSON frontmatter for metadata. The prompt content is loaded via `load_prompt()` from `think/prompts.py`, which uses Python's `string.Template` with `safe_substitute`. This means:
8
9- Variables use `$name` or `${name}` syntax
10- Undefined variables are left as-is (no errors)
11- Use `$$` to escape a literal dollar sign
12
13The system supports three categories of variables with the following precedence (highest to lowest):
14
151. **Context variables** - Passed by callers at runtime
162. **Identity variables** - From journal configuration
173. **Template variables** - From reusable template files
18
19## File Format
20
21Prompt files use JSON frontmatter with `{` and `}` as delimiters (braces on their own lines):
22
23```markdown
24{
25 "title": "Activity Synthesis",
26 "color": "#00bcd4",
27 "schedule": "segment"
28}
29
30$segment_preamble
31
32# Segment Activity Synthesis
33
34Your prompt content here...
35```
36
37Files without metadata can omit the frontmatter entirely - just write the prompt content directly.
38
39## Variable Categories
40
41### Identity Variables
42
43Identity variables come from the `identity` block in `config/journal.json`. These are available in all prompts automatically.
44
45**Common variables:**
46- `$name` - Full name
47- `$preferred` - Preferred name or nickname
48- `$bio` - Self-description
49- `$timezone` - IANA timezone identifier
50
51**Pronoun variables** (flattened from nested structure):
52- `$pronouns_subject` - e.g., "he", "she", "they"
53- `$pronouns_object` - e.g., "him", "her", "them"
54- `$pronouns_possessive` - e.g., "his", "her", "their"
55- `$pronouns_reflexive` - e.g., "himself", "herself", "themselves"
56
57**Uppercase-first versions** are automatically generated for all identity variables:
58- `$Name`, `$Preferred`, `$Bio`
59- `$Pronouns_subject`, `$Pronouns_possessive`, etc.
60
61The flattening logic converts nested objects using underscore separators. For example, `identity.pronouns.subject` becomes `$pronouns_subject`.
62
63**References:**
64- Identity configuration: [config.md](../talent/journal/references/config.md) (identity section)
65- Flattening implementation: `think/prompts.py` → `_flatten_identity_to_template_vars()`
66
67### Template Variables
68
69Template variables come from `.md` files in the `think/templates/` directory. Each file's stem becomes a variable name containing its contents.
70
71**Current templates:**
72- `$daily_preamble` - Preamble for full-day output analysis
73- `$segment_preamble` - Preamble for single-segment analysis
74- `$activity_preamble` - Preamble for activity-level analysis (uses `$activity_*` context variables)
75
76Templates can themselves use identity and context variables, enabling composable prompt construction. For example, `daily_preamble.md` uses `$preferred` and `$day`.
77
78**Pattern:** To add a new template variable, create `think/templates/mytemplate.md` and it becomes available as `$mytemplate` in all prompts.
79
80**Reference:** `think/templates/` directory
81
82### Context Variables
83
84Context variables are passed at runtime by the code calling `load_prompt()`. These are use-case specific and not globally available.
85
86**Common generator context:**
87- `$day` - Human-readable date (e.g., "Friday, January 24, 2026")
88- `$day_YYYYMMDD` - Day in YYYYMMDD format (e.g., "20260124")
89- `$facet` - Focused facet name when the prompt is dispatched per-facet (bare name, e.g. `work`)
90- `$activity_md_dir` - Directory path containing per-activity narrative `.md` outputs for the focused facet/day, with trailing slash
91- `$now` - Current date and time with timezone (e.g., "Monday, February 3, 2025 at 10:30 AM PST")
92- `$segment` - Segment key (e.g., "143022_300")
93- `$segment_start` - Formatted start time (e.g., "2:30 PM")
94- `$segment_end` - Formatted end time (e.g., "2:35 PM")
95
96**Activity context** (available for `schedule: "activity"` agents):
97- `$activity_id` - Activity record ID (e.g., "coding_095809_303")
98- `$activity_type` - Activity type (e.g., "coding", "meeting")
99- `$activity_description` - Description of the activity
100- `$activity_level` - Average engagement level (0-1)
101- `$activity_entities` - Comma-separated active entities
102- `$activity_segments` - Comma-separated segment keys
103- `$activity_duration` - Estimated duration in minutes
104
105Context variables also get automatic uppercase-first versions (`$Day`, `$Day_yyyymmdd`, etc.).
106
107**References:**
108- Generator context building: `think/generate.py` (search for `prompt_context`)
109- Other callers: `observe/extract.py`, `observe/enrich.py`
110
111## Usage Patterns
112
113### For Generators
114
115Generator prompts typically compose a shared preamble with agent-specific instructions:
116
117```markdown
118{
119 "title": "My Generator",
120 "color": "#4caf50",
121 "schedule": "segment"
122}
123
124$segment_preamble
125
126# Segment Activity Synthesis
127
128Your specific instructions here...
129```
130
131The `$segment_preamble` or `$daily_preamble` template provides standardized context about what's being analyzed, while the rest of the prompt defines the specific analysis task.
132
133**Optional model configuration:** Add `max_output_tokens` (response length limit) and `thinking_budget` (model thinking token budget) to override provider defaults.
134
135**Reference:** `talent/*.md` for examples (files with `schedule` field but no `tools` field)
136
137### For Agents
138
139Agent prompts are `.md` files with configuration in frontmatter:
140
141```markdown
142{
143 "title": "My Agent",
144 "tier": 2,
145 "tools": "journal"
146}
147
148You are a helpful assistant...
149```
150
151**Optional model configuration:** Add `max_output_tokens` (response length limit) and `thinking_budget` (model thinking token budget) to override provider defaults. Note: OpenAI uses fixed reasoning and ignores `thinking_budget`.
152
153**Reference:** `think/talent.py` → `get_talent()` for agent configuration loading
154
155### The load_prompt() Function
156
157```python
158load_prompt(
159 name: str, # Prompt filename (without .md)
160 base_dir: Path | None = None, # Directory containing prompt
161 context: dict | None = None, # Runtime context variables
162) -> PromptContent
163```
164
165Returns a `PromptContent` named tuple with `text` (substituted content), `path` (source file), and `metadata` (frontmatter dict).
166
167**Reference:** `think/prompts.py` → `load_prompt()`
168
169## Adding New Variables
170
171### Identity Variables
172
173Edit `config/journal.json` to add or modify identity fields. Nested objects are automatically flattened with underscore separators.
174
175### Template Variables
176
177Create a new `.md` file in `think/templates/`. The filename stem becomes the variable name.
178
179### Context Variables
180
181Pass via the `context` parameter when calling `load_prompt()`:
182
183```python
184load_prompt("myprompt", context={"custom_var": "value"})
185```
186
187## Reference Index
188
189| Category | Authoritative Source |
190|----------|---------------------|
191| Identity config schema | [config.md](../talent/journal/references/config.md) (identity section) |
192| Identity flattening | `think/prompts.py` (`_flatten_identity_to_template_vars`) |
193| Template loading | `think/prompts.py` (`_load_templates`) |
194| Core load function | `think/prompts.py` (`load_prompt`) |
195| Template files | `think/templates/*.md` |
196| Test coverage | `tests/test_template_substitution.py` |
197| Generator prompts | `talent/*.md` (files with `schedule` field but no `tools`) |
198| Agent prompts | `talent/*.md` (files with `tools` field) |