Appearance
核心接口怎么建模:Protocol、ABC、dataclass 与 Pydantic
先分成“行为接口”和“数据边界”
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: datetimePydantic:把不可信输入挡在边界上
API body、配置、Structured Output 等外部输入更适合先用 Pydantic 做解析、类型转换和 schema validation。验证通过后,再映射成内部 domain object,避免整个 Runtime 都依赖外部 wire format。
因此可以记成:
text
Protocol / ABC → 行为边界
Pydantic → 外部数据边界
Dataclass → 内部 domain value这三个工具不是互相替代,而是解决不同层的问题。
自测
- 一个对象只需要满足
generate(request)->response行为,而不要求继承体系;这里更适合 Protocol 还是 ABC?为什么? - 外部 JSON、内部 RunState、可替换行为接口分别更适合 Pydantic、dataclass、Protocol/ABC 中的哪一类?