"""统一意图分类出口,业务 Agent 不得自行解析模型分类结果。""" import json from typing import Any, Protocol from pydantic import BaseModel, ConfigDict, Field, ValidationError from app.core.contracts import IntentResult from app.core.errors import RecoverableAgentError, ValidationAgentError from app.service.model_gateway import ModelGenerationService class IntentEndpointResolver(Protocol): async def resolve(self, *, agent_type: str, task_type: str) -> list[Any]: ... class _IntentPayload(BaseModel): model_config = ConfigDict(extra="forbid") intent: str = Field(min_length=1, max_length=64) confidence: float = Field(ge=0, le=1) class IntentClassifier: """使用已路由模型输出严格 JSON,并绑定 Agent 声明的意图集合。""" def __init__(self, model_service: ModelGenerationService, *, threshold: float = 0.65) -> None: if not 0 <= threshold <= 1: raise ValueError("intent threshold must be between 0 and 1") self.model_service = model_service self.threshold = threshold async def classify( self, *, message: str, supported_intents: tuple[str, ...], endpoints: list[Any] ) -> IntentResult: if not message.strip(): raise ValidationAgentError("待分类消息不能为空") if not supported_intents: raise ValidationAgentError("Agent 未声明支持的意图") prompt = ( "请仅输出 JSON,不要 Markdown。字段必须为 intent 和 confidence。" f"intent 只能从 {list(supported_intents)!r} 中选择,confidence 为 0 到 1 的数字。" f"用户消息:{message}" ) execution = await self.model_service.generate(endpoints, prompt) payload = self._parse(execution.text) if payload.intent not in supported_intents: raise ValidationAgentError("模型返回了未声明的意图") return IntentResult( intent=payload.intent, confidence=payload.confidence, needs_clarification=payload.confidence < self.threshold, ) @staticmethod def _parse(text: str) -> _IntentPayload: candidate = text.strip() if candidate.startswith("```"): candidate = candidate.removeprefix("```") candidate = candidate.removeprefix("json").removesuffix("```").strip() try: value = json.loads(candidate) return _IntentPayload.model_validate(value) except (json.JSONDecodeError, ValidationError, TypeError) as exc: raise RecoverableAgentError("模型意图输出不是有效 JSON") from exc