请解释 Protocol、TypeVar、Generic、Literal 的作用,并说明如何在 AI Agent 框架设计中用它们实现类型安全的工具注册和 Agent 接口约束。
🔷 用 Protocol、TypeVar、Generic、Literal 构建类型安全的 Agent 框架¶
🎯 一句话理解它们的分工¶
-
Protocol:定义“你能做什么”,不靠继承,靠结构。
-
TypeVar:类型变量,保住泛型函数和泛型类里的“关系”。
-
Generic:让类可以参数化,比如
Agent[MyTool]。 -
Literal:把参数的取值精确到字面量,像枚举但更轻。
在 Agent 框架里,这四个组合起来,能让你在不运行代码时就靠静态检查发现“工具签名不对”“Agent 能力不匹配”等问题。
🧱 逐一拆解¶
🔹 Protocol —— 结构化的“能力契约”¶
Protocol 就是给鸭子类型上户口。任何对象,只要它具备约定的方法签名,不用继承,就被类型系统认可为这个接口。
from typing import Protocol
class Tool(Protocol):
name: str
def __call__(self, input: str) -> str: ...
上面这个 Tool,任何有 name 属性和 call 方法的对象都自动符合,静态检查器会认。
🔸 TypeVar —— 保持类型关联的“变量”¶
写泛型函数时,我们希望输入是什么类型,输出就是什么类型,TypeVar 就是那个占位符。
用它,类型关系就“绑”住了。
🧩 Generic —— 参数化类的模具¶
Generic 配合 TypeVar,让类可以带上类型参数,比如一个 Agent 能携带它处理的“状态类型”。
from typing import Generic, TypeVar
S = TypeVar("S")
class Agent(Generic[S]):
state: S
def get_state(self) -> S:
return self.state
💠 Literal —— 把值变成类型的一部分¶
当你希望参数只能是指定的几个字符串时,Literal 就是最精准的选择。
set_mode("fast") 合法,set_mode("slow") 直接静态报错。
🤖 实战:设计一个类型安全的 AI Agent 框架¶
假设我们要做一个可扩展的 Agent 框架,核心有两个部分:工具注册 和 Agent 接口约束。我们直接用上面四个组件来搭。
工具注册:强类型工具协议¶
先用 Protocol 定义一个工具的样子,并用 TypeVar 让工具能处理不同的输入输出,保留精确签名。
from typing import Protocol, TypeVar, Generic, Literal, Any
# 泛型工具:支持任意输入/输出,但保持一对一的类型关系
T_in = TypeVar("T_in", contravariant=True)
T_out = TypeVar("T_out", covariant=True)
class Tool(Protocol[T_in, T_out]):
"""任何符合此结构的可调用对象都是工具"""
name: str
category: Literal["search", "code", "api", "custom"]
def __call__(self, input: T_in) -> T_out: ...
-
Protocol[T_in, T_out] 定义了一个泛型工具接口。
-
Literal 把
category的可能值精确限制为四种,防止手误。 -
变量上的
covariant和contravariant保证子类型关系合理(细节可以先不提)。
现在,注册函数的时候就能享受类型安全:
def register_tool(tool: Tool[Any, Any]) -> None:
print(f"注册工具: {tool.name},类型: {tool.category}")
# 用户自定义工具,完全不用继承
class WebSearch:
name = "web_search"
category = "search" # Literal 约束生效,写错 IDE 直接标红
def __call__(self, input: str) -> str:
return f"搜索结果: {input}"
register_tool(WebSearch()) # 静态检查通过
Agent 接口约束:泛型 Agent 装配工具¶
Agent 本身也需要接口约束,我们可以用 Generic 让 Agent 带上“它会使用的工具类型”,并用 Protocol 定义它必备的行为。
from typing import Sequence
# 用一个 TypeVar 代表某类工具
TTool = TypeVar("TTool", bound=Tool[Any, Any])
class Agent(Protocol[TTool]):
name: str
role: Literal["planner", "executor", "reflector"]
tools: Sequence[TTool]
def run(self, task: str) -> str: ...
这里:
-
Protocol[TTool] 让 Agent 协议对工具类型参数化。
-
Literal 限制角色为有限的语义标签,框架可以依此做路由。
-
Sequence[TTool] 确保工具列表类型统一。
现在可以写出非常精准的工厂函数:
def create_agent(agent: Agent[WebSearch]) -> None:
# 类型检查确保 agent.tools 里的工具都是 WebSearch 一类
print(f"创建 Agent: {agent.name},角色: {agent.role}")
如果你不小心把一个不具有 search 能力的工具放进 WebSearch 类型的 Agent,静态检查就会立刻提醒。
🧭 面试时怎么收得自然¶
你可以这样总结:
“这四个工具单独用,每一个都只是增强类型标注的准确度;但把它们组合起来,就能构建出一个零运行时开销、IDE 友好、接口高度解耦的 AI Agent 框架。
-
Protocol让任何第三方工具不需要改代码就能接入 -
TypeVar+Generic保证工具链的类型连贯 -
Literal把大部分配置错误提前到代码编写阶段发现
这就是类型系统给框架设计带来的安全感——错误发现得越早,成本越低。在异步、多 Agent 的场景下,这套类型约束能显著降低调试的心智负担。”