news 2026/10/16 1:48:23

ECC Python 模式规则完全指南:Protocol、Dataclass DTO 与 with 的 4 条纪律,让 Agent 写的代码可控

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ECC Python 模式规则完全指南:Protocol、Dataclass DTO 与 with 的 4 条纪律,让 Agent 写的代码可控

ECC Python 模式规则完全指南:Protocol、Dataclass DTO 与 with 的 4 条纪律,让 Agent 写的代码可控

【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC

ECC 是一套面向 Claude Code、Codex、Opencode、Cursor 的 Agent Harness Performance Optimization 系统(技能库、规则引擎、记忆与安全)。它内置的 rules/python/patterns.md 把 Python 编码纪律浓缩为 4 条:Protocol 鸭子类型、Dataclass DTO、上下文管理器、生成器,且每条都能在仓库 src/llm 目录的真实实现里找到对应证据。本文先拆机制,再翻源码对账,最后给出三步落地法和一份避坑清单。

为什么 Agent 写 Python 总是翻车?先把规则文件找对位置

Agent 生成的 Python 代码常见的毛病很固定:业务函数里直接 new 一个数据库连接、跨层传数据靠一坨散装 dict、文件句柄开了就忘关。根因是上下文里缺"硬约束"——你不写下来,它就不会遵守。

ECC 的做法是把约束写成 Markdown 按技术栈分目录存放。Python 专项那份是 rules/python/patterns.md,全文不到 40 行,只写了四件事:

  • Protocol (Duck Typing)——结构化鸭子类型
  • Dataclasses as DTOs——用 dataclass 承载跨层数据
  • Context Managers & Generators——with管资源、生成器省内存
  • Reference——指向更深的技能库python-patterns

这个文件开头有一句自我定位(英文原文大意):它不是独立规则,而是对跨语言公共模式规则 rules/common/patterns.md 的 Python 扩展。西班牙语版在 docs/es/rules/python/patterns.md,同目录还躺着 coding-style、testing、security、hooks 四份兄弟文件。理解这条规则要看三层:底层是公共模式里的 Repository 模式与 API 响应信封约定,中间层就是这四个 Python 惯用法,外层是更完整的 skills/python-patterns/SKILL.md。

规则自动加载的关键在文件头几行

规则写出来却加载不到,等于白写。这份文件的 YAML frontmatter 只有两条 glob:**/*.py和**/*.pyi。意思是——只要 Agent 在仓库里碰任何 Python 源码或类型桩文件,这条规则就会命中并进入上下文,不需要人工提醒"写 Python 前记得看模式规则"。

规则文件提示:同目录下每份规则都有各自的 paths 声明,合起来覆盖风格、模式、测试、安全四个维度。也就是说"什么时候生效"这件事,是文件头自己声明的,不是靠人记。

Protocol 结构化类型:只看形状,不查身份证

规则给出的第一个模式是Protocol。打个比方:传统抽象类像查身份证——你必须拿着"我继承自 Repository"这张证才进门;Protocol像看形状——你身上只要有find_by_id和save这两个方法、签名对得上,谁放进来都能用,继承关系可有可无。

规则文件里的示例是这样一个数据访问接口:

from typing import Protocol class Repository(Protocol): def find_by_id(self, id: str) -> dict | None: ... def save(self, entity: dict) -> dict: ...

效果是:数据库实现、内存实现、HTTP 远端实现,只要方法签名匹配,mypy / pyright 都认定它兼容Repository,业务代码一行不用改就能替换数据源。

那 ECC 自己为什么有时用 ABC?

这是个好问题。翻开 src/llm/core/interface.py 会发现,抽象 LLM 提供商时仓库选的是经典ABC而不是Protocol:LLMProvider继承ABC,generate、list_models、validate_config三个方法挂@abstractmethod强制实现,同时supports_tools直接return True、supports_vision直接return False,作为所有实现共享的默认行为。

两者分工其实是清晰的:

  • 接口是自家实现族(一堆 Provider 子类)都要遵守的契约,且要提供公共默认实现、漏写关键方法就该报错时 → 用ABC;
  • 约束对象是外部传进来的数据,你只关心"形状对得上"、不想让调用方被迫继承一个类时 → 用Protocol。

往上看一层:它接的是 Repository 模式

