feat: phase 2 of AI pipeline hardening — tool-based structured outputs + prompt caching #3
Reference in New Issue
Block a user
Delete Branch "feat/ai-pipeline-hardening-phase-2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Every structured-output call now uses an Anthropic tool instead of
parsing JSON out of free-form prose, and stable system prompts are
sent as cacheable blocks. Behaviour-equivalent to phase 1 from the
caller's point of view; the savings show up in token usage and in the
absence of "AI returned non-JSON response" failure modes.
emit_knowledge_graph, emit_handbook_delta, emit_learning_article /
_slides / _infographic / _all, emit_custom_topic, emit_quiz_questions,
emit_graph_actions, plus five article-patch tools (set_intro,
set_section, add_section, remove_section, replace_takeaways).
applyAndValidate; rebuilds the article from a sequence of patch tool
calls and re-validates against learningArticleSchema. set_section
falls back to appending when no matching heading exists so the
model's intent is preserved rather than silently dropped.
registered in toolSchemaRegistry so callLLM validates them
automatically.
toolChoice.name, so the UI keeps working with Simulation Mode on
after the structured-output migration.
analyzeHandbookDelta migrated to callLLM + tool use. System prompts
sent as { cache_control: ephemeral } blocks. Handbook results pass
through normalizeHandbookResult to collapse legacy "executes"
relations into executed_by with swapped source/target.
tool per selectedType; generateCustomTopic uses emit_custom_topic;
refineLearningContent now drives the five patch tools with
toolChoice 'any' and rejects the whole turn if the patched article
fails validation. Article-only refinement is intentional for phase 2;
refining a topic without an article surfaces a clear error.
the reasoning tier (Opus) since graph-wide consolidation benefits
from a stronger reasoner.
text blocks: stable preamble (cached), KB context (cached, hash-bust
deferred to phase 5), per-turn user/admin tail (uncached).
sequencing, post-patch validation failure, and tool/registry shape.
Acceptance: lint and 45/45 tests green; build succeeds; no
match(/\{[\s\S]*\}/)JSON extraction left in src/. Live verificationof cache hits on a second extraction within 5 minutes is deferred to
manual smoke testing — needs real
/api/anthropictraffic.Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Every structured-output call now uses an Anthropic tool instead of parsing JSON out of free-form prose, and stable system prompts are sent as cacheable blocks. Behaviour-equivalent to phase 1 from the caller's point of view; the savings show up in token usage and in the absence of "AI returned non-JSON response" failure modes. * src/lib/llmTools.js — single source of truth for tool definitions: emit_knowledge_graph, emit_handbook_delta, emit_learning_article / _slides / _infographic / _all, emit_custom_topic, emit_quiz_questions, emit_graph_actions, plus five article-patch tools (set_intro, set_section, add_section, remove_section, replace_takeaways). * src/lib/articlePatches.js — pure applyArticlePatches + applyAndValidate; rebuilds the article from a sequence of patch tool calls and re-validates against learningArticleSchema. set_section falls back to appending when no matching heading exists so the model's intent is preserved rather than silently dropped. * src/lib/llmSchemas.js — Zod schemas for the five patch ops, registered in toolSchemaRegistry so callLLM validates them automatically. * src/lib/llm.js — simulation mode now returns a tool_use stub matching toolChoice.name, so the UI keeps working with Simulation Mode on after the structured-output migration. * src/lib/extractionPipeline.js — processSourceText and analyzeHandbookDelta migrated to callLLM + tool use. System prompts sent as { cache_control: ephemeral } blocks. Handbook results pass through normalizeHandbookResult to collapse legacy "executes" relations into executed_by with swapped source/target. * src/lib/learningService.js — generateLearningContent picks the right tool per selectedType; generateCustomTopic uses emit_custom_topic; refineLearningContent now drives the five patch tools with toolChoice 'any' and rejects the whole turn if the patched article fails validation. Article-only refinement is intentional for phase 2; refining a topic without an article surfaces a clear error. * src/lib/testService.js — quiz generation via emit_quiz_questions. * src/components/admin/KnowledgeGraph.jsx — analyzeGraph routed through the reasoning tier (Opus) since graph-wide consolidation benefits from a stronger reasoner. * src/components/chat/prompts.js — buildSystemPrompt now returns three text blocks: stable preamble (cached), KB context (cached, hash-bust deferred to phase 5), per-turn user/admin tail (uncached). * src/lib/__tests__/ — 13 new tests covering each patch op, multi-op sequencing, post-patch validation failure, and tool/registry shape. Acceptance: lint and 45/45 tests green; build succeeds; no `match(/\{[\s\S]*\}/)` JSON extraction left in src/. Live verification of cache hits on a second extraction within 5 minutes is deferred to manual smoke testing — needs real `/api/anthropic` traffic. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>