首页
/ 解读 Memos 的 AGENTS.md:一份为 AI 编码代理而写的可执行工程手册

解读 Memos 的 AGENTS.md:一份为 AI 编码代理而写的可执行工程手册

2026-09-03 15:27:25作者:齐冠琰

AGENTS.md 是 Memos 仓库中面向 AI 编码代理的"操作说明书":它不是一篇泛泛的架构介绍,而是把技术栈快照、工作守则、可复制的命令集、代码地图、变更路由表和验证策略压缩在一个文件里。读完本文,你将掌握 Memos 前后端、Proto、数据库迁移各子系统的标准开发/验证命令,知道每类改动应该触碰哪些文件、跑哪些测试,并理解这些约定如何在 CI 中真正被执行——这也是你编写或维护自己仓库 AGENTS.md 时可直接套用的范式。

设计哲学:短、具体、命令必须真的能跑

文档开篇就立下了两条元规则(AGENTS.md):

  • 保持文件短小、具体,并且每一条都绑定到"在这个仓库里确实能跑通的命令";
  • 如果文件中的某个事实与源码或 CI 配置冲突,以源文件为准,并回头更新这份指南。

第二条规则本身就值得注意——它让 AGENTS.md 成了一个"自我修正"的文档。当前仓库里就有实例:文档快照中写 "TypeScript 6",而 web/package.json 的 devDependencies 实际是 typescript: ^7.0.2。按文档自己的规则,应以源文件为准。这种"文档可能过时、源码是唯一真相"的立场,正是 AI 代理在仓库中工作时最需要的防幻觉机制。

项目快照:技术栈与生成产物的边界

AGENTS.md 用一小节(AGENTS.md)给出了 Memos 的技术栈全貌,每一条都能在仓库中找到证据:

声明 证据
Go 1.27.0 go.mod 第 3 行 go 1.27.0
Echo v5 go.mod 依赖 github.com/labstack/echo/v5 v5.3.1
Connect RPC / gRPC-Gateway / Protobuf go.mod 中 connectrpc.com/connectgithub.com/grpc-ecosystem/grpc-gateway/v2google.golang.org/protobuf
React 19、Vite 8、Tailwind CSS v4、React Query v5 web/package.jsonreact ^19.2.6vite ^8.0.11tailwindcss ^4.2.4@tanstack/react-query ^5.100.9
SQLite / MySQL / PostgreSQL 目录 store/db/sqlitestore/db/mysqlstore/db/postgres

快照中最有实操价值的一句是生成产物的定位

  • Go 与 OpenAPI 生成代码在 proto/gen/
  • TypeScript 生成代码在 web/src/types/proto/

proto/buf.gen.yaml 印证了这一点:buf generate 一次会调用五个远程插件(protocolbuffers/go、grpc/go、connectrpc/go、grpc-ecosystem/gateway、google-gnostic-openapi),全部输出到 gen/;而 bufbuild/es 插件则把 target 指向 ../web/src/types/proto。也就是说,改一次 .proto 文件,Go 和 TypeScript 两侧的生成代码必须成对出现在提交里——这正是后文"Database And Proto Rules"中"必须包含 Go/OpenAPI 与 TypeScript 两侧生成产物"这一条的底层原因。

工作守则:七条约束划定代理的改动边界

AGENTS.md 的 Working Rules 是全文中"负面清单"最集中的部分,七条规则可以归为三类:

  1. 改动前理解上下文:编辑前先读相关代码;优先复用仓库内已有模式,而不是引入新抽象。
  2. 控制 diff 范围:不做全仓库清理、不做依赖刷新(dependency churn)、不重写生成文件——除非任务本身要求。这条规则直接决定了代理产出 diff 的可审查性。
  3. 高风险操作先询问:新增重量级依赖、修改认证/token 行为、改动 Docker/发布流程之前,必须先问。

其中两条规则有明确的"机制级"支撑:

  • "不要手改生成代码,改 .proto 后跑 buf generate" —— 因为 buf.gen.yaml 里启用了 managed: enabled(managed mode),文件头部的 go_package 等选项由 buf 统一管理,手改几乎必然在下一次生成时被打掉;
  • "改 schema 时给所有数据库驱动加迁移" —— 因为仓库为三个驱动各自维护一套增量迁移目录(store/migration/{sqlite,mysql,postgres}/),漏掉任何一套都会让对应驱动的升级路径断裂。

标准命令集:三组命令全部与 CI 对齐

