Skip to content

项目结构、uv 与类型系统

1. 为什么这组知识要一起学

补齐构建 Shadow Harness 所需的 Python 工程基础,重点放在接口边界、类型、测试、异常和依赖管理。

这几个知识点处在同一条工程链上。如果只记单个名词,很容易在真实系统里把责任放错层:例如让模型管理程序事实、让数据库 transaction 承担外部 API 原子性,或把一个 provider SDK 的行为误认为 Agent 的通用规律。

2. Mental Model

Python 工程化目标不是学语言冷知识,而是让 Runtime 的类型、配置、依赖、错误和测试边界清晰可维护。

text
pyproject/uv → package boundaries → typed domain models → validation → services/adapters → tests/config

3. 核心机制

Project / Package

pyproject.toml 作为项目根和依赖声明,uv.lock 固定解析版本;.python-version/requires-python 明确解释器边界,便于本地与部署复现。

uv / dependency management

pyproject.toml 作为项目根和依赖声明,uv.lock 固定解析版本;.python-version/requires-python 明确解释器边界,便于本地与部署复现。

typing

typing 提供静态契约;Protocol 适合 adapter/port 的结构化接口,ABC 适合需要共享实现和显式继承的层。

4. 最小实现 / 伪代码

下面代码只表达边界和生命周期,不要求照抄到项目中:

python
class ToolPort(Protocol):
    async def execute(self, args: ToolArgs) -> ToolResult: ...

class ToolArgs(BaseModel):
    query: str
    limit: int = 20

真正实现时应把 I/O、状态持久化、错误翻译和策略注入拆成可测试组件,而不是把示例扩成一个巨型函数。

5. 在 Shadow Harness 中怎么落地

src/shadow_harness 按 domain 分包;Protocol 定边界,Pydantic 处理外部数据验证,dataclass 处理内部轻量状态。

建议为本章涉及的行为留下明确的 domain object、interface 和 trace event;只要一个关键行为只能通过读日志猜测,就说明 Runtime contract 仍不够清晰。

6. Production Engineering 检查项

  • 锁定依赖与 Python 版本
  • 异常分层
  • logging 结构化
  • 配置与 secret 分离
  • fixture/fake 优先于过度 mock

7. Failure Modes

7.1 循环 import

当出现「循环 import」时,用类型检查、异常链、dependency graph 和 isolated tests 定位;优先消除隐式全局状态和 provider SDK 泄漏。 这类问题通常需要修改 contract、policy、state 或 adapter,而不是只追加 Prompt。

7.2 mutable default

当出现「mutable default」时,用类型检查、异常链、dependency graph 和 isolated tests 定位;优先消除隐式全局状态和 provider SDK 泄漏。 这类问题通常需要修改 contract、policy、state 或 adapter,而不是只追加 Prompt。

7.3 except Exception 后吞掉

当出现「except Exception 后吞掉」时,用类型检查、异常链、dependency graph 和 isolated tests 定位;优先消除隐式全局状态和 provider SDK 泄漏。 这类问题通常需要修改 contract、policy、state 或 adapter,而不是只追加 Prompt。

7.4 全局 singleton 隐式依赖

当出现「全局 singleton 隐式依赖」时,用类型检查、异常链、dependency graph 和 isolated tests 定位;优先消除隐式全局状态和 provider SDK 泄漏。 这类问题通常需要修改 contract、policy、state 或 adapter,而不是只追加 Prompt。

7.5 测试绑定真实网络

当出现「测试绑定真实网络」时,用类型检查、异常链、dependency graph 和 isolated tests 定位;优先消除隐式全局状态和 provider SDK 泄漏。 这类问题通常需要修改 contract、policy、state 或 adapter,而不是只追加 Prompt。

8. Trade-offs

ABC 提供显式继承;Protocol 支持结构化 typing;内部边界通常优先 Protocol,生命周期复杂时可用显式 base class。

设计记录最好明确:当前约束是什么、备选方案有哪些、为什么现在选这个、未来什么条件出现时需要重构。 这样 ADR 才能随着模型和基础设施变化被重新审视。

9. Experiment / Evaluation

从一个直接 SDK 脚本重构为 package + Protocol + fake adapter + pytest,观察可替换性和测试速度。

实验应固定数据集、版本和环境,至少记录 success、latency、token/cost、attempt/step 数以及失败类型;涉及随机模型时需要重复运行而不是只看一次结果。

10. 常见问题

基础:Project / Package 最容易被误解的点是什么?

pyproject.toml 作为项目根和依赖声明,uv.lock 固定解析版本;.python-version/requires-python 明确解释器边界,便于本地与部署复现。

机制:这些能力在一次 Run 的哪个生命周期阶段生效?

沿着 pyproject/uv → package boundaries → typed domain models → validation → services/adapters → tests/config 找位置,并明确它的输入、输出、持久化事实和失败传播。

工程:如果这一层失败,应该由谁恢复?

先区分 transient failure、invalid input、permission、semantic failure 与 irreversible side effect。恢复策略属于拥有该状态与副作用的 Runtime/adapter,而不是交给模型自由决定。

设计:规模扩大 10 倍后,哪个假设最先失效?

优先检查 context/token、并发/连接池、catalog 大小、持久化吞吐、trace 体积、身份与租户隔离。不要默认“多加机器”能解决语义和一致性问题。

11. Sources

12. 本文结论

掌握本文的标准不是能背定义,而是能画出数据流、写出最小 contract、解释失败恢复,并用测试或 benchmark 证明设计没有只停留在概念层。

AI Engineering · Agent · Harness · Backend Systems