跳到主要内容

开发规范

本文档面向 Everpast 项目的开发者,涵盖代码风格、工程实践和协作流程。AI 辅助开发时另有 AGENTS.md(仓库根目录)作为补充约束。


基本准则

复用优先

  • Share 子模块(everpast_share)封装了 MQTT 通信、日志、组件间文件传输等常用基础能力——优先使用这些组件而不是自己重写
  • 发现 Share 缺少功能或存在 BUG,直接修改 Share 本身,而非在业务代码中绕行
  • 新增任何功能前,先检查仓库中是否已有可复用的组件、库或模板
  • 新组件必须暴露通用接口,不与特定业务逻辑强耦合
  • 思考:这个功能是否可能在其他上下文再次使用? 若是,抽离为独立工具

设计原则

  • 单一职责:一个模块/类只做一件事
  • 开闭原则:对扩展开放,对修改封闭,多用参数化配置
  • 标准库优先:基础功能首选 Python 标准库;确实不够时再选成熟可信的第三方库

不懂就问

遇到需求不明确、设计取舍不确定等情况,先停下来确认,不要自行猜测或私自增删。优化建议也请先沟通。


代码风格

元素规范
模块/包名snake_case(小写+下划线)
类名PascalCase
函数/变量/方法snake_case
常量UPPER_SNAKE_CASE(全大写)

语言

项目首选语言为简体中文——注释、日志、WebUI 界面均以中文为主要实现目标。


导入规范

导入应分组排列,组间用空行分隔:

# ── 标准库 + 第三方:from ... import ... 在前,import ... 在后 ──
# ── 本地模块:相对导入(同目录) / 绝对导入(跨目录) ──

具体顺序:

  1. from ... import ... 语句(标准库 + 第三方,按字母排)
  2. import ... 语句(标准库 + 第三方,按字母排)
  3. 空行
  4. 同目录模块——使用相对导入
  5. 跨目录模块——使用 from src.xxx.yyy 绝对导入,尽量使第二层包名相同的放在一起

引用外部模块的导入块必须放在本地导入块前面。

避免随意引入新依赖。如确有必要,先评估许可证、维护状态和社区活跃度,在注释和 commit 中说明理由,然后同步修改 pyproject.toml 并运行 uv lock


日志系统

Everpast 使用结构化日志,通过 everpast_share.logging 统一管理。

初始化

每个组件在启动入口处调用 configure_component_logging()

from everpast_share.logging import configure_component_logging

# 在 main() / run_app() 等入口函数中
configure_component_logging(
"组件ID",
jsonl=True, # 写入 data/logs/<组件ID>/YYYY-MM-DD.jsonl
console=True, # 控制台 RichHandler 输出
mqtt_bus=bus, # 可选:通过 MQTT 发布日志
level="INFO",
)

调用后会自动完成三件事:

  • 配置 structlog 的 render_to_log_kwargs 管道(时间戳、模块名、级别、栈信息)
  • 注册标准 logging handlers(控台、JSONL 文件、可选的 MQTT)
  • 返回一个预绑定 component_idsource 的 logger

业务代码中使用

from everpast_share.logging import get_logger

_logger = get_logger("组件名.子模块") # 例如 "nvr_bridge.frigate_api"

_logger.debug("请求详情", method=method, url=url)
_logger.info("事件已转发", camera=camera, type=event_type)
_logger.warning("配置缺失", field=field_name)
_logger.error("处理失败", error=str(exc), component=component_id)

不需要手动拼接字符串——结构化参数以关键字参数传入,自动与日志事件绑定。

何时打印日志

  • 系统边界(请求入口、响应出口)
  • 关键业务状态机流转
  • 外部依赖交互(MQTT 发布/订阅、数据库操作、API 调用)
  • 异常捕获块
  • 批处理或定时任务的进度变化

使用 DEBUG 等级记录请求参数、操作步骤等详细信息;INFO 等级标识关键业务里程碑。

格式统一

Share 的 get_logger() 在首次调用时会自动配置 structlog,所有组件获得一致的输出格式:

2026-07-19T15:05:07.450685Z [warning ] 警告消息 [nvr_bridge] component=test