AGENTS.md 的 Commands 一节按后端、前端、Protocol Buffers 三组给出命令,且默认从仓库根目录执行(除非命令以 cd 开头)。完整命令如下,可直接复制使用:

# Backend
go run ./cmd/memos --port 8081    # Start backend dev server
go test ./...                      # Run all Go tests
go test -v ./store/...             # Store tests, including DB drivers via TestContainers
go test -v -race ./server/...      # Server tests with race detector
go test -v -race ./internal/...    # Internal package tests with race detector
go test -v -run TestFoo ./pkg/...  # Run matching Go tests
go mod tidy -go=1.27.0             # Match CI tidy check
golangci-lint run                  # Go lint, config: .golangci.yaml
golangci-lint run --fix            # Auto-fix lint, including goimports

# Frontend
cd web && pnpm install             # Install dependencies
cd web && pnpm dev                 # Dev server on :3001, proxying API to :8081
cd web && pnpm lint                # Type check + Biome lint
cd web && pnpm test                # Vitest unit tests
cd web && pnpm build               # Production build
cd web && pnpm release             # Build SPA into server/router/frontend/dist

# Protocol Buffers
cd proto && buf generate           # Regenerate Go + TypeScript + OpenAPI
cd proto && buf lint               # Lint proto files
cd proto && buf format -w          # Format proto files

逐条对照仓库后,有几个细节值得展开:

go mod tidy -go=1.27.0 为什么要带版本参数。后端 CI 的第一步"静态检查"就是跑 go mod tidy -go=1.27.0 然后 git diff --exit-code.github/workflows/backend-tests.yml)——本地如果不带 -go=1.27.0 参数 tidy,产生的 go.mod/go.sum 差异会被 CI 直接判为失败。这是"本地命令必须模拟 CI"的典型例子。

pnpm releasepnpm build 的区别web/package.jsonbuild 就是 vite build,而 releasevite build --mode release --outDir=../server/router/frontend/dist --emptyOutDir——它把 SPA 产物直接打进后端的静态资源目录,scripts/Dockerfile 的注释也明确写了"请先构建前端,这样静态文件才可用"。所以"只改前端想验证完整容器"时应跑 release,"只想看 Vite 构建是否通过"时跑 build 即可。

store 测试为什么特别。注释说"including DB drivers via TestContainers",go.mod 中确实引入了 testcontainers-go 及其 mysql/postgres/minio 模块;后端 CI 对 store 组还特意设置了 DRIVER 环境变量逻辑(store 组不设置 DRIVER,即跑全部驱动;其余组用 sqlite),见 backend-tests.yml

代码地图:20 行表格定位整个仓库

AGENTS.md 用一张路径→职责的表把仓库核心区域全部点出。为便于检索,这里按"入口 → 服务层 → 存储层 → API 定义 → 前端"分层重新组织,全部条目与原文档一致:

路径 职责
cmd/memos/main.go Cobra/Viper CLI 装配与服务启动
server/server.go Echo HTTP 服务器、路由装配与优雅停机
server/auth/ JWT access token、refresh token、PAT 处理
server/router/api/v1/ Connect/gRPC-Gateway 服务、ACL 配置、SSE hub
server/router/frontend/ 静态 SPA 服务
server/router/fileserver/ 原生 HTTP 文件服务、缩略图、range 请求
server/runner/ Memo payload 重建
store/ Store 门面、缓存、迁移、驱动接口
store/db/{sqlite,mysql,postgres}/ 各数据库驱动的专属实现与 SQL
proto/api/v1/ 对外 API 服务定义
proto/store/ 内部存储 Proto 消息
internal/ 应用私有包:scheduler、cron、email、CEL filter、markdown、idp、S3
web/src/connect.ts Connect RPC 客户端、auth 拦截器、access token 刷新
web/src/auth-state.ts token 存储与 BroadcastChannel 跨标签页同步
web/src/hooks/ 服务端状态的 React Query hooks
web/src/contexts/ 客户端/UI 状态的 React context
web/src/components/ Radix/Tailwind UI 组件与业务组件
web/src/themes/ 基于 OKLch 色彩 token 的 CSS 主题

以代码地图的第一行为例做实证:cmd/memos/main.go 中确实能看到 cobra.Command 根命令 + viper.SetDefault("port", 8081) 的装配逻辑——这就是文档里 go run ./cmd/memos --port 8081 默认端口 8081 的来源,也与前端开发服务器代理到 :8081 的说明相互咬合。

