# AGENTS.md

本文件面向两类协作者：

- 手写代码的人类朋友
- 会在此仓库中协助开发的 AI Agents

这个项目以手写为主，欢迎协作，但请先理解这里的约束和结构，再开始改动。

## 项目定位

- 这是一个娱乐性质的、私域使用的 QQ Bot 项目。
- 虽然主要用于熟人环境，但依然要按“不信任输入”的标准写代码。
- 不要因为使用场景偏内部，就默认消息内容、安全边界、调用参数一定可靠。

## 基本原则

### 1. 默认不信任用户输入

所有来自聊天消息、命令参数、平台事件等的输入，都应视为不可信。

开发时至少注意以下几点：

- 不假设输入类型正确，先校验再使用。
- 不假设输入长度合理，注意超长文本、大量参数、异常嵌套结构。
- 不假设输入内容安全，避免直接拼接到文件路径、SQL、shell 参数、HTML 或模板中。
- 不假设用户一定按预期使用命令，错误输入要能优雅失败。
- 对任何外部请求、文件读写、渲染、执行型逻辑，都要先考虑滥用风险。

### 2. 优先保持现有风格

- 这是一个以人工维护为主的项目，改动应尽量贴近现有写法。
- 除非有明确收益，不要为了“看起来更现代”而大规模重构。
- 新增能力时，优先复用已有通用模块，而不是重复造轮子。

### 3. 小步修改，影响清晰

- 尽量做局部、明确、可解释的改动。
- 修改插件时，避免顺手改动无关插件。
- 如果要调整公共模块，先确认是否会影响大量插件行为。

## 仓库结构

### `konabot/`

核心代码目录。

#### `konabot/common/`

通用模块目录。

- 放置可复用的基础能力、工具模块、公共逻辑。
- 如果某段逻辑可能被多个插件共享，应优先考虑放到这里。
- 修改这里的代码时，要额外关注兼容性，因为它可能被很多插件依赖。

#### `konabot/docs/`

Bot 内部文档系统使用的文档目录。

- 这是给用户看的文档来源。
- 文档会通过 `man` 指令被触发和展示。
- 虽然文档文件通常使用 `.txt` 后缀，但内容可以按 markdown 风格书写。
- `.md` 后缀文件会被忽略，因此 `.md` 更适合只留给仓库维护者阅读的附加说明。
- 文档文件名就是用户查询时使用的指令名，应保持简洁、稳定、易理解。

补充说明：

- `konabot/docs/user/` 是直接面向用户检索的文档。
- `konabot/docs/lib/` 更偏向维护者参考。
- `konabot/docs/concepts/` 用于记录概念。
- `konabot/docs/sys/` 用于特定范围可见的系统文档。

#### `konabot/plugins/`

插件目录。

- 插件数量很多，是本项目最主要的功能承载位置。
- 插件可以是单文件，也可以是文件夹形式。
- 新增插件或修改插件时，请先观察相邻插件的组织方式，再决定采用单文件还是目录结构。
- 如果逻辑已经明显超出单文件可维护范围，应拆成目录插件，不要把一个文件堆得过大。

## 根目录文档

### `docs/`

仓库根目录下的 `docs/` 主要用于记录一些可以通用的模块说明和开发文档。

- 这里的内容主要面向开发和维护。
- 适合放公共模块说明、集成说明、配置说明、开发笔记。
- 不要把面向 `man` 指令直接展示给用户的文档放到这里；那类内容应放在 `konabot/docs/` 下。

## 对 AI Agents 的具体要求

如果你是 AI Agent，请遵守以下约定：

### 修改前

- 先阅读将要修改的文件以及相关上下文，不要只凭文件名猜用途。
- 先判断目标逻辑属于公共模块、用户文档，还是某个具体插件。
- 如果需求可以在局部完成，就不要扩大改动范围。

### 修改时

- 优先延续现有命名、目录结构和编码风格。
- 不要因为“顺手”而批量格式化整个项目。
- 不要擅自重命名大量文件、移动目录、替换现有架构。
- 涉及用户输入、路径、网络、数据库、渲染时，主动补上必要的校验与防御。
- 如果要新增 `konabot/common/` 或其他会被多处依赖的模块，优先考虑 NoneBot2 框架下的依赖注入方式，而不是把全局状态或硬编码依赖散落到调用方。
- 写文档时，区分清楚是给 `man` 系统看的，还是给仓库维护者看的。

### 修改后

- 检查改动是否误伤其他插件或公共模块。
- 如果新增了用户可见功能，考虑是否需要补充 `konabot/docs/` 下对应文档。
- 如果新增或调整了通用能力，考虑是否需要补充根目录 `docs/` 下的说明。

