Agent 全家福 + 元 Agent
昨天(D14)把旗舰 sre-rca 逐文件读透了。今天(D15)横向扫全部 21 个 agent:各干什么、结构上有哪些变体,再钻进最精彩的"用 Agent 造 Agent"——两个元 Agent 的 builder.py 真代码,以及平台"自己开发自己"的 spec 四段流水线和它的安全护栏。这是第 3 周收官——之后进入第 4 周"平台化"(门户/部署/CI/起新 agent/OpenSpec)。
agent-creator 专门照着标准模板培训新服务队(一次成型),skiller-creator 培训完还自己出题考核、不合格回炉重训(RLHF 循环)。再往上,物业甚至有一条"自己给自己搞装修改造"的流水线(发现需求→出方案→施工→验收),每一环都被限权死死框住、关键决策留给人。今天 10 讲就是这张全家福 + 两支教练队 + 一条自装修流水线,全部落到 builder.py 真代码。21 个 agent 分类速览
apps/ 下实际有 21 个 agent(ls apps/ | wc -l = 21,文档常说 9/12,Day 01 讲过的漂移)。按用途大致分四类:
业务/运维执行类(≈14 个)
故障根因分析(旗舰,D14 精读)
PR 变更风险评审
告警治理,高频在线
研发文档要点检查
安全审计,多专家扫 secrets/SAST/依赖
脱敏聊天/工单 → 带出处 FAQ 草稿
架构合规:文档分析 + 代码扫描
业务链路治理:周期巡检 + 治理清单
网关 FAQ 问答
生成 Prometheus 接入 5 件套
前端容器化:dry-run 生成 Dockerfile+k8s
扫 Jira/GitLab/Prom 推导人月月报
自动接入/升级 Java bmc-boot-parent
元 Agent(造东西的 Agent,2 个)
agent-creator(造 agent)、skiller-creator(造 skill)。L03-L05 精读。
自我开发流水线(4 个)
requirement-discoverer → spec-author → spec-executor → release-coordinator,让平台"自己开发自己"。L06-L09 精读。
调度/载体类
supervisor-router-agent(跨 agent 路由)、bmc-agent-mcp(MCP 形态载体,D13 讲过)。
结构变体谱系:一套框架,多种裁剪
所有 agent 共用同一套 toolkit,但按需裁剪出不同"形态"。理解这些变体,能体会框架的弹性:
| 维度 | 取值 → 代表 agent | 为什么这么裁 |
|---|---|---|
| Critic 强度 | L1+L2三态 → sre-rca / 仅L1不调LLM → alert-triage / L1不重试 → requirement-discoverer / 无 → risk-reviewer | 责任重多审、延迟敏感用轻量、有的刻意做减法 |
| 专家数量 | 0(纯路由 supervisor-router)/ 2(risk-reviewer)/ 3(requirement-discoverer)/ 4(sre-rca)/ 5(security-audit) | make_specialist_node 让加减专家几乎零成本 |
| 拓扑 | 并行取证(sre-rca)/ 串行流水(agent-creator)/ 带循环(skiller-creator RLHF、spec-author repair)/ 5 门并行(release-coordinator) | 任务性质决定,见 L04-L08 |
| 载体 | FastAPI pod(默认)/ CLI / MCP(bmc-agent-mcp) | D12/D13 讲过 |
什么是"元 Agent"
元 Agent(meta-agent)= 造 Agent/组件的 Agent。它的输出不是"业务分析结果",而是"另一段代码/一个新组件"。它们本身也是标准的 LangGraph agent(有 State、builder、节点、Critic),只是节点干的活是"分析需求、渲染模板、生成文件"而非"查日志、算指标"。本框架有两个:
- agent-creator:一句自然语言描述 → 生成一个符合规范、可直接跑的新 agent 代码骨架(一次成型)。
- skiller-creator:生成并注册新的 Skill 组件,带自动评测-修订的迭代闭环(RLHF 思想)。
{"description":"做一个巡检 K8s Pod 重启次数、超阈值就出告警清单的 agent","mode":"dry_run"}→ 需求分析(解析要几个专家、要不要 critic) → 架构设计 → scaffolder 渲染模板 → critic_l1 查硬错 → assemble
输出(
mode="dry_run" 只预览不落盘):一份完整、能直接跑的新 agent 代码骨架。mode 还能取 write(真写盘)/ bundle(打 zip)/ sandbox——用状态控制"副作用"大小(agent_creator/state.py:7 的 Mode = Literal["dry_run","write","bundle","sandbox"])。agent-creator:一条串行流水线(看真代码)
agent-creator 的图是纯串行——需求→架构→脚手架→评测设计→查错→组装,没有分支没有循环(apps/agent-creator/agent_creator/builder.py:40-59):
def build_agent_creator_graph(): # builder.py:40
builder = StateGraph(AgentCreatorState)
builder.add_node("requirements_analyst", requirements_analyst_node)
builder.add_node("architect", architect_node)
builder.add_node("scaffolder", scaffolder_node)
builder.add_node("evals_designer", evals_designer_node)
builder.add_node("critic_l1", build_critic_l1()) # ← 复用 D07 business_layer1!
builder.add_node("warn_check", check_eval_count_warning)
builder.add_node("assemble_outcome", assemble_outcome)
# 一条直线,无分支
builder.add_edge(START, "requirements_analyst")
builder.add_edge("requirements_analyst", "architect")
builder.add_edge("architect", "scaffolder")
builder.add_edge("scaffolder", "evals_designer")
builder.add_edge("evals_designer", "critic_l1")
builder.add_edge("critic_l1", "warn_check")
builder.add_edge("warn_check", "assemble_outcome")
builder.add_edge("assemble_outcome", END)
return builder.compile()
build_critic_l1()它的 critic 复用 D07 讲过的 build_business_critic_l1!打开 agent_creator/critic.py:162 能看到 R1-R5 是 error 级规则、R6 是 warn 级——用纯规则校验生成的 agent 骨架合不合格,0 成本不调 LLM。scaffolder_node渲染模板生成文件。用白名单模板 + fragments(预写好的 critic_l1/memory 等标准件)拼装,而不是让 LLM 自由发挥——因为标准件有固定写法,让 LLM 编反而易错。warn_check把 R6 那条 warn 级规则的结果物化成 state.warnings(比如"评测样本太少"),提示但不阻断。skiller-creator:带 RLHF 自迭代的造 Skill(两个路由函数是精髓)
skiller-creator 比 agent-creator 多了一个自我评测-修订循环。它的图有两条路径,靠两个条件函数控制。先看图组装(apps/skiller-creator/skiller_creator/builder.py:83-117):
g.add_edge(START, "skill_writer")
g.add_edge("skill_writer", "scaffolder")
g.add_edge("scaffolder", "critic_l1")
g.add_conditional_edges("critic_l1", _after_critic, { # ← 路由①
"assemble": "assemble",
"test_query_generator": "test_query_generator",
"evaluator": "evaluator"})
g.add_edge("test_query_generator", "evaluator")
g.add_conditional_edges("evaluator", _after_eval, { # ← 路由②
"assemble": "assemble",
"reviser": "reviser"})
g.add_edge("reviser", "scaffolder") # ← 修订后回炉!循环边
g.add_edge("assemble", END)
真正的精髓是评测后的路由 _after_eval(builder.py:65-80)——它决定"达标交付 or 回炉重写":
def _after_eval(state) -> str: # builder.py:65
eval_report = state.get("eval_report") or {}
passes = bool(eval_report.get("passes_threshold", False))
current_iter = int(state.get("iteration") or 0)
max_iter = int(state.get("max_iterations") or 0)
if passes:
return "assemble" # 达标 → 交付
if current_iter >= max_iter:
return "assemble" # ← 预算用尽也交付(不死循环)
return "reviser" # 不达标 + 还有预算 → 修订后回炉
reviser → scaffolder这条边是"循环"的关键:修订完不是结束,而是回到 scaffolder 重新组装、再评测,形成"生成→考核→改→再考"的闭环。current_iter >= max_iter边界/防死循环:哪怕一直不达标,跑满 max_iterations 也会 return "assemble" 交付(附带"未达标"标记)。绝不无限重写烧钱。_after_critic 三态另一个路由(builder.py:40):critic 失败/skip_eval/max_iter=0 → 直接 assemble;已有 test_queries → 去 evaluator;首次进 RLHF → 先生成测试问句。evaluator 节点,复用 D10 评测能力),所以能做"不达标就自己改"的闭环。而一个完整 agent 的骨架,质量很难用一个分数自动判定(要不要人看、要不要跑真实业务),强行循环意义不大。只在"输出能被自动量化"时才值得上自迭代循环——这是把 D10 的评测用在了"生成质量把关"上的精准判断。spec 四段流水线:平台开发自己
最精彩的部分——4 个 agent 串成一条"平台自己给自己提需求、写规格、改代码、评审发布"的流水线(就是 Day 04 route_chain 那条链):
discoverer发现需求→ spec-author写 OpenSpec 规格→ spec-executor改代码+测试→ release-
coordinator5 道 gate 评审
| agent | 做什么(真实结构) | 安全约束(scope guard) |
|---|---|---|
| requirement-discoverer | 3 专家(cases/prom/静态)并行 → synthesizer → critic → writeback | critic 不 retry,产出 IssueDraft,不自动起 spec |
| spec-author | 8 节点 + repair loop,issue → OpenSpec change 文件(L07) | safe_change_write 只能写本次 change 目录(L09) |
| spec-executor | M0 计划 / M1 执行 双模,读 change → 改代码 → 跑测 → commit(L08) | 只 commit 到 agent/<change-id> 分支,不碰 main |
| release-coordinator | 5 道 gate 并行 → merge_decisions → reporter(L08) | 只出 PR decision,不自动合并 |
先看 requirement-discoverer 的 critic 路由——它体现了"cron 场景不该重试"(apps/requirement-discoverer/requirement_discoverer/builder.py:33-37):
def _route_after_critic(state) -> str: # builder.py:33
"""critic 三态:pass → writeback / fail → __end__(等下次 cron · 不 retry · 防 noise)。"""
if state.get("critique_passed", False):
return "writeback"
return "end" # ← 不过就结束,不回炉重试
spec-author:带 repair loop 的自修复图
spec-author 把一段需求文本变成 OpenSpec change 的 4 个文件(proposal/tasks/design/spec-delta),写完还会自校验、失败自修复(apps/spec-author/spec_author/builder.py)。它的图骨架(文件头 docstring 画得很清楚):
# builder.py:1 docstring 描述的图:
# classify → scan_related_specs → generate_proposal → generate_tasks →
# generate_design → generate_spec_delta → write_files → validate
# │
# ┌───── validate.status ───────┤
# valid failed
# │ self_repair_count < 2 ?
# │ ┌────────────┴──────────┐
# │ yes(repair→write→loop) no(fail_2x)
# └────────────────┴────────────────────────┘
# ↓ reporter → END
REPAIR_LIMIT = 2 # builder.py:40 · 最多自修复 2 次
...
def build_spec_author_graph(): # builder.py:55
write_files → validate写完文件立刻自校验(OpenSpec 结构合不合法)。这是"生成完先自己检查一遍"的思路,和 skiller 的 evaluator 异曲同工。failed 且 count < 2校验失败且没到上限 → 进 repair 节点修 → 重新 write → 再 validate,构成 repair loop。REPAIR_LIMIT = 2又是"受控循环 + 硬上限":修 2 次还不合法就带着 fail_2x 状态走 reporter,绝不无限修。跟 sre-rca 的 max_retries、skiller 的 max_iterations 同一个模式。spec-executor 双模 + release-coordinator 五门并行
spec-executor 用一张图跑两种模式(apps/spec-executor/spec_executor/builder.py:1-20 docstring):m0_plan 只做计划就 reporter 收尾;m1_execute 才真正 prepare_worktree → patch_files → run_quick_test → commit_branch。而 commit 的约束写在节点里:
# commit_branch.py:1
"""commit_branch · M1 only · 测试 pass 才 commit 到 agent/<change-id>。"""
# → 只提到临时分支 agent/*,永不碰 main;测试不过不 commit
release-coordinator 则是"5 道 gate 并行"的裁判(apps/release-coordinator/release_coordinator/builder.py:38-63)。它并行跑 ruff/mypy/pytest/arch/risk/doc 多道闸,用一个 reducer 合并各闸结果:
def _merge_gate_results(left: dict, right: dict) -> dict: # builder.py:38
"""gate_results 字段的 reducer · 并行 gate 写各自 key · LangGraph 合并 dict。"""
return {**(left or {}), **(right or {})} # ← 各 gate 写不同 key,字典合并零冲突
m0 / m1 双模同一张图用 mode 字段走不同分支——"只出计划"和"真改代码"复用大部分节点,避免维护两套图。_merge_gate_results和 D14 sre-rca 的 4 专家一个道理:多个并行节点写同一 state 字段(gate_results),用 reducer 做字典合并,各 gate 写自己的 key 互不覆盖。这是并行安全的通用招式。只出 decision跑完 5 门只给"建议合/不合",绝不自动按合并键——最终拍板留给人。👶 小白:让 AI 自己给自己改代码,不会哪天失控把主干搞崩吗?
👨🏫 老师:这正是这条流水线最克制的地方——每一环都被"限权"死死框住。spec-author 的 safe_change_write 只准写本次 change 目录(L09);spec-executor 的 commit 只到 agent/* 临时分支、永不碰 main,测试不过就不提;release-coordinator 跑完 5 门也只出 decision、不自动合并;连最上游的 requirement-discoverer 都只产 IssueDraft、不自动起 spec。就像装修队能施工,但"拆承重墙"和"最终验收签字"必须人来。
safe_change_write:元 Agent 会写文件,护栏怎么写
元 Agent 和 spec 流水线都会往磁盘写文件——这是最危险的副作用。spec-author 的写文件全走 safe_change_write 这道闸(apps/spec-author/spec_author/tools/safe_change_write.py)。核心判定函数:
_DENY_PREFIXES = ( # safe_change_write.py:14 · 黑名单
"openspec/specs/", # capability spec 是 ground truth,走 archive 流程,禁直改
"apps/spec-author/", # 禁改自己(防自递归污染)
".env", ".git/", "secrets/", "deploy/",
)
def is_change_path_allowed(rel_path: str, change_id: str) -> tuple[bool, str]: # :41
rp = _normalize(rel_path) # 统一斜杠 / 拒绝绝对路径
if ".." in rp.split("/"):
return False, "path 含 `..` parent traversal 拒" # ① 防目录穿越
for deny in _DENY_PREFIXES:
if rp.startswith(deny):
return False, f"黑名单前缀 {deny!r}" # ② 黑名单
expected_prefix = f"openspec/changes/{change_id}/"
if not rp.startswith(expected_prefix):
return False, f"必须在 {expected_prefix!r} 子树" # ③ 白名单:只准写本次 change
if not re.match(r"^\d{4}-\d{2}-\d{2}-[a-z0-9][a-z0-9-]*$", change_id):
return False, f"change_id 格式不规范:{change_id!r}" # ④ change_id 格式校验
return True, ""
白名单唯一只准写 openspec/changes/<change_id>/ 这一棵子树。哪怕 LLM 想写别处,路径一对不上就被拒。默认拒绝,显式放行。黑名单强制就算落在白名单,碰到 .git//.env/secrets//改自己 也一律拒。双保险。".." 穿越检查防 openspec/changes/x/../../etc/passwd 这种用 .. 逃出白名单的经典攻击。不覆盖已存在文件safe_change_write_file(:69)默认 overwrite=False,还会 resolve() 后校验没逃出 repo_root。层层设防。.env、改坏 .git、甚至改自己的代码造成自递归灾难。safe_change_write 的做法是把"能写哪"缩到一棵最小子树,再叠加黑名单、路径穿越检查、格式校验、禁覆盖、resolve 越界检查——五层防护,任一层不过就 SafeWriteRejected。这是"让 AI 自我开发"能安全落地的技术底座:能力越大,闸门越要提前收窄。今日小结 + 动手
🧠 第 3 周收官自测
- 21 个 agent 大致分哪四类?结构变体有哪几个维度?
- 为什么故意保留"无 Critic""仅 L1""critic 不重试"变体?(真实需求 + 压测 toolkit 通用性)
- 元 Agent 是什么?agent-creator 怎么防止 LLM 编造标准件?(白名单 fragments + 规则 critic_l1)
- 为什么 skiller-creator 有 RLHF 循环、agent-creator 一次成型?(产物能否自动评分)
- sre-rca / skiller / spec-author 的"生成→自检→修/退"骨架为什么都长一样?
- 为什么 requirement-discoverer 的 critic 不重试、sre-rca 要重试?(cron vs 实时)
- 自我开发流水线 4 环各有什么 scope guard?safe_change_write 有几层防护?
✋ 动手
# 1. 扫一遍所有 agent(数一下确实 21 个)
ls apps/ | wc -l && ls apps/
# 2. 元 Agent 的图:串行 vs RLHF 循环
sed -n '40,59p' apps/agent-creator/agent_creator/builder.py
sed -n '40,117p' apps/skiller-creator/skiller_creator/builder.py
# 3. spec 四段流水线的 4 个 builder(读 docstring 图)
sed -n '1,40p' apps/spec-author/spec_author/builder.py
sed -n '1,40p' apps/spec-executor/spec_executor/builder.py
sed -n '1,40p' apps/release-coordinator/release_coordinator/builder.py
sed -n '1,45p' apps/requirement-discoverer/requirement_discoverer/builder.py
# 4. 安全护栏(元 Agent 写盘的底座)
cat apps/spec-author/spec_author/tools/safe_change_write.py
# 5. 复用 D07 的规则 critic 造 agent
sed -n '160,175p' apps/agent-creator/agent_creator/critic.py