|
| 1 | +--- |
| 2 | +name: run-optimizations |
| 3 | +description: Run an Opik prompt optimization (opik-optimizer) for an example in this repo, and optionally promote the result to the Prompt Library. Use when the user says "optimize the prompt", "run the optimizer", "improve the prompt", "run Optimization Studio", "tune the prompt", or wants a measured better prompt for an example. Covers the metric-callable shape, reading the score delta + run link, and versioning the winner. Require a target example with an eval dataset; refuse to optimize without a dataset to score against. |
| 4 | +--- |
| 5 | + |
| 6 | +# Run a prompt optimization |
| 7 | + |
| 8 | +`opik-optimizer` searches for a better prompt by scoring candidates against a dataset with a |
| 9 | +metric. It tunes the **prompt and model params** — not the retriever. Retrieval quality is a |
| 10 | +separate concern, watched via `ContextRecall` in `run-evals`. |
| 11 | + |
| 12 | +Reference: `run_optimization` in |
| 13 | +[`optimization.py`](../../../use-cases/f1_radio_rag/src/f1_radio_rag/optimization.py); |
| 14 | +`promote` in [`prompts.py`](../../../use-cases/f1_radio_rag/src/f1_radio_rag/prompts.py); |
| 15 | +CLI [`cli.py`](../../../use-cases/f1_radio_rag/src/f1_radio_rag/cli.py) `optimize`/`promote`. |
| 16 | + |
| 17 | +## When to refuse |
| 18 | + |
| 19 | +Need a **target example with an eval dataset** to score against — optimization is meaningless |
| 20 | +without a metric over real data. If there's no dataset, route to `add-dataset-items` first. |
| 21 | + |
| 22 | +## Step 1 — Credentials gate |
| 23 | + |
| 24 | +Needs `OPIK_API_KEY` + `OPIK_WORKSPACE` and the model provider key (e.g. `ANTHROPIC_API_KEY`). |
| 25 | +Dry-run first: |
| 26 | + |
| 27 | +```bash |
| 28 | +cd use-cases/f1_radio_rag |
| 29 | +uv run f1rag optimize # DRY_RUN: prints what it would optimize |
| 30 | +export ANTHROPIC_API_KEY=... OPIK_API_KEY=... OPIK_WORKSPACE=... |
| 31 | +uv run f1rag optimize # real run: prints initial -> optimised score + run link |
| 32 | +``` |
| 33 | + |
| 34 | +## Step 2 — The metric is a plain callable |
| 35 | + |
| 36 | +Optimizer metrics are **not** the metric classes directly — they're callables |
| 37 | +`(dataset_item: dict, llm_output: str) -> float` with `__name__` set (the name shows in the |
| 38 | +Opik UI). Wrap an LLM-judge metric to score against something real: |
| 39 | + |
| 40 | +```python |
| 41 | +from opik.evaluation.metrics import AnswerRelevance |
| 42 | + |
| 43 | +def _relevance_metric(dataset_item: dict, llm_output: str) -> float: |
| 44 | + result = AnswerRelevance(model=JUDGE_MODEL).score( |
| 45 | + input=dataset_item["query"], output=llm_output, context=dataset_item["messages"], |
| 46 | + ) |
| 47 | + return result.value |
| 48 | + |
| 49 | +_relevance_metric.__name__ = "answer_relevance" |
| 50 | +``` |
| 51 | + |
| 52 | +## Step 3 — Build the prompt and run the optimizer |
| 53 | + |
| 54 | +```python |
| 55 | +from opik_optimizer import ChatPrompt, MetaPromptOptimizer |
| 56 | + |
| 57 | +prompt = ChatPrompt(name=PROMPT_NAME, system=SYSTEM_PROMPT, user=USER_TEMPLATE, model=GEN_MODEL) |
| 58 | +optimizer = MetaPromptOptimizer(model=OPTIMIZER_MODEL, n_threads=4, skip_perfect_score=False) |
| 59 | +result = optimizer.optimize_prompt( |
| 60 | + prompt=prompt, dataset=dataset, metric=_relevance_metric, max_trials=12, n_samples=8, |
| 61 | +) |
| 62 | +``` |
| 63 | + |
| 64 | +- `ChatPrompt.user` uses single-brace `{field}` placeholders that bind to dataset item fields. |
| 65 | +- `skip_perfect_score=False` forces every round even when the baseline already scores high. |
| 66 | +- `max_trials` / `n_samples` trade cost for thoroughness — keep them modest for demos. |
| 67 | + |
| 68 | +## Step 4 — Read the result |
| 69 | + |
| 70 | +- `result.initial_score` → `result.score` — the improvement (or lack of it). Report both. |
| 71 | +- `result.get_run_link()` — link to the run in Optimization Studio. Surface it. |
| 72 | +- `result.prompt.get_messages()` — the optimised messages, for promotion. |
| 73 | + |
| 74 | +## Step 5 — Promote (optional, only when asked or score improved) |
| 75 | + |
| 76 | +Saving versions the prompt in the Prompt Library; re-using the same `name` appends a version |
| 77 | +(upsert — no separate update call, no in-place edit). |
| 78 | + |
| 79 | +```python |
| 80 | +client.create_chat_prompt( |
| 81 | + name=PROMPT_NAME, |
| 82 | + messages=result.prompt.get_messages(), |
| 83 | + change_description=f"optimised score {result.score:.3f}", |
| 84 | + tags=["optimised"], |
| 85 | +) |
| 86 | +``` |
| 87 | + |
| 88 | +Don't promote a regression. If `result.score <= result.initial_score`, report it and stop — |
| 89 | +ask before saving a worse prompt over a working one. |
| 90 | + |
| 91 | +## Verify |
| 92 | + |
| 93 | +- Dry-run prints the prompt/dataset it would optimize before the real run. |
| 94 | +- Real run prints `initial -> optimised` score and a run link; open it to inspect trials. |
| 95 | +- After promote, the new version appears under the prompt name in the Opik Prompt Library UI. |
0 commit comments