{
  "slug": "agent-feedback-loop",
  "title": "Agent Feedback Loop",
  "description": "Curator edits and rejection notes are saved per agent and injected into the next run as few-shot examples — agents learn from corrections automatically.",
  "category": "concepts",
  "order": 3,
  "locale": "en",
  "translationGroup": "410a9b4a-67e6-4f48-bb75-28bee7aa5640",
  "helpCardId": null,
  "content": "## The problem\n\nEarly on, the agent runner read past corrections from `_data/agents/{id}/feedback.json` and injected them into the system prompt as few-shot examples. The intent was that agents would learn from curator edits over time. The catch: **nothing in the codebase ever wrote to that file.** Curators could fix every draft and reject every bad one and the agent would happily repeat the same mistakes the next day.\n\nThe feedback loop closes that gap. Every curator action — edit a field before approving, reject with a note — is now persisted to the agent's feedback file automatically. The next time that agent runs, the most recent corrections are baked into its system prompt as concrete examples.\n\n## How corrections are recorded\n\nWhen a queue item is created by an agent, the runner snapshots the original `contentData` onto the queue item under `originalContentData`. When a curator approves the item, the curation route diffs the current `contentData` against that snapshot, and writes one **correction** entry per changed string field:\n\n```json\n{\n  \"id\": \"fb-...-...\",\n  \"type\": \"correction\",\n  \"queueItemId\": \"qi-...\",\n  \"field\": \"title\",\n  \"original\": \"Why TypeScript Generics Matter\",\n  \"corrected\": \"How TypeScript Generics Save You From Refactoring Hell\",\n  \"createdAt\": \"2026-04-08T...\"\n}\n```\n\nIf you don't edit anything before approving, no corrections are recorded — the original output was good enough.\n\n## How rejections are recorded\n\nWhen you reject a queue item with a note, the rejection route writes a **rejection** entry containing the curator's notes:\n\n```json\n{\n  \"id\": \"fb-...-...\",\n  \"type\": \"rejection\",\n  \"queueItemId\": \"qi-...\",\n  \"notes\": \"Tone is too dry — needs more personality and specific examples\",\n  \"createdAt\": \"2026-04-08T...\"\n}\n```\n\nRejection notes are visible on the agent detail page but **not currently injected into the next system prompt** (only `correction` and `edit` entries with both `original` and `corrected` strings are used as few-shot examples). They're still useful as an audit trail, and may be folded into the prompt context in a future revision.\n\n## What gets injected into the next run\n\nThe agent runner calls `loadFeedbackForPrompt(agentId, 5)` and pulls the **last 5 correction examples** in chronological order. They're appended to the system prompt under a `## Learn from past corrections` section, like this:\n\n```\n## Learn from past corrections\nExample 1:\nOriginal: Why TypeScript Generics Matter\nCorrected: How TypeScript Generics Save You From Refactoring Hell\n\nExample 2:\nOriginal: A serene sunrise over snow-capped mountains\nCorrected: Snow-covered alpine valley with frozen lake at golden hour\n```\n\nThe model treats these as concrete editorial preferences and tends to mimic the patterns on its next run.\n\n## The Recent feedback panel\n\nThe agent detail page shows a **Recent feedback** card with the last 5 entries. Each entry has a colored type badge (green for correction, blue for edit, red for rejection), the field name where applicable, a strikethrough diff for corrections, and a timestamp.\n\nThe footer notes the maximum injected count so curators understand what's actually flowing back into the agent.\n\n## API\n\nThe endpoint is at `POST /api/cms/agents/[id]/feedback` and accepts:\n\n```json\n{\n  \"type\": \"correction\" | \"rejection\" | \"edit\",\n  \"queueItemId\": \"qi-...\",\n  \"field\": \"title\",\n  \"original\": \"...\",\n  \"corrected\": \"...\",\n  \"notes\": \"...\"\n}\n```\n\nMost curators never call this directly — the curation approve/reject routes handle it automatically. It exists for programmatic submissions and for the in-page panel.\n\n## Storage and limits\n\n- File: `_data/agents/{agentId}/feedback.json`\n- Max entries: **200** (oldest are dropped on append)\n- Format: JSON array of `FeedbackEntry` objects\n- Backwards compatible: legacy `{ original, corrected }` shape is read as a `correction` type\n\n## Why only 5 examples?\n\nFew-shot examples are the most expensive part of the system prompt — each one is hundreds of tokens, and the cost is paid on every run. Five is enough to communicate consistent editorial preferences without bloating the prompt to the point of token waste. If you want the agent to learn a *new* preference quickly, that one will land in the top 5 within a few approvals.\n\n## See also\n\n- [Per-agent cost guards](/docs/agent-cost-guards) — keep an over-eager agent from ignoring your feedback by limiting its budget.\n- [Curation queue](/docs/curation-queue) — where corrections happen.",
  "excerpt": "The problem\n\nEarly on, the agent runner read past corrections from data/agents/{id}/feedback.json and injected them into the system prompt as few-shot examples. The intent was that agents would learn from curator edits over time. The catch: nothing in the codebase ever wrote to that file. Curators c",
  "seo": {
    "metaTitle": "Agent Feedback Loop — webhouse.app Docs",
    "metaDescription": "Curator edits and rejections are persisted per agent and injected into the next run as few-shot examples. Agents learn from corrections automatically.",
    "keywords": [
      "webhouse",
      "cms",
      "ai",
      "feedback",
      "agents",
      "learning"
    ]
  },
  "createdAt": "2026-04-08T00:00:00.000Z",
  "updatedAt": "2026-04-08T00:00:00.000Z"
}