Skip to content
预计 1 分钟右侧目录可快速定位

核心接口怎么建模:Protocol、ABC、dataclass 与 Pydantic ​

前置:Python 项目怎样可复现又可维护:Package、uv 与 Typing

先分成“行为接口”和“数据边界” ​

Harness 里常见两种问题:

text
谁能实现 ModelClient / StateStore?      → 行为 contract
外部 JSON / API / LLM 输出是否合法?     → 数据 validation

把两者混在一起,很容易让 Provider SDK、ORM 或 Pydantic model 穿进 Runtime 核心。

Protocol / ABC:表达“对象能做什么” ​

Protocol 适合结构化接口:对象只要满足方法签名即可,不要求继承同一个基类,因此 Fake、不同 Provider Adapter 更容易替换。

ABC 适合确实需要显式继承、共享实现或统一 lifecycle 的类层次。Harness 的 port/adapter 边界通常优先从 Protocol 开始,只有有明确共享行为时再使用 ABC。

python
class ModelPort(Protocol):
    async def generate(self, request: ModelRequest) -> ModelResponse: ...

FakeModel 和 OpenAIAdapter 都能实现这个 contract,而 Runtime 不需要知道它们继承自谁。

dataclass:表示已经进入 Domain 的内部值 ​

dataclass 很适合 RunLimits、Usage、RetryDecision 这类内部 typed value。它表达结构和不可变性,但默认不负责把来自网络/JSON 的脏数据校验成可信对象。

python
@dataclass(frozen=True)
class RunLimits:
    max_turns: int
    deadline_at: datetime

Pydantic:把不可信输入挡在边界上 ​

API body、配置、Structured Output 等外部输入更适合先用 Pydantic 做解析、类型转换和 schema validation。验证通过后,再映射成内部 domain object,避免整个 Runtime 都依赖外部 wire format。

因此可以记成:

text
Protocol / ABC → 行为边界
Pydantic       → 外部数据边界
Dataclass      → 内部 domain value

这三个工具不是互相替代,而是解决不同层的问题。

自测 ​

  1. 一个对象只需要满足 generate(request)->response 行为,而不要求继承体系;这里更适合 Protocol 还是 ABC?为什么?
  2. 外部 JSON、内部 RunState、可替换行为接口分别更适合 Pydantic、dataclass、Protocol/ABC 中的哪一类?

Sources ​

AI Engineering · Agent · Harness · Backend Systems