feat: phase 2 of AI pipeline hardening — tool-based structured outputs + prompt caching
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>
This commit is contained in:
@@ -183,6 +183,31 @@ export const proposeGraphDeltaSchema = z.object({
|
||||
relations: z.array(deltaRelationSchema).max(5).optional(),
|
||||
});
|
||||
|
||||
// ── Article patch operation schemas (Phase 2.4) ──────────────────────────────
|
||||
|
||||
export const setIntroPatchSchema = z.object({
|
||||
intro: z.string().min(1),
|
||||
});
|
||||
|
||||
export const setSectionPatchSchema = z.object({
|
||||
heading: z.string().min(1),
|
||||
body: z.string().min(1),
|
||||
});
|
||||
|
||||
export const addSectionPatchSchema = z.object({
|
||||
heading: z.string().min(1),
|
||||
body: z.string().min(1),
|
||||
position: z.enum(['start', 'end']),
|
||||
});
|
||||
|
||||
export const removeSectionPatchSchema = z.object({
|
||||
heading: z.string().min(1),
|
||||
});
|
||||
|
||||
export const replaceTakeawaysPatchSchema = z.object({
|
||||
items: z.array(z.string().min(1)).min(1),
|
||||
});
|
||||
|
||||
/**
|
||||
* Registry mapping known tool names to their input schemas. `callLLM`
|
||||
* consults this when the caller does not pass an explicit `toolSchemas`
|
||||
@@ -199,4 +224,9 @@ export const toolSchemaRegistry = {
|
||||
emit_custom_topic: customTopicSchema,
|
||||
emit_graph_actions: graphActionsSchema,
|
||||
propose_graph_delta: proposeGraphDeltaSchema,
|
||||
set_intro: setIntroPatchSchema,
|
||||
set_section: setSectionPatchSchema,
|
||||
add_section: addSectionPatchSchema,
|
||||
remove_section: removeSectionPatchSchema,
|
||||
replace_takeaways: replaceTakeawaysPatchSchema,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user