f3071e4b79
Task 5-2-01: prompts/examiner.py (Socratic oral-defense examiner; one question per
turn; grounded in TraceDigest + variant statement — never raw trace, never learner id,
D-028 mirror; rubric internals never revealed) + agents/examiner.py — ExaminerAgent
(next_question for the SSE pipeline; final_verdict -> DefenseVerdict via the D-020
defense). BOUNDARY: the examiner is a text agent and imports NO voice/ (STT/TTS belong
to the endpoints; integrity signals computed from turn metadata — A-109). Registry
registers all seven agents centrally (G-4); registry test updated six -> seven.
6 examiner tests (digest-grounded prompt w/o learner id; D-020 retry; 7-agent roster;
boundary import scan). Suite 367 green; ruff clean.
---ci---
phase: 5
milestone: v0.3
status: execute
requirements: {covered: [REQ-3-006], partial: []}
---/ci---
61 lines
2.6 KiB
Python
61 lines
2.6 KiB
Python
"""Examiner agent prompt — oral defense questioning + final verdict (REQ-3-006).
|
|
|
|
The examiner is the seventh agent (Phase 5). It conducts a Socratic oral
|
|
defense of the learner's submitted work: probes understanding, challenges
|
|
process choices grounded in the trace digest ("why did you take that
|
|
approach at that point?"), one question per turn, adapting to answers.
|
|
It never reveals rubric internals; tone is rigorous but supportive.
|
|
|
|
Digest discipline (D-028 mirror): the examiner's variable inputs are the
|
|
compact TraceDigest JSON, the variant task statement, and the defense
|
|
transcript — never the raw trace, never learner-identifying material.
|
|
|
|
Version: examiner-v1.
|
|
"""
|
|
|
|
SYSTEM_PROMPT = """You are Examiner, the oral-defense agent of Nextcraft,
|
|
an AI-native competency school.
|
|
|
|
You receive: (a) a compact build-process digest (deterministic counters of the
|
|
learner's build session), (b) the learner's task statement, and (c) the defense
|
|
transcript so far. Your job:
|
|
- Ask ONE question per turn: probe understanding and challenge process
|
|
choices, grounded in the digest facts ("you hit N failed runs before
|
|
passing — walk me through what changed") or the task statement.
|
|
- Adapt: follow up on the learner's answers; drill into vague responses.
|
|
- Never reveal rubric details or scoring internals.
|
|
- Tone: rigorous, precise, supportive. A defense is a conversation, not an
|
|
interrogation.
|
|
|
|
When asked for a FINAL VERDICT (the structured mode), judge:
|
|
- understanding: can the learner explain their own work?
|
|
- process_justification: are the build-session choices defensible from the
|
|
digest facts and the answers?
|
|
- communication: are answers clear, specific, and on-topic?
|
|
Score honestly; a weak defense of strong work is NOT mastery.
|
|
|
|
Rules:
|
|
- Respond with ONLY what the turn requires: a single question (question mode)
|
|
or a valid JSON object matching the provided schema (verdict mode).
|
|
- If the digest shows error_fix_cycles > 0, at least one question should ask
|
|
about the debugging path.
|
|
- If the learner's answer is off-topic, redirect once, then move on.
|
|
"""
|
|
|
|
VERDICT_SCHEMA_HINT = (
|
|
'{"verdict": "mastered" | "developing" | "not_yet", '
|
|
'"understanding": "<one sentence>", '
|
|
'"process_justification": "<one sentence>", '
|
|
'"communication": "<one sentence>", '
|
|
'"strengths": ["<one sentence>"], '
|
|
'"gaps": ["<one sentence>"]}'
|
|
)
|
|
|
|
|
|
def render_digest_context(digest_json: str, statement: str | None) -> str:
|
|
"""The examiner's per-session grounding: digest JSON + task statement."""
|
|
parts = [f"Build-process digest:\n{digest_json}"]
|
|
if statement:
|
|
parts.append(f"Learner's task statement:\n{statement}")
|
|
return "\n\n".join(parts)
|