父文档 rules/common/patterns.md 定义了 Repository 的设计纪律:数据访问封装在统一接口后面,标准操作是 findAll / findById / create / update / delete 这一套语义,业务逻辑只依赖抽象接口而非存储机制。上面那个Repository(Protocol)示例正是这套纪律的 Python 化——find_by_id对应 findById,save对应 create/update。推荐把 Protocol 放领域层,具体存储放基础设施层,mock 测试和换数据源都会变简单。

Dataclass DTO:跨层传数据的形状,一次写死

第二个模式是 dataclass 当 DTO(数据传输对象)。规则示例是一个建用户的请求:name: str、email: str必填,age: int | None = None表示可空可省。@dataclass自动生成__init__、__repr__、__eq__,DTO 保持薄、可比较、可打印。

光有形状还不够。结合技能库 Dataclasses 章节,实战加固有三招:

  1. 可变默认值用field(default_factory=...)。写tags: list[str] = []会让所有实例共享同一个列表,这是 Python 经典暗坑;default_factory=list才是正解。
  2. 不需要改的 DTO 加frozen=True,字段构造后禁止改写,数据流向每一层都不会被顺手污染。
  3. 构造期校验塞进__post_init__,邮箱格式、年龄范围这类检查在对象诞生那一刻就拦下,不让脏数据流到后面。

DTO 走到 HTTP 边界时还有一层约定:父规则要求所有 API 响应走统一信封——成功/状态指示、数据负载(出错时可空)、错误信息(成功时可空)、分页元数据total/page/limit。分工是:DTO 定"内部传输的形状",信封定"对外暴露的形状",两层分开,接口才不容易漂移、字段才不容易泄漏。

with 和生成器:把资源释放和低内存变成默认行为

规则文档在这两块只给了两条一句话纪律,但含金量最高:资源用上下文管理器(with语句)管理,迭代用生成器做懒求值。

with:不写 finally 也能保证资源必还

Python 没有确定性的析构时机,文件句柄、连接、锁都要显式归还。手写 try/finally 正确但啰嗦,with把"获取 → 使用 → 必然释放"固化成语法。技能库给了两种自定义写法:函数式(@contextlib.contextmanager包一层计时逻辑)适合轻量场景;类式实现__enter__/__exit__适合需要按有没有异常决定提交还是回滚的事务场景:

class DatabaseTransaction: def __enter__(self): self.connection.begin_transaction() return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is None: self.connection.commit() else: self.connection.rollback() return False # 不吞异常

最后一行是重点:__exit__返回False表示异常继续向上传播,只有刻意想"消化"异常才返回True。很多团队的事务 Bug 就出在这里写反了。

生成器:从"整份数据"降级为"一次一行"

对百万级数据求和,sum(x * x for x in range(1_000_000))任意时刻内存里只有一个数;换成列表推导[...]则先撑出一整张列表再求和。技能库里的典型配方是逐行读大文件:with open(path) as f套一个yield line.strip()的生成器函数——with保证迭代结束文件必关,yield保证内存里只有一行。两条纪律在这里咬合成一个整体。

另外它提醒了一个可读性问题:一行里叠两层以上条件的推导式(比如两个if连写)建议直接回退成普通 for 循环。可读性优先于炫技。

打开仓库源码:src/llm 目录里的三处证据

光懂概念不够,直接翻仓库代码看看。src/llm 目录本身就是这套规则的活体样例。

证据一:ABC 定义边界。前面已验证LLMProvider(ABC)的用法。同文件还定义了一棵异常树:LLMError为基类,下挂AuthenticationError、RateLimitError、ContextLengthError、ModelNotFoundError、ToolExecutionError——正好对应技能库"自定义异常层级"那一章,错误分类在接口层就定了型。

证据二:frozen dataclass 层层传递。打开 src/llm/core/types.py,Message、ToolDefinition、ToolCall、ToolResult、LLMInput、LLMOutput、ModelInfo全部是@dataclass(frozen=True)。以LLMInput为例:

@dataclass(frozen=True) class LLMInput: messages: list[Message] model: str | None = None temperature: float = 1.0 max_tokens: int | None = None stream: bool = False metadata: dict[str, Any] = field(default_factory=dict)

str | None可空写法贯穿全文件,与规则示例一模一样;扩展性字段metadata用field(default_factory=dict)收尾,不污染强类型主字段。

