跳转至

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 状态能输出」的同时不破坏子类型关系。