"""`AgentSpec` 模型(Prompt 外置方案A · 步1)。 从 `specs.py` 原样抽出的 frozen Pydantic 声明,字段不变(`system_prompt` 仍是 `str`,import 期由 `load_prompt` 填入;不改 Callable——缓存断点前块需确定字符串)。 内置 Agent 与用户 Skill 同构:一份只读声明,由编排器加载、经网关按 `tier` 执行。 - 不变量 #2:agent 只声明 `tier`,**不**写具体 model。 - 不变量 #3:四审 `writes=[]`(只读),任何 AI 产出入库必经验收事务。 """ from __future__ import annotations from pydantic import BaseModel, ConfigDict from ww_llm_gateway.types import Tier class AgentSpec(BaseModel): """单一 Agent / Skill 的声明(ARCH §5.1)。""" model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True) name: str tier: Tier # 能力档位(网关解析 provider+model);不含具体 model system_prompt: str # 入/出参契约(Pydantic 模型类型);writer 纯文本 → output_schema 可为 None input_schema: type[BaseModel] | None = None output_schema: type[BaseModel] | None = None # 不可变序列:frozen 只防整字段重绑,不防 list 原地变异;tuple 无 append/clear, # 守住不变量 #3(四审 writes==() 不可被 .append 旁路)。Pydantic v2 coerce list→tuple。 reads: tuple[str, ...] = () # 声明式表读权限 writes: tuple[str, ...] = () # 声明式表写权限(经验收才生效) genre: str | None = None # 题材适用(Skill 用) scope: str = "builtin" # builtin / custom / community