## 插件开发建议

- 单个插件内部优先保持自洽，不要把特定业务逻辑过早抽成公共模块。
- 当多个插件开始重复同类逻辑时，再考虑上移到 `konabot/common/`。
- 插件应尽量对异常输入有稳定反馈，而不是直接抛出难理解的错误。
- 如果插件会访问外部服务，要考虑超时、失败降级和返回内容校验。

### 最基本的用户交互书写建议

- 先用清晰、可收敛的规则匹配消息，再进入处理逻辑，不要一上来就在 handler 里兜底解析所有输入。
- 在 handler 里尽早提取纯文本、拆分命令和参数，并对缺失参数、非法参数、异常格式给出稳定反馈。
- 如果用户输入只允许有限枚举值，先定义允许集合，再进行归一化和校验。
- 输出优先保持简单直接；能一句话说明问题时，不要返回难懂的异常堆栈或过度技术化提示。
- 涉及渲染、网络请求、图片生成等较重操作时，先确认输入合理，再执行昂贵逻辑。
- 如果插件只是做单一交互，优先保持 handler 简短，把渲染、请求、转换等逻辑拆成独立函数。
- 倾向于使用 `UniMessage` / `UniMsg` 这一套消息抽象来组织收发消息，而不是把平台细节和文本拼接散落在各处。
- 倾向于显式构造返回消息并发送，而不是大量依赖 NoneBot2 原生的 `.finish()` 作为主要输出路径，除非该场景确实更简单清晰。

### 关于公共能力的依赖方式

- 新建通用能力时，优先设计成可注入、可替换、可测试的接口。
- 如果一个模块未来可能被多个插件依赖，优先考虑 NoneBot2 的依赖注入，而不是让调用方手动维护重复的初始化流程。
- 除非确有必要，不要让插件直接依赖隐藏的全局副作用。
- 如果使用单例、缓存或全局管理器，要明确其生命周期、并发行为以及关闭时机。

## 运行环境与部署限制

这个项目默认会跑在 Docker 环境里，修改功能时请先意识到运行环境不是一台“什么都有”的开发机。

### 容器环境

- 运行时基础镜像是 `python:3.13-slim`，不是完整桌面 Linux；很多系统库默认不存在。
- 项目运行依赖 Playwright Chromium、字体库、图形相关库，以及部分额外二进制工具。
- 构建阶段和运行阶段是分离的；不要假设在 builder 里装过的系统包，runtime 里也一定可用。
- 额外制品目前通过多阶段构建放进镜像，例如 `typst`。

### Docker 相关要求

- 如果你新增的 Python 依赖背后还需要 Linux 动态库、字体、图形库、编译工具或其他系统包，必须同步检查并在 `Dockerfile` 中补齐。
- 不要只让本地虚拟环境能跑；要默认以容器可运行作为完成标准之一。
- 如果新功能依赖系统命令、共享库、浏览器能力或字体，请在提交说明里明确写出原因。
- `.dockerignore` 当前会排除 `/.env`、`/.git`、`/data` 等内容；不要依赖这些文件被复制进镜像。
- 关于额外制品的管理，优先先阅读根目录文档 `docs/artifact.md`；适合统一管理的二进制或外部资源，倾向于复用 `konabot/common/artifact.py`，而不是在各插件里各自处理下载和校验。

### 本地运行

- 本地开发可参考 `justfile`，当前主要入口是 `just watch`。
- 如果你的改动影响启动方式、依赖准备方式或运行命令，记得同步更新对应文档或脚本。

## 分支与协作流程

- 本项目托管在个人 Gitea 实例：`https://gitea.service.jazzwhom.top/mttu-developers/konabot`。
- 如果需要创建 Pull Request，优先倾向使用 `tea` CLI：`https://gitea.com/gitea/tea`。
- Pull Request 创建后，当前主要会有自动机器人做初步评审，项目维护者会手动查看；不要催促立即合并，也不要默认会马上进主分支。
- 如果当前是在仓库本体上直接开发、而不是在 fork 上工作，尽量提醒用户不要直接在主分支持续改动，优先使用功能分支。
- 除非用户明确要求，否则不要擅自把改动直接合并到主分支。

## 文档编写建议

### 面向 `man` 的文档

- 放在 `konabot/docs/` 对应子目录。
- 文件名直接对应用户查询名称。
- 建议内容简洁，优先说明“做什么、怎么用、示例、注意事项”。
- 使用 `.txt` 后缀；内容可以写成接近 markdown 的可读格式。

### 面向开发者的文档

- 放在仓库根目录 `docs/`。
- 主要描述公共模块、配置方法、设计说明、维护经验。
- 可以使用 `.md`。

