跳转到内容

Agent 工作流

Plumego 不只是一套 Go HTTP 工具包。它有一套机器可读的控制面,让人和 AI coding assistant 在改代码之前都能先分类工作归属。

如果你还在学习归属是如何被分类的,先读仓库边界。这一页专门讲的是 AI agent 如何与这套仓库配合工作。

大多数 AI coding assistant 能写出能编译、能过测试的代码,但会违反架构约定——不是因为模型本身错误,而是因为这些约定存在于人的知识和团队记忆里,而不是以模型能读懂的形式存在。

Plumego 把这些约定外化出来:

约定位置机器可读?
哪个模块负责这类工作specs/task-routing.yaml
各层之间允许哪些 importspecs/dependency-rules.yaml
正确的修改顺序是什么specs/change-recipes/
模块在本地承诺什么<module>/module.yaml
改动落地前需要通过哪些 gateinternal/checks/*是——CI 强制执行
specs/task-routing.yaml → 在写代码之前,把工作路由到所属模块
specs/dependency-rules.yaml → 验证 import 边界没有被违反
specs/change-recipes/ → 对已知改动类型,按定义好的顺序执行
<module>/module.yaml → 读取本地作用域、风险等级和验证规则
internal/checks/* → 在提交之前运行边界和 manifest 检查器
make gates → 本地运行与 CI 等效的完整 gate
步骤读什么文件
1这类工作属于哪个模块?specs/task-routing.yaml
2允许哪些 import?specs/dependency-rules.yaml
3是否有预定义的 recipe?specs/change-recipes/<type>.yaml
4这个模块在本地承诺什么?<module>/module.yaml
5哪些检查器适用?internal/checks/

按这个顺序阅读,意味着 agent 在打开任何 Go 文件之前就已经完成了归属分类。这一步刻意被前置,这样边界违规会在计划阶段就暴露,而不是出现在 diff 里。

specs/task-routing.yaml 把工作描述映射到所属模块。当 agent 在评估”给 greet 端点加入站限流”时,可以读路由表来确认这属于 middleware(传输层站限流),而不是 x/resilience(出站电路保护)。

这正是人类在 PR review 时做的分类——Plumego 只是把规则显式化到 agent 也能在写代码之前应用的程度。

任务描述:“给 greet 端点加入站限流”

第一步 — 读取 specs/task-routing.yaml 中的路由规则:

# specs/task-routing.yaml(摘录)
routing_rules:
stable_root_work:
intent: 修改内核、生命周期、路由结构、传输合约、
传输层中间件、认证 primitive 或存储 primitive。
destination: stable
packages:
- middleware # ← 传输层限流在这里
- core
- router
- contract
- security
- store
- health
- log
- metrics
extension_work:
intent: 修改产品能力、业务功能、协议适配或扩展行为。
destination: extension
primary_families:
- x/resilience # ← 出站熔断保护在这里
- x/tenant
- x/gateway
# ...

第二步 — 分类:

任务中的信号路由匹配归属
”入站”传输关注点,不是产品/业务逻辑stable_root_work
入站路径上的”限流”传输层中间件,不是熔断器middleware
”greet 端点”应用本地路由注册app_wiring → 参考 reference/standard-service

第三步 — 确认所属模块的 task 条目:

tasks:
middleware:
start_with:
- middleware/module.yaml
- docs/modules/middleware/README.md
- docs/reference/canonical-style-guide.md
avoid:
- core
- contract

结果: 改动属于 middleware/,从 middleware/module.yaml 开始。x/resilience 是出站电路保护——不同的分类。先开 x/resilience 是路由表可以预防的归属错误。

路由表不能取代读代码,它的作用是在 agent 打开任何 Go 文件之前缩小搜索空间。

动手试试 — 选择一个场景,查看路由决策:

给定一个任务,路由表会把它分配到哪个模块?

选一个场景,看路由决策过程:

specs/dependency-rules.yaml 定义了各层之间允许的 import 方向:

  • 稳定根不得 import x/*
  • x/* 家族可以 import 稳定根
  • 应用本地代码可以 import 两者

internal/checks/dependency-rules 机械地强制执行这条规则。如果 agent 添加了违反边界的 import,gate 就会失败:

Terminal window
go run ./internal/checks/dependency-rules

这个检查作为 make gates 的一部分运行,是任何改动落地前的必要条件。它不依赖评审者的记忆。

specs/change-recipes/ 里包含 YAML 文件,定义了已知改动类型的正确执行顺序。每个 recipe 声明:改动范围、有序步骤,以及止步条件(recipe 绝对不能做的事)。

Recipe适用场景
add-http-endpoint.yaml在应用本地代码中添加新路由和处理器
add-middleware.yaml向 middleware 包添加纯传输层中间件
fix-bug.yaml定位并修复缺陷,同时添加回归测试
http-endpoint-bugfix.yaml修复 HTTP 处理器、路由装配或传输契约中的缺陷
symbol-change.yaml对导出符号进行重命名、删除或行为变更
new-extension-module.yaml创建新的 x/* 能力家族
new-stable-module.yaml向稳定根添加新包
add-websocket-room.yamlx/websocket 中添加新房间类型或连接策略
add-grpc-method.yaml通过 x/rpc 在现有 HTTP 表面旁添加 gRPC 方法
add-ai-tool.yamlx/ai/tool 注册新工具或将其接入 session
add-acceptance-tests.yaml编写预先失败的验收测试以定义任务卡的完成条件
tenant-policy-change.yaml修改租户解析、策略评估、配额或速率限制行为
stable-root-boundary-review.yaml仅审查稳定根边界安全性,不编写代码
analysis-only.yaml研究和规划任务——不允许修改任何文件
review-only.yaml代码审查任务——只产出发现,不打补丁

任务描述:“添加 GET /users/:id 处理器,返回用户详情。”

第一步 — 用 specs/task-routing.yaml 分类。这是应用本地 HTTP 功能工作 → 路由条目为 app_wiring,recipe 为 add-http-endpoint

第二步 — 读取 recipe,按步骤顺序执行:

reference/standard-service/internal/app/routes.go
mux.Get("/users/:id", handlers.GetUser)
// reference/standard-service/internal/handlers/users.go
func GetUser(w http.ResponseWriter, r *http.Request) {
id := router.Param(r, "id")
user, err := svc.FindUser(r.Context(), id)
if err != nil {
_ = contract.WriteError(w, r, contract.NewErrorBuilder().
WithType(contract.TypeNotFound).
WithMessage("user not found").
Build())
return
}
_ = contract.WriteResponse(w, r, http.StatusOK, user, nil)
}

第三步 — 按 recipe 要求运行验证:

Terminal window
go test -race ./internal/...
go run ./internal/checks/dependency-rules

结论: recipe 消除了处理器放在哪里、使用什么响应助手、运行哪些测试的歧义——无需评审者记忆任何规则。

按照 recipe 工作的 agent 读取定义好的步骤,依次完成每一步,然后在认为改动完成之前运行 recipe 中指定的检查器。

在任何 agent 生成的改动之后,推送之前先运行完整 gate:

Terminal window
make gates

make gates 与 CI 等效,包含:

  • gofmt -l . — 格式检查
  • go vet ./... — 静态分析
  • go test ./... — 测试套件
  • go run ./internal/checks/dependency-rules — 边界检查
  • go run ./internal/checks/agent-workflow — 工作流合规
  • go run ./internal/checks/module-manifests — manifest 一致性
  • go run ./internal/checks/reference-layout — 参考应用形态

任何检查失败,就修复对应的问题再重新运行。不要用 --no-verify 绕过或跳过单个检查器。

如果 agent 的改动同时涉及稳定根和 x/* 扩展——例如同时修改了 middlewarex/resilience——把它们视为独立改动,分别分类:

  1. 对改动中的每类工作读取 specs/task-routing.yaml
  2. 确认每类工作的所属模块
  3. 如果归属不同,先拆分再编辑

在同一次提交里跨越边界不是自动错误,但必须是刻意的:PR 描述应该说明改动为什么跨层,以及每个部分适用哪个 recipe。

下一个问题页面
一般情况下工作如何被分类?仓库边界
应该打开哪个稳定根或 x/* 家族?模块总览
发布姿态对采用意味着什么?发布姿态