| # 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 |
| ``` |
|
|