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:

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.

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__:

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:

elif template == "my_builder":
    self.context_builder = MyContextBuilder(self.config)

Then activate with:

prompt:
  template: my_builder