explain refinements: no opt-out row, one committed field (owner 2026-09-27)
Two follow-up rulings on the explain tool:
1. No 'I don't know' row — not needed in free recall. If he doesn't
know, he says so in his own words; his text is the only answer
channel. Dock: text mode is rowless again (the composer IS the
surface); doctrine and the shared prompt chunk updated; the format
branch and card revert to quiz-only dontKnow.
2. explanation + correctAnswer collapse into ONE field — the separate
explanation was quiz remnant (MC needs the why outside its options;
a free-text commitment carries its own reasoning). Schema is now
{question, correctAnswer, details?}; the reveal shows the single
committed answer; no explanation snapshots anywhere on the explain
path.
1047/1047 tests, typecheck/lint/build green.
This commit is contained in:
@@ -4,9 +4,8 @@ Two instruments. The quiz tool (its exact name depends on the quizzing mechanic
|
||||
|
||||
### Quiz checks — shared doctrine
|
||||
|
||||
- `explanation` (required) says why the correct answer is correct. It comes BEFORE `correctAnswer` in the call by design: reason it out first, then commit to the answer the reasoning supports.
|
||||
- An "I don't know" choice is always available to the learner — never author your own opt-out. A dontKnow result is a genuine gap, not a wrong answer.
|
||||
- Answers may carry a free-text `note` — read it; it says what he was thinking.
|
||||
- Multiple choice carries a separate `explanation` (why the correct option is correct, written BEFORE `correctAnswer`: reason it out first, then commit). Free recall has ONE field — `correctAnswer`, the committed answer with its reasoning inside.
|
||||
- Option-style answers may carry a free-text `note` — read it; it says what he was thinking.
|
||||
- ONE question per turn: ask, then end your turn — his answer determines the next.
|
||||
|
||||
### `ask_user_question`
|
||||
|
||||
+4
-5
@@ -113,12 +113,11 @@ const QUIZ_OPTION_CRAFT =
|
||||
* the teach variant appends the nodeId tagging paragraph. */
|
||||
const EXPLAIN_DOCTRINE =
|
||||
"Ask the learner to EXPLAIN something in his own words — a graded check with NO options: he types a free-text " +
|
||||
"answer. Unlike ask_user_question (no right answer), explain always has one. THE COMMITMENT RULE: write " +
|
||||
"explanation (your reasoning) first, then correctAnswer — the FULL correct answer, committed before he ever sees " +
|
||||
"the question. When his answer returns, grade it strictly against your committed answer in your reply: never " +
|
||||
"answer. Unlike ask_user_question (no right answer), explain always has one. THE COMMITMENT RULE: write correctAnswer — the FULL " +
|
||||
"correct answer in your own words, reasoning included — committed before he ever sees the question. When his answer returns, grade it strictly against your committed answer in your reply: never " +
|
||||
"revise the commitment after seeing his words, and never mark a wrong answer right to be kind — a near-miss is " +
|
||||
"partial understanding; name exactly what he got and what he missed. He sees the committed answer and the " +
|
||||
"explanation on submit. An 'I don't know' choice is always available to him — a genuine gap, not a wrong answer. " +
|
||||
"partial understanding; name exactly what he got and what he missed. He sees the committed answer on submit. If he genuinely doesn't know, he says so in his own words — that is a genuine " +
|
||||
"gap, not a wrong answer. " +
|
||||
"ONE question per turn: call a single explain, then end your turn — his answer determines the next.";
|
||||
|
||||
export const probeQuizTool: AgentTool<typeof probeQuizInputSchema> = {
|
||||
|
||||
+14
-22
@@ -224,26 +224,22 @@ export const probeQuizInputSchema = quizInputBase;
|
||||
// correct answer BEFORE posing (the commitment rides the persisted call
|
||||
// args, is revealed to the learner on submit, and is restated in the answer
|
||||
// result the model grades against; the client never grades free text).
|
||||
// Mechanics doctrine lives in the TOOL DESCRIPTIONS (owner ruling
|
||||
// 2026-09-27: pi-native — doctrine travels with the armed tool), not in
|
||||
// the system prompt.
|
||||
// ONE committed answer, reasoning included (owner ruling 2026-09-27: the
|
||||
// separate explanation field was quiz remnant — collapsed away).
|
||||
// Mechanics doctrine lives in the TOOL DESCRIPTIONS (pi-native ruling),
|
||||
// not in the system prompt.
|
||||
|
||||
export const explainInputBase = Type.Object({
|
||||
question: Type.String({
|
||||
description:
|
||||
"The single question to ask. ONE question per turn: ask, then end your turn — the answer determines the next.",
|
||||
}),
|
||||
explanation: Type.String({
|
||||
description:
|
||||
"REQUIRED. Explanation revealed AFTER the learner answers (shown however he did). It is written BEFORE " +
|
||||
"correctAnswer by design: reason it out first, then commit to the answer the reasoning supports.",
|
||||
}),
|
||||
correctAnswer: Type.String({
|
||||
description:
|
||||
"REQUIRED. The full correct answer in your own words — COMMITTED before the learner ever sees the question. " +
|
||||
"The commitment is immutable: when his answer returns, grade it strictly against this text — never revise the " +
|
||||
"commitment after seeing his words, and never mark a wrong answer right to be kind. Both this and the explanation " +
|
||||
"are shown to him on submit; your next reply grades the nuance of what he wrote.",
|
||||
"REQUIRED. The full correct answer in your own words, reasoning included — COMMITTED before the learner " +
|
||||
"ever sees the question. The commitment is immutable: when his answer returns, grade it strictly against this " +
|
||||
"text — never revise the commitment after seeing his words, and never mark a wrong answer right to be kind. " +
|
||||
"It is shown to him on submit; your next reply grades the nuance of what he wrote.",
|
||||
}),
|
||||
details: Type.Optional(
|
||||
Type.String({ description: "Optional extra context or instructions shown under the question." }),
|
||||
@@ -338,6 +334,7 @@ export interface QuestionData {
|
||||
correctIndices?: number[];
|
||||
// explain only. NEVER send to the client before answering:
|
||||
committedAnswer?: string;
|
||||
// quiz only (the MC "why"). NEVER send to the client before answering:
|
||||
explanation?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
@@ -356,7 +353,7 @@ export interface QuestionAnswerData {
|
||||
correct?: boolean; // quiz only
|
||||
correctIndices?: number[]; // quiz only (rendering)
|
||||
committed?: string; // explain only (rendering): the model's committed answer
|
||||
explanation?: string; // quiz/explain only (rendering)
|
||||
explanation?: string; // quiz only (rendering)
|
||||
dismissed?: boolean;
|
||||
answeredAt: string;
|
||||
}
|
||||
@@ -595,9 +592,8 @@ export function buildAnswer(qd: QuestionData, req: AnswerRequest): QuestionAnswe
|
||||
}
|
||||
if (qd.kind === "explain") {
|
||||
// No client grading — the model grades in its reply, against the
|
||||
// commitment. Snapshots ride along for the reveal + rendering.
|
||||
// commitment. The commitment snapshots for the reveal + rendering.
|
||||
base.committed = qd.committedAnswer;
|
||||
base.explanation = qd.explanation;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
@@ -627,13 +623,9 @@ export function formatAnswerMessage(ad: QuestionAnswerData): string {
|
||||
}
|
||||
lines.push(`Correct: ${correctStr}`);
|
||||
} else if (ad.kind === "explain") {
|
||||
if (ad.dontKnow) {
|
||||
lines.push(
|
||||
`The learner selected "I don't know" — they did NOT attempt an answer (a genuine knowledge gap, not a wrong guess).`,
|
||||
);
|
||||
} else {
|
||||
lines.push(`The learner answered: ${ad.text ?? "(no answer)"}`);
|
||||
}
|
||||
// No opt-out row on explain (owner ruling 2026-09-27): if he doesn't
|
||||
// know, he says so in his own words — his text is all there is.
|
||||
lines.push(`The learner answered: ${ad.text ?? "(no answer)"}`);
|
||||
if (ad.committed) {
|
||||
lines.push(`Your committed correct answer (immutable — grade against it, never revise it): ${ad.committed}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user