fix(context): name emit_artifact in the schema instruction (#416)
The pinned schemaInstruction said only "Respond with a single JSON object matching this schema", contradicting every role prompt that says to emit via the emit_artifact tool. Two stated channels for one artifact, and the pinned one won. ResponseFormat.Json is built under exactly the condition that offers the tool (an llmEmitted slot — see emitArtifactTool), so the tool is always available wherever this instruction renders, and the kernel prefers it: tool-calling models are more reliable at it and it sidesteps llama.cpp's grammar+tools incompatibility. The instruction now names it, and keeps raw JSON as what to do when told to stop calling tools — which is what the tools-less final pass demands and what the executor already accepts either way (llmArtifactOverride ?: response.text). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+12
-2
@@ -52,11 +52,21 @@ internal fun SessionOrchestrator.evictArtifactContentCache(sessionId: SessionId)
|
||||
internal suspend fun SessionOrchestrator.buildSchemaEntries(
|
||||
responseFormat: ResponseFormat,
|
||||
stageId: StageId,
|
||||
artifactName: String,
|
||||
): List<ContextEntry> {
|
||||
if (responseFormat !is ResponseFormat.Json) return emptyList()
|
||||
val compactSchema = Json.encodeToString(JsonSchema.serializer(), responseFormat.schema)
|
||||
val instruction = "Respond with a single JSON object matching this schema. " +
|
||||
"Do not include markdown, code fences, or commentary outside the JSON. " +
|
||||
// #416: names emit_artifact as the channel. ResponseFormat.Json is emitted under exactly the
|
||||
// condition that offers the tool (an llmEmitted slot — see emitArtifactTool), so the tool is
|
||||
// always available here, and the kernel prefers it: tool-calling models are more reliable at it
|
||||
// and it sidesteps llama.cpp's grammar+tools incompatibility. The old text said only "respond
|
||||
// with a single JSON object", contradicting every role prompt that says to call emit_artifact.
|
||||
// Raw JSON stays valid as the second sentence describes, since the executor accepts either
|
||||
// (llmArtifactOverride ?: response.text) and the tools-less final pass explicitly demands it.
|
||||
val instruction = "Produce the '$artifactName' artifact by calling the $EMIT_ARTIFACT_TOOL tool " +
|
||||
"with its fields filled in, matching this schema. If you are told to stop calling tools, " +
|
||||
"output the same object as a single JSON object in your final message instead. Either way, " +
|
||||
"no markdown, no code fences, and no commentary outside the JSON. " +
|
||||
"Schema: $compactSchema"
|
||||
return listOf(
|
||||
ContextEntry(
|
||||
|
||||
+5
-1
@@ -152,7 +152,11 @@ internal suspend fun SessionOrchestrator.executeStage(
|
||||
?.let { ResponseFormat.Json(it.kind.deriveJsonSchema()) }
|
||||
?: ResponseFormat.Text
|
||||
|
||||
val schemaEntries = buildSchemaEntries(responseFormat, stageId)
|
||||
val schemaEntries = buildSchemaEntries(
|
||||
responseFormat,
|
||||
stageId,
|
||||
llmEmittedSlots.firstOrNull()?.name?.value ?: "",
|
||||
)
|
||||
val intentEntries = buildIntentEntry(sessionId)
|
||||
val steeringEntries = buildSteeringNoteEntries(sessionId)
|
||||
val clarificationEntries = buildClarificationAnswerEntries(sessionId)
|
||||
|
||||
Reference in New Issue
Block a user