生命周期与类型状态¶
Agent 以生命周期状态为类型参数(Type-State Pattern):状态写泛型参数里,
迁移方法返回下一个状态,于是「没初始化就执行」「已终结还改提示」这类错误在静态检查阶段就报错,
而不是等到运行时炸。
状态¶
状态类在 agent_lifecycle.py 中定义,并以 Agent.T 的形式对外暴露:
| 标注 | 含义 | 可用操作 |
|---|---|---|
Agent[T.Uninit] |
已构造未初始化(裸 Agent 的默认状态) |
initialize()、system()、instruct()、挂部件 |
Agent[T.Init] |
已初始化 | execute()、execute_command()、info() / get_choice() 等显示方法 |
Agent[T.Final] |
已终结,不可再用 | 只有 finalize() 的幂等空操作 |
Agent[T.Alive] |
Uninit \| Init,仍可用 |
构建类方法 system() / instruct() |
Agent[T.Any] |
任意状态,用于收参数的函数签名 | — |
stateDiagram-v2
[*] --> Uninit: Agent(...)
Uninit --> Init: initialize()
Init --> Final: finalize()
Uninit --> Final: finalize()
Final --> Final: finalize()(空操作)
note right of Final: initialize() 报错 RuntimeError
必须重新绑定¶
迁移方法返回的就是同一个对象,但类型不同,所以要重新赋值:
agent = Agent()
agent = agent.initialize() # Agent[T.Init]
agent = agent.finalize() # Agent[T.Final]
不重新绑定的话静态类型会停在旧状态,检查能力就失效了。system() 与 instruct() 对状态是透明的
(泛型约束在 Agent[T.Alive] 上),它们返回 self,因此可以随意链式拼接,但接到 Final 上会报错。
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]
agent_f = agent_i.finalize()
if TYPE_CHECKING: reveal_type(agent_f) # Agent[Agent.T.Final]
# 静态检查会拦下这两处:
# agent_u.execute() # Cannot access attribute "execute" for class "Agent[Agent.T.Uninit]"
# agent_f.system("hi") # Cannot access attribute "system" for class "Agent[Agent.T.Final]"
with 语句¶
__enter__ 调用 initialize(),__exit__ 调用 finalize(),忘记收尾也不会漏掉解绑:
with Agent() as agent:
result = agent.instruct("Hi~").execute()
print(result.unwrap())
还有一层兜底:构造时注册了 weakref.finalize,被丢弃且已初始化的智能体在回收时自动终结,
显示层不会一直以为自己还挂着一个活着的智能体。
运行时同样有守卫¶
类型标注只在静态检查时生效,运行时也有对应的检查:
| 位置 | 行为 |
|---|---|
execute() / execute_command() |
未初始化时抛 RuntimeError,提示调用 initialize() 或使用 with |
initialize() |
已初始化时直接返回自身(幂等);已终结时抛 RuntimeError |
finalize() |
已终结、或从未初始化的智能体上是空操作(只迁移状态,不触发钩子与解绑) |
Agent.is_initialized(agent) / Agent.is_finalized(agent) |
TypeGuard 静态方法,用来在运行时收窄类型 |
is_initialized 之所以是静态方法而不是属性,是因为 Pylance 只接受至少带一个显式参数的自定义 TypeGuard。
这套设计挡住的四类错误¶
- 未初始化就执行:扩展没加载、显示层没绑定、工作区没准备、模型没探测。
- 终结后继续用:显示层已解绑,事件与回复会静默丢失。
- 重复初始化 / 忘记收尾:两个迁移方法都幂等,
with与weakref.finalize负责兜底。 - 变量类型过期:迁移方法返回新状态,逼你重新绑定,后续调用点的类型始终与现实一致。
派生时的状态¶
Agent.inherit(parent, ...) 返回 Agent[T.Uninit]——派生出来的子智能体需要自己 initialize(),
这样才能重放扩展、绑定显示层、安装压缩策略。派生细节见 子智能体派生。