JustinTX's picture
Add files using upload-large-folder tool
7f611c5 verified
# Context Builder
The context builder turns program state into LLM prompts. It is called once per iteration and returns `{"system": ..., "user": ...}`.
For most tasks you do not need to touch this. Just set the system prompt in `config.yaml`:
```yaml
prompt:
system_message: |-
You are an expert at optimizing load balancing algorithms.
```
Only write a custom builder if your algorithm has search-state data (tree path, island ID, rejection history) that the LLM should see.
---
## Structure
```
context_builder/
base.py ContextBuilder ABC (one method: build_prompt)
utils.py TemplateManager (loads .txt templates from directories)
human_feedback.py File-based human feedback injection
default/
builder.py DefaultContextBuilder (handles diff / rewrite / image / prompt modes)
templates/ .txt prompt templates
evox/
builder.py EvoxContextBuilder (extends Default, adds LLM-generated summaries)
templates/ .txt templates (override default ones with the same filename)
```
Each builder owns its own `TemplateManager`. Later directories passed to `TemplateManager` override earlier ones on filename conflicts.
---
## Default templates
| File | Role | When used |
|------|------|-----------|
| `system_message.txt` | system | Default system prompt (overridden by config) |
| `diff_user_message.txt` | user | Diff-based generation (default mode) |
| `full_rewrite_user_message.txt` | user | Full rewrite mode |
| `full_rewrite_prompt_opt_user_message.txt` | user | Prompt optimization tasks |
| `image_user_message.txt` | user | Image generation mode |
| `evaluator_system_message.txt` | system | LLM judge (only with llm_as_judge) |
| `evaluator_user_message.txt` | user | LLM judge user message |
---
## Writing a custom builder
The most common pattern is extending `DefaultContextBuilder` and injecting extra guidance via the `{search_guidance}` placeholder. The default templates already include this slot; an empty string makes it disappear cleanly.
```python
from pathlib import Path
from skydiscover.context_builder.default import DefaultContextBuilder
from skydiscover.context_builder.utils import TemplateManager
class MyContextBuilder(DefaultContextBuilder):
def __init__(self, config):
super().__init__(config)
# load your templates on top of the defaults
default_templates = str(Path(__file__).parent.parent / "default" / "templates")
my_templates = str(Path(__file__).parent / "templates")
self.template_manager = TemplateManager(default_templates, my_templates)
def build_prompt(self, current_program, context=None, **kwargs):
context = context or {}
# format whatever the manager put into the context dict
guidance = self._format_guidance(context.get("my_key"))
return super().build_prompt(current_program, context, search_guidance=guidance, **kwargs)
def _format_guidance(self, data):
if not data:
return ""
return f"## CONTEXT\n{data}"
```
The manager populates `context["my_key"]` before calling `build_prompt()`, and sets the builder in its `__init__`:
```python
self.context_builder = MyContextBuilder(self.config)
```
Example to copy: `adaevolve/builder.py` (adds evaluator feedback, paradigm guidance, and sibling context).
---
## Registration
To make a builder available via config instead of hardcoding it in a manager, add it to `_init_context_builder()` in `search/default_discovery_controller.py`:
```python
elif template == "my_builder":
self.context_builder = MyContextBuilder(self.config)
```
Then activate with:
```yaml
prompt:
template: my_builder
```