Type-State 模式¶
Agent 把「生命周期状态」编码进类型参数,让越序调用在静态检查阶段就报错,而不是等到运行时。
三个状态¶
from xun import Agent
T = Agent.T # 命名空间,等价于 xun.agent_state.T
| 状态 | 含义 | 典型来源 |
|---|---|---|
T.Uninit |
已构造、未初始化 | Agent()(裸类型的默认参数就是它) |
T.Init |
已初始化、可执行 | agent.initialize()、with 进入时、setup_agent() |
T.Final |
已收尾、不可再用 | agent.finalize()、with 退出时 |
T.Alive |
Uninit | Init 的并集 |
system() / instruct() 的约束域 |
T.Any |
任意状态 | 只做容器或参数时使用 |
StateT 是协变的:Agent[T.Init] 可以出现在要求 Agent[T.Any] 的位置,反之不行。
必须重绑定¶
状态转换通过就地改写内部 _lifecycle 并 cast 返回自身完成,因此返回值与入参是同一个对象——但类型不同。为了保持类型精确,请重绑定:
agent = Agent()
agent = agent.initialize() # Agent[Agent.T.Init]
...
agent = agent.finalize() # Agent[Agent.T.Final]
with 语句自动完成这件事:
with Agent() as agent: # agent: Agent[Agent.T.Init]
result = agent.instruct("Hi").execute()
方法可用性¶
| API | Uninit |
Init |
Final |
|---|---|---|---|
initialize() |
可以 | 静态拒绝(运行时幂等返回自身) | 运行时 RuntimeError |
execute() / execute_command() |
不可 | 可以 | 不可 |
system() / instruct() |
可以 | 可以 | 不可 |
info() / warning() / error() / get_choice() / get_confirm() |
不可 | 可以 | 不可 |
display_event() |
可以 | 可以 | 可以 |
finalize() |
可以 | 可以 | 可以 |
cancel() / check_cancel() / is_running |
可以 | 可以 | 可以 |
Agent.inherit() / is_initialized() / is_finalized() |
可以(入参为 Agent[T.Any]) |
可以 | 可以 |
典型静态报错:
agent_u = Agent()
# agent_u.execute() # error: Cannot access attribute `execute` for class `Agent[Agent.T.Uninit]`
agent_f = Agent().initialize().finalize()
# agent_f.system("hi") # error: Cannot access attribute `system` for class `Agent[Agent.T.Final]`
在 notebook 里可以用 reveal_type() 观察推断结果:
from typing import reveal_type, TYPE_CHECKING
agent_u = Agent()
if TYPE_CHECKING: reveal_type(agent_u) # Agent[Agent.T.Uninit]
agent_i = agent_u.initialize()
if TYPE_CHECKING: reveal_type(agent_i) # Agent[Agent.T.Init]
运行时与静态检查的关系¶
约束只存在于类型层:运行时并没有强制的门卫,越序调用不会立刻崩。因此框架在最关键的位置补了运行时检查——execute() 入口处会验证 Agent.is_initialized(self),未初始化时抛:
RuntimeError: Agent 'xxx' is not initialized. Call agent.initialize() or use 'with agent:'.
判定用 TypeGuard 辅助函数表达,便于在函数签名里收窄类型:
from xun import Agent
def run(agent: Agent[Agent.T.Any]) -> str | None:
if Agent.is_initialized(agent): # 收窄为 Agent[Agent.T.Init]
return agent.instruct("ping").execute().unwrap()
return None
实现要点¶
- 类型层:基类
_AgentState(v=0,即T.Any)与三个子类_Uninit/_Init/_Final(v=1/2/3),靠互不相同的类属性v区分,由T命名空间暴露。 - 运行时层:实例字段
_lifecycle保存其中一个实例,比较只看_lifecycle.v。 - 转换函数
_cast_self(state)改写_lifecycle并cast返回self——这是全局唯一的状态写入点。 Agent.T必须是普通类属性而不是type T = T别名,否则Agent.T.Init在某些 IDE 里无法解析。- 与显示相关的辅助方法定义在
AgentDisplayMixin[StateT]上,其self注解写成 mixin 而非Agent,从而在保持「只有Init状态能输出」的同时不破坏子类型关系。