首页
/ Multica 的 AI 代理开发契约:AGENTS.md 仓库指南详解——架构分层、状态管理硬规则与数据库迁移约束

Multica 的 AI 代理开发契约:AGENTS.md 仓库指南详解——架构分层、状态管理硬规则与数据库迁移约束

2026-09-05 20:03:51作者:凌朦慧Richard

AGENTS.md 是 Multica 仓库为 AI 代理(以及任何新加入的工程师)编写的「单一入口」开发指南:它声明了 Go 后端 + pnpm/Turborepo 前端 monorepo 的整体架构,并给出状态管理、包边界、数据库迁移四类不可妥协的硬规则。读完后你将掌握 Multica 各目录的职责划分、React Query 与 Zustand 的状态分工依据、跨 web/desktop 共享代码的边界约束,以及迁移系统刻意放弃整体事务的设计原因。

AGENTS.md 的定位:指针文档,而非规则本体

AGENTS.md 开头就声明了自己的角色:

Single source of truth: This file is a concise pointer document. All authoritative architecture, coding rules, and conventions live in CLAUDE.md at the project root.

也就是说,这份文件是「快速参考 + 指针」:完整的权威规则在同级目录的 CLAUDE.md 中(例如命令列表以 Makefilepackage.jsonpnpm-workspace.yaml 为准)。对 AI 代理来说,这种分层设计是刻意为之——代理先读 AGENTS.md 建立仓库心智模型,需要更深规则(乐观更新四条件、API 兼容性、UUID 处理、测试分层表等)时再跳转 CLAUDE.md,避免两份文档互相复制后失同步。

架构总览:Go 后端 + 共享包分层的前端 monorepo

AGENTS.md 的 Quick Reference 给出了一张目录级架构图:

Go backend + monorepo frontend (pnpm workspaces + Turborepo) with shared packages.

- server/          - Go backend (Chi router, sqlc, gorilla/websocket)
- apps/web/        - Next.js frontend (App Router)
- apps/desktop/    - Electron desktop app
- apps/mobile/     - Expo / React Native iOS app (read apps/mobile/CLAUDE.md first)
- apps/docs/       - Fumadocs documentation site
- packages/core/   - Headless business logic (Zustand stores, React Query hooks, API client)
- packages/ui/     - Atomic UI components (shadcn/Base UI, zero business logic)
- packages/views/  - Shared business pages/components
- packages/tsconfig/     - Shared TypeScript config
- packages/eslint-config/ - Shared ESLint config

