开发规范
本文档面向 Everpast 项目的开发者,涵盖代码风格、工程实践和协作流程。AI 辅助开发时另有 AGENTS.md(仓库根目录)作为补充约束。
基本准则
复用优先
Share子模块(everpast_share)封装了 MQTT 通信、日志、组件间文件传输等常用基础能力——优先使用这些组件而不是自己重写- 发现 Share 缺少功能或存在 BUG,直接修改 Share 本身,而非在业务代码中绕行
- 新增任何功能前,先检查仓库中是否已有可复用的组件、库或模板
- 新组件必须暴露通用接口,不与特定业务逻辑强耦合
- 思考:这个功能是否可能在其他上下文再次使用? 若是,抽离为独立工具
设计原则
- 单一职责:一个模块/类只做一件事
- 开闭原则:对扩展开放,对修改封闭,多用参数化配置
- 标准库优先:基础功能首选 Python 标准库;确实不够时再选成熟可信的第三方库
不懂就问
遇到需求不明确、设计取舍不确定等情况,先停下来确认,不要自行猜测或私自增删。优化建议也请先沟通。
代码风格
| 元素 | 规范 |
|---|---|
| 模块/包名 | snake_case(小写+下划线) |
| 类名 | PascalCase |
| 函数/变量/方法 | snake_case |
| 常量 | UPPER_SNAKE_CASE(全大写) |
语言
项目首选语言为简体中文——注释、日志、WebUI 界面均以中文为主要实现目标。
导入规范
导入应分组排列,组间用空行分隔:
# ── 标准库 + 第三方:from ... import ... 在前,import ... 在后 ──
# ── 本地模块:相对导入(同目录) / 绝对导入(跨目录) ──
具体顺序:
from ... import ...语句(标准库 + 第三方,按字母排)import ...语句(标准库 + 第三方,按字母排)- 空行
- 同目录模块——使用相对导入
- 跨目录模块——使用
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_id和source的 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 | 控制台日志级别 | 文件日志 | 锁机制 |
|---|---|---|---|---|
run | dev/prod | 全部(DEBUG+) | 全部保存 | 启动时检查锁,退出时清理锁 |
up | dev/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 start 和 genapsed 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 中注明是否破坏兼容性。若破坏兼容,需给出迁移提示。