- 人工智能
- AI Agent
- Agent 记忆
- RAG
【免费下载链接】EverOS
One portable memory layer for every AI agent: local-first, Markdown-native, user-owned, and self-evolving across apps, tools, and workflows.
本文是 EverOS 仓库.claude/rules/编码规则体系中module-docstring.md的深度解读与实战指南。它面向所有为src/everos/infra/、src/everos/memory/、src/everos/service/、src/everos/component/、src/everos/core/等层新增或修改 Python 模块的开发者,说明如何为每个非平凡模块撰写解释「意图与契约」的文档字符串(docstring),而非一句标签了事。读完本文,你将掌握三要素写作法(What / Invariants / External usage)、仓库内真实范例的拆解方式,以及如何在日常变更中让 docstring 与代码保持同步。
一、规则适用范围:哪些模块必须遵循
该规则的 Frontmatter 明确声明了生效的路径集合(module-docstring.md):
paths: - "src/everos/infra/**/*.py" - "src/everos/memory/**/*.py" - "src/everos/service/**/*.py" - "src/everos/component/**/*.py" - "src/everos/core/**/*.py"也就是说,规则约束的是仓库中全部「领域层(domain)与基础设施层(infra)」的代码,这与 docs/engineering.md 中描述的 DDD 分层思想一脉相承:这些目录承载了记忆持久化、OME 调度引擎、搜索编排、组件能力门控等系统关键路径,模块之间的契约一旦被破坏,代价是隐性的运行时错误,而非直观的语法报错。因此规则要求:每个非平凡模块必须以文档字符串开头,解释它的意图与契约,而不是只写一行标签。
提示:
.claude/rules/是一套可选的 Claude Code 辅助配置,除了本文的模块文档字符串规则,还包含 architecture.md、imports.md、datetime-handling.md、init-py-and-reexport.md、testing.md 等。使用 Claude Code 并非贡献本仓库的前提,CI 门禁才是最终依据(见 docs/engineering.md)。
二、三要素写作法:一个合格模块 docstring 必须回答什么
规则将一个「好」的模块 docstring 拆解为三个要素,缺一不可:
要素 1:What —— 模块职责(一句话)
用一句话说清这个模块负责什么。这句话应该能回答「新读者打开这个文件时,最先需要知道的事」,而不是复述类名。例如「这个模块负责……」「这个模块是……的顶层编排器」。
要素 2:Load-bearing invariants —— 承重不变量
这是三要素中最关键、也最容易被省略的部分。规则列举的典型不变量包括:
- 分区键(partition keys):数据按什么维度隔离,跨分区读取会有什么后果;
- 什么会被写入、什么不会被写入:模块对存储是只读还是读写;
- 默认值:未显式配置时采用什么行为;
- 被忽略的开关:哪些参数/标志在该模块的特定路径下是不生效的——这是最容易让下一个工程师踩坑的地方。
规则的原话值得逐字理解:这些不变量是「读者想要安全地修改它就必须知道的规则」(the rules a reader must know to change it safely)。
要素 3:External usage —— 包门面模块的导入示例
当模块作为包门面(package facade,例如__init__.py对外暴露统一入口)时,docstring 里应附上一小段导入用法示例,让调用方无需阅读全部源码就能确认「怎么用」。
规则给出的标准示例
规则文档本身附带了一个来自 src/everos/memory/search/manager.py 的缩写示例:
"""SearchManager — top-level orchestrator for POST /api/v2/memory/search. Hard partition by owner_type: user → episodes (+ profiles), agent → agent_cases + agent_skills. The manager never writes to storage; it only reads LanceDB + markdown. """短短三行就覆盖了:What(搜索端点顶层编排器)、Invariants(按owner_type硬分区、只读存储)、External usage 的雏形(端点定位)。接下来我们到真实源码中看完整版本。
三、范例拆解:SearchManager的完整 docstring 究竟写了什么
在 src/everos/memory/search/manager.py 中,真实的模块 docstring 比规则示例更长、更完整,逐段拆解如下:
"""SearchManager — top-level orchestrator for ``POST /api/v2/memory/search``. Hard partition by ``owner_type``: * ``user`` → ``episodes`` (+ ``profiles`` when ``include_profile=true``) * ``agent`` → ``agent_cases`` + ``agent_skills`` Per kind, :func:`memory.search.adapter.resolve_pipeline` decides whether the path is "single-route recall, no fusion" (``KEYWORD`` / ``VECTOR``) or "sparse + dense → everalgo.rank" (``HYBRID`` / ``AGENTIC``). Component guards (embedding / cross-encoder / LLM) raise early when a method is selected without its prerequisites. ``HYBRID`` defaults to **no LLM rerank** — the response comes back straight after the heap-expand pipeline (RRF-ordered expansion → LR-calibrated global top-N competition with fact eviction). ``enable_llm_rerank`` is **ignored** for the hierarchy path. ``AGENTIC`` keeps its own internal cross-encoder rerank loop; the flag is ignored there. ``SearchEpisodeItem.atomic_facts`` is populated **only** when the HYBRID pipeline runs over episodes. The other methods leave it empty: there is no query-relevance score we can assign to a fact pulled by parent_id alone, and emitting ``score=0.0`` facts would muddy the contract. The manager never writes to storage; it only reads LanceDB + markdown. """这个范例把三要素体现得淋漓尽致:
- What:第一行即点明「POST /api/v2/memory/search 的顶层编排器」;
- Invariants:
- 按
owner_type硬分区(user → episodes/profiles,agent → agent_cases/agent_skills); - 管道分派规则:
KEYWORD/VECTOR走单路召回不融合,HYBRID/AGENTIC走稀疏+稠密 → everalgo.rank; - 两个被忽略的开关:
HYBRID路径下enable_llm_rerank被忽略(默认不做 LLM 重排),AGENTIC路径下该标志同样被忽略(走内部 cross-encoder 重排循环); - 默认值与边界条件:
SearchEpisodeItem.atomic_facts仅在 HYBRID 管道处理 episodes 时填充,其余方法留空,因为「按 parent_id 拉取的事实没有查询相关性得分,输出score=0.0会污染契约」; - 只读约束:Manager 从不写存储,只读 LanceDB + markdown。
- 按
其中「被忽略的开关」是最具实战价值的信息:如果读者不知道enable_llm_rerank在 hierarchy 路径下被忽略,就会想当然地以为打开该开关能强制 LLM 重排,从而浪费一次调试会话。这正是规则反复强调「prefer prose that would save the next engineer a debugging session」(优先写能救下一个工程师一场调试的文字)的原因。
四、仓库中的更多真实范例:同一套写法在不同模块的落地
规则并非纸上谈兵——仓库中大量模块的 docstring 都遵循同一结构。这里列举几个代表性案例,帮助理解「三要素」在不同场景下的变体。
4.1GetManager:用分区表代替散文
src/everos/memory/get/manager.py 的 docstring 把不变量组织成一张清晰的映射表:
"""GetManager — top-level orchestrator for ``POST /api/v2/memory/get``. Hard partition by ``(owner_type, memory_type)`` (validated by :class:`GetRequest`): * ``user`` + ``episode`` → ``data.episodes`` * ``user`` + ``profile`` → ``data.profiles`` (one-row KV fetch from the ``user_profile`` table; at most one item) * ``agent`` + ``agent_case`` → ``data.agent_cases`` * ``agent`` + ``agent_skill`` → ``data.agent_skills`` Reads only — never writes. Filters are compiled through :func:`compile_filters_for_get` so the column allow-list stays shared with :mod:`memory.search`. Pagination + in-memory sort runs through :meth:`LanceRepoBase.find_where_paginated`. """注意它与SearchManager的呼应:同样是「硬分区 + 只读」,但分区键变成了(owner_type, memory_type)二元组,并且补充了「过滤条件列允许名单与 memory.search 共享」「分页排序走LanceRepoBase.find_where_paginated」这类跨模块契约信息。它还展示了 docstring 中的 Sphinx 风格交叉引用(:class:、:func:、:meth:、:mod:),这让 IDE 和文档生成器能把模块文档字符串与其它符号关联起来。
4.2ReflectionOrchestrator:一句话讲清流水线
src/everos/memory/reflection/orchestrator.py 用一行「Select -> Merge -> Re-extract -> Deprecate」概括了整个编排流水线,然后立刻给出不变量:合并后的 episode 写入 md、通过EpisodeExtracted事件重新抽取原子事实、原 episode 在 md frontmatter 与 LanceDB 中同时被弃用(deprecate)。读者无需读 1100 行实现,就能知道该模块对存储的两类写操作是什么、以及它们在两个存储端的一致性要求。
4.3EventDispatcher:把「门禁顺序」作为承重契约
src/everos/infra/ome/_dispatch/dispatcher.py 的 docstring 突出展示了「顺序即契约」的不变量:
"""EventDispatcher — routing layer applying the three OME gates. For each dispatched event, every candidate strategy is run through three gates in order: 1. ``enabled`` — strategy may be hot-disabled via config 2. ``applies_to`` — per-strategy predicate over the event payload 3. ``Counter`` — N-of-M rate/threshold gate against :class:`CounterStore` :meth:`dispatch` is the read-write entry point — passing the counter gate increments the counter and returns ``(meta, run_id)`` pairs to enqueue. :meth:`inspect` is its dry-run twin — same gates, no counter mutation; returns one :class:`StrategyRouteInfo` per matched strategy including a snapshot of the counter so debug callers can see why a strategy will or won't fire. By design ``inspect`` does not accept ``force_enabled`` / ``strategy_filter``: those are runtime overrides for the routing side (``trigger_manual``), not properties a debugger should second-guess. """这里有三个值得学习的设计决策被写进了 docstring:
- 三道门禁(
enabled→applies_to→Counter)的执行顺序被固定,并注明 Counter 门禁会写计数器; dispatch与inspect是「写路径 / 干跑镜像」的孪生关系,inspect不修改计数器,还返回计数器快照供调试;- 刻意声明
inspect不接受force_enabled/strategy_filter——这两个参数是运行时路由(trigger_manual)的覆盖手段,调试器不应擅自绕过。这又是一处「被忽略/被禁止的参数」不变量,和SearchManager中被忽略的enable_llm_rerank如出一辙。
4.4IdleScanner:真正简单时,一行就够
src/everos/infra/ome/_background/idle_scanner.py 是一个对照样本:
"""IdleScanner — periodic scan of idle_store, emits IdleTick for overdue buckets."""规则明确规定:如果模块确实平凡(比如一个 3 行的常量定义),一行 docstring 完全可以接受——但「这个仓库里的大多数模块都不是」。IdleScanner这个例子的语义是「周期扫描 idle_store,为过期的 bucket 发射 IdleTick 事件」,它同时交代了 What(扫描)与对外副作用(发事件),属于「一行但信息完整」的合格写法,而非「模块名复读机」式的占位。
五、从architecture.md到module-docstring.md:规则体系如何协同
要理解这条规则在整个工程中的位置,需要把它放进.claude/rules/的规则族里看:
- architecture.md:规定 DDD 分层与模块职责边界,决定「这个模块属于哪个层、为什么存在」——这是模块 docstring 中 What 要素的上位依据;
- imports.md:规定依赖方向,而模块 docstring 中「它读什么、不写什么」的不变量往往就是依赖方向的直接体现;
- init-py-and-reexport.md:规定
__init__.py的 re-export 方式,对应三要素中的 External usage 要素——门面模块的导入示例正是写给包使用者的; - datetime-handling.md / testing.md / logging-observability.md:分别约束时区处理、测试策略与日志可观测性,这些约束若属于模块级行为,也应被写进该模块的 docstring。
简而言之:architecture 决定模块的「位」,module-docstring 决定模块的「言」。当架构调整(例如某个搜索路径的存储后端从 LanceDB 换成别的实现)改变了模块的读写边界时,对应模块的 docstring 不变量必须同步更新,否则它就从「帮助」退化为「误导」。
六、变更纪律:如何让 docstring 与代码永远同步
6.1 修改模块时先问三个问题
规则给出了一个可操作的变更检查流程。当你准备修改上述路径下的任意模块时,先自问:
- 我的改动是否改变了模块的职责边界(What 是否还准确)?
- 我的改动是否改变了不变量——分区键、写入目标、默认值、被忽略的开关?
- 如果这是包门面模块,我的导入方式是否有变化?
任何一项为「是」,就必须同步更新模块 docstring。
6.2 仓库如何保证文档体系不被破坏
虽然模块 docstring 本身没有被 CI 强制解析(它属于「约定 + 评审」范畴),但仓库对 Markdown 文档与链接的有效性有严格门禁:scripts/check_docs.py 会遍历仓库内所有*.md文件,校验每个仓库内相对链接的目标是否存在、是否越出仓库边界(_check_active_relative_links),并校验.env.example与 src/everos/templates/env.template 的一致性。docs/engineering.md中进一步说明,CI 的make docs-check与make lint等门禁共同保证 Markdown 与内部链接有效,main分支受保护,所有改动经评审的 Pull Request 合入。
这意味着:如果你在模块 docstring 里引用了其它文件(例如「参见
local/2026-06-14-reflection-everos-design.md」,见 ReflectionOrchestrator),虽然不会触发 docstring 校验,但若你在仓库文档中写相对链接,则必须保证链接目标真实存在,否则 scripts/check_docs.py 会令 CI 失败。
6.3 docstring 中的交叉引用建议
从仓库范例看,模块 docstring 中常使用 Sphinx/RST 风格的引用:
:class:CounterStore、`:class:`StrategyRouteInfo—— 引用类;:func:compile_filters_for_get、`:func:`resolve_pipeline—— 引用函数;:meth:dispatch、`:meth:`LanceRepoBase.find_where_paginated—— 引用方法;:mod:memory.search`` —— 引用模块。
这些引用让 IDE 悬停提示、自动补全与文档生成器能解析 docstring 中的符号,属于「锦上添花」的加分项,规则本身未强制,但仓库实践中普遍采用。
七、速查清单:提交前检查你的模块 docstring
把规则浓缩为一张提交前自查清单:
| 检查项 | 要求 | 不合格示例 |
|---|---|---|
| 开头位置 | 非平凡模块的第 1 行就是 docstring | 类定义或import之前没有任何模块级 docstring |
| What | 一句话说清职责 | 只复述模块名:「This is the search manager module.」 |
| 分区键 | 明确写出数据隔离维度 | 涉及多 owner/多类型数据却只字不提分区 |
| 读写边界 | 明确「读什么 / 写什么 / 从不写什么」 | 涉及 LanceDB/markdown/SQLite 却不说清方向 |
| 默认值 | 未配置时行为是什么 | 有默认配置分支却无说明 |
| 被忽略的开关 | 哪些参数在特定路径下不生效 | 有enable_llm_rerank式开关却未注明忽略路径 |
| 外部用法 | 门面模块附短导入示例 | 包门面却无任何调用示例 |
| 平凡模块例外 | 3 行常量可用一行 docstring | 把「平凡」当万能借口跳过所有注释 |
八、小结
EverOS 的module-docstring.md规则把模块文档字符串从「格式化礼仪」提升为「工程契约」:What 回答模块为何存在,Invariants 回答模块如何被安全修改,External usage 回答模块如何被正确调用。从SearchManager的硬分区与忽略开关、GetManager的分区表、ReflectionOrchestrator的合并流水线,到EventDispatcher的三道门禁顺序,仓库源码提供了大量可以直接参考的高质量范例。
对贡献者而言,最实用的一条经验是:如果你在调试某个模块时,不得不阅读实现源码才能确认「这个开关到底生不生效」「这里到底写不写库」——那正是该模块 docstring 失职的证据。写文档字符串时,请优先写那些能救下一个工程师一场调试的句子,而不是凑足三行客套话。
- 人工智能
- AI Agent
- Agent 记忆
- RAG
【免费下载链接】EverOS
One portable memory layer for every AI agent: local-first, Markdown-native, user-owned, and self-evolving across apps, tools, and workflows.
相关推荐
如何编写清晰的Contoso Chat Bicep模块文档:完整注释规范指南
如何编写清晰的Contoso Chat Bicep模块文档:完整注释规范指南 Contoso Chat是一个基于Azure云服务构建的智能聊天应用,其基础设施采
基础设施即代码模块化文档:Awesome Sysadmin
基础设施即代码模块化文档:Awesome Sysadmin 你是否在管理服务器时遇到过配置混乱、部署繁琐、文档零散的问题?作为系统管理员(System Admi
知识库运维提升LLMWare可维护性:模块文档字符串规范化实践指南
提升LLMWare可维护性:模块文档字符串规范化实践指南 在LLMWare这样的企业级大型语言模型 LLM 开发框架中,代码可维护性直接影响团队协作效率和功能迭
RAGAI AgentAI 应用后端NLP
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考