Agent 工作流
Agent 工作流
Section titled “Agent 工作流”Plumego 不只是一套 Go HTTP 工具包。它有一套机器可读的控制面,让人和 AI coding assistant 在改代码之前都能先分类工作归属。
如果你还在学习归属是如何被分类的,先读仓库边界。这一页专门讲的是 AI agent 如何与这套仓库配合工作。
这套方案解决了什么问题
Section titled “这套方案解决了什么问题”大多数 AI coding assistant 能写出能编译、能过测试的代码,但会违反架构约定——不是因为模型本身错误,而是因为这些约定存在于人的知识和团队记忆里,而不是以模型能读懂的形式存在。
Plumego 把这些约定外化出来:
| 约定 | 位置 | 机器可读? |
|---|---|---|
| 哪个模块负责这类工作 | specs/task-routing.yaml | 是 |
| 各层之间允许哪些 import | specs/dependency-rules.yaml | 是 |
| 正确的修改顺序是什么 | specs/change-recipes/ | 是 |
| 模块在本地承诺什么 | <module>/module.yaml | 是 |
| 改动落地前需要通过哪些 gate | internal/checks/* | 是——CI 强制执行 |
从 agent 视角看控制面结构
Section titled “从 agent 视角看控制面结构”specs/task-routing.yaml → 在写代码之前,把工作路由到所属模块specs/dependency-rules.yaml → 验证 import 边界没有被违反specs/change-recipes/ → 对已知改动类型,按定义好的顺序执行<module>/module.yaml → 读取本地作用域、风险等级和验证规则internal/checks/* → 在提交之前运行边界和 manifest 检查器make gates → 本地运行与 CI 等效的完整 gateAgent 在改代码之前要读什么
Section titled “Agent 在改代码之前要读什么”| 步骤 | 读什么 | 文件 |
|---|---|---|
| 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 里。
通过 task-routing.yaml 路由工作
Section titled “通过 task-routing.yaml 路由工作”specs/task-routing.yaml 把工作描述映射到所属模块。当 agent 在评估”给 greet 端点加入站限流”时,可以读路由表来确认这属于 middleware(传输层站限流),而不是 x/resilience(出站电路保护)。
这正是人类在 PR review 时做的分类——Plumego 只是把规则显式化到 agent 也能在写代码之前应用的程度。
路由实战 — 一个完整示例
Section titled “路由实战 — 一个完整示例”任务描述:“给 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 文件之前缩小搜索空间。
动手试试 — 选择一个场景,查看路由决策:
给定一个任务,路由表会把它分配到哪个模块?
选一个场景,看路由决策过程:
给某个入站 HTTP 端点加入限流(如 greet 路由)。
从这里开始
middleware/module.yamldocs/modules/middleware/README.mddocs/reference/canonical-style-guide.md
避开
corecontract
分类依据
"入站" 映射到传输层。请求边界的限流是中间件工作,不是出站熔断(x/resilience)。目标:middleware/。
修复 HTTP handler 返回错误状态码或响应体格式错误的缺陷。
从这里开始
AGENTS.mddocs/reference/canonical-style-guide.mdspecs/change-recipes/http-endpoint-bugfix.yamlreference/standard-service/internal/app/routes.go- +2 more
避开
middlewarex/gateway
分类依据
端点缺陷 → 使用 http_endpoint_bugfix recipe。从 contract + router 开始;避免 middleware,保持传输配线的清晰。
为每个租户加入 JWT 校验、quota 限制或策略评估。
从这里开始
AGENTS.mdspecs/change-recipes/tenant-policy-change.yamlx/tenant/module.yamldocs/concepts/x-tenant-blueprint.md- +1 more
避开
middlewarestorecore
分类依据
租户身份与策略在 x/tenant,不在通用 auth middleware。稳定根承担 HTTP 基线;x/tenant 承担租户层。
加入从 AI provider 流式返回响应的端点(如 OpenAI 兼容的 SSE)。
从这里开始
x/ai/module.yamldocs/modules/x-ai/README.mdx/ai/entrypoints.go
避开
coreroutermiddleware
分类依据
AI 能力是产品工作,不是传输基础设施。extension_work → x/ai。稳定根在 AI API 形态演化时保持兼容。
在现有 HTTP 服务中加入 WebSocket 升级处理。
从这里开始
x/websocket/module.yamldocs/modules/x/websocket/README.mdx/websocket/websocket.go
避开
corerouter
分类依据
协议适配(HTTP → WS)是扩展工作。x/websocket 与 HTTP 路由共存,使用同一个中间件链。
在边缘加入反向代理、上游路由或负载均衡。
从这里开始
x/gateway/module.yamlx/gateway/entrypoints.gospecs/extension-taxonomy.yaml
避开
x/restreference/standard-service
分类依据
边缘传输是 gateway_edge_transport 任务 → x/gateway。不改变上游服务使用的稳定路由模型。
加入 multipart 文件上传和下载处理。
从这里开始
x/fileapi/module.yamldocs/modules/x/fileapi/README.mdx/fileapi/handler.go
避开
store/filex/data/file
分类依据
文件上传是产品能力,不是 store primitive。file_api 任务 → x/fileapi。避免 store/file 和 x/data/file,保持 primitive 分离。
在现有服务中新增或重构路由和 handler 配线。
从这里开始
reference/standard-service/main.goreference/standard-service/internal/app/app.goreference/standard-service/internal/app/routes.go
避开
x/restx/gatewayx/webhook
分类依据
路由注册是应用本地配线,不是稳定根变更。app_wiring → reference/standard-service internal/app/routes.go。
更新 specs/dependency-rules.yaml 中各层之间的 import 方向。
从这里开始
specs/dependency-rules.yamlspecs/repo.yamlAGENTS.md
避开
reference/standard-servicecmd/plumego
分类依据
架构规则变更属于控制面。repo_rules → specs/。这里不改 Go 包——规则是纯机器可读的。
评估或添加一个新包到稳定根(如新的传输 primitive)。
从这里开始
AGENTS.mdspecs/change-recipes/stable-root-boundary-review.yamlspecs/repo.yamlspecs/dependency-rules.yaml- +9 more
避开
reference/with-messagingreference/with-gateway
分类依据
稳定根扩展在编码前需要边界审查 recipe。stable_root_boundary_review → 读取全部九个现有稳定根的 manifest。
边界强制执行
Section titled “边界强制执行”specs/dependency-rules.yaml 定义了各层之间允许的 import 方向:
- 稳定根不得 import
x/* x/*家族可以 import 稳定根- 应用本地代码可以 import 两者
internal/checks/dependency-rules 机械地强制执行这条规则。如果 agent 添加了违反边界的 import,gate 就会失败:
go run ./internal/checks/dependency-rules这个检查作为 make gates 的一部分运行,是任何改动落地前的必要条件。它不依赖评审者的记忆。
Change recipes
Section titled “Change recipes”specs/change-recipes/ 里包含 YAML 文件,定义了已知改动类型的正确执行顺序。每个 recipe 声明:改动范围、有序步骤,以及止步条件(recipe 绝对不能做的事)。
完整 recipe 索引
Section titled “完整 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.yaml | 在 x/websocket 中添加新房间类型或连接策略 |
add-grpc-method.yaml | 通过 x/rpc 在现有 HTTP 表面旁添加 gRPC 方法 |
add-ai-tool.yaml | 在 x/ai/tool 注册新工具或将其接入 session |
add-acceptance-tests.yaml | 编写预先失败的验收测试以定义任务卡的完成条件 |
tenant-policy-change.yaml | 修改租户解析、策略评估、配额或速率限制行为 |
stable-root-boundary-review.yaml | 仅审查稳定根边界安全性,不编写代码 |
analysis-only.yaml | 研究和规划任务——不允许修改任何文件 |
review-only.yaml | 代码审查任务——只产出发现,不打补丁 |
实例演练:添加新 HTTP 端点
Section titled “实例演练:添加新 HTTP 端点”任务描述:“添加 GET /users/:id 处理器,返回用户详情。”
第一步 — 用 specs/task-routing.yaml 分类。这是应用本地 HTTP 功能工作 → 路由条目为 app_wiring,recipe 为 add-http-endpoint。
第二步 — 读取 recipe,按步骤顺序执行:
mux.Get("/users/:id", handlers.GetUser)
// reference/standard-service/internal/handlers/users.gofunc 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 要求运行验证:
go test -race ./internal/...go run ./internal/checks/dependency-rules结论: recipe 消除了处理器放在哪里、使用什么响应助手、运行哪些测试的歧义——无需评审者记忆任何规则。
按照 recipe 工作的 agent 读取定义好的步骤,依次完成每一步,然后在认为改动完成之前运行 recipe 中指定的检查器。
Agent 生成改动之后运行 gates
Section titled “Agent 生成改动之后运行 gates”在任何 agent 生成的改动之后,推送之前先运行完整 gate:
make gatesmake 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 改动跨越边界时
Section titled “当 agent 改动跨越边界时”如果 agent 的改动同时涉及稳定根和 x/* 扩展——例如同时修改了 middleware 和 x/resilience——把它们视为独立改动,分别分类:
- 对改动中的每类工作读取
specs/task-routing.yaml - 确认每类工作的所属模块
- 如果归属不同,先拆分再编辑
在同一次提交里跨越边界不是自动错误,但必须是刻意的:PR 描述应该说明改动为什么跨层,以及每个部分适用哪个 recipe。
| 下一个问题 | 页面 |
|---|---|
| 一般情况下工作如何被分类? | 仓库边界 |
应该打开哪个稳定根或 x/* 家族? | 模块总览 |
| 发布姿态对采用意味着什么? | 发布姿态 |