TaskHandler 类型设计;typing 模块详解;
TaskHandler 类型设计
问题起因
新增任务 interrupt 支持后,TaskHandler 签名从 1 个参数变成 3 个,导致不需要 interrupt 的 run_pdf_structured_task 也必须接收多余参数。
三种解决方案
方案一:统一签名(最简单,现在用的)
TaskHandler = Callable[[dict, TaskRecord, TaskDispatcherService], Awaitable[dict]]
所有 handler 都必须声明三个参数,不用就忽略。
缺点:不需要 interrupt 的 handler 被迫接收无意义参数。
方案二:try/except 判断签名(hack,不推荐)
try:
return await handler(payload, task_record=record, dispatcher=self)
except TypeError:
return await handler(payload)
用异常来探测函数签名,属于 hack 写法——
hack 写法指绕过正常设计、靠副作用或错误来实现功能,可以跑但不可靠、难维护。
方案三:注册时标记(最终采用)
SimpleTaskHandler = Callable[[dict], Awaitable[dict]]
InterruptTaskHandler = Callable[[dict, TaskRecord, TaskDispatcherService], Awaitable[dict]]
TaskHandler = SimpleTaskHandler | InterruptTaskHandler
# handler注册处
dispatcher.register_handler(TaskType.RAG_CHAT, run_rag_chat_task, supports_interrupt=True)
dispatcher.register_handler(TaskType.PDF_STRUCTURED, run_pdf_structured_task) # 默认 False
_dispatch 根据 supports_interrupt 决定传几个参数,意图最清晰。
typing 模块
Python 是动态类型语言,运行时不强制检查类型。
typing 提供的工具是给类型检查器(Pyright、mypy)和开发者看的,运行时没有实际约束。
容器类型
list[str] # 字符串列表
dict[str, Any] # 键为 str 的字典
tuple[int, str] # 固定结构元组
函数类型
Callable[[int, str], bool] # 接收 int/str,返回 bool 的函数
Awaitable[dict] # async 函数的返回值
特殊标记
Any # 任意类型,跳过检查
Literal["a", "b"] # 只能是这几个值
Optional[str] # str 或 None,等价于 str | None
NotRequired[str] # TypedDict 里的可选字段
结构描述
TypedDict # 描述 dict 的字段结构,项目中 AdaptiveRagState 用的就是这个
Protocol # 描述任意对象必须实现的接口
注:SimpleTaskHandler = Callable[[dict[str, Any]], Awaitable[dict[str, Any]]]这个“=”号是定义类型别名本身
浙公网安备 33010602011771号