本文以 mystu(铁矿石价格预测 Agent) 项目为例,记录 Compound Engineering(以下简称 CE)在 Cursor 中的安装、命令链、产出文件的生命周期,以及
/ce-test-browser --port 5000的实际效果与工作原理。
1. Compound Engineering 是什么?
Compound Engineering 是一套面向 AI 编程助手的工作流插件,目标不是「写一次代码就结束」,而是让需求 → 计划 → 实现 → 测试 → 审查 → 知识沉淀形成可复用的闭环。
核心理念叫 Compounding(复利):
- 第一次解决「Redis Cluster 启动失败」可能要查文档 30 分钟;
- 用
/ce-compound沉淀后,下次同类问题 2 分钟检索docs/solutions/即可; - 团队知识随每次修复而累积,而不是散落在聊天记录里。
在本项目中,CE 与 Cursor Agent 配合,主要入口是 Slash 命令 (如 /ce-setup、/ce-plan)和 Skill 文件(插件内置的操作规程)。
2. 安装与环境准备
2.1 安装 Compound Engineering 插件
CE 作为 Cursor 插件安装(Compound Engineering local plugin)。安装后,Agent 会话中可使用 /ce-* 系列命令。
2.2 运行 /ce-setup 做健康检查
首次使用或环境变更后,执行:
/ce-setup
它会检查:
| 类别 | 典型工具 | 用途 |
|---|---|---|
| CLI 工具 | agent-browser |
浏览器自动化(/ce-test-browser 专用) |
gh |
GitHub PR/Issue | |
jq |
JSON 处理 | |
ffmpeg / vhs |
演示录屏 | |
ast-grep |
结构化代码搜索 | |
| Agent Skills | ast-grep skill 等 |
子 Agent 能力扩展 |
Windows 环境说明: 官方 health check 脚本为 bash,在 Windows 上可能需手动安装缺失项。本项目中通过 npm 全局安装 agent-browser:
npm install -g agent-browser
agent-browser install # 安装浏览器驱动
2.3 项目级配置文件
/ce-setup 会在仓库中引导创建:
.compound-engineering/
├── config.local.example.yaml # 提交 Git,团队可见的配置模板
└── config.local.yaml # 本地偏好,已在 .gitignore,不提交
作用:
config.local.example.yaml:文档化所有可选项(输出格式、Product Pulse 等),默认全部注释;config.local.yaml:你的机器专属偏好,例如plan_output: html让计划输出 HTML。
生命周期: example 随插件模板更新而刷新;local 只创建一次,由开发者自行维护。
3. CE 命令链与完整工作流
3.1 总览:从想法到知识沉淀
#mermaid-svg-PUEYkUmHaNTKj51O{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PUEYkUmHaNTKj51O .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PUEYkUmHaNTKj51O .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PUEYkUmHaNTKj51O .error-icon{fill:#552222;}#mermaid-svg-PUEYkUmHaNTKj51O .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PUEYkUmHaNTKj51O .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PUEYkUmHaNTKj51O .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PUEYkUmHaNTKj51O .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PUEYkUmHaNTKj51O .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PUEYkUmHaNTKj51O .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PUEYkUmHaNTKj51O .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PUEYkUmHaNTKj51O .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PUEYkUmHaNTKj51O .marker.cross{stroke:#333333;}#mermaid-svg-PUEYkUmHaNTKj51O svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PUEYkUmHaNTKj51O p{margin:0;}#mermaid-svg-PUEYkUmHaNTKj51O .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PUEYkUmHaNTKj51O .cluster-label text{fill:#333;}#mermaid-svg-PUEYkUmHaNTKj51O .cluster-label span{color:#333;}#mermaid-svg-PUEYkUmHaNTKj51O .cluster-label span p{background-color:transparent;}#mermaid-svg-PUEYkUmHaNTKj51O .label text,#mermaid-svg-PUEYkUmHaNTKj51O span{fill:#333;color:#333;}#mermaid-svg-PUEYkUmHaNTKj51O .node rect,#mermaid-svg-PUEYkUmHaNTKj51O .node circle,#mermaid-svg-PUEYkUmHaNTKj51O .node ellipse,#mermaid-svg-PUEYkUmHaNTKj51O .node polygon,#mermaid-svg-PUEYkUmHaNTKj51O .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PUEYkUmHaNTKj51O .rough-node .label text,#mermaid-svg-PUEYkUmHaNTKj51O .node .label text,#mermaid-svg-PUEYkUmHaNTKj51O .image-shape .label,#mermaid-svg-PUEYkUmHaNTKj51O .icon-shape .label{text-anchor:middle;}#mermaid-svg-PUEYkUmHaNTKj51O .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PUEYkUmHaNTKj51O .rough-node .label,#mermaid-svg-PUEYkUmHaNTKj51O .node .label,#mermaid-svg-PUEYkUmHaNTKj51O .image-shape .label,#mermaid-svg-PUEYkUmHaNTKj51O .icon-shape .label{text-align:center;}#mermaid-svg-PUEYkUmHaNTKj51O .node.clickable{cursor:pointer;}#mermaid-svg-PUEYkUmHaNTKj51O .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PUEYkUmHaNTKj51O .arrowheadPath{fill:#333333;}#mermaid-svg-PUEYkUmHaNTKj51O .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PUEYkUmHaNTKj51O .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PUEYkUmHaNTKj51O .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PUEYkUmHaNTKj51O .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PUEYkUmHaNTKj51O .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PUEYkUmHaNTKj51O .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PUEYkUmHaNTKj51O .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PUEYkUmHaNTKj51O .cluster text{fill:#333;}#mermaid-svg-PUEYkUmHaNTKj51O .cluster span{color:#333;}#mermaid-svg-PUEYkUmHaNTKj51O div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PUEYkUmHaNTKj51O .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PUEYkUmHaNTKj51O rect.text{fill:none;stroke-width:0;}#mermaid-svg-PUEYkUmHaNTKj51O .icon-shape,#mermaid-svg-PUEYkUmHaNTKj51O .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PUEYkUmHaNTKj51O .icon-shape p,#mermaid-svg-PUEYkUmHaNTKj51O .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PUEYkUmHaNTKj51O .icon-shape .label rect,#mermaid-svg-PUEYkUmHaNTKj51O .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PUEYkUmHaNTKj51O .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PUEYkUmHaNTKj51O .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PUEYkUmHaNTKj51O :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 持续维护
交付阶段
实施阶段
规划阶段
/ce-brainstorm
docs/brainstorms/*.md
/ce-plan
docs/plans/*.md
/ce-work plan:...
业务代码 + tests/
/ce-test-browser
浏览器验收报告
/ce-code-review
审查意见 / 修复
/ce-commit
Git 提交
/ce-compound
docs/solutions/ + CONCEPTS.md
/ce-compound-refresh
更新过时 learning
3.2 各命令职责一览
| 命令 | 输入 | 产出 | 何时用 |
|---|---|---|---|
/ce-setup |
无 | 环境诊断 + CE 配置 | 首次、换机器、缺工具时 |
/ce-brainstorm |
功能想法 | docs/brainstorms/ 需求文档 |
需求不清晰、多方案权衡 |
/ce-plan |
brainstorm 或描述 | docs/plans/ 实施计划 |
多文件/跨层改动前 |
/ce-work |
plan 路径 | 代码实现 | 按计划编码 |
/ce-test-browser |
--port 5000 等 |
浏览器测试报告 | UI/路由/SSE 改动后 |
/ce-code-review |
可选 plan/base | 结构化审查 | 大改动合并前 |
/ce-commit |
无 | Git commit | 用户明确要求提交时 |
/ce-compound |
可选上下文 | docs/solutions/ |
问题已解决、要沉淀经验 |
/ce-compound-refresh |
范围 hint | 更新旧文档 | 发现 learning 过时 |
3.3 本项目的真实执行顺序(用户认证功能)
以下为本仓库 feat/user-auth 分支上的实际路径:
本地服务 :5000 文件系统 CE Agent 开发者 本地服务 :5000 文件系统 CE Agent 开发者 #mermaid-svg-iiq8JnPvHuSkkbcN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iiq8JnPvHuSkkbcN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iiq8JnPvHuSkkbcN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iiq8JnPvHuSkkbcN .error-icon{fill:#552222;}#mermaid-svg-iiq8JnPvHuSkkbcN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iiq8JnPvHuSkkbcN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iiq8JnPvHuSkkbcN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iiq8JnPvHuSkkbcN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iiq8JnPvHuSkkbcN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iiq8JnPvHuSkkbcN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iiq8JnPvHuSkkbcN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iiq8JnPvHuSkkbcN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iiq8JnPvHuSkkbcN .marker.cross{stroke:#333333;}#mermaid-svg-iiq8JnPvHuSkkbcN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iiq8JnPvHuSkkbcN p{margin:0;}#mermaid-svg-iiq8JnPvHuSkkbcN .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-iiq8JnPvHuSkkbcN text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-iiq8JnPvHuSkkbcN .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-iiq8JnPvHuSkkbcN .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-iiq8JnPvHuSkkbcN .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-iiq8JnPvHuSkkbcN .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-iiq8JnPvHuSkkbcN #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-iiq8JnPvHuSkkbcN .sequenceNumber{fill:white;}#mermaid-svg-iiq8JnPvHuSkkbcN #sequencenumber{fill:#333;}#mermaid-svg-iiq8JnPvHuSkkbcN #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-iiq8JnPvHuSkkbcN .messageText{fill:#333;stroke:none;}#mermaid-svg-iiq8JnPvHuSkkbcN .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-iiq8JnPvHuSkkbcN .labelText,#mermaid-svg-iiq8JnPvHuSkkbcN .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-iiq8JnPvHuSkkbcN .loopText,#mermaid-svg-iiq8JnPvHuSkkbcN .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-iiq8JnPvHuSkkbcN .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-iiq8JnPvHuSkkbcN .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-iiq8JnPvHuSkkbcN .noteText,#mermaid-svg-iiq8JnPvHuSkkbcN .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-iiq8JnPvHuSkkbcN .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-iiq8JnPvHuSkkbcN .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-iiq8JnPvHuSkkbcN .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-iiq8JnPvHuSkkbcN .actorPopupMenu{position:absolute;}#mermaid-svg-iiq8JnPvHuSkkbcN .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-iiq8JnPvHuSkkbcN .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-iiq8JnPvHuSkkbcN .actor-man circle,#mermaid-svg-iiq8JnPvHuSkkbcN line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-iiq8JnPvHuSkkbcN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} /ce-brainstormdocs/brainstorms/2026-06-22-user-auth-management-requirements.md/ce-plandocs/plans/2026-06-22-user-auth-management-plan.md修改 plan(Redis Session)更新 plan 中 KTD、.env、U1-U8/ce-work plan:...mystu/auth/, config/, controller/, static/, tests/scripts/sql/init.sql, AGENTS.md, pyproject.toml/ce-test-browser --port 5000uv run python main.pyagent-browser 打开 /login, /, /admin/users浏览器测试结果表(全部 Pass)/ce-commitgit commit 88288e9/ce-compounddocs/solutions/runtime-errors/redis-cluster-....mdCONCEPTS.mdAGENTS.md(discoverability 补充)
4. 生成文件的作用与生命周期
4.1 文档类(决策产物,长期保留)
| 路径 | 创建命令 | 作用 | 生命周期 |
|---|---|---|---|
docs/brainstorms/*.md |
/ce-brainstorm |
需求、决策、成功标准(R/S 编号) | 只读决策档案;plan 引用它,代码变更不反向修改 brainstorm |
docs/plans/*.md |
/ce-plan |
实施计划:U1-U8 单元、API、文件布局、测试场景 | 执行指南 ;/ce-work 读取但不修改 plan 正文 |
docs/solutions/**/*.md |
/ce-compound |
已解决问题的 playbook(YAML frontmatter 可检索) | 复利知识库 ;可被 /ce-compound-refresh 更新 |
CONCEPTS.md |
/ce-compound |
项目领域词汇(Session、角色、用户等) | 随 learning 累积;Agent orient 时参考 |
AGENTS.md |
人工 + CE 补充 | AI 协作规范、端口、目录、CE 命令索引 | Agent 的「项目 README」;随项目演进更新 |
重要原则: Plan 是决策 artifact,进度记在 Git commit 和任务追踪里,而不是在 plan 里打勾。
4.2 配置类(环境/偏好)
| 路径 | 是否提交 Git | 生命周期 |
|---|---|---|
.compound-engineering/config.local.example.yaml |
✅ 提交 | 模板;/ce-setup 可覆盖刷新 |
.compound-engineering/config.local.yaml |
❌ gitignore | 本机 CE 偏好;长期存活 |
.env |
❌ gitignore | MySQL/Redis/LLM 密钥;运行时读取 |
4.3 代码与测试类(可运行产物)
| 路径 | 创建阶段 | 作用 |
|---|---|---|
mystu/auth/, mystu/config/ |
/ce-work U1-U3 |
鉴权、Session、MySQL/Redis |
mystu/controller/auth_api.py 等 |
U4-U6 | HTTP API + 页面路由 |
mystu/static/login.html 等 |
U7 | 前端三页 |
scripts/sql/init.sql |
U1 | 数据库初始化 |
tests/ |
U8 | pytest(内存替身,不依赖真实 Redis/MySQL) |
生命周期: 随功能演进修改 → /ce-commit 入库 → CI/本地 pytest 持续验证。
5. /ce-test-browser --port 5000 详解
5.1 它测什么?依据哪个「测试用例文件」?
关键结论:CE 浏览器测试不依赖仓库里单独的 browser-tests.yaml 之类文件。
测试范围由 Skill 工作流 + Git 变更 + 项目约定 共同决定,数据来源如下:
#mermaid-svg-WdMf3x6yBRkTFAEB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WdMf3x6yBRkTFAEB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WdMf3x6yBRkTFAEB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WdMf3x6yBRkTFAEB .error-icon{fill:#552222;}#mermaid-svg-WdMf3x6yBRkTFAEB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WdMf3x6yBRkTFAEB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WdMf3x6yBRkTFAEB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WdMf3x6yBRkTFAEB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WdMf3x6yBRkTFAEB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WdMf3x6yBRkTFAEB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WdMf3x6yBRkTFAEB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WdMf3x6yBRkTFAEB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WdMf3x6yBRkTFAEB .marker.cross{stroke:#333333;}#mermaid-svg-WdMf3x6yBRkTFAEB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WdMf3x6yBRkTFAEB p{margin:0;}#mermaid-svg-WdMf3x6yBRkTFAEB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-WdMf3x6yBRkTFAEB .cluster-label text{fill:#333;}#mermaid-svg-WdMf3x6yBRkTFAEB .cluster-label span{color:#333;}#mermaid-svg-WdMf3x6yBRkTFAEB .cluster-label span p{background-color:transparent;}#mermaid-svg-WdMf3x6yBRkTFAEB .label text,#mermaid-svg-WdMf3x6yBRkTFAEB span{fill:#333;color:#333;}#mermaid-svg-WdMf3x6yBRkTFAEB .node rect,#mermaid-svg-WdMf3x6yBRkTFAEB .node circle,#mermaid-svg-WdMf3x6yBRkTFAEB .node ellipse,#mermaid-svg-WdMf3x6yBRkTFAEB .node polygon,#mermaid-svg-WdMf3x6yBRkTFAEB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WdMf3x6yBRkTFAEB .rough-node .label text,#mermaid-svg-WdMf3x6yBRkTFAEB .node .label text,#mermaid-svg-WdMf3x6yBRkTFAEB .image-shape .label,#mermaid-svg-WdMf3x6yBRkTFAEB .icon-shape .label{text-anchor:middle;}#mermaid-svg-WdMf3x6yBRkTFAEB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WdMf3x6yBRkTFAEB .rough-node .label,#mermaid-svg-WdMf3x6yBRkTFAEB .node .label,#mermaid-svg-WdMf3x6yBRkTFAEB .image-shape .label,#mermaid-svg-WdMf3x6yBRkTFAEB .icon-shape .label{text-align:center;}#mermaid-svg-WdMf3x6yBRkTFAEB .node.clickable{cursor:pointer;}#mermaid-svg-WdMf3x6yBRkTFAEB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-WdMf3x6yBRkTFAEB .arrowheadPath{fill:#333333;}#mermaid-svg-WdMf3x6yBRkTFAEB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WdMf3x6yBRkTFAEB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WdMf3x6yBRkTFAEB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WdMf3x6yBRkTFAEB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WdMf3x6yBRkTFAEB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WdMf3x6yBRkTFAEB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-WdMf3x6yBRkTFAEB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WdMf3x6yBRkTFAEB .cluster text{fill:#333;}#mermaid-svg-WdMf3x6yBRkTFAEB .cluster span{color:#333;}#mermaid-svg-WdMf3x6yBRkTFAEB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WdMf3x6yBRkTFAEB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WdMf3x6yBRkTFAEB rect.text{fill:none;stroke-width:0;}#mermaid-svg-WdMf3x6yBRkTFAEB .icon-shape,#mermaid-svg-WdMf3x6yBRkTFAEB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WdMf3x6yBRkTFAEB .icon-shape p,#mermaid-svg-WdMf3x6yBRkTFAEB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-WdMf3x6yBRkTFAEB .icon-shape .label rect,#mermaid-svg-WdMf3x6yBRkTFAEB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WdMf3x6yBRkTFAEB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WdMf3x6yBRkTFAEB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WdMf3x6yBRkTFAEB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 执行
推导
输入
/ce-test-browser --port 5000
git diff 变更文件列表
ce-test-browser SKILL.md文件→路由映射表
AGENTS.md端口 5000
docs/plans/*.md页面路由 + 成功标准 S1-S8
静态页与 controller 路由
待测 URL 列表
交互场景(登录/改密/登出/聊天)
agent-browser CLI
测试报告表格
5.2 执行原理(agent-browser)
CE 强制 使用 agent-browser CLI,不用 Playwright MCP 等替代方案。
FastAPI :5000 Headless/Headed Chrome agent-browser CE Agent FastAPI :5000 Headless/Headed Chrome agent-browser CE Agent #mermaid-svg-PDRhtA17ldQr8gQf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PDRhtA17ldQr8gQf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PDRhtA17ldQr8gQf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PDRhtA17ldQr8gQf .error-icon{fill:#552222;}#mermaid-svg-PDRhtA17ldQr8gQf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PDRhtA17ldQr8gQf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PDRhtA17ldQr8gQf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PDRhtA17ldQr8gQf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PDRhtA17ldQr8gQf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PDRhtA17ldQr8gQf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PDRhtA17ldQr8gQf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PDRhtA17ldQr8gQf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PDRhtA17ldQr8gQf .marker.cross{stroke:#333333;}#mermaid-svg-PDRhtA17ldQr8gQf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PDRhtA17ldQr8gQf p{margin:0;}#mermaid-svg-PDRhtA17ldQr8gQf .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PDRhtA17ldQr8gQf text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-PDRhtA17ldQr8gQf .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-PDRhtA17ldQr8gQf .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-PDRhtA17ldQr8gQf .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-PDRhtA17ldQr8gQf .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-PDRhtA17ldQr8gQf #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-PDRhtA17ldQr8gQf .sequenceNumber{fill:white;}#mermaid-svg-PDRhtA17ldQr8gQf #sequencenumber{fill:#333;}#mermaid-svg-PDRhtA17ldQr8gQf #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-PDRhtA17ldQr8gQf .messageText{fill:#333;stroke:none;}#mermaid-svg-PDRhtA17ldQr8gQf .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PDRhtA17ldQr8gQf .labelText,#mermaid-svg-PDRhtA17ldQr8gQf .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-PDRhtA17ldQr8gQf .loopText,#mermaid-svg-PDRhtA17ldQr8gQf .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-PDRhtA17ldQr8gQf .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-PDRhtA17ldQr8gQf .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-PDRhtA17ldQr8gQf .noteText,#mermaid-svg-PDRhtA17ldQr8gQf .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-PDRhtA17ldQr8gQf .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PDRhtA17ldQr8gQf .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PDRhtA17ldQr8gQf .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PDRhtA17ldQr8gQf .actorPopupMenu{position:absolute;}#mermaid-svg-PDRhtA17ldQr8gQf .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-PDRhtA17ldQr8gQf .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PDRhtA17ldQr8gQf .actor-man circle,#mermaid-svg-PDRhtA17ldQr8gQf line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-PDRhtA17ldQr8gQf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 检查 agent-browser 是否安装确认服务监听 5000(或提示启动)open http://localhost:5000/login导航HTTP GET /loginlogin.htmlsnapshot -i可交互元素 @e1 @e2 ...fill @e2 admin / fill @e3 密码click @e4 登录POST /auth/api/loginSet-Cookie + JSONsnapshot / screenshot汇总 Pass/Fail 表格
常用命令:
agent-browser open http://localhost:5000/login
agent-browser snapshot -i # 获取带 ref 的可点击元素
agent-browser fill '@e2' 'admin' # PowerShell 中 ref 需引号
agent-browser click '@e4'
agent-browser screenshot out.png
agent-browser --headed open ... # 可见浏览器窗口
5.3 本次 mystu 项目的测试结果(与截图一致)
| 路由 | 状态 | 说明 | 依据来源 |
|---|---|---|---|
/login |
✅ Pass | 登录表单正常 | login.html + S1 |
/(未登录) |
✅ Pass | 302 → /login |
controller/__init__.py + S1 |
/(登录后) |
✅ Pass | 聊天页、退出、管理入口 | index.html + 权限矩阵 |
| 强制改密 | ✅ Pass | 首次登录弹出改密面板 | plan S5 + auth_api |
/admin/users |
✅ Pass | 用户列表与 CRUD 按钮 | admin/users.html + S3 |
| 退出登录 | ✅ Pass | 退出后 / 回登录页 |
auth.js + Session 设计 |
| 聊天 + Agent API | ✅ Pass | 发「你好」流式回复正常 | api.py 鉴权 + SSE |
与 pytest 的分工:
| 层级 | 工具 | 覆盖 |
|---|---|---|
| API/逻辑 | pytest tests/ |
登录 API、权限 403、软删除等 11 项 |
| UI/E2E | /ce-test-browser |
页面渲染、Cookie、重定向、流式聊天 |
两者互补;浏览器测试不替代 tests/test_auth_api.py。
6. 其它 CE 命令的使用效果(本项目实例)
6.1 /ce-work --- 实施
- 输入:
plan:docs/plans/2026-06-22-user-auth-management-plan.md - 效果: 按 U1→U8 顺序生成约 30+ 文件,11 个 pytest 全绿
- 分支: 自动使用
feat/user-auth
6.2 /ce-commit --- 提交
- 效果: 中文 conventional commit,例如
feat(auth): 新增用户登录与 Session 鉴权体系 - 原则: 仅用户明确要求时提交;不主动 push
6.3 /ce-compound --- 知识沉淀
- 触发场景: Redis Cluster 启动
AttributeError: 'dict' object has no attribute 'host' - 产出:
docs/solutions/runtime-errors/redis-cluster-startup-nodes-clusternode-dict-host.mdCONCEPTS.md(Session、用户、角色等术语)AGENTS.md增加docs/solutions/检索提示
frontmatter 示例(便于 Agent 检索):
module: auth
problem_type: runtime_error
tags: [redis-cluster, clusternode, startup-nodes]
7. 文件生命周期总图
#mermaid-svg-XMsJsiG4zQ1if6cK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XMsJsiG4zQ1if6cK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XMsJsiG4zQ1if6cK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XMsJsiG4zQ1if6cK .error-icon{fill:#552222;}#mermaid-svg-XMsJsiG4zQ1if6cK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XMsJsiG4zQ1if6cK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XMsJsiG4zQ1if6cK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XMsJsiG4zQ1if6cK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XMsJsiG4zQ1if6cK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XMsJsiG4zQ1if6cK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XMsJsiG4zQ1if6cK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XMsJsiG4zQ1if6cK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XMsJsiG4zQ1if6cK .marker.cross{stroke:#333333;}#mermaid-svg-XMsJsiG4zQ1if6cK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XMsJsiG4zQ1if6cK p{margin:0;}#mermaid-svg-XMsJsiG4zQ1if6cK defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-XMsJsiG4zQ1if6cK g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-XMsJsiG4zQ1if6cK g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-XMsJsiG4zQ1if6cK g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-XMsJsiG4zQ1if6cK g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-XMsJsiG4zQ1if6cK g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-XMsJsiG4zQ1if6cK .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-XMsJsiG4zQ1if6cK .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-XMsJsiG4zQ1if6cK .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-XMsJsiG4zQ1if6cK .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-XMsJsiG4zQ1if6cK .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-XMsJsiG4zQ1if6cK .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-XMsJsiG4zQ1if6cK .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-XMsJsiG4zQ1if6cK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XMsJsiG4zQ1if6cK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XMsJsiG4zQ1if6cK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XMsJsiG4zQ1if6cK .edgeLabel .label text{fill:#333;}#mermaid-svg-XMsJsiG4zQ1if6cK .label div .edgeLabel{color:#333;}#mermaid-svg-XMsJsiG4zQ1if6cK .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-XMsJsiG4zQ1if6cK .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-XMsJsiG4zQ1if6cK .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-XMsJsiG4zQ1if6cK .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-XMsJsiG4zQ1if6cK .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-XMsJsiG4zQ1if6cK .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XMsJsiG4zQ1if6cK .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XMsJsiG4zQ1if6cK #statediagram-barbEnd{fill:#333333;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XMsJsiG4zQ1if6cK .cluster-label,#mermaid-svg-XMsJsiG4zQ1if6cK .nodeLabel{color:#131300;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-XMsJsiG4zQ1if6cK .note-edge{stroke-dasharray:5;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagram-note text{fill:black;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagram-note .nodeLabel{color:black;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagram .edgeLabel{color:red;}#mermaid-svg-XMsJsiG4zQ1if6cK #dependencyStart,#mermaid-svg-XMsJsiG4zQ1if6cK #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-XMsJsiG4zQ1if6cK .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XMsJsiG4zQ1if6cK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} /ce-commit
/ce-brainstorm
/ce-plan
/ce-work
/ce-test-browser
pytest tests/
/ce-compound
/ce-compound-refresh
更新过时文档
Brainstorm
docs/brainstorms/*.md
Plan
docs/plans/*.md
Code
mystu/**
tests/**
scripts/**
BrowserTest
Pytest
Commit
Solution
docs/solutions/**
CONCEPTS.md
Refresh
各阶段文件是否修改:
| 阶段 | 可修改 | 不应修改 |
|---|---|---|
/ce-work 执行中 |
源代码、tests、AGENTS.md | plan 正文(决策档案) |
/ce-compound |
新增 solutions、CONCEPTS | 已关闭的 brainstorm |
| 生产运行 | .env、本地 config |
config.local.example 以外的密钥进 Git |
8. 推荐使用方式(Checklist)
8.1 新功能(中大型)
/ce-setup--- 确认agent-browser等工具就绪/ce-brainstorm--- 产出需求文档/ce-plan--- 产出实施计划(含 U 单元、测试场景)/ce-work plan:docs/plans/xxx.md--- 编码pytest tests/ -q--- 自动化回归python main.py+/ce-test-browser --port 5000--- UI 验收/ce-code-review--- 大改动审查/ce-commit--- 提交- 遇坑并解决后
/ce-compound--- 沉淀
8.2 小修复
- 可直接
/ce-work或 Agent 改代码 - 仍建议
/ce-compound记录非显然的问题(如 Redis ClusterNode)
8.3 Agent 如何找到历史经验
在 AGENTS.md 中已补充:
docs/solutions/--- 按module、tags、problem_type检索CONCEPTS.md--- 理解 Session、用户等领域词
9. 常见问题
Q1:为什么没有 browser-tests.yml?
CE 的设计是 「变更驱动 + Skill 规程 + 计划成功标准」,而不是维护第二套静态 E2E 用例文件。好处是与 PR/分支 diff 自动对齐;代价是测试步骤由 Agent 现场执行,需本地服务与账号可用。
Q2:/ce-test-browser 和 pytest 哪个优先?
先 pytest(快、稳定、可 CI),再 browser(验 UI、Cookie、SSE、重定向)。
Q3:plan 里的 U 单元和 browser 测试的关系?
- U 单元 :
/ce-work的实施顺序与完成定义 - U 内 Test scenarios:指导 pytest 写哪些 case
- S1--S8 成功标准 :指导
/ce-test-browser验哪些用户路径
三者同源(plan),但执行载体不同。
Q4:Windows 上 bash 脚本失败怎么办?
/ce-setup 的 check-health 可能无法运行;可手动 npm install -g agent-browser,并参照 AGENTS.md 端口与命令。
10. 总结
Compound Engineering 把 AI 编程从「一次性对话」升级为可检索、可复用、可验收的工程流程:
| 层次 | 代表命令 | 复利价值 |
|---|---|---|
| 环境 | /ce-setup |
工具与配置一次就绪 |
| 决策 | brainstorm → plan | 需求与方案可追溯 |
| 交付 | /ce-work + pytest |
可运行代码 |
| 体验 | /ce-test-browser |
真实浏览器路径验证 |
| 记忆 | /ce-compound |
下次同类问题更快 |
/ce-test-browser --port 5000 的测试表并非来自单一配置文件,而是 Git 变更 + Skill 路由映射 + plan 成功标准 + 静态页/路由实现 共同推导;Agent 用 agent-browser 在真实浏览器里执行登录、改密、管理页、流式聊天等路径,最终给出你截图中的 Pass 表。