这条描述可以与仓库实际内容一一印证:

  • 后端server/go.mod 中声明了 github.com/go-chi/chi/v5 v5.3.0github.com/gorilla/websocket v1.5.3,与 AGENTS.md 的「Chi router、gorilla/websocket」一致;sqlc 代码生成由 make sqlc 驱动(Makefile 中的 sqlc: 目标注释为 "Regenerate sqlc code")。
  • 工作区pnpm-workspace.yaml 只声明了 apps/*packages/* 两组 glob,与目录树一一对应;根 package.jsondev:web/build/typecheck 等脚本全部通过 turbo ... --filter= 调度,engines 要求 node >= 22,与 CLAUDE.md 中「CI runs Node 22」的表述吻合。
  • 移动端是孤岛:文档特意注明进 apps/mobile/ 之前先读 apps/mobile/CLAUDE.md。根 package.json 里 build/typecheck/test/lint 全部带 --filter=!@multica/mobile,从源码结构看,移动端确实被显式排除在统一的 Turborepo 流水线之外,拥有独立的 React 版本与构建管线。

CLAUDE.md 进一步补充了一条依赖方向规则:共享包以原始 .ts/.tsx 源码导出、由消费方应用编译,依赖方向是 views -> core + ui,且 coreui 必须保持相互独立。

状态管理(critical):React Query 管服务端,Zustand 管客户端

这是 AGENTS.md 中标注 critical 的章节,四条规则逐条展开:

  1. React Query 拥有全部服务端状态——issues、members、agents、inbox、workspace 列表等一切来自 API 的数据;
  2. Zustand 拥有客户端/视图状态——视图过滤器、草稿、模态框、桌面端 tab 状态;当前 workspace 身份由路由驱动,仅向平台层镜像(用于请求头、存储命名空间、WebSocket 重连);
  3. 所有 Zustand store 必须放在 packages/core/,禁止出现在 packages/views/ 或各 app 目录;
  4. WS 事件更新 React Query 缓存;store 只允许用于「清空客户端自己持有的指针」,且必须带单一响应者/自事件守卫。

仓库中的实际代码印证了第 3 条:Zustand 的 create() 调用集中在 packages/core/ 下的各域,例如 view-store.ts、actor-issues-view-store.ts、my-issues-view-store.tsconfig store。而 CLAUDE.md 对第 4 条给出了更严的操作定义:WebSocket 事件只能 invalidate 或 patch Query 缓存,绝不许把服务端 payload 镜像进 Zustand;只有当「本客户端自己可能触发了该事件」时,才允许清理 active session、selection 等客户端指针,且必须通过 self-initiated guard 防止自己发的消息又把自己清理掉。

这条规则的工程动机从仓库结构也能看出:web 与 desktop 共享同一套 packages/core/ 的 hooks 和 stores,如果 WS 事件写 Zustand 缓存数据,两端各自实现一次守卫,很容易在 Electron 多窗口(每个渲染进程一个 WS 连接)场景下产生竞争——而「单一响应者 + 自事件守卫」正是为多窗口环境设计的。

版本层面,pnpm-workspace.yamlcatalog: 段锁定了 @tanstack/react-query: ^5.96.2zustand: ^5.0.0,即文档中的 React Query 指 TanStack Query v5。

包边界(hard rules):用依赖方向换取三端共享

AGENTS.md 的四条硬边界:

包/目录 硬约束
packages/core/ react-dom、零 localStorage、零 process.env
packages/ui/ @multica/core 导入
packages/views/ next/*、零 react-router-dom,路由一律走 NavigationAdapter
apps/web/platform/ Next.js API 的唯一落点

这组约束的本质是:coreui 互不依赖,views 依赖两者,于是同一份业务代码能同时被 Next.js(web)和 Electron(desktop)两个平台编译。CLAUDE.md 补充了对应的正向做法与额外约束:

  • core 中持久化要用 StorageAdapter 而非 localStorage,让桌面端可以换成自己的存储;
  • packages/views/ 使用 NavigationAdapteruseNavigation()<AppLink> 做路由抽象;apps/desktop/src/renderer/src/platform/react-router-dom 的唯一接线处;
  • 每个 workspace 必须在自己 package.json 中声明直接导入的外部依赖,共享依赖版本统一走 pnpm-workspace.yamlcatalog: 机制(apps/mobile/ 例外,直接钉住 Expo/React Native 相关版本)。

「零 react-dom、零 localStorage、零 process.env」这条规则之所以值得单独强调,是因为这三样恰好是 headless 包在 SSR(Next.js 服务端渲染)和 Electron 主进程环境下最容易踩的雷:SSR 阶段没有 window.localStorage,Electron 中 process.env 的注入方式与浏览器完全不同。把约束钉死在包边界上,而不是依赖开发者自觉,是该仓库共享代码规模能做大的前提。

数据库迁移(hard rules):禁外键 + 索引必须 CONCURRENTLY

AGENTS.md 给出两条迁移硬规则,它们都能在源码中找到落点:

1. 禁止外键与级联

Never add database foreign keys or cascading actions. Enforce relationships and perform dependent cleanup explicitly in the application layer, using transactions when the operation must be atomic.

即:关系校验与依赖清理全部显式写进应用代码;当清理必须与父操作原子提交/回滚时,用应用层事务。CLAUDE.md 的表述一致:禁止 FOREIGN KEY/REFERENCES、级联删除、级联更新。

2. 每个索引必须 CREATE [UNIQUE] INDEX CONCURRENTLY,且单独成文件

Every index created by a migration, including unique indexes and indexes on new tables, must use CREATE [UNIQUE] INDEX CONCURRENTLY. Keep each concurrent index build in its own single-statement migration file.

仓库的迁移目录大量遵循该模式,例如 170_skill_label_lookup_index.up.sql418_seat_capacity_due_index.up.sql 等均使用 CREATE INDEX CONCURRENTLY

为什么必须单独成文件?答案在迁移执行器源码里。server/cmd/migrate/main.go 的注释写得很直白:

// We deliberately do NOT wrap the loop in a single transaction: the
// repo already ships migrations using CREATE INDEX CONCURRENTLY,
// which Postgres rejects inside a transaction block.

迁移循环刻意包在单一事务里(同时用 pg_advisory_lock 固定一条 pgxpool.Conn 做会话级锁,避免锁挂在被回收的随机连接上)。因为 PostgreSQL 拒绝在事务块内执行并发建索引,所以每个 CONCURRENTLY 语句必须独占一个单语句迁移文件。

CLAUDE.md 还补充了一条容易被忽略的规则:条件跳过的迁移仍会记入 schema_migrations,因此台账只证明顺序、不证明每条 SQL 都执行过;后续涉及「条件存在的对象」的迁移必须写幂等 DDL(IF EXISTS / IF NOT EXISTS)。server/cmd/migrate/README.md 就给出了一个真实运维案例:迁移 371 在 pg_bigm 可用时建 bigram 索引、否则回退 pg_trgm 索引,一旦回退索引被误删,需要手工在事务外逐条执行 CREATE INDEX CONCURRENTLY 恢复,并用 pg_indexindisvalid/indisready/indislive 三个标志验证后才可恢复流量。

命令速查:从文档到可运行的验证管线

AGENTS.md 给出的最小命令集:

make dev              # Auto-setup + start everything
pnpm typecheck        # TypeScript check
pnpm test             # TS unit tests (Vitest)
make test             # Go tests
make check            # Full verification pipeline

对照仓库实现,这些命令的真实行为是:

  • Makefiledev: 目标注释为 "Bootstrap this checkout end-to-end: create env if needed, ensure DB, migrate, start services"——即自动建环境、确保数据库、跑迁移、起服务;test: 目标会在跑 Go 测试前先确保目标库存在且迁移已应用;
  • check: 目标执行 "Run typecheck, TS tests, Go tests, and Playwright E2E for the current checkout",实际委托给 scripts/check.sh,其内部管线为 typecheck → 单测 → Go 测试 → E2E(check.sh 开头注释即 "Full verification pipeline: typecheck → unit tests → Go tests → E2E");
  • package.jsontest 脚本是 turbo test --filter=!@multica/mobile,即 Vitest 单测经 Turborepo 调度且排除移动端,与文档中「TS unit tests (Vitest)」对应。

CLAUDE.md 在此基础上给出完整的开发环境命令族(make up / status / list / down / destroy / worktree-env 等)及其细节:make up 把每个开发环境登记到 ~/.multica/dev/,在锁下分配 API/Web/Desktop 端口与数据库名,并通过 DATABASE_URL 而非 docker exec 验证数据库;worktree 之间共享一个 PostgreSQL 容器,用 .env.worktree 隔离库名与端口。此外 CI 环境为 Node 22 + 最新 Go 1.26 patch + pgvector/pgvector:pg17 PostgreSQL 服务。

总结

AGENTS.md 作为指针文档的价值在于「少而硬」:它把 Multica 真正容易出错的四件事——目录职责、服务端/客户端状态归属、共享包依赖方向、迁移 DDL 约束——压缩成一张速查表,其余细节通过 CLAUDE.md 与 Makefile/pnpm-workspace.yaml 这三个「单一事实源」继续下钻。如果你在 AI 代理协助下向这个仓库提交代码,值得逐字对照的正是文中两个 (hard rules) 章节:包边界违反会让三端共享代码退化,迁移规则违反则可能让 CONCURRENTLY 建索引在事务里直接失败或阻塞线上写入。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384