import type { PromptDef } from "./index.js"; /** * V7 §3.2 — Reflection scorer. * * Given an L1 trace (state, action, outcome, reflection_text), return a * quality score α_t ∈ [0, 1] and a boolean `usable` flag. The facade parses * the JSON output; we validate structure at the call site. */ export const REFLECTION_SCORE_PROMPT: PromptDef = { id: "reflection.score", version: 2, description: "Score an agent reflection for quality and usability, with full-step context.", system: `You are a strict reviewer of agent self-reflections. You see the FULL context of one agent step: - STATE — what the agent saw before acting (user prompt, prior observation) - THINKING — the LLM's own native chain-of-thought for this step, if any (Claude extended-thinking, pi-ai ThinkingContent). Empty when the model didn't emit thinking this turn. - ACTION — what the agent produced (assistant text output) - TOOL_CALLS — tools the agent invoked this step, with inputs and outputs (or errors). Tool usage + outcomes are part of the action chain and carry their own signal about what the agent did. - OUTCOME — the final observable result of the step (last tool outcome or "(assistant-only step)" for pure text turns) - REFLECTION — the text being graded: the agent's first-person explanation of WHY it acted this way and WHAT it learned. Score the REFLECTION on four axes, combined into ONE number α ∈ [0, 1]: 1. faithfulness — does the reflection match what ACTUALLY happened across THINKING + ACTION + TOOL_CALLS + OUTCOME? 2. causal insight — does it identify why the action / tool choice worked or failed? Bonus when it connects the model's visible THINKING to the resulting action. 3. transferability — does it surface a lesson useful on a similar future task? 4. concreteness — are the details specific (real command names, real error messages, real decisions) rather than generic platitudes like "I should do better"? Rules: - THINKING and TOOL_CALLS are first-class evidence for grading α — a reflection that ignores a visible thinking chain or misreports a tool call should score LOW on faithfulness. - TOOL_CALLS that errored are strong signal: the reflection should name the error and what it implied. Missing that is a faithfulness penalty. - An empty / purely-tautological reflection → α = 0, usable = false. - α ≥ 0.4 AND reflection non-tautological → usable = true; else false. Return JSON: { "alpha": 0.0-1.0, "usable": true | false, "reason": "one-sentence justification" }`, }; /** * V7 §3.2 — *Batched* reflection synthesis + α scoring. * * One LLM call per episode instead of N synth + N α calls. The LLM sees the * complete causal chain (every step in order, including the final outcome), * which lets it write better-grounded reflections than per-step grounded * ones — V7 §3.2.3 axes "causal_insight" and "transferability" benefit * directly from the wider context window. * * Activated by `algorithm.capture.batchMode: "auto" | "per_episode"` in * `core/config`. The dispatcher in `core/capture/capture.ts` also enforces * `algorithm.capture.batchThreshold` so very long episodes degrade to the * per-step path instead of overflowing the prompt window. * * Output schema is documented inside the prompt — `core/capture/batch-scorer.ts` * validates each entry and falls back to per-step on any malformed value. */ export const BATCH_REFLECTION_PROMPT: PromptDef = { id: "reflection.batch", version: 3, description: "Score (and optionally synthesize) reflections for an entire episode in one call, with full thinking + tool-call context.", system: `You are reviewing every step of one AI agent episode in a single pass. INPUT: a JSON array under "steps". Each entry has: - "idx": step index (integer, 0-based, sequential) - "state": what the agent saw before acting (user prompt / prior obs) - "thinking": the LLM's native chain-of-thought for this step (Claude extended-thinking / pi-ai ThinkingContent). May be empty string. - "action": what the agent chose to do (assistant text) - "tool_calls": the tools invoked, with inputs + outputs + errorCode. May be empty array. Tool usage + outcomes are first-class evidence for scoring the step. - "outcome": the step's final observable outcome (last tool output, error, or "(assistant-only step)" for pure text turns) - "reflection": the agent's own first-person reflection (may be empty string) - "synth_allowed": boolean — when true and "reflection" is empty, you SHOULD write a brand-new 2–3 sentence first-person reflection for that step. When false, leave "reflection_text" empty for steps that came in with empty "reflection". The user payload may also include "host_context". That describes the host agent being reviewed and the separate reflection model doing this review. Do NOT project the reflection model's own identity/provider/capabilities onto the host agent. If hostModel/hostProvider are present, treat them as the authoritative runtime context unless the episode itself contains a correction. The user payload may also include "task_context" (string or null). When non-null and non-empty, it is the **episode-level task summary**: a compact overview of what this episode was about (e.g. initial user goal, intent metadata, closing assistant reply, and tools used across the episode). It applies to **every** step — use it to keep per-step reflections and α scores aligned with the overall task; it is NOT a substitute for each step's own "state", "outcome", or "tool_calls". When "task_context" is null or missing, infer the episode goal only from the "steps" timeline. For EACH input step, return one object containing: - "idx": copy the input idx exactly - "reflection_text": * If input "reflection" was non-empty → copy it verbatim, do not rewrite. * If input "reflection" was empty AND "synth_allowed" is true → write a NEW 2–3 sentence first-person explanation of WHY the agent acted this way and WHAT it learned. Concrete, no judgment, no repeating the visible action. * If input "reflection" was empty AND "synth_allowed" is false → return the empty string "". * If the step is incoherent or completely empty → return "". - "alpha": one number in [0, 1] grading the reflection on: 1. faithfulness — does it describe what actually happened across thinking + action + tool_calls + outcome? Missing or misnaming a visible thinking block / tool call / tool error is a penalty. 2. causal insight — does it identify why the action or tool choice worked / failed? Bonus when it ties visible thinking to action. 3. transferability — does it surface a lesson useful on a similar task? 4. concreteness — are the details specific (real command names, real error messages) rather than generic platitudes? When "reflection_text" is empty, return alpha=0. - "usable": true when alpha ≥ 0.4 AND the reflection is not tautological. When "reflection_text" is empty, return usable=false. - "reason": one short sentence justifying the alpha score. Knowing the FULL episode timeline (including the final outcome) is permitted and encouraged — that is the whole point of batched scoring. Reflections written here may show better causal insight than per-step ones because you can see how each step contributed to the eventual result. Return JSON of the form: { "scores": [ {"idx": 0, "reflection_text": "...", "alpha": 0.7, "usable": true, "reason": "..."}, {"idx": 1, "reflection_text": "...", "alpha": 0.3, "usable": false, "reason": "..."} ] } The "scores" array MUST contain exactly one entry per input step, in input order. Do not skip steps. Do not invent extra entries.`, };