Imported from Mai-with-u/Amaidesu (
AGENTS.md). Install upstream withnpx skills add Mai-with-u/Amaidesu. Copyright stays with the author.
AGENTS.md
为在此代码库中工作的 AI 编码代理提供指南。
本文件只写对 AI 的行为约束(硬约束/流程约束),以"做什么"的正向表述为主。代码结构、API 用法、命令、配置细节等不在此罗列——AI 自行探索代码库与 docs/ 目录获取,避免文档与代码漂移耦合。通用编程/工具链常识(Python 语法、git 基础、uv 用法等)不收录。git log 是一切变更历史的事实源。
核心思想
第一性原理:设计与开发从问题本质出发推导,而非惯例惯性。做设计决策前先问:这个组件存在的根本目的是什么?解决问题的最小必要机制是什么?结论落在代码,推导落在文档(范例:docs/architecture/v2-architecture.md,理解本项目架构先读这篇)。
快速方向
- 架构与设计文档:
docs/architecture/ - 开发指南文档:
docs/guides/ - 安装与运行:README.md
硬约束
必须遵守
- 使用中文和用户沟通以及编写文档、注释
- 如实汇报工作进度与受阻状态;任务达成标准一经确认即保持,调整须经用户同意
- 测试策略(改动范围优先):改哪里测哪里——只跑改动相关的测试文件 +
uv run ruff check(限改动文件)。全量uv run pytest tests/留给收口/并入主线/跨模块重构/新工作树基线对齐。提交前:目标测试绿 + ruff 绿 +uv run ruff format . - 移动或者重命名文件时使用
git mv保留历史记录 - 机器本地信息与敏感信息不入库:本地绝对路径、仅适用于开发者个人环境的信息(如工作树物理路径、机器布局)、密钥/token 等敏感信息,一律登记于
AGENTS.local.md(不入库,opencode 会将其识别为本地规则文件) - 需要用户决定的事项写在回复"备注/待你确认"区;todo 列表只放 agent 可自行推进的事项
架构红线(最高约束)
主体性判据(★):组件按驱动方式三分——
- 主播 Agent:自我驱动(唯一)——直播期间持续运行决策循环,没人调也在跑
- 游戏 Agent:命令驱动(类 Code Agent)——收到命令才启动任务内有界循环,任务完成即停、空闲零消耗;因有自身状态与任务内自主决策,仍属 Agent
- 工具:被动驱动,被调才干活,无循环(TTS 等基础能力属基础设施,走独立装配通道)
- 直播内容 = 编排配置 + Planner 上下文/行为模式的变化,代码模块零新增
- 新组件的定类判据序列(Q1 持过程 → Q2 推进权 → Q3 数据源)见 组件开发指南
边界规则(判据的直接应用,违反即架构回退):
- 架构红线三分(详见
docs/guides/component.md红线三分节):- LLM 可调的内部工具 → 注册 + 名单隔离(自己的工具填自己;如
streamer_reply/minecraft_todo) - Planner / Replyer 类本体 → 留在 Agent 内部,不进表(决策循环与表达引擎;代码直连)
- 代码直连的内部件 → 不是工具、不进表(无 ToolSpec、无注册;如 ProactiveTrigger、命令解析原语)
- LLM 可调的内部工具 → 注册 + 名单隔离(自己的工具填自己;如
- 游戏/内容逻辑内聚
src/agents/<name>/包内(目录名 = Agent 注册名,无分类层级):加内容 = 加包 + 配置,框架零改动 - 采集器只发布数据事件;下游结果的查询诉求走工具实现(发现平面口诀:"能挥手吗"可问,"刚才挥手成功了吗"不可问)
- 快照型能力(被调才看)实现为工具,持续流型实现为采集器
实现纪律
- 新能力用 Collector(持续流数据源)/ BaseAgent 子类(业务 Agent)/ ToolProvider(被动能力)承载——插件系统与服务注册机制已从架构移除,其历史只见于 git log
- 事件名一律引用
CoreEvents常量,拼写正确性与全局可检索性都靠它保证 - 每个异常路径都记录日志并携带原因上下文,空 catch 块按缺陷处理
- 测试失败即缺陷暴露:转绿的途径是修复代码或修复测试本身,删除/跳过测试达成的绿灯不构成验证
- bug 修复保持最小改动面,重构意图另开任务
- 可变实例状态在
__init__初始化,类属性只放常量与不可变默认
代码约定
- 命名:类名 PascalCase;函数/变量 snake_case;私有成员前导下划线;Collector/Agent/工具/拦截器类以类型名结尾
- 数据类型:数据模型/配置 Schema/事件 Payload 用 Pydantic BaseModel;简单内部统计用 dataclass;接口协议用 Protocol
- 时间字段:统一毫秒——时刻用
intUnix epoch 毫秒,时长/超时命名<name>_ms,秒单位变体(timestamp_s/duration_seconds)与本仓库约定冲突;历史timestamp字段用 Pydantic alias 兼容 - 依赖注入:服务对象(LLM/提示词/事件总线等)一律构造器注入;跨组件传递用参数显式传递,Context 容器只装上下文数据
- import 纪律:import 一律放文件顶部(
from __future__之后),按 isort 排序。函数体内 import 限 5 种情形,且必须自足注释说明原因:TYPE_CHECKING 块、循环 import 规避、可选重型依赖延迟加载、Pydanticmodel_rebuild()forward-ref、测试可 mock 性。顶部已有同模块 import 时,函数内直接复用顶部引用 - 类型注解:所有函数与方法必须含完整类型注解(参数与返回值类型);缺注解视为缺陷,必要时通过
TYPE_CHECKING推迟导入以避免循环 - 日志使用:统一通过
src.modules.logging.get_logger取 logger,参数用类名或模块名(命令行--filter <name>按此过滤可见输出);异常路径一律logger.error(..., exc_info=True)或logger.warning(...)携带上下文,不得静默吞异常
AI 痕迹防范(防 AI slop)
原则:注释/docstring 回答"这段代码是什么、为什么这样设计",用自足的自然语言表述。代码内引用外部文档位置是反模式——文档会漂移,过时引用误导读者。以下具名反例一旦出现即为残留,按右列清理:
| 具名反例 | 清理方式 |
|---|---|
章节引用(§1.50) |
改写为直接陈述架构含义 |
版本变更记录(vN.N.N 修复/新增、重构后已删除) |
删除;git log 是事实源 |
ADR-XXX 编号引用 |
删除编号,保留技术实质 |
步骤编号注释(# --- 1. ... ---) |
改纯客观标题;函数过长优先拆函数 |
| docstring"变更历史"段 | 只保留功能/架构描述 |
配置与存储变更
- 配置系统是"Schema 即真相"(Pydantic Schema 驱动生成/验证/迁移),权威入口在
src/modules/config/。配置布局为 6 文件(agents / collectors / tools / model / storage / infra),每文件自带[meta].version,版本流彻底独立——每文件只推进到"作用于它的最后一个已执行升级钩子的 target",无适用钩子的文件版本保持原值;CONFIG_BASELINE_VERSION仅作新生成文件的版本种子,不参与既有文件的推进调度,不存在"单 stamp 管全部文件"的写法 - 版本字段由
FileMetaConfig(per-file)承载,WebUI 侧只读(显示不可编辑);文件存在但版本字段缺失 → 启动硬错,杜绝"无版本→绕过钩子"的退化路径 - 结构修改(增删改字段/段移动)分两档:纯新增字段零成本——漂移写回自动补默认值,不写钩子不升版本;数据变换(字段重命名/段拆分/类型转换)——升该文件的
[meta].version+ 注册该文件升级 hook(原地修改 dict、幂等、返回变更路径)+ 配迁移测试;跨文件搬移的 hook 声明target_file(双写 + 双版本同升),无独立机制 - 升版本 ≠ 迁移生效:提交前实际验证迁移写回落盘(跑
tests/config/或手动触发配置加载检查升级日志)——"只改 Schema 不升版本/不验证迁移"是本区最高频事故形态,此类提交视为未完成 - 存储表结构变更升
SCHEMA_VERSION,迁移记录幂等推进 - 组件嵌套配置(采集器/Agent/工具包)变更时同步更新对应 schema 测试
文档维护
单一事实源:每个事实只在权威处定义一次,其他位置用链接引用,绝不复制。可导出事实(组件清单、目录树、工具目录、配置键列表、事件名清单等)一律以代码为唯一事实源,文档不手抄。
| 事实 | 权威处 |
|---|---|
| 事件名(有哪些/常量值) | 代码 src/modules/events/names.py 的 CoreEvents |
| 事件语义/订阅契约/拦截器 | docs/architecture/event-system.md |
| 事件命名规则 | docs/architecture/event-naming.md |
| 数据流边界与禁止模式 | docs/architecture/data-flow.md |
| 组件清单/目录结构/工具目录/生命周期 | 代码(src/、ToolRegistry、BaseAgent/BaseCollector) |
| 配置键/Schema/迁移 | 代码 src/modules/config/ |
| 存储表结构 | 代码 src/modules/storage/ + SCHEMA_VERSION |
| 硬约束/代码约定/提交纪律/配置与存储规程/文档规程/ADR 政策 | AGENTS.md(本文件) |
| 架构决策 | docs/decisions/ |
| 三范式开发流程 | docs/guides/component.md |
| 版本与发布 | docs/guides/release.md |
| 游戏 Agent 范式 | docs/architecture/minecraft-agent.md |
ADR 编写规范:决策记录位于 docs/decisions/,文件名 NNN-短横线描述.md,编号按创建时间递增、全局连续、不得跳号或重复;失效 ADR 直接删除(git 历史保留)。每篇标题下必须含三行元数据——状态(已采纳/已废弃/草案)、日期(YYYY-MM-DD)、实现提交(完整 40 位 hash 加 git show <hash> 可追溯的提交信息)。正文采用 Nygard 四段式:背景 / 决策 / 替代方案 / 后果。新增 ADR 后同步更新 docs/decisions/README.md 清单。
链接规范:文档间引用一律相对路径,目录层级需正确换算(docs/architecture/ 引用 docs/guides/ 加 ../);图片统一以  引用;锚点基于标题生成,标题编号会保留在锚点中。
- 变更历史只在 git log,文档内不设"变更记录/最后更新"条目——文末堆积式 changelog 是本项目真实发生过的事故(持续污染每次 AI 会话上下文);文档正文陈述当前事实即可
- 图片放
docs/images/,视频放docs/videos/
流程约束
git 提交纪律
提交先获用户显式授权:git commit / git push 前确认用户明确要求。计划文件(.omo/plans/*.md)的 Commit 策略仅覆盖该计划范围内的任务;计划外任务不得继承提交授权——委托子代理时 prompt 只写"完成后展示结果,由用户决定提交",不写入 commit 指令。
提交体规范(Conventional Commits):
- 格式
type(scope): subject。type 从封闭集合取值:feat / fix / docs / refactor / perf / test / chore / build / revert - scope 与代码顶层模块/目录名一致(小写英文,单复数以目录为准),拿不准就省略;任务性词汇(wiring/cleanup/comments 类,均为本项目真实漂移案例)使 log 检索失真,禁止用作 scope
- subject 用中文、动词开头、句末不加句号,≤30 字符(约 60 显示宽,保证
git log --oneline不截断);同批多个意图用 "+" 分列 - body 用中文说明"为什么"与关键取舍,每行 ≤36 个中文字符(72 显示宽);diff 自明的(措辞/格式/笔误)可省略,行为变化或含取舍的必须写
- 测试结果仅在有额外信息量时写入提交信息(如个别失败属预期过渡态),常规通过数留在本地
- revert 手写:
revert(scope): 回滚 <原 subject 摘要>,body 注明原提交 hash 与回滚原因 - 协作署名(Co-Authored-By 等)仅按用户明确要求添加
- Windows PowerShell:提交 message 一律
git commit -F <file>(UTF-8 无 BOM 文件);临时用引号时仅限单引号(双引号会展开$var、解释反引号) - 提交后
git log -1复核无乱码(含 BOM)、无截断
一次提交一个意图:无关变更分批提交;连环缺陷同因修复可合批,subject 用 "+" 分列。
版本与发布
- pyproject
version是对外版本号的唯一声明处,git tagvX.Y.Z是发布事实源;版本号只在发布时 bump,发布动作(CHANGELOG + bump + tag)在开发主线 v2.0.0 上完成,main 仅作--ff-only快进的发布线,不产生自己的提交 - 配置
[meta].version与存储SCHEMA_VERSION是数据迁移机制,版本流独立,不参与发布 - 发布步骤与 CHANGELOG 格式见
docs/guides/release.md,决策依据见docs/decisions/016-versioning-and-release-model.md
多工作树并行开发
使用 git worktree 为并行任务提供隔离检出环境。任务工作树物理路径属机器本地信息,登记于 AGENTS.local.md(不入库);放置于仓库外同级目录;同一分支同时只允许一个工作树。工作树分两类:
- 临时工作树:一次性任务用,
git worktree add创建;收口并入主线后删除工作树目录与已并入分支 - 常驻工作树:持续复用的固定目录;工作树目录与其历史任务分支长期保留(供追溯),接新任务时换班——先侦察主线位置与他人未提交变更,再从主线最新位置长出新分支
git switch -c task/<新名称> v2.0.0 - 交集预检(硬性前置):开工前列出新任务目标文件集,与主工作区未提交变更求交集;非零重叠改为协调串行
- 被
.gitignore排除但测试所需的运行时素材,从主工作区手动补齐 - 收口:先 rebase 主线;冲突解决后做语义双验(双方意图的特征标记在合并结果中均可检索)并重新全量测试;并入主线优先快进,主工作区被占用时推进分支指针后立即回主工作区对本任务路径做路径级 checkout 同步陈旧副本,改动范围限于本任务路径、避开他人未提交文件;最后临时工作树删除工作树目录与已并入分支,常驻工作树的目录与分支均保留待下次换班
- 工作树中测试失败时,先排查环境差异(缺失的忽略文件、过期基线),再怀疑代码