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

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

前置:会运行 Python;本模块仅在 Shadow Harness 实现需要时按需补齐

shadow harness.runtime 只依赖 domain protocol;adapters.openai 可以依赖 SDK,反向依赖禁止。CI 使用 lockfile 创建环境,避免开发机安装了未声明依赖导致线上 ImportError。

Mental Model ​

text
Project config/types → domain implementation → controlled resources/errors → tests → reproducible package/runtime

Python 工程底座的目标是把依赖、类型、资源、异常、日志和测试变成清晰边界,使 Agent Runtime 的复杂逻辑可以被静态检查和确定性测试。

Project / Package ​

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

项目结构应通过 package/module 边界表达 domain、adapter、runtime 依赖方向,避免所有代码堆在 utils.py 。可安装 package 也让测试/CLI/import 行为一致。

uv / dependency management ​

uv 负责解释器/依赖/lock 等工程流程;关键不是命令本身,而是 pyproject.toml + lockfile + requires-python 让本地、CI 和部署解析同一依赖图。

依赖管理要固定 direct dependencies、lockfile、Python 版本和可重复环境。uv 可统一 project env、lock、run,但工具选择次于“版本可重建”原则。

typing ​

Typing 的价值是把 ModelRequest、ToolCall、RunState 这类内部 contract 在开发期显式化,让 static checker 提前发现字段和接口漂移。它不负责证明外部数据可信:来自 JSON、LLM 或网络的输入仍需运行时 schema validation。

具体什么时候用 Protocol、ABC、dataclass 或 Pydantic,放到下一篇单独讲,避免在项目基础章节提前重复。

类型提示用于表达接口 contract、Optional/union、generic 和静态检查,尤其能约束 Agent 的复杂 state/tool 类型。它不是运行时 validation 的替代。

最小实现 / 数据结构 ​

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

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

Failure Modes ​

依赖版本不可复现 ​

症状: 开发机只有 pip install 历史,没有 lock/pyproject。

为什么危险: 本地能跑、CI/部署失败,升级变化也不可追踪。

处理: pyproject.toml + uv lock 固化依赖,CI 按 lock 安装。

用 Any/裸 dict 穿透核心 Domain Boundary ​

症状: Model/Tool/State 之间传任意 dict。

为什么危险: 字段错误只在运行深处暴露,重构缺少保护。

处理: 用 Protocol/typed dataclass/Pydantic 表达跨层 contract。

如何验证 ​

Case断言
依赖版本不可复现清空环境两次安装,解析版本一致且测试相同。
用 Any/裸 dict 穿透核心 Domain Boundarytype checker + unit test 让错字段/错类型在边界处失败。

自测 ​

  1. 两台机器从同一仓库安装后得到不同依赖版本,哪一部分项目配置没有真正形成可复现环境?
  2. 为什么 domain 层的类型接口应尽量依赖稳定抽象,而不是直接 import 某个 Provider SDK 的具体类?

Sources ​

AI Engineering · Agent · Harness · Backend Systems