OpenSRE 核心代码库命名规范:让 `core/` 里的每个文件与类型名如其义
2026/9/15 14:29:19 网站建设 项目流程

OpenSRE 核心代码库命名规范:让core/里的每个文件与类型名如其义

【免费下载链接】opensreBuild your own AI SRE agents. The open source toolkit for the AI era.项目地址: https://gitcode.com/GitHub_Trending/op/opensre

本文是 OpenSRE 开源仓库core/目录的命名规范技术指南。它定义了一套小而可执行的词汇表与命名纪律,目标是让任何读者仅凭文件名与类型名,就能区分“数据类型”与“运行进程”、“可变状态”与“冻结快照”,并推断出某个包的核心职责。读完本文,你将掌握 OpenSRE 核心包(docs/NAMING.md)中从模块命名、类型命名到导入路径的完整约定,并理解这些约定在 core/agent 与 core/agent_harness 源码中的真实落地方式,可以直接指导你在此仓库中的阅读、评审与二次开发。

一、术语表:一个术语只表达一个含义

OpenSRE 的命名哲学首先体现在“一词一义”上。core/中最容易混淆的几个概念,在规范中都被严格收敛为单一含义:

术语含义仓库示例
State在一次运行中逐步演变的可变 agent/会话事实AgentState、harness 端口SessionState
Storage/Repo持久化存储后端(而非内存中的回合端口)SessionStoreSessionRepoJsonlSessionStore
Snapshot在某个边界(回合开始、运行开始)捕获的冻结视图TurnSnapshot
RunInput/RunResult一次Agent.run()边界的输入与输出AgentRunInputAgentRunResult
Resources单次工具调用中传给工具执行器的句柄ToolCallResources
BudgetLLM token/上下文窗口策略——不是应用状态enforce_token_budget
Host算法所驱动的回调契约(一个ProtocolLoopHost

这组区分在源码中都有对应实体,且命名与职责高度一致:

  • State 是“活”的,Snapshot 是“冻”的SessionState(定义于 core/agent_harness/ports.py)是一个@runtime_checkableProtocol,描述引擎在回合中读写的可变字段——会话历史historysession_id、推理强度reasoning_effort、集成解析缓存resolved_integrations_cache等;而TurnSnapshot(core/agent_harness/turns/turn_snapshot.py)是回合开始时通过TurnSnapshot.from_session构建一次的不可变上下文快照,下游 prompt 构建器只读它,写入仍走实时 session。一个词是"活状态",另一个词是"边界冻结视图",互不越界。
  • State 与 Storage 分家InMemorySessionState(core/agent_harness/turns/headless_adapters.py)是无头(headless)模式下的内存态;而SessionStore/SessionRepo是两个持久化Protocol(core/agent_harness/session/persistence/contracts.py 与 #L133),JSONL 落地实现是JsonlSessionStore(core/agent_harness/session/persistence/jsonl_store.py)。"Store/Repo" 一词只留给持久化后端。
  • Budget 不是状态:上下文预算控制由 core/context_budget.py 中的enforce_context_budget(#L394)等纯函数承担,它是一套 token/window 策略,不属于会话状态字段,因此名字里不带任何 State 字样。

二、模块命名:{domain}_{role}.py

规范要求:用文件所承载的概念命名,而不是用笼统的"桶"词core/agent/目录是教科书式的范例,七个文件各司其职、一读即懂:

core/agent/ agent.py # the Agent facade(门面) react_loop.py # ReactLoop + run_react_loop(算法本身) loop_host.py # LoopHost(回调契约) run_io.py # AgentRunInput, AgentRunResult(运行边界的 I/O) mixins.py # the reusable *Mixin behaviors(可复用行为) provider_hooks.py # ProviderHookDelegate

从源码可以验证这组职责划分是真实、可执行的:

  • agent.py里的Agent是一个薄门面:它持有配置(LLM、system prompt、工具、迭代上限),run()把一次运行解析为AgentRunInput后交给run_react_loop,自身不包含循环逻辑(见 core/agent/agent.py);
  • react_loop.py是"思考 → 调用工具 → 观察结果"的循环算法本体,ReactLoop运行循环、run_react_loop是单行函数式入口,循环本身对Agent一无所知(core/agent/react_loop.py);
  • loop_host.py只声明LoopHost这一个Protocol,即循环需要驱动方提供的回调集合(core/agent/loop_host.py)。

文件名与内容一一对应、无歧义,这就是{domain}_{role}.py约定的价值:看到react_loop.py就知道是算法,看到loop_host.py就知道是回调契约,看到run_io.py就知道是边界数据。

三、类型命名三条铁律

3.1 Mixin 必须带Mixin后缀

Mixin 不能独立存在——它们假设宿主提供了某些字段/方法。因此规范要求强制携带后缀,例如EventEmitterMixinToolFilterMixinSteeringMixin。在 core/agent/mixins.py 中:

  • EventEmitterMixin(kind, data)元组事件与类型化运行时事件分发给监听回调,且回调失败会被吞掉——事件渲染绝不能打断循环;
  • ToolFilterMixin提供_filter_tools钩子,用于收窄 agent 可见的工具列表(默认恒等);
  • SteeringMixin提供steer()(在下一次 LLM 回合前注入用户消息)与follow_up()(在循环本将停止时追加消息)。

Agent正是由EventEmitterMixin, ToolFilterMixin, SteeringMixin组合而成(core/agent/agent.py),并在__init__中初始化 mixin 依赖的_steering_messages/_follow_up_messages队列。后缀即契约:看到Mixin,就知道它依赖宿主环境、不能单独实例化。

3.2 Protocol 按角色命名,不挂Protocol后缀

与标准库IterableSupportsRead的风格一致,OpenSRE 的Protocol按"它是什么角色"命名,而不是叫XxxProtocolLoopHost就是典型——它不叫LoopHostProtocol。这一点在 core/agent_harness/ports.py 中同样成立:OutputSink(输出渲染)、SessionState(会话可变状态)都是按角色命名的Protocol

结构化的好处是:Session无需继承SessionState,只要字段与方法结构匹配即可满足协议(鸭子类型),run_react_loop只依赖LoopHost+AgentRunInput,任何具备这些方法的对象都能驱动循环,不必认识Agent

3.3 不要用包名给类型加前缀

core/agent/内部,类是EventEmitterMixin而不是AgentEventEmitter——命名空间本身已经声明了 "agent",重复前缀纯属噪音。这一点在 core/agent/init.py 的包注释中得到呼应:每个文件只放一个职责,类型名自解释,无需包名前缀兜底。

四、反模式清单:新代码不要踩的坑

规范明确列出了core/中禁止新增的反模式,每一类都有清晰的替代方案:

反模式问题正确做法
context.py(出现在core/core/agent/根)"context" 一词在仓库中已严重重载直接命名概念,如run_io.pyturn_snapshot.py
只装运行 I/O 的models.py过于含糊,无法表达模型是什么说清模型用途,如run_io.py
无领域前缀的*Context(当已有同名类型存在时)产生歧义与误引带领域前缀
包内只有一个子包的包多余的包裹层折叠掉包装层
把可变 harness 会话端口叫SessionStore"Store" 专指持久化端口是SessionState(无头模式用InMemorySessionState);JSONL 持久化保持SessionStore/SessionRepo
模块级可变全局量承载 "当前会话"隐藏依赖、难以测试显式传递SessionState(配合 no-globals 设计)

其中"Store 与 State 分家"这条在仓库中尤其重要:SessionState端口在无头/测试运行里由InMemorySessionState满足(core/agent_harness/turns/headless_adapters.py),而需要落盘时用JsonlSessionStore。同一份会话事实,内存态与持久态用完全不同的词,杜绝了"存了却不知道存哪"的歧义。

core/state/README.md(core/state/README.md)也印证了这套边界纪律:该包只放跨回合对话状态MutableAgentState与 transcript 窗口压缩助手;上下文裁剪/排序/预算逻辑留在 core/context_budget.py,终端 UI、REPL 会话状态、slash 命令留在surfaces/interactive_shell/,集成客户端留在integrations/,基础设施服务留在infrastructure/——每个词、每个目录只表达一件事。

五、导入约定:全限定路径 + 单符号重导出

代码内部使用全限定路径导入,文档/口语中使用简短的心理标签。规范给出了两组对应关系:

心理标签导入语句
ReAct 运行 I/Ofrom core.agent.run_io import AgentRunInput, AgentRunResult
ReAct 循环from core.agent.react_loop import run_react_loop
Loop 回调契约from core.agent.loop_host import LoopHost
Agent 原语from core.agent import Agent
Harness 回合快照from core.agent_harness.turns.turn_snapshot import TurnSnapshot

重导出规则同样克制:包__init__.py只为唯一权威符号做重导出,而不是把包内一切全部导出。core/agent/__init__.py(core/agent/init.py)只重导出AgentAgentRunResult两个符号——from core.agent import Agent是入口,其余符号一律走全限定子模块路径。这样做避免了from core.agent import *式的命名空间污染:读者永远知道一个名字来自哪个模块,重命名与静态分析也更为安全。

六、这套规范的实践价值

命名规范看似"文风问题",在 OpenSRE 这种多 surface(交互式 shell、gateway、headless API)共享同一核心运行时的仓库里,它实际上是可维护性的基础设施

  • 降低认知成本TurnSnapshot一定不可变、SessionState一定可变、SessionStore一定持久化——读到名字即获得契约信息,无需追读实现;
  • 保证架构边界:"Store/Repo" 只归持久化、"State" 只归运行期可变事实、"Budget" 只归 token 策略,反模式清单阻止新代码悄悄模糊这些边界(例如把内存端口误命名为SessionStore);
  • 服务代码评审与 AI 辅助开发:全限定导入 + 单符号重导出让依赖关系一目了然,LoopHost这类按角色命名的Protocol也使循环算法可以被任何结构化兼容的对象驱动,测试与替换实现都极其轻量。

如果你正在为 OpenSRE 贡献新模块,请把这份规范当作core/的"母语":先问"这个概念在术语表里叫什么",再问"文件名能否用{domain}_{role}.py说清职责",最后检查类型名是否满足 Mixin 后缀、Protocol 按角色命名、不加包名前缀这三条铁律。遵循它,你的代码在仓库中就会"名如其义"。

【免费下载链接】opensreBuild your own AI SRE agents. The open source toolkit for the AI era.项目地址: https://gitcode.com/GitHub_Trending/op/opensre

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询