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---
75 lines
2.5 KiB
Python
75 lines
2.5 KiB
Python
"""Agent registry — explicit name → agent factory map (D-018, G-4).
|
|
|
|
Agents are registered centrally in their own phases (P3-P5) via
|
|
`registry.register(name, factory)`. One registration pattern, one registry.
|
|
"""
|
|
|
|
from collections.abc import Callable
|
|
|
|
from ..config import Settings
|
|
from ..llm.base import LLMProvider
|
|
from .base import BaseAgent
|
|
|
|
AgentFactory = Callable[[LLMProvider, Settings], BaseAgent]
|
|
|
|
|
|
def register_builtin_agents(registry: "AgentRegistry") -> None:
|
|
"""Central registration of all seven shipped agents (G-4: one pattern).
|
|
|
|
coach, tutor, lab, assessor, proctor, mentor, examiner (Phase 5).
|
|
New agents register here in their landing phase.
|
|
"""
|
|
from .assessor import AssessorAgent
|
|
from .coach import CoachAgent
|
|
from .examiner import ExaminerAgent
|
|
from .lab import LabAgent
|
|
from .mentor import MentorAgent
|
|
from .proctor import ProctorAgent
|
|
from .tutor import TutorAgent
|
|
|
|
registry.register("coach", lambda provider, settings: CoachAgent(provider, settings))
|
|
registry.register("tutor", lambda provider, settings: TutorAgent(provider, settings))
|
|
registry.register("lab", lambda provider, settings: LabAgent(provider, settings))
|
|
registry.register(
|
|
"assessor", lambda provider, settings: AssessorAgent(provider, settings)
|
|
)
|
|
registry.register(
|
|
"proctor", lambda provider, settings: ProctorAgent(provider, settings)
|
|
)
|
|
registry.register(
|
|
"mentor", lambda provider, settings: MentorAgent(provider, settings)
|
|
)
|
|
registry.register(
|
|
"examiner", lambda provider, settings: ExaminerAgent(provider, settings)
|
|
)
|
|
|
|
|
|
class UnknownAgentError(KeyError):
|
|
"""Raised when resolving an agent name that was never registered."""
|
|
|
|
|
|
class DuplicateAgentError(ValueError):
|
|
"""Raised when registering an agent name that already exists."""
|
|
|
|
|
|
class AgentRegistry:
|
|
def __init__(self) -> None:
|
|
self._factories: dict[str, AgentFactory] = {}
|
|
|
|
def register(self, name: str, factory: AgentFactory) -> None:
|
|
if name in self._factories:
|
|
raise DuplicateAgentError(f"agent {name!r} already registered")
|
|
self._factories[name] = factory
|
|
|
|
def names(self) -> list[str]:
|
|
return sorted(self._factories)
|
|
|
|
def get(self, provider: LLMProvider, settings: Settings, name: str) -> BaseAgent:
|
|
try:
|
|
factory = self._factories[name]
|
|
except KeyError:
|
|
raise UnknownAgentError(
|
|
f"unknown agent {name!r}; registered: {self.names()}"
|
|
) from None
|
|
return factory(provider, settings)
|