orizu 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -3
- package/dist/help.js +611 -0
- package/dist/index.js +268 -15
- package/dist/skill-installer.js +180 -0
- package/package.json +3 -3
- package/scripts/vendor-orizu-skill.mjs +54 -0
- package/src/help.ts +676 -0
- package/src/index.ts +353 -15
- package/src/skill-installer.ts +260 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/__init__.py +4 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +24 -1
- package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +469 -21
- package/vendor/skills/orizu-cli/SKILL.md +186 -0
- package/vendor/skills/orizu-cli/agents/openai.yaml +4 -0
- package/vendor/skills/orizu-cli/references/building-apps.md +1461 -0
- package/vendor/skills/orizu-cli/references/building-judges.md +218 -0
- package/vendor/skills/orizu-cli/references/cli-reference.md +325 -0
- package/vendor/skills/orizu-cli/references/optimization-reports.md +174 -0
- package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +291 -0
- package/vendor/skills/orizu-cli/references/primer.md +245 -0
- package/vendor/skills/orizu-cli/references/prompt-control-plane.md +832 -0
- package/vendor/skills/orizu-cli/scripts/test-app.mjs +492 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Optimization Reports
|
|
2
|
+
|
|
3
|
+
Use this reference when writing the markdown report attached to an Orizu optimization run. The report explains what happened in one run; the skill docs explain how to interpret and act on it. A good report is diagnostic and prescriptive, not just a metric dump.
|
|
4
|
+
|
|
5
|
+
Attach the final markdown with:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
orizu optimizations finish <run-id> --best-candidate <candidate-id> --report-file ./reports/<run-id>.md
|
|
9
|
+
orizu optimizations fail <run-id> --reason "<reason>" --report-file ./reports/<run-id>.md
|
|
10
|
+
orizu optimizations cancel <run-id> --reason "<reason>" --report-file ./reports/<run-id>.md
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For budget-exhausted runs, `run-gepa` pauses the optimization before auto-promotion. Write the report from the paused run, then manually finish with `--best-candidate`, `--result-prompt-version` if promoted, and `--report-file` once the decision is made.
|
|
14
|
+
|
|
15
|
+
## Source Artifacts
|
|
16
|
+
|
|
17
|
+
Prefer the local GEPA log directory printed by `run-gepa`:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
logs/<optimization_run_id>/
|
|
21
|
+
result.json
|
|
22
|
+
evaluations.jsonl
|
|
23
|
+
reflections.jsonl
|
|
24
|
+
events.jsonl
|
|
25
|
+
trainset.json
|
|
26
|
+
valset.json
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Read them in this order:
|
|
30
|
+
|
|
31
|
+
1. `result.json` for seed score, best score, best candidate id/text, promotion id, and budget state.
|
|
32
|
+
2. `evaluations.jsonl` for fixed rows, regressions, persistent failures, per-row feedback, latency, cost, and cache state.
|
|
33
|
+
3. `reflections.jsonl` for what the reflection model saw and why each child prompt was proposed.
|
|
34
|
+
4. `events.jsonl` for iteration order, candidate lineage, Pareto updates, budget events, pauses, failures, and promotions.
|
|
35
|
+
|
|
36
|
+
If local logs are unavailable, export the server-side archive:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
orizu optimizations export <run-id> --out <run-id>.optimization.json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Use export for diagnostics because it is the portable source for per-candidate bodies, per-iteration minibatch and validation scores, candidate parent/child trajectory, score over time, prompt contexts, scorer contexts, and split metadata. Diff the seed body against the best body to understand what the optimizer actually learned.
|
|
43
|
+
|
|
44
|
+
## Report Structure
|
|
45
|
+
|
|
46
|
+
Write these sections, in this order.
|
|
47
|
+
|
|
48
|
+
### 1. Headline
|
|
49
|
+
|
|
50
|
+
Include:
|
|
51
|
+
- Seed score -> best score, absolute change, and percentage-point delta when the metric is a rate.
|
|
52
|
+
- The headline scorer name, metric key, split, scorer version, and dataset split.
|
|
53
|
+
- For judge alignment, prefer Cohen's kappa as the headline metric when labels are imbalanced. Kappa has an interpretable zero point: 0 is chance agreement, negative is worse than chance, and positive means real discrimination.
|
|
54
|
+
- Always include the confusion matrix alongside kappa. On small validation sets, the matrix movement often matters more than the single estimate.
|
|
55
|
+
- Promoted prompt version id, promotion label, and Orizu dashboard link when available.
|
|
56
|
+
- Small-n uncertainty note when `n < 30`. For example, at `n = 15`, a rough kappa standard error can be about +/- 0.25, so call out directional signals rather than overclaiming precision.
|
|
57
|
+
|
|
58
|
+
### 2. What The Optimizer Changed
|
|
59
|
+
|
|
60
|
+
Summarize the seed body -> best body diff in bullets rather than pasting a full raw diff. For each notable change, mark it as:
|
|
61
|
+
- Targeted fix: addresses a specific failure cluster found in train/validation feedback.
|
|
62
|
+
- General restructure: reorganizes the prompt, output contract, or reasoning order.
|
|
63
|
+
- Over-correction risk: may improve one cluster while hurting another.
|
|
64
|
+
|
|
65
|
+
When the reflection model added the same rules you were about to hand-write, do not hand-edit the seed just to feel useful. If GEPA can see and fix the failure from train examples, the next move is usually more budget, better feedback, or better data, not manual prompt patching. Hand-edit only when the optimizer cannot see the failure mode, such as zero train examples for that category.
|
|
66
|
+
|
|
67
|
+
### 3. Per-Row Outcome Decomposition
|
|
68
|
+
|
|
69
|
+
Decompose validation or held-out outcomes:
|
|
70
|
+
- Fixed rows: wrong before, right after. Include row ids and failure-mode tags.
|
|
71
|
+
- Regressed rows: right before, wrong after. Explain the likely cost of the new instructions.
|
|
72
|
+
- Persistent failures: wrong before and after. Classify each as one of:
|
|
73
|
+
- Data gap: failure mode underrepresented or absent in train.
|
|
74
|
+
- Contested gold: label or rationale appears questionable and needs human re-review.
|
|
75
|
+
- Beyond prompt scope: the failure needs tools, state, retrieval, or reasoning outside this prompt.
|
|
76
|
+
- Calibration overshoot: instructions pushed too far in one direction.
|
|
77
|
+
|
|
78
|
+
For judge optimization, include flag/ok recall and precision alongside the confusion matrix. A jump in rare-class recall can be more important than a modest kappa movement.
|
|
79
|
+
|
|
80
|
+
### 4. Optimizer Health Signals
|
|
81
|
+
|
|
82
|
+
Report:
|
|
83
|
+
- Pareto frontier size. A frontier of 1 or 2 means limited parent diversity.
|
|
84
|
+
- Acceptance rate by iteration. Many rejections suggest reflection is exploring but not finding hills; many acceptances suggest it is climbing a real signal.
|
|
85
|
+
- Budget state. If budget was exhausted, the run paused before auto-promotion; manually decide whether to promote/finish. If max iterations was reached with budget remaining, the run likely converged under the configured search.
|
|
86
|
+
- `scoreOverTime` / validation trajectory. Still climbing at the end suggests raising budget or iterations. Plateaued curves point toward scorer, feedback, or data work upstream.
|
|
87
|
+
- Reflection model, inference model, scorer model, provider settings, minibatch size, resolved thread count, budget preset/limit, candidate selection strategy, seed, and cache settings.
|
|
88
|
+
|
|
89
|
+
### 5. Recommendations
|
|
90
|
+
|
|
91
|
+
Pick concrete next steps and state why each should help. Do not pattern-match from the final score alone.
|
|
92
|
+
|
|
93
|
+
Common recommendations:
|
|
94
|
+
- Increase budget: budget was binding, acceptance rate was healthy, and row analysis shows remaining fixable headroom.
|
|
95
|
+
- Increase minibatch size: the positive/flag class is rare, small batches miss the rare class, or reflection looks noisy.
|
|
96
|
+
- Multi-seed or broaden parent diversity: Pareto frontier stayed at 1 or 2 candidates.
|
|
97
|
+
- Switch reflection model or template: minibatch acceptance is healthy but validation gains do not generalize.
|
|
98
|
+
- Change metric or scorer contract: metric movement diverges from human judgment.
|
|
99
|
+
- Improve scorer feedback: reflection saw generic feedback and had to guess the failure mode.
|
|
100
|
+
- Data work upstream: persistent failures cluster on patterns missing from train.
|
|
101
|
+
- Gold label re-review: persistent failures cluster on single-reviewer rows, generic rationales, or rationales that contradict the label.
|
|
102
|
+
|
|
103
|
+
### 6. What Not To Do
|
|
104
|
+
|
|
105
|
+
List unsupported moves so the next agent does not burn cycles. Examples:
|
|
106
|
+
- Do not hand-edit the prompt when the best candidate already learned the intended failure rules.
|
|
107
|
+
- Do not raise budget when score over time plateaued and persistent failures are data/gold issues.
|
|
108
|
+
- Do not switch metrics because one small validation result looks noisy; first measure variance across repeated runs or larger samples.
|
|
109
|
+
- Do not optimize against a scorer whose feedback is too generic for reflection to learn from.
|
|
110
|
+
|
|
111
|
+
### 7. Links And Reproducibility
|
|
112
|
+
|
|
113
|
+
End with:
|
|
114
|
+
- Run id and dashboard link.
|
|
115
|
+
- Local log path or export artifact path.
|
|
116
|
+
- Prompt/scorer/runner/optimizer version ids.
|
|
117
|
+
- Dataset version id, split set id, train/validation split names, row counts, and random seed.
|
|
118
|
+
- Commands used to start/export/finish the run, with secrets omitted.
|
|
119
|
+
- Links to relevant skill sections, especially `optimization-with-gepa.md`, `prompt-control-plane.md`, `building-judges.md`, and this report guide.
|
|
120
|
+
|
|
121
|
+
## Interpretation Rules
|
|
122
|
+
|
|
123
|
+
### Pre-Flight Checks
|
|
124
|
+
|
|
125
|
+
Before launching GEPA, rerun the seed locally on validation and decompose the baseline by headline metric plus confusion matrix. Do not trust reported lift until the seed was measured through the same runner/scorer path.
|
|
126
|
+
|
|
127
|
+
Confirm train/validation class balance and failure-mode coverage. If train has zero examples of a category, GEPA cannot learn that category from reflection; call it data work, not optimizer failure.
|
|
128
|
+
|
|
129
|
+
Set an explicit expectation before the run: for example, "if best kappa does not beat 0.35 on validation and rare-class recall does not improve, the bottleneck is data/scorer feedback."
|
|
130
|
+
|
|
131
|
+
### Budget Versus Iteration Cap
|
|
132
|
+
|
|
133
|
+
Default budget presets are DSPy-style metric-call budgets. `light`, `medium`, and `heavy` map to DSPy's `auto="light"`, `auto="medium"`, and `auto="heavy"` scales; `auto` is treated as the balanced `medium` preset. The final metric-call limit scales with validation-set size, rather than using fixed tiny constants. `--max-metric-calls` overrides this directly, while `--max-full-evals` is converted to a metric-call budget over `train + validation` rows.
|
|
134
|
+
|
|
135
|
+
Budget is checked only between iterations. Once an iteration starts, `run-gepa` completes the parent minibatch, child minibatch, and full validation eval for an accepted child even if that overshoots the nominal budget. If a run ends as `budget_exhausted`, it pauses and does not auto-promote. If auto-promotion matters, either increase budget to fit the iteration target or lower `--max-iterations` to fit the budget.
|
|
136
|
+
|
|
137
|
+
### Reflection Signal Quality
|
|
138
|
+
|
|
139
|
+
The scorer `feedback` string is what the reflection model sees per row. Generic feedback such as only a numeric error forces the model to infer the failure mode. Rich feedback should include the gold rationale, model/judge rationale, failure label, and the concrete disagreement.
|
|
140
|
+
|
|
141
|
+
Frame scorer output as a score where higher is better. Avoid naming the value as a loss/error while also setting `higher_is_better: true`; that gives reflection mixed directional signals.
|
|
142
|
+
|
|
143
|
+
Drop fields that are not intended to be scored. "Informational; not scored" columns create noise that reflection can overfit.
|
|
144
|
+
|
|
145
|
+
### Binary Verdict Versus Continuous Score
|
|
146
|
+
|
|
147
|
+
If gold is binary, ask the judge for a binary class and map it to numeric score after parsing. Continuous scores let a judge hedge and earn partial credit while being directionally wrong. For binary judge alignment, report the verdict confusion matrix and kappa, not just average continuous score.
|
|
148
|
+
|
|
149
|
+
### Kappa And Confusion Matrices
|
|
150
|
+
|
|
151
|
+
Use kappa as the headline metric for binary judge alignment when class imbalance matters. It corrects for majority-class baselines and has a meaningful chance-agreement zero point.
|
|
152
|
+
|
|
153
|
+
Always report the confusion matrix with kappa. On small `n`, kappa is unstable, and the operational change may be in a cell shift such as false negatives dropping or rare-class recall jumping.
|
|
154
|
+
|
|
155
|
+
### Aggregate Set Scorers
|
|
156
|
+
|
|
157
|
+
Set scorers are aggregate metric contracts today, not whole-split runner execution contracts. `runners exec --scorer-version` invokes the scorer runner per row even when the scorer manifest says `mode: "set"`.
|
|
158
|
+
|
|
159
|
+
For aggregate metrics such as kappa, accuracy, precision, recall, or F1:
|
|
160
|
+
1. Run or reuse row-level judge results.
|
|
161
|
+
2. Compute the aggregate locally.
|
|
162
|
+
3. Submit the score run with `scoreValue`, `diagnostics`, `feedbackSummary`, row counts, and optional `resultsJsonl` via the score submission API.
|
|
163
|
+
|
|
164
|
+
Temporary CLI limitation: `orizu scores submit <file>` uploads row results as JSONL and does not preserve top-level aggregate fields from a JSON file. Until aggregate score flags or a set-scorer execution command lands, POST directly to `/api/cli/scores/submit?project=<team/project>` when you need to preserve aggregate JSON fields.
|
|
165
|
+
|
|
166
|
+
### Gold Label Audit
|
|
167
|
+
|
|
168
|
+
Gold label audit comes before more optimization when persistent failures cluster on rows with:
|
|
169
|
+
- single-reviewer labels,
|
|
170
|
+
- generic or missing rationales,
|
|
171
|
+
- rationales that contradict the label,
|
|
172
|
+
- contentious domain judgments.
|
|
173
|
+
|
|
174
|
+
Fixing gold can move headline metrics more than another optimizer run. Call this out clearly instead of hiding it under "model still fails."
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# Optimization With GEPA
|
|
2
|
+
|
|
3
|
+
How to optimize text candidates against validated judges/scorers. For Orizu-tracked runs, prefer `prompt-control-plane.md` and the bundled `orizu optimizations run-gepa` command when optimizing one text candidate. Use this reference for GEPA mechanics, custom optimizer implementations, and optional DSPy context for customers already using DSPy.
|
|
4
|
+
|
|
5
|
+
## Inputs
|
|
6
|
+
|
|
7
|
+
You should arrive here with:
|
|
8
|
+
- One or more **validated judges** (TPR > 90%, TNR > 90% on a held-out test set).
|
|
9
|
+
- A **dataset** of inputs to optimize against — usually the same exported labels, plus any harder cases you've added since.
|
|
10
|
+
- A **starting prompt or program** for the LLM application you want to improve.
|
|
11
|
+
|
|
12
|
+
If you don't have a validated judge, stop. Optimizing against an unvalidated judge means you'll hill-climb on a noisy or biased signal — Goodhart's law in action.
|
|
13
|
+
|
|
14
|
+
## Why GEPA-Style Optimization
|
|
15
|
+
|
|
16
|
+
- **GEPA** is a gradient-free prompt optimizer that uses an LLM to propose prompt edits, scores candidates against your metric, and keeps the best. It's well-suited to prompt-level optimization where you can't backprop through the model.
|
|
17
|
+
- In Orizu, runners execute candidates, scorers produce metrics/feedback, and optimization events make the loop inspectable and promotable.
|
|
18
|
+
- DSPy is not part of Orizu's bundled optimizer. Treat DSPy examples here as an external integration pattern only.
|
|
19
|
+
|
|
20
|
+
## Workflow at a glance
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
┌─────────────────────────────┐
|
|
24
|
+
│ Dataset (labeled inputs) │
|
|
25
|
+
└──────────┬──────────────────┘
|
|
26
|
+
│
|
|
27
|
+
▼
|
|
28
|
+
┌─────────────────────────────┐ ┌─────────────────────┐
|
|
29
|
+
│ Runner (your LLM app call) │ ◄── │ Validated scorers │
|
|
30
|
+
│ file-contract execution │ │ row/set metrics │
|
|
31
|
+
└──────────┬──────────────────┘ └──────────┬──────────┘
|
|
32
|
+
│ │
|
|
33
|
+
└───────────────┬────────────────────┘
|
|
34
|
+
▼
|
|
35
|
+
┌─────────────────────────┐
|
|
36
|
+
│ GEPA optimization run │
|
|
37
|
+
│ (proposes, scores, keeps)│
|
|
38
|
+
└──────────┬──────────────┘
|
|
39
|
+
▼
|
|
40
|
+
┌─────────────────────────┐
|
|
41
|
+
│ Optimized prompt │
|
|
42
|
+
│ Compare before/after │
|
|
43
|
+
└─────────────────────────┘
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Orizu-tracked optimization
|
|
47
|
+
|
|
48
|
+
Use the prompt control plane when you want runs, candidates, score charts, Pareto/frontier views, and promotions in Orizu:
|
|
49
|
+
|
|
50
|
+
1. Push the candidate runner and prompt/judge prompt.
|
|
51
|
+
2. Register a row scorer for reflection. GEPA reflection requires row-level feedback.
|
|
52
|
+
3. Snapshot a dataset version and create a train/validation split set.
|
|
53
|
+
4. Use `orizu optimizations run-gepa` for the common text-candidate case, or `orizu optimizations start` plus event logging for a custom optimizer.
|
|
54
|
+
5. Use set scorers for selection/tracked reporting when the meaningful metric is batch-level; execute builtin set scorers with `orizu scorers exec` or submit precomputed aggregates with `orizu scores submit --aggregate`.
|
|
55
|
+
6. Promote only candidates that passed validation.
|
|
56
|
+
7. Write and attach an optimization report from the local logs or export artifact; see `optimization-reports.md`.
|
|
57
|
+
|
|
58
|
+
The bundled Orizu GEPA-style optimizer supports configurable budget, minibatch size (default 3), candidate selection strategy, reflection model/template, reflection provider settings, evaluation caching, and optional auto-promotion. It redacts row snapshots and reflection text by default; only pass `--log-row-snapshots` when raw data in event logs is intentional.
|
|
59
|
+
|
|
60
|
+
Reflection output contract:
|
|
61
|
+
- The reflective LM's final text is used verbatim as the next candidate prompt.
|
|
62
|
+
- The default reflection prompt asks for only the complete updated prompt body. Do not ask the model to wrap the candidate in markdown fences or tags; real prompts often contain those characters.
|
|
63
|
+
- Put provider-native reasoning controls in `--reflection-provider-settings <json|@file>`, not in the prompt body. For OpenAI reasoning models, use a shape such as `{"reasoning":{"effort":"medium","summary":"auto"}}`. For Anthropic Claude models with thinking controls, use a shape such as `{"thinking":{"type":"adaptive","display":"omitted"},"output_config":{"effort":"medium"}}`.
|
|
64
|
+
- Reflection max-token limits are explicit. `--reflection-max-tokens <n>` maps to Anthropic `max_tokens` and OpenAI `max_output_tokens`; Anthropic native Messages reflection requires it, while OpenAI may omit it when no cap is desired.
|
|
65
|
+
|
|
66
|
+
Full command syntax and event contracts: `prompt-control-plane.md`.
|
|
67
|
+
|
|
68
|
+
## Step 1: Wrap your application as an Orizu runner
|
|
69
|
+
|
|
70
|
+
For Orizu-tracked optimization, the candidate runner receives one dataset row and one candidate text body through the file contract. The scorer runner receives the source row plus candidate output and returns a score and feedback. See `prompt-control-plane.md` for the exact runner I/O shape.
|
|
71
|
+
|
|
72
|
+
Keep the runner close to the production inference path: same model family, temperature, tools, parsing, and output schema wherever possible.
|
|
73
|
+
|
|
74
|
+
## Optional: DSPy program wrapper
|
|
75
|
+
|
|
76
|
+
If a customer already uses DSPy, they can express their application as a `dspy.Module` and build a custom optimizer around it. This is not how Orizu's bundled `orizu-gepa` package runs.
|
|
77
|
+
|
|
78
|
+
Express the LLM call as a `dspy.Module` with a `Signature`:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
import dspy
|
|
82
|
+
|
|
83
|
+
class SupportAgentSignature(dspy.Signature):
|
|
84
|
+
"""Generate a support response for a customer message.
|
|
85
|
+
|
|
86
|
+
The response should resolve the issue when possible, escalate when the
|
|
87
|
+
situation requires human handling, and always include a case reference.
|
|
88
|
+
"""
|
|
89
|
+
customer_message: str = dspy.InputField()
|
|
90
|
+
conversation_context: str = dspy.InputField()
|
|
91
|
+
response: str = dspy.OutputField()
|
|
92
|
+
should_escalate: bool = dspy.OutputField()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class SupportAgent(dspy.Module):
|
|
96
|
+
def __init__(self):
|
|
97
|
+
super().__init__()
|
|
98
|
+
self.respond = dspy.ChainOfThought(SupportAgentSignature)
|
|
99
|
+
|
|
100
|
+
def forward(self, customer_message: str, conversation_context: str):
|
|
101
|
+
return self.respond(
|
|
102
|
+
customer_message=customer_message,
|
|
103
|
+
conversation_context=conversation_context,
|
|
104
|
+
)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
If your real application is multi-step (retrieval + generation + tool use), build a multi-Module program. GEPA can optimize each step's prompt independently.
|
|
108
|
+
|
|
109
|
+
## Step 2: Register scorers
|
|
110
|
+
|
|
111
|
+
For Orizu, register scorers with readable names, directionality, row/set mode, and dataset requirements. Row scorers should return numeric `score` and textual `feedback`; feedback is what GEPA reflection consumes. Set scorers can be selection or tracked scorers, but they should not be used as reflection scorers.
|
|
112
|
+
|
|
113
|
+
If using DSPy externally, a metric takes `(example, prediction, trace=None)` and returns a number. Wrap each validated judge:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
def escalation_metric(example, prediction, trace=None) -> float:
|
|
117
|
+
judge_result = run_escalation_judge(
|
|
118
|
+
input={"customer_message": example.customer_message,
|
|
119
|
+
"conversation_context": example.conversation_context},
|
|
120
|
+
output={"response": prediction.response,
|
|
121
|
+
"should_escalate": prediction.should_escalate},
|
|
122
|
+
)
|
|
123
|
+
return 1.0 if judge_result["pass"] else 0.0
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def case_ref_metric(example, prediction, trace=None) -> float:
|
|
127
|
+
return 1.0 if has_case_reference(prediction.response) else 0.0
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def combined_metric(example, prediction, trace=None) -> float:
|
|
131
|
+
# Equal weighting; adjust if some failure modes are more critical.
|
|
132
|
+
return (escalation_metric(example, prediction) +
|
|
133
|
+
case_ref_metric(example, prediction)) / 2
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Weighting note:** if one failure mode is much more costly (escalation miss = lost trust; missing case ref = annoyance), weight accordingly. Don't hide critical failures inside an averaged score.
|
|
137
|
+
|
|
138
|
+
## Step 3: Build the dataset
|
|
139
|
+
|
|
140
|
+
Convert your labeled export into `dspy.Example` objects:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
import json
|
|
144
|
+
import dspy
|
|
145
|
+
|
|
146
|
+
def load_examples(path: str) -> list[dspy.Example]:
|
|
147
|
+
examples = []
|
|
148
|
+
with open(path) as f:
|
|
149
|
+
for line in f:
|
|
150
|
+
row = json.loads(line)
|
|
151
|
+
ex = dspy.Example(
|
|
152
|
+
customer_message=row["customer_message"],
|
|
153
|
+
conversation_context=row["conversation_context"],
|
|
154
|
+
# Outputs are unused as targets here — judges produce the signal.
|
|
155
|
+
).with_inputs("customer_message", "conversation_context")
|
|
156
|
+
examples.append(ex)
|
|
157
|
+
return examples
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
examples = load_examples("./labels.jsonl")
|
|
161
|
+
trainset, devset = examples[:int(0.7 * len(examples))], examples[int(0.7 * len(examples)):]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Step 4: Run GEPA-Style Optimization
|
|
165
|
+
|
|
166
|
+
For Orizu, use `orizu optimizations run-gepa` first. It starts the run, fetches candidate/scorer contexts, executes local runners, logs seed validation, minibatches, reflection, child candidates, validation, Pareto updates, and optionally promotes.
|
|
167
|
+
|
|
168
|
+
`run-gepa` also writes a local trace directory by default:
|
|
169
|
+
|
|
170
|
+
```text
|
|
171
|
+
logs/<optimization_run_id>/
|
|
172
|
+
run.json
|
|
173
|
+
prompt_context.json
|
|
174
|
+
scorer_context.json
|
|
175
|
+
trainset.json
|
|
176
|
+
valset.json
|
|
177
|
+
events.jsonl
|
|
178
|
+
evaluations.jsonl
|
|
179
|
+
reflections.jsonl
|
|
180
|
+
result.json
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Use this directory as the primary artifact for coding-agent analysis. It contains the full row inputs, model outputs, scores, feedback, scorer responses, reflection prompts, reflection responses, candidate text, and final result. Override the root with `--log-dir <dir>`; disable persistence with `--no-local-log` only when raw rows/reflection context must not be written to disk.
|
|
184
|
+
|
|
185
|
+
If the local log is missing or the run happened elsewhere, export the server-side archive:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
orizu optimizations export <optimization-run-id> --out ./optimization.json
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Export returns one JSON object with raw events plus derived seed-vs-best, Pareto frontier, candidates, score-over-time, iterations, minibatch rows, validation rows, scorer context, prompt versions, and dataset split information. It fetches all optimization events and rehydrates row inputs from dataset artifacts when possible. Server events redact row snapshots and reflection prompts by default, but bundled `run-gepa` includes reflection responses in the event stream.
|
|
192
|
+
|
|
193
|
+
After the run ends, write a markdown report before the context is lost. Use `optimization-reports.md` for the report structure: headline score and confusion matrix, what changed, per-row fixed/regressed/persistent failures, optimizer health, recommendations, and what not to do next.
|
|
194
|
+
|
|
195
|
+
DSPy GEPA example for customers already on DSPy:
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from dspy.teleprompt import GEPA
|
|
199
|
+
|
|
200
|
+
# Configure the LM that DSPy uses for both the program and the optimizer.
|
|
201
|
+
dspy.configure(lm=dspy.LM("openai/gpt-4o", temperature=0.0))
|
|
202
|
+
|
|
203
|
+
program = SupportAgent()
|
|
204
|
+
|
|
205
|
+
optimizer = GEPA(
|
|
206
|
+
metric=combined_metric,
|
|
207
|
+
auto="medium", # GEPA budget preset; "light" / "medium" / "heavy"
|
|
208
|
+
reflection_lm=dspy.LM("openai/gpt-4o", temperature=0.7),
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
optimized_program = optimizer.compile(
|
|
212
|
+
student=program,
|
|
213
|
+
trainset=trainset,
|
|
214
|
+
valset=devset,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
optimized_program.save("./optimized_support_agent.json")
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
GEPA will:
|
|
221
|
+
1. Run the program on `trainset`, score with `metric`.
|
|
222
|
+
2. Use `reflection_lm` to propose prompt edits based on failures.
|
|
223
|
+
3. Score candidate prompts on the trainset, keep the best.
|
|
224
|
+
4. Validate on `valset` to avoid overfitting.
|
|
225
|
+
|
|
226
|
+
Orizu's bundled optimizer is intentionally narrower than DSPy GEPA today: text candidates only, local runner/scorer directories, and Orizu event logging built in.
|
|
227
|
+
|
|
228
|
+
## Step 5: Compare before / after on a held-out set
|
|
229
|
+
|
|
230
|
+
This is the step teams skip and regret.
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
def evaluate(program, examples) -> dict:
|
|
234
|
+
scores = []
|
|
235
|
+
per_metric = {"escalation": [], "case_ref": []}
|
|
236
|
+
for ex in examples:
|
|
237
|
+
pred = program(**ex.inputs())
|
|
238
|
+
per_metric["escalation"].append(escalation_metric(ex, pred))
|
|
239
|
+
per_metric["case_ref"].append(case_ref_metric(ex, pred))
|
|
240
|
+
scores.append(combined_metric(ex, pred))
|
|
241
|
+
return {
|
|
242
|
+
"combined": sum(scores) / len(scores),
|
|
243
|
+
"escalation": sum(per_metric["escalation"]) / len(examples),
|
|
244
|
+
"case_ref": sum(per_metric["case_ref"]) / len(examples),
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
# Hold out a fresh set the optimizer never saw.
|
|
249
|
+
held_out = load_examples("./held-out.jsonl")
|
|
250
|
+
|
|
251
|
+
before = evaluate(SupportAgent(), held_out)
|
|
252
|
+
after = evaluate(optimized_program, held_out)
|
|
253
|
+
|
|
254
|
+
print(f"Before: {before}")
|
|
255
|
+
print(f"After: {after}")
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Read the per-metric numbers, not just the combined score.** A combined improvement of +5 points might hide a regression on one failure mode. If any individual metric drops, investigate before shipping.
|
|
259
|
+
|
|
260
|
+
## Step 6: Ship and feed the loop
|
|
261
|
+
|
|
262
|
+
If the optimized prompt holds up on held-out:
|
|
263
|
+
- Replace the production prompt.
|
|
264
|
+
- Sample new production traces over the next week.
|
|
265
|
+
- Upload them as a new dataset (`primer.md` Step 1).
|
|
266
|
+
- Annotate failures the optimized system *now* exhibits — they'll be different from the ones the previous version had.
|
|
267
|
+
- Build judges for the new failure modes if they're frequent enough.
|
|
268
|
+
- Re-optimize.
|
|
269
|
+
|
|
270
|
+
Each pass through the loop reveals the next layer.
|
|
271
|
+
|
|
272
|
+
## Common pitfalls
|
|
273
|
+
|
|
274
|
+
- **Optimizing against an unvalidated judge.** You'll improve the metric and degrade the system. Always validate first.
|
|
275
|
+
- **No held-out comparison.** "It's better, look at the metric" without a held-out set is meaningless — GEPA will overfit if you let it.
|
|
276
|
+
- **Hiding regressions in the average.** Track per-failure-mode metrics, not just combined.
|
|
277
|
+
- **Over-budgeting GEPA.** Heavy budgets give diminishing returns and burn LM spend. Start with `auto="light"`, scale up only if needed.
|
|
278
|
+
- **Ignoring temperature.** Run optimization with the same LM config (model, temperature) you use in production. Optimizing against gpt-4o at temp=0 doesn't transfer to gpt-4o-mini at temp=0.7.
|
|
279
|
+
- **Recreating Orizu logging by hand for text prompts.** Use `orizu optimizations run-gepa` unless the optimizer is genuinely custom.
|
|
280
|
+
|
|
281
|
+
## Checklist
|
|
282
|
+
|
|
283
|
+
Before declaring an optimization run successful:
|
|
284
|
+
|
|
285
|
+
- [ ] Each metric is backed by a validated judge (TPR/TNR > 90%)
|
|
286
|
+
- [ ] Train / val / held-out splits, and the held-out set is genuinely untouched during optimization
|
|
287
|
+
- [ ] Per-metric numbers reviewed (not just combined)
|
|
288
|
+
- [ ] Same LM/temperature in eval as in production
|
|
289
|
+
- [ ] Optimization report written from logs/export and attached with `--report-file` when the run is finished, failed, or cancelled
|
|
290
|
+
- [ ] Optimized program saved/version-controlled before shipping
|
|
291
|
+
- [ ] Plan in place to sample new traces post-deploy and run the loop again
|