证据三:序列化随 DTO 内聚。ToolDefinition除了to_dict(),还提供了to_openai_tool()和to_anthropic_tool()(同文件 L63-L79)——"同一份数据怎么转成 OpenAI 协议、怎么转成 Anthropic 协议"封装在对象自己身上。层间契约稳定,字段变化只改一处。

顺带一提,src/llm/core/...这种 src-layout 目录结构,本身就是技能库"包组织"一章推荐的形态。

从"短约束"到"长配方":ECC Python 规则的三级结构

把前面提到的三层捋成一条递进链:

  • 第一级:rules/common/patterns.md。回答跨语言的架构问题——Repository 怎么封装、API 响应信封长什么样。
  • 第二级:rules/python/patterns.md。回答"在 Python 里怎么落地"——Protocol、Dataclass DTO、with、生成器。它是短约束,40 行封顶。
  • 第三级:skills/python-patterns/SKILL.md。回答"更复杂的问题用什么配方"——类型注解与 TypeVar 泛型、错误处理与异常链、三种装饰器写法、线程池/进程池/asyncio 并发、src-layout 包组织、__slots__与join的内存优化,外加black/isort/ruff/mypy/pytest/bandit/pip-audit的完整工具链。它的 frontmatter 明确写了激活时机:写新代码、评审代码、重构、设计 Python 包/模块时。

短约束保证 Agent 随时被提醒"底线在哪",长配方保证遇到具体问题时有现成骨架可抄。

落地三步:把 Python 模式规则注入 Agent 并跑通工具链

第一步,靠路径自动加载,不靠自觉。凡.py/.pyi文件都会命中 patterns 规则,同目录的 coding-style.md(PEP 8 细节)、testing.md(pytest 纪律)、hooks.md(ECC hook 集成)、security.md 各管一摊,四个维度同时生效。规则文件是 Markdown 约束文档,可直接作为系统提示或规则注入内容喂给 Claude Code、Codex、Opencode、Cursor 等 Harness——这正是 ECC 的标准用法。

第二步,遇到具体问题再去技能库抄配方。装饰器怎么带参、并发怎么选池、包怎么拆,按 SKILL.md 里的代码骨架落即可,不必整篇塞进上下文。

第三步,让机器侧检查兜底。跑ruff check .、mypy .、pytest做机器验证;技能库里的pyproject.toml模板([tool.black]、[tool.ruff]、[tool.mypy]、[tool.pytest.ini_options]四段)可以直接和仓库根目录真实的 pyproject.toml 对齐。人定的规则和机器做的检查,各管一半。

常见坑:五类要主动避开的反模式

技能库末节列了一份反模式清单,与前面四条纪律正好一一对照,值得贴在心里:

  • 可变默认参数:def f(items=[])让所有调用共享同一列表;改成items=None再函数内建,或用field(default_factory=...)。
  • type(obj) == list判类型:绕过了继承关系,子类全被拒之门外;一律isinstance。
  • == None比较:应该is None,避免对象重写__eq__后行为漂移。
  • from module import *:命名空间污染且无法追踪来源,显式写出要导的符号。
  • 裸except:吞异常:except: pass等于把故障埋进土里;只捕获具体异常,必要时raise ... from e保住异常链。

外加一条风格纪律:推导式条件叠两层以上就拆回 for 循环——这条在技能库的"可读性优先"原则下反复出现。

写在最后

规则管"底线在哪",技能库管"配方怎么抄",src/llm管"真实代码长这样"——三层咬合起来,Agent 写出的 Python 才不只是能跑,而是可静态检查、可测试、可长期演进的结构化资产。

【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 1:45:18

ResNet-152农业定制版:植物病害识别流水线实战

简介:本资源是基于ResNet152深度卷积神经网络构建的植物病害智能识别模型,面向人工智能初学者、计算机视觉实践者及农业智能化应用开发者,解决植物叶片图像细粒度分类(共38类病害)这一典型迁移学习任务。压缩包含2000个…

作者头像 李华
网站建设 2026/10/16 1:44:43

Netty线程模型与核心组件实战:从EventLoop到Handler避坑指南

Netty 这套东西我用了差不多两年多,期间从零搭过推送服务、网关、私有协议解析,也在生产环境里排查过内存泄漏和线程阻塞的问题。回过头看,最值得掰开揉碎讲清楚的,还是它的线程模型和那几个核心组件。倒不是因为面试爱问&#xf…

作者头像 李华