彩色控制台输出(ConsoleRenderer)——开发友好;切换到 configure_component_logging 后自动改用 RichHandler + JSONL 模式。


注释

  • 所有公开函数、类、模块必须包含中文 docstring
  • 重构时保留原注释(除非删除对应代码)
  • 较长的功能块或复杂逻辑应添加行注释解释意图

类型注解

  • 重构时若原代码有类型注解,保持并更新准确
  • 若原代码无类型注解,重构时应添加以提高可读性和可维护性
  • 参数化泛型使用 typing 模块中的注解

依赖管理

  • 包管理统一使用 uv
  • 依赖声明在 pyproject.toml,同步更新 requirements.txt
  • 不使用 fallback 做错误掩盖——发现错误应该完整暴露
  • 精准定位问题根源,避免用兜底逻辑绕过(兜底难以维护)

Git 提交信息

采用 Angular 规范,优先使用中文。

<type>(<scope>): <简短描述>

<详细说明(可选)>

<footer(可选)>

各模块的每项修改对应一次独立 commit。若当前未 commit 但用户要求进行无关修改,应提醒并询问是否先完成提交。


工作流程

仓库结构

Everpast/ ← 主仓库(uv workspace)
├── doc/ → everpast-AI/doc(子模块:Docusaurus 文档站)
├── Share/ → everpast-AI/share(子模块:可独立发布 PyPI)
├── src/
│ ├── genapsed/ ← 核心守护组件
│ ├── minivlm/ ← 视觉事件生成
│ ├── nvr_bridge/ ← NVR 适配
│ ├── timeline/ ← 时间线存储与查询
│ └── www_server/ ← WebUI 后端
│ └── web-ui/ ← Vue 前端
├── scripts/ → 开发辅助脚本
└── pyproject.toml ← uv workspace 根配置

每个包的代码直接在 src/xxx/ 内修改,依赖由 uv workspace 统一管理。需要新增子模块时请与项目管理沟通。

首次克隆

git clone --recursive ssh://git@code.everpast.cn:222/everpast-AI/Everpast.git

已 clone 后补子模块:

git submodule update --init --recursive

开发环境

./everpast setup # 首次:子模块同步 + uv sync + 数据目录
./everpast # 默认:dev run --mode dev
./everpast dev up # 后台启动
./everpast dev status # 查看状态
./everpast dev down # 停止

Genapsed 启动命令

Genapsed 提供了三个管理自身生命周期的命令,均通过进程锁文件 (data/genapsed/genapsed.pid) 保证单实例运行:

# 前台启动(打印所有日志到控制台)
genapsed run --mode dev [--data-root data] [-f]

# 部署启动(前台仅显示 WARNING/ERROR,全部日志保存到文件)
genapsed up --mode prod [--data-root data] [-f]

# 通过锁文件停止 genapsed 进程
genapsed down [--data-root data] [--wait-timeout 10] [-f]
命令--mode控制台日志级别文件日志锁机制
rundev/prod全部(DEBUG+)全部保存启动时检查锁,退出时清理锁
updev/prod仅 WARNING+全部保存启动时检查锁,退出时清理锁
down读取锁文件 PID → SIGTERM → 超时询问是否 SIGKILL
  • -f / --force 参数可自动回答「清理过期锁」「强制杀死」等提示
  • down--wait-timeout 控制等待进程优雅退出的秒数(默认 10s),超时后询问或自动强制杀死
  • --mode 参数设置 EVERPAST_MODE 环境变量,所有组件及子进程可通过 os.environ.get("EVERPAST_MODE") 读取当前运行模式
  • 当前 dev 与 prod 模式启动逻辑相同,EVERPAST_MODE 供后续差异化使用

兼容旧命令 genapsed startgenapsed dev up/down/status 仍可使用。

子模块提交流程

Share 和 doc 是独立子模块。修改它们后:

cd Share # 或 cd doc
git add . && git commit -m "..." && git push
cd ..
git add Share && git commit -m "..."

提交时检查各子模块是否有新 commit,如有则更新主仓库指向。

重构与向下兼容

修改公用模块、导出接口或数据结构时,必须在注释或 commit 中注明是否破坏兼容性。若破坏兼容,需给出迁移提示。