Skip to content
Back to skills

2522 Build Program 6794f052

ASecurity

- **Inside GEPA (the `gepa` library)**, candidates are plain dicts: - `candidate: dict[str, str]` mapping **component names** → **text blobs**. - For normal predictors: `{"predictor_name": "new instruction text", ...}`. - For tool-optimized ReAct modules: special entries like `{"tool_module:<extract_predictor_name>": "<JSON string with instructions + tools>"}`.

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
developmentpythonreact

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 2522-build_program_6794f052 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 2522 Build Program 6794f052?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 2522 Build Program 6794f052
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-2522-build-program-6794f052/badge)](https://www.skillsdirectory.com/skills/tools-only-2522-build-program-6794f052)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# How GEPA Candidate JSON Becomes a DSPy Program

## High-level flow

- **Inside GEPA (the `gepa` library)**, candidates are plain dicts:
  - `candidate: dict[str, str]` mapping **component names** → **text blobs**.
  - For normal predictors: `{"predictor_name": "new instruction text", ...}`.
  - For tool-optimized ReAct modules: special entries like  
    `{"tool_module:<extract_predictor_name>": "<JSON string with instructions + tools>"}`.

- **In DSPy's GEPA wrapper** (`dspy/teleprompt/gepa/gepa.py`):
  - Seed candidate for GEPA is built from the original `student` via `_build_seed_candidate`.
  - GEPA runs and returns a `GEPAResult` with `candidates` and `best_candidate`.
  - DSPy then **post-processes** the best candidate:

    ```python
    new_prog = adapter.build_program(gepa_result.best_candidate)
    ```

  - If `track_stats=True`, it also calls `DspyGEPAResult.from_gepa_result`, which maps **all** raw candidates to actual DSPy programs via `adapter.build_program(c)`.

So: **`Result.best_candidate` is turned into a DSPy program by `DspyAdapter.build_program` as a final post-processing step.**

---

## What `build_program` actually does

In `dspy/teleprompt/gepa/gepa_utils.py`:

- **Clone original student**: `new_prog = self.student.deepcopy()`.
- **Predictor text**:
  - All non-`tool_module` keys become `predictor_candidates[name] = instruction_string`.
  - For tool-optimized entries (`tool_module:...`), the JSON is parsed; any string-valued entries keyed by predictor name are merged into `predictor_candidates`.
- **Signatures**:
  - For each predictor in `new_prog.named_predictors()`, it calls  
    `pred.signature = pred.signature.with_instructions(...)`.
  - This **only swaps the instructions field**; the rest of the signature (input/output fields, types, and any stored metadata) is preserved.
- **Tools** (when `enable_tool_optimization=True`):
  - `_update_tool_descriptions` finds all `Tool` objects in the module tree and updates:
    - `tool.desc`
    - `tool.args[arg_name]["description"]` when provided.

After `build_program`, you have a normal **DSPy `Module`** with full signatures, any pre-existing demos, and updated instructions/tool descriptions.

### What build_program actually does
In `dspy/teleprompt/gepa/gepa_utils.py`:
```python
def build_program(self, candidate: dict[str, str]):
    new_prog = self.student.deepcopy()

    # 1) Start with plain string instructions
    predictor_candidates = {k: v for k, v in candidate.items() if not k.startswith(TOOL_MODULE_PREFIX)}

    tool_candidates = {}
    if self.enable_tool_optimization:
        for key, value in candidate.items():
            if not key.startswith(TOOL_MODULE_PREFIX):
                continue

            config = json.loads(value)

            for pred_name, instruction in config.items():
                if isinstance(instruction, str):
                    predictor_candidates[pred_name] = instruction

            tool_candidates.update(config.get("tools", {}))

    # 2) Update predictor instructions
    for name, pred in new_prog.named_predictors():
        if name in predictor_candidates:
            pred.signature = pred.signature.with_instructions(predictor_candidates[name])

    # 3) Update tool descriptions (ReAct tool config)
    if tool_candidates:
        self._update_tool_descriptions(new_prog, tool_candidates)

    return new_prog
```

---

## How this becomes "DSPy-style JSON" and how `program.load` fits in

The **saving/loading** mechanism is generic DSPy, not GEPA-specific:

- `Module.save(path)` (from `BaseModule.save`) persists **state** (parameters, including instructions and demos) to:
  - `path.json` or `path.pkl` when `save_program=False`.
- `Module.save(path, save_program=True)` persists a **full program** (architecture + state) as:
  - Directory `path/` containing `program.pkl` and `metadata.json`.

- **For state-only JSON**:
  - You create a base program with the same architecture, then call `program.load("state.json")` to load its saved instructions/demos/etc.
- **For full-program directories**:
  - You use `dspy.utils.saving.load("dir", allow_pickle=True)` to get back the exact module.
- `teleprompt/utils.save_candidate_program` uses `program.save(...)` to dump candidate programs, and later code calls `trial_program.load(trial["program_path"])` to restore them.

In the GEPA integration, **nothing special is added** on top of this: once `new_prog = adapter.build_program(...)` exists, you can call `.save()` yourself, and later `load()` behaves exactly like with any other DSPy program.

---

## Where do DEMOS come from?

**Short answer:** GEPA **does not create demos at all.** It preserves whatever demos the `student` already has; it only evolves text instructions (and optionally tool descriptions).

Details:

- GEPA's adapter builds a **reflective dataset** for the instruction proposer (`make_reflective_dataset`) using trajectories. These `ReflectiveExample` entries are **temporary supervision** fed into the reflection LLM (via `InstructionProposalSignature` or `ToolProposer`) to generate new instructions; they are **not stored as `.demos`** on the final program.

- The **only place demos are systematically created** in this codebase is in the few-shot / bootstrap utilities (e.g. `create_n_fewshot_demo_sets` in `dspy/teleprompt/utils.py`), which is separate from GEPA.

**Therefore:**

- If your `student` was produced by something like `BootstrapFewShot` (or already had `.demos` set), those demos are **carried through** because `build_program` deep-copies the student and only touches `signature.instructions` and tools.
- GEPA's optimization itself **does not synthesize or modify demos**; it only uses reflective datasets internally to improve instructions.

---

## Summary

- **GEPA candidate JSON** = dict mapping component names to text (or JSON for tool modules).
- At the end, **`gepa_result.best_candidate` is turned into a DSPy `Module`** via `DspyAdapter.build_program`, which:
  - Clones the original student,
  - Rewrites `signature.instructions` per predictor,
  - Optionally updates tool descriptions.
- To get a **DSPy-style JSON file**, you then call `new_prog.save("program.json")` or `new_prog.save("prog_dir", save_program=True)`. Later you restore with either `program.load("program.json")` (state-only) or `dspy.load("prog_dir", allow_pickle=True)` (full program).
- **DEMOS**:
  - Are **not created by GEPA**.
  - Whatever demos were present on the input `student` remain on the optimized program.
  - GEPA's internal reflective datasets are separate and not persisted as demos.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.