变更路由表:每类改动"改哪里 + 验什么"

AGENTS.md 的 Change Routing 是全文对 AI 代理最有指导意义的表格:它把"你要改什么"映射到"该动哪些文件"和"用什么命令自证"。完整继承如下:

改动类型 应更新 验证命令
Go 服务或路由行为 server/ 下对应 service 代码、包邻近测试 go test -v -race ./server/...
Store 或迁移行为 store/、三个 DB 驱动的迁移、LATEST.sql go test -v ./store/...
内部包逻辑 对应 internal/ 包的测试 go test -v -race ./internal/...
前端行为 web/src/ 下组件/hooks/contexts cd web && pnpm lint && pnpm test
前端生产构建产物 Vite 配置或影响发布的 UI cd web && pnpm buildpnpm release
Proto API .proto 源文件加生成产物 cd proto && buf generate && buf lint
公开的免鉴权路由 server/router/api/v1/acl_config.go 针对性 server 测试或手工路由检查

表中"Go 服务或路由行为"一行的注释与 CI 相互印证:backend-tests.yml 里 server 组的注释写着 ./server/test 会通过 server.NewServer 真实启动服务器并冒烟它挂载的所有 router——所以 server 组测试本身就是一次"真实服务器启动"级别的验证。

而"公开免鉴权路由必须登记到 acl_config.go"这一行,经确认该文件确实存在于 server/router/api/v1/acl_config.go。这意味着 Memos 的 ACL 不是散落各处的 e.GET(...),而是集中声明——对代理来说这是一条"改路由不登记 = 权限模型不一致"的硬约束。

Go 代码规范:由 golangci-lint 强制执行的约定

AGENTS.md 的五条 Go 规范几乎都能在 .golangci.yaml 里找到对应的执行器:

  1. 错误包装用 errors.Wrap(err, "context")(github.com/pkg/errors),不用 fmt.Errorf。这条不是风格偏好,而是被 forbidigo linter 硬编码禁止的——.golangci.yamlforbid 模式 'fmt\.Errorf(# Please use errors\.Wrap\|Wrapf\|Errorf instead)?' 会直接让 lint 失败。同理 ioutil.ReadDir 也被禁,要求改用 os.ReadDir
  2. 服务错误用 status.Errorf(codes.X, "message") 返回,保持 gRPC/Connect 语义一致。
  3. import 分组:标准库 → 第三方 → github.com/usememos/memos。执行器是 goimports 格式化器,其 local-prefixes 精确配置为 github.com/usememos/memosgolangci-lint run --fix 会自动执行。
  4. 导出标识符加 doc 注释,godot 强制导出注释标点.golangci.yaml 确实启用了 godot linter。
  5. 避免包级可变状态,除非所在包本身已采用该模式——这是一条纯约定,靠 code review 与代理自律执行。

前端代码规范:Biome 配置即契约

