Appearance
配置和依赖应该在哪一层注入:Configuration 与 Dependency Boundary
前置:如何隔离外部依赖做测试:pytest、Fixtures 与 Test Doubles
核心问题:环境变量、Run policy、Secret 和具体 Adapter 依赖应该在哪一层注入,怎样避免 Domain 直接读取全局配置并耦合基础设施?
最难调试的配置往往是“谁都能读”的配置
如果 Planner、ToolExecutor、HTTP route、DB adapter 都随手 os.getenv(),同一 Run 到底用了什么 policy 很难回答。测试也只能不停 monkeypatch 全局环境。
Harness 更适合把依赖和配置在composition root 一次组装:
text
process startup
→ load + validate static config
→ create provider/db/redis/http adapters
→ wire Runtime services
→ request creates per-run policy
→ executeDomain/Runtime 使用自己定义的 Protocol,不负责寻找依赖。
四类“配置”不要混成一个 Settings 对象
Static process config
例如 DB DSN、worker role、server port、默认 pool size。启动时加载并验证,错误应 fail fast。
Secret
API key、OAuth token、database password。它们可能通过 secret manager/environment 注入,但不应该被复制到 RunState、Prompt、Trace 或普通 config dump。
Per-run policy
例如 max_turns、cost budget、allowed tools、deadline、approval policy。它属于这次执行事实,往往要跟 Run 一起记录版本/快照。
Dynamic control-plane config
例如限流策略、模型 routing policy。它可能运行时更新,但要明确“新 Run 生效还是正在运行的 Run 也改变”,否则恢复行为不可解释。
把这四类都塞进 Settings 并到处传,会混淆生命周期和持久化要求。
Runtime 依赖 Protocol,Adapter 依赖 SDK
例如:
python
class ModelClient(Protocol):
async def generate(self, request: ModelRequest) -> ModelResponse: ...
class StateStore(Protocol):
async def load_run(self, run_id: str) -> RunState: ...
async def commit(self, change: StateChange) -> None: ...Runtime 接受 ModelClient 和 StateStore,但不知道它们背后是哪个 Provider SDK、Postgres 还是测试 Fake。
Composition root 才写:
python
model = OpenAIModelAdapter(...)
store = PostgresStateStore(...)
runtime = Runtime(model=model, state_store=store, ...)这样“换 Provider”“内存测试”“CLI/HTTP 两种入口”都不需要修改 Runtime 内核。
Route/CLI/Worker 是 Adapter,不是 Domain owner
FastAPI route 的职责应类似:
text
HTTP DTO
→ validate authentication/request shape
→ domain command
→ RunService
→ HTTP response不要在 route 里直接创建 AsyncOpenAI()、执行 Tool、写 checkpoint。否则 HTTP 生命周期和 Run 生命周期被绑死,后面做 queue worker、resume 或测试会非常困难。
Worker 同理:它负责拿 work item、claim ownership、调用 Runtime;业务状态迁移仍由 Runtime/StateStore contract 负责。
配置如何进入 Run Manifest
并不是把所有环境变量 dump 进去。Manifest 应记录影响行为且允许记录的版本/策略,例如:
text
model/provider/version
prompt/instruction version
tool catalog version
runtime policy version
retrieval/index version
application commitSecret 只记录引用/identity,不记录 secret value。这样失败 Run 能解释“当时运行了什么”,又不会把凭据变成长期审计数据。
最小测试
Configuration/DI 最值得测三件事:非法启动配置 fail fast;Runtime 测试可完全注入 Fake Model/Tool/StateStore 而不访问网络;同一 Run 的 policy snapshot 不会因进程全局配置后来变化而悄悄改变。
如果为了测试 Runtime 还必须启动 FastAPI、真实数据库和 Provider SDK,说明 dependency boundary 已经泄漏。
自测
- Domain 对象内部直接
os.getenv("API_KEY")会带来什么测试和依赖问题?更合适的注入点在哪里? - 环境配置、每个 Run 的 policy、Secret、Adapter wiring 为什么不应该都塞进同一个全局 Settings 对象?