# Optimization Cycle This guide walks you through the **Build → Run → Analyze → Update** optimization cycle — the core workflow for iteratively improving your Yagra workflows using AI agents. ## Overview Yagra's optimization cycle closes the loop between workflow execution and workflow improvement: ``` ┌─────────────────────────────────────────────────────────┐ │ Optimization Cycle │ │ │ │ 1. Build → Define workflow in YAML │ │ 2. Run → Execute and capture traces │ │ 3. Analyze → Identify improvement opportunities │ │ 4. Propose → Generate a YAML diff │ │ 5. Test → Validate with golden cases │ │ 6. Apply → Commit the change (or rollback) │ │ │ │ ↑ │ │ │ └─────────── iterate ─────────────────┘ │ └─────────────────────────────────────────────────────────┘ ``` All steps run locally — no external SaaS or cloud service required. ## Prerequisites Install Yagra with LLM and MCP support: ```bash uv add 'yagra[llm,mcp]' ``` Verify the installation: ```bash yagra --help ``` Set your LLM API key (example: OpenAI): ```bash export OPENAI_API_KEY=sk-... ``` ## Step 1: Build — Define Your Workflow Create a workflow YAML. Use a template to get started quickly: ```bash yagra init --template branch --output my-workflow cd my-workflow ``` Validate before proceeding: ```bash yagra validate --workflow workflow.yaml ``` Expected output: ``` ✓ ワークフローは valid です。 ``` See [Workflow YAML Reference](workflow_yaml.md) for the full schema. ## Step 2: Run & Observe — Capture Traces Enable observability when building the graph, then invoke it with `trace=True` to persist the trace to disk: ```python from yagra import Yagra from my_handlers import registry, AgentState app = Yagra.from_workflow( workflow_path="workflow.yaml", registry=registry, state_schema=AgentState, observability=True, ) result = app.invoke({"query": "What is the refund policy?"}, trace=True) print(result) ``` Traces are saved under `.yagra/traces//` as JSON files: ``` .yagra/traces/my-workflow/my-workflow_20260222T120000_a1b2c3d4.json ``` You can also retrieve the latest trace in memory without file I/O: ```python last_trace = app.get_last_trace() # WorkflowRunTrace | None ``` ## Step 3: Analyze — Identify Improvement Opportunities Use the MCP tools to analyze accumulated traces. Start the MCP server: ```bash yagra mcp ``` Then call `analyze_traces` from your AI agent or MCP client: ```json { "tool": "analyze_traces", "arguments": { "workflow_name": "my-workflow", "limit": 20 } } ``` Example response: ```json { "workflow_name": "my-workflow", "total_traces": 20, "node_summaries": [ { "node_id": "classifier", "avg_latency_ms": 342, "error_rate": 0.0 }, { "node_id": "answer_faq", "avg_latency_ms": 1820, "error_rate": 0.05, "notes": "5% of responses were empty — likely a prompt gap" } ], "suggestions": [ "answer_faq: add fallback instructions to the system prompt" ] } ``` To inspect individual traces, call `get_traces`: ```json { "tool": "get_traces", "arguments": { "workflow_name": "my-workflow", "limit": 5 } } ``` ## Step 4: Propose & Review — Generate a YAML Diff The `propose_update` tool takes the **full candidate YAML** you want to apply, not a natural-language instruction. The agent is responsible for generating the improved YAML first (e.g., by editing the prompt file or modifying the workflow), then submitting it for preview. ```json { "tool": "propose_update", "arguments": { "workflow_path": "workflow.yaml", "candidate_yaml": "version: \"1.0\"\nstart_at: classifier\n...", "reason": "Add fallback instruction to answer_faq's system prompt" } } ``` Parameters: - `workflow_path` (required): path to the current workflow YAML - `candidate_yaml` (required): full YAML content to apply - `reason` (optional): human-readable explanation of the change Example response: ```json { "workflow_path": "workflow.yaml", "is_valid": true, "issues": [], "diff": "--- a/workflow.yaml\n+++ b/workflow.yaml\n@@ -12,6 +12,8 @@\n params:\n prompt_ref: \"../prompts/support_prompts.yaml#faq\"\n+ fallback_message: \"I'm sorry, I don't have information on that topic. Please contact support.\"\n model:\n provider: openai", "reason": "Add fallback instruction to answer_faq's system prompt", "current_yaml": "...", "candidate_yaml": "..." } ``` Review the `diff` and `is_valid` fields before applying. If `is_valid` is `false`, fix the candidate YAML and call `propose_update` again. ## Step 5: Regression Test — Validate with Golden Cases Before applying the change, verify that existing behavior is preserved using golden cases. ### Save a golden case from a successful trace ```bash yagra golden save \ --trace .yagra/traces/my-workflow/my-workflow_20260222T120000_a1b2c3d4.json \ --name happy-path ``` ### Run golden tests via CLI ```bash yagra golden test --workflow workflow.yaml ``` Example output: ``` Running golden tests for workflow.yaml ✓ happy-path (2/2 nodes passed) Results: 1 passed, 0 failed ``` ### Run golden tests via MCP tool ```json { "tool": "run_golden_tests", "arguments": { "workflow_path": "workflow.yaml" } } ``` Example response: ```json { "results": [ { "case_name": "happy-path", "passed": true, "execution_path_match": true, "node_results": [ {"node_id": "classifier", "status": "pass", "strategy_used": "exact"}, {"node_id": "answer_faq", "status": "pass", "strategy_used": "structural"} ], "summary": "All 2 nodes passed" } ], "total": 1, "passed": 1, "failed": 0 } ``` If any test fails, revise the proposal before applying. ## Step 6: Apply or Rollback — Commit the Change ### Apply the proposal Once golden tests pass, apply the change by passing the same `candidate_yaml` you previewed: ```json { "tool": "apply_update", "arguments": { "workflow_path": "workflow.yaml", "candidate_yaml": "version: \"1.0\"\nstart_at: classifier\n..." } } ``` Parameters: - `workflow_path` (required): path to the workflow YAML to update - `candidate_yaml` (required): the new YAML content to write - `base_revision` (optional): expected current revision for conflict detection - `backup_dir` (optional): backup directory (default: `.yagra/backups`) The workflow YAML is updated in place. A backup is created automatically and a `backup_id` is returned. Example response: ```json { "success": true, "workflow_path": "workflow.yaml", "backup_id": "workflow_20260222T130000_a1b2c3d4", "saved_revision": "sha256:abcd1234..." } ``` ### Rollback if needed If the applied change causes issues, roll back using the `backup_id` from the `apply_update` response: ```json { "tool": "rollback_update", "arguments": { "workflow_path": "workflow.yaml", "backup_id": "workflow_20260222T130000_a1b2c3d4" } } ``` Parameters: - `workflow_path` (required): path to the workflow YAML to restore - `backup_id` (required): backup ID returned by `apply_update` - `backup_dir` (optional): backup directory (default: `.yagra/backups`) The workflow YAML is restored to the backed-up version. The current state is saved as a safety backup before overwriting. --- ## Worked Example: Improving a Translation Workflow This end-to-end example shows how to improve a simple translation workflow step by step. ### Initial Setup Create the workflow directory: ```bash mkdir translate-workflow && cd translate-workflow ``` Create `workflow.yaml`: ```yaml version: "1.0" start_at: translate end_at: - finish nodes: - id: translate handler: llm params: prompt_ref: "prompts/translate_prompts.yaml#translate" output_key: translation model: provider: openai name: gpt-4.1-mini - id: finish handler: passthrough # user-defined no-op handler (see handlers.py below) edges: - source: translate target: finish ``` Create `prompts/translate_prompts.yaml`: ```yaml translate: system: "You are a translator." user: "Translate the following text to English:\n\n{text}" ``` Create `handlers.py`: ```python from typing import TypedDict from yagra import Yagra from yagra.handlers import create_llm_handler class TranslateState(TypedDict, total=False): text: str translation: str def passthrough(state: TranslateState, params: dict) -> dict: return {} registry = { "llm": create_llm_handler(), "passthrough": passthrough, } ``` Validate: ```bash yagra validate --workflow workflow.yaml # ✓ ワークフローは valid です。 ``` ### Run & Capture Traces Create `run.py`: ```python from handlers import registry, TranslateState from yagra import Yagra app = Yagra.from_workflow( workflow_path="workflow.yaml", registry=registry, state_schema=TranslateState, observability=True, ) test_inputs = [ {"text": "こんにちは、世界!"}, {"text": "今日は天気がいいですね。"}, {"text": ""}, # edge case: empty input ] for inp in test_inputs: app.invoke(inp, trace=True) print("Traces saved to .yagra/traces/") ``` ```bash python run.py ``` ### Analyze Traces Start MCP server in a separate terminal: ```bash yagra mcp ``` Call `analyze_traces` from your agent: ```json { "tool": "analyze_traces", "arguments": {"workflow_name": "workflow", "limit": 10} } ``` The agent finds that the `translate` node returns an empty string for the empty-input case. Suggestion: add input validation instructions to the system prompt. ### Save a Golden Case ```bash yagra golden save \ --trace .yagra/traces/workflow/workflow_20260222T120000_a1b2c3d4.json \ --name normal-translation \ --strategy translate:structural ``` ### Propose an Update The agent first edits `prompts/translate_prompts.yaml` to improve the system prompt: ```yaml translate: system: | You are a professional translator. If the input text is empty, return an empty string without any additional message. user: "Translate the following text to English:\n\n{text}" ``` Then submits the updated `workflow.yaml` (which references the prompt file) to `propose_update` for preview: ```json { "tool": "propose_update", "arguments": { "workflow_path": "workflow.yaml", "candidate_yaml": "version: \"1.0\"\nstart_at: translate\nend_at:\n - finish\nnodes:\n - id: translate\n handler: llm\n params:\n prompt_ref: \"prompts/translate_prompts.yaml#translate\"\n output_key: translation\n model:\n provider: openai\n name: gpt-4.1-mini\n - id: finish\n handler: passthrough\nedges:\n - source: translate\n target: finish\n", "reason": "Update translate node's system prompt to handle empty input gracefully" } } ``` Review the `diff` in the response and confirm `is_valid: true`. Note the `candidate_yaml` — you'll pass it to `apply_update` after golden tests pass. ### Run Golden Tests ```bash yagra golden test --workflow workflow.yaml # ✓ normal-translation (1/1 nodes passed) ``` All tests pass. ### Apply the Change ```json { "tool": "apply_update", "arguments": { "workflow_path": "workflow.yaml", "candidate_yaml": "version: \"1.0\"\nstart_at: translate\n..." } } ``` The improved workflow is now live. The response includes a `backup_id` you can use with `rollback_update` if needed. Run more test cases and repeat the cycle as needed. --- ## Agent Automation For AI coding agents (Claude Code, Cursor, Copilot, etc.), you can automate the entire cycle using Yagra's MCP tools. The recommended tool call sequence is: ``` analyze_traces → identify problems in recent executions propose_update → generate a YAML diff run_golden_tests → verify existing behavior is preserved apply_update → commit the improvement (or rollback_update if tests failed) ``` For system prompt examples and a full automation guide, see: [Agent Integration Guide](../../../agent-integration-guide.md) ## Next Steps - [Workflow YAML Reference](workflow_yaml.md) — complete YAML schema - [CLI Reference](../cli_reference.md) — `yagra golden`, `yagra mcp`, and more - [Examples](../examples.md) — ready-to-run workflow examples