AGENTS.md 的前端六条规范,与 web/biome.json 逐项对应:

  • @/ 绝对导入:由 tsconfig/Vite 的别名机制支撑,pnpm lint 中的 tsc --noEmit 会验证其有效性(package.json 的 lint 脚本是 tsc --noEmit --skipLibCheck && biome check src tests);
  • 2 空格缩进、双引号、分号、140 列宽biome.jsonindentWidth: 2lineWidth: 140,JavaScript formatter 段(biome.json)中 quoteStyle: "double"semicolons: "always"
  • 服务端数据放 web/src/hooks/ 的 React Query hooks,纯 UI 状态放 context 或组件 state:这是数据架构约定,web/src/hooks/ 目录里 30 余个 use*Queries/use* 文件即是该模式的实体;
  • Tailwind v4 工具类 + cn() 合并 + CVA 变体:package.json 依赖中有 clsxtailwind-mergeclass-variance-authority
  • 优先复用 Radix 基元与现有组件(该仓库 UI 层实际同时依赖 @base-ui/react,从 package.json 依赖看代理应以现有组件代码为准);
  • web/src/types/proto/ 下的生成 TypeScript 不手改、也不参与 Biome 重写biome.jsonfiles.includes 明确排除了 !src/types/proto,lint 的 includes 同样排除 !src/types/proto/**——所以本地跑 pnpm lint 不会碰生成代码,与 AGENTS.md 的禁令在工具层面完全闭环。

数据库与 Proto 规则:三条不可妥协的等价性约束

AGENTS.md 给出四条规则:

  1. schema 变更 = 三份迁移 + 三份 LATEST.sql。仓库结构印证了"三份":store/migration/sqlitestore/migration/mysqlstore/migration/postgres 各自按版本目录(0.17 … 0.31)存放增量 SQL,且每个目录下都有 LATEST.sql(全新安装的完整 schema)。
  2. 全新安装 SQL 与增量迁移必须保持等价——即"从空库跑 LATEST.sql"和"从旧版一步步跑增量"必须收敛到同一 schema。
  3. Proto 字段变更默认必须保持兼容,除非任务显式允许破坏性变更。
  4. 改 proto 后重新生成,且提交必须同时包含 Go/OpenAPI 与 TypeScript 两侧产物——呼应 buf.gen.yaml 中六个插件一次生成两类输出的事实。

对维护者的实际含义:一次 schema 改动的最小完整提交 = 新迁移文件 × 3 + LATEST.sql × 3 + store 层代码(如需要)+ go test -v ./store/... 通过(store 测试会经 TestContainers 对真实 MySQL/PostgreSQL 跑迁移)。

验证策略:迭代时窄验证,收工前按路由面验证

AGENTS.md 的 Verification Policy 给出了四条节奏规则:

  • 迭代过程中跑最窄的相关检查(比如只改一个 store 驱动就只跑对应包测试);
  • 收尾前,按 Change Routing 表中"被改动面"对应的命令完整跑一遍;
  • 纯文档改动 git diff --check 即可,除非文档里包含可运行示例;
  • 某个必需检查在本地跑不了时,必须报告原因和确切命令——这条专为 AI 代理设计,防止"悄悄跳过验证"。

CI 参考:文档声明的每条 CI 事实都可复核

AGENTS.md 的 CI Reference 列出的版本与流水线事实,逐一与仓库对账:

文档声明 仓库证据
后端 CI:Go 1.27.0、go mod tidy -go=1.27.0、golangci-lint v2.13.1、测试组 store/server/internal/other backend-tests.yml GO_VERSION: "1.27.0"第 43 行 version: v2.13.1第 51-52 行 matrix test-group: [store, server, internal, other]
前端 CI:Node 24、pnpm 11.0.1、pnpm lint/pnpm test/pnpm build frontend-tests.yml NODE_VERSION: "24"PNPM_VERSION: "11.0.1",lint job 与 build job 分别执行对应脚本
Proto CI:buf lintbuf format 检查 proto-linter.yml:buf-lint-action 输入 proto,并显式 buf format -d 判空失败
Docker:scripts/Dockerfile、Alpine 3.21、非 root 用户、端口 5230 DockerfileFROM alpine:3.21、创建 nonroot 用户(uid 10001)、ENV MEMOS_PORT="5230"EXPOSE 5230

文档还提到 Docker 是"multi-arch amd64/arm64/arm/v7"。Dockerfile 中使用 --platform=$BUILDPLATFORMARG TARGETOS TARGETARCHDockerfile),是标准的构建平台/目标平台分离写法,为多架构发布提供了基础设施;具体架构列表以发布流水线(.github/workflows/release.yml)为准。

结语:AGENTS.md 为什么值得被当作模板研究

AGENTS.md 拆开看,它其实回答了 AI 代理在陌生仓库中会问的四类问题,且每类问题都绑定了可机器验证的出口:

  1. "这个项目是什么、用什么技术" → Project Snapshot,且声明了生成代码边界,避免代理误改生成文件;
  2. "我该怎么动手" → Working Rules + Change Routing,用"改动类型 → 文件 → 验证命令"三元组把自由度压缩到可审查范围;
  3. "什么命令才算验证通过" → Commands + Verification Policy,与 CI 工作流逐条对齐,本地即 CI;
  4. "写代码要遵守什么风格" → Go/Frontend Conventions,且大部分规则有 lint 工具兜底(forbidigo 禁 fmt.Errorf、godot 查注释标点、goimports 管分组、Biome 管格式与引号)。

对阅读 Memos 源码的开发者而言,这份文件同样是最快的仓库导航图;对要给自己的仓库编写 AGENTS.md 的团队而言,它的"命令与 CI 对齐 + 事实冲突时以源文件为准 + 跑不了的验证必须显式报告"三条原则,是比任何章节标题都更值得抄走的部分。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
docsdocs
暂无描述
Markdown
889
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341