先来看一个漫画:

❌ 错误的 AI 编程:告诉 AI 要做什么,AI 写代码,发现完全没法用
✅ 正确的 AI 编程:与 AI 共同迭代 PRP,AI 连续编程数小时,一发入魂!
你可能从未接触过 AI 编程工具,也可能已经尝试过 AI 编程但是碰了一鼻子灰,不论哪种群体,本文足以扫清你对于 AI 编程的所有阻碍,让你足以驾驭最先进的 AI 工具,提升 3x 生产力!
- 具体实操步骤:全面拥抱业界最佳实践!
- AI 编程方法论:知其然,也知其所以然!
- 团队如何全面拥抱 AI:下个季度的KPI有了!
全文上万字,前百度工程师倾力打造,请提前准备好一杯咖啡!
AI 编程
正确的方法论
我理解在当今碎片化的时代大家都很急,笔者能不能不卖关子,直接告诉我正确的 AI 编程流程是什么
当然可以!下面是开发一个需求的 AI 编程业界最佳实践:
- 将自己的需求写出一份 initial.md
- 让 AI 根据 initial.md 生成一份 PRP
- AI 阅读规则文件、项目总览文件、项目代码、外部信息源,收集必要的信息,产出 PRP
- 开发者理解 PRP,与 AI 一同迭代 PRP,直至完美
- AI 花费大概一小时时间,自主编程和测试,完成 PRP
- 此时功能已经基本可用
- 开发者介入,与 AI 进行小修小补,跑完最后一公里
本文接下来将会一步一步的讲解上述的各个步骤的原理和实操过程,但是有一点我必须要强调:
AI 编程并不是一个开箱即用的美图秀秀一般的工具,它更像 Photoshop、VS Code 这种专业工具,需要一定的理论基础和学习,用起来才能得心应手
对于这种专业工具,虽然它不如学习一门语言一样困难,但是也不是一天学习或一次尝试就可以掌握的。我经常被问到"有没有什么方法可以快速让团队接入,让大家先用起来,提升生产力",答案显然是没有的。AI 编程是需要一定的基础知识和学习才能掌握的
所需要的基础知识
首先我们改个名字,不叫 AI 编程了,目前业界的主流叫法叫做 Vibe Coding (氛围编程).那么为了驾驭 Vibe Coding 工具,你需要了解 LLM 的基础知识和 PE 技巧,比如说如果下面的概念你都理解,那么你就拥有了足够的基础知识:
- 令牌(Token)
- 上下文窗口(Context Window)
- 系统和用户提示词(System / User Prompt)
- 少样本提示(Few-shot Prompting)
不了解也没关系,笔者写过另一篇文章,专门介绍 LLM 各种基础知识,请移步 -> juejin.cn/post/728860...
可以说 PE(Prompt Engineering) 是 Vibe Coding 的语言。字母都不认识的人,自然无法也写出文章
最好的工具
目前大概有两种类型的工具可供选择
| 类型 | 说明 | 优点 | 缺点 |
|---|---|---|---|
| 开源方案 | 使用开源的工具(如 Cline),配合自购LLM API (如 OpenRouter) | 功能迭代快,适合喜欢折腾的高级用户可以根据需求灵活切换模型 | 需要手动配置按 Token 计费价格较贵,长期使用个人几乎无法承担,需要有团队预算 |
| 商业化方案 | 自购或团队采购成熟的商业化方案,比如 Claude Code,Codex,Cursor | 功能成熟,使用体验极佳高频使用情况下,总成本十分便宜 | 无法切换模型,和某个供应商绑死 |
开源 Agent 的选择可以参考下面的流行度排行:

新手建议从 Cline 开始,学成之后开始长期使用时,切换成 Claude Code
扩展阅读:
Roo Code 是从 Cline 派生而来,而 Kilo Code 是 Roo Code 派生而来
这里本来想点名几个比较糟糕的工具,但是担心有点风险,因此还是仅仅简单写一写什么样的工具不适合用来做全自动的 Vibe Coding 吧:
- 有强硬的对话限制(最常见)。很多工具因为成本原因,自动对话有强硬的时间或Token限制,不论开发任务是否完成,超过一定时间会自动停止编程
- 是助手而不是全全能代理。很多工具仅仅能做基本的对话和代码修改建议,没办法从0开发一个大功能,这种工具实际上更像是一个编程助手,而不是可以全自动的代理
- 内置 Prompt 糟糕。如果你发现你模型用到很好的模型,比如说 Claude,但是 LLM 还是不听话,那么可能是工具本身的 Prompt 很糟糕。或者你发现每次压缩,上下文都会丢失的非常夸张,LLM 完全不记得之前要干嘛,那么也可能是 Prompt 太糟糕 |
部分工具可以选择模型,接下来是模型的推荐:
- Claude Sonnet 4.5 (需要科学)
- Grok Code Fast 1(需要科学)
- Qwen3 Coder (千问,国内模型)
下面是 Cline 官方指南推荐的模型:

(Model Selection Guide - Cline)
你也可以根据你使用的工具,选择最多人选择的模型。如下图是 Cline 的模型使用情况:

注意:
模型和工具的推荐有时效性,建议读者点击本文提供的原链接,自行查看当今什么最流行
具体怎么配置环境?
我们这次使用的环境如下,推荐读者安装配置相同的环境
| 类别 | 选择 | 安装 / 配置步骤 |
|---|---|---|
| 编程工具 | VS Coding | 下载安装 VS Code |
| Vibe Coding 工具 | Cline | 打开 VS Code 插件,搜索 cline 安装 |
| 模型 & 提供商 | 千问3 - 阿里云 | 打开下面的链接,生成一个 akhttps://bailian.console.aliyun.com/?tab=model#/api-key |
然后在 VS Code 界面打开 Cline 配置,配置如下:

(Cline 配置)
回到 Cline 主界面,测试一下,能收到来自 LLM 的回复即成功:

(Cline 回复你好)
至此,我们的 Vibe Coding 工具配置已经完成。接下来是本文的核心------如何正确的使用工具。如何使用工具非常重要,就像一台相机,有人可以拍出精妙绝伦的照片,而有的人却怎么拍都不好看
工具的选择只是第一步,重点在于如何使用工具
上下文管理
这里介绍一个非常重要的概念------上下文管理(Context Engineering)。良好上下文管理是 Vibe Coding 不可或缺的一部分
LLM 的行为和人类非常类似。现在我们情景模拟一下:
你刚刚加入公司,现在被要求修改一个线上项目的 Bug,你要怎么做?
首先,你确实对于这个项目本身一无所知,根本无法动手写代码对吧?你至少需要:
- 了解项目:你需要知道这个项目是干嘛的,用了什么技术栈,怎么跑起来,怎么测试等等。你需要确保你对于项目有个大致理解,不能说我们在修复一个数据库问题,你放着已有的 MySQL 不用,新拉起了一个 MangoDB
- 明确你的任务:你需要了解这个 Bug,比如说 Bug 的预期和现状是什么,修改后是不是会影响其他模块等等。确保你实际修改的时候是正确的
类似的,上下文管理主要解决两个问题:
- 低成本、快速地获取到 LLM 需要的上下文
- 确保 LLM 编程时,上下文窗口里面的内容尽可能都是 LLM 需要的
假设我们现在让 LLM 修复一个购物车功能相关的 Bug,下面有两个 LLM 和它们的上下文窗口:

很显然,第二个 LLM 的上下文窗口都是和购物车 Bug 无关的内容,那么在实际编辑代码时,第二个 LLM 更容易产出错误的代码、使用不存在的模块、重复造轮子
项目上下文
项目总览文档
一般来说,引入 Vibe Coding 的仓库需要维护一个项目总览文档,并提交进 git。该文件主要由 LLM 生成维护,也主要供 LLM 阅读,通常包含:项目的业务背景、技术栈、各个模块说明、环境说明,等等各种需要动手写代码前最好都了解的信息
这是目前业界的普遍的实践,有和没有项目总览文档的区别非常大!它可以有效的帮助 LLM 来
- 完成任务更加迅速,且节约 Token
- 可以更加有效的复用代码,而不是重新造轮子
- 该文件可以跨工具跨模型使用
推荐将项目总览文档放到 vibe/getting-started.md
扩展阅读:
部分工具也提供项目总览文件的自动维护,比如 Serena MCP。但是由于这个文件通常需要跨工具跨模型使用,因此单独建立维护是有必要的
链接文档和工具
大部分工具都支持 Rule 文件,该文件是一个每次任务开始前都会注入给 LLM 的文件

(Cline 中添加 Rule 文件)
我们需要在 Rule 文件里面明确要求 LLM:
- 任务开始时,读取项目总览文档
- 任务结束时,根据情况,更新项目总览文档
下面是一个简化版示例 Prompt。将它添加进 Rule 文件 里面,这样 LLM 就可以自动的读取和维护该文件了
Plain
## 项目总览文档
我们的项目总览文档在 `vibe/getting-started.md`
每当开始进行任何任务时,你永远会在任务开始前阅读这个文件。同时,每当任何任务结束前,你也需要检查这个文件,评估是否需要将本次改动更新进改文件
该 Prompt 仅仅为一个简化版示例,本文的末尾提供了一个完整模板,读者可以直接克隆,里面包含了完整的 Prompt 的文件目录
生成/更新项目总览文档
为了生成和更新项目总览文件,推荐定义一个任务说明,描述如何生成或更新该文件。当开发者发现该文件过时时,或者第一次生成项目总览文件,可以运行该任务,LLM将会自动的生成/更新项目总览文档。下面是一个任务描述的简化版示例 Prompt:
Plain
## 任务 - 生成/更新项目总览文档
接下来你需要生成/更新我们的项目总览文档 `vibe/getting-started.md`
- 如果改文档已经存在,你需要阅读它,然后更新,去重。确保其内容实时性和有效性
- 如果改文档不存在,你需要创建它
你需要阅读必要的代码和项目结构,尽可能多多的阅读,然后生成/更新我们的项目总览文档
任务上下文
在动手写代码之前,最好先充分调研整理出一份技术方案。同样的,当要求 LLM 完成一个任务时,要求 LLM 首先在宏观角度生成一份技术文档是目前业界最普遍的实践,效果也十分显著
目前操作 LLM 来完成编程任务大概有这几种流程:
| 类型 | 说明 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 直接对话执行 | 直接打字让 LLM 动手写代码 | 完成速度快 | 可能会因为缺乏项目理解,而一步错步步错;难以人工介入任务执行方案 | 简单任务 |
| 计划模式 | 几乎所有的工具都有一个 Plan 模式来首先根据任务来生成一份文档。比如 Roo Code 的 Architect 模式,Claude 的 Plan Mode | 开箱即用 | 极度依赖工具本身对于 Plan Mode 的支持度,性能不稳定(目前大部分的 Plan Mode 仅仅针对任务进行计划,不主动调研现有的项目结构,经常重复造轮子) | 建议用 PRP 代替 |
| PRP-based**(推荐)** | 将一个任务拆解为:撰写需求INITIAL.md -> 生成详细方案(PRP) -> 执行详细方案 | 调研彻底;任务完成度极高;自动记录 PRP | 需要手动介入两次,任务周期通常较长 | 功能开发 |
PRP (Product Requirements Prompts) 对于复杂任务的效果十分惊艳,远超目前所有工具的计划模式,且效果可以跨工具跨模型使用。并且由于 PRP 的效果实在是太好了,虽然现在工具并没有原生支持 PRP,但过不了多久工具一定陆续原生支持 PRP
下面是一个示例,分别是我的需求输入(INITIAL.md),以及 LLM 经过详细调研后生成的 PRP(PRPs/mcp-publishing-data-format-fix.md)
在 PRP 中你会发现 PRP 里面的方案非常详细!包含了实实在在的完整上下文以及完整方案:事情的前因后果、功能的现状和预期、需要注意的坑、多层次测试方案、关键代码示例、要改的文件和结构等等等
推荐读者大概一眼,不需要彻底读懂里面的信息,仅了解 INITIAL 和 PRP 大概长什么样,包含什么内容即可:
Markdown
## FEATURE:
现在 Schema MCP 发布的时候,写入的数据是 {"type":"schema","schema":{}}
Remote 写入的格式也不对,现在是 {"protocol":"sse","version":"1.0.0"}
预期是:
{"url": "http://std-mcp-boe.example.net/sse", "type": "sse"} 或者 {"url":
"http://std-mcp-boe.example.net/mcp-server-relay", "type": "streamable_http"}
Remote 就直接用第一个生产网地址,Schema 也是,只是它有自己的生成规则
## DOCUMENTATION:
使用
APP_ENV=boei18n.ip npm run db:query -- 'select * from aiai_mcp'
看线上的结构,也就是我们的目标
使用
APP_ENV=local npm run db:query -- 'select * from aiai_mcp'
看我们本地的数据
你可以在
- http://localhost:3000/mcps/1?tab=versions 点击发布,发布 Schema 来测试,然后查看db数据
- http://localhost:3000/mcps/3?tab=versions 点击发布,发布 Remote 来测试,然后查看db数据
## OTHER CONSIDERATIONS:
不需要考虑数据迁移问题
# MCP 发布数据格式修复
## 目标
修复 MCP 发布操作时写入 `aiai_mcp` 表的错误数据格式,使其符合生产环境要求并确保与 Agent 模块的正确集成。
## 为什么
- **生产环境兼容性**:本地 MCP 发布目前写入的格式不正确,与生产数据库结构不匹配
- **Agent 集成**:Agent 模块依赖 `aiai_mcp` 表具有正确格式以进行 MCP 选择和执行
- **远程服务连接**:错误的 URL 和类型格式会阻止在生产环境中正确连接 MCP 服务器
## 什么
修复 `src/server/actions/mcp-versions.ts` 中的 `syncRemoteMCPToAivoTable` 和 `syncSchemaMCPToAivoTable` 函数,使其在发布操作期间向 `aiai_mcp` 表写入正确的配置格式。
### 成功标准
- [ ] Remote MCP 发布写入:`{"url": "http://production-url", "type": "sse"}` 或 `{"url": "http://production-url", "type": "streamable_http"}`
- [ ] Schema MCP 发布写入与 Remote MCP 相同的格式:`{"url": "http://schema-url", "type": "sse"}`,而不是 `{"type":"schema","schema":{}}`
- [ ] 本地数据库查询显示与生产环境匹配的正确格式
- [ ] Remote 和 Schema MCP 的发布操作都能无错误工作
- [ ] 现有功能保持完整(无回归)
## 所需上下文
### 文档和参考
```yaml
# 必须阅读 - 包含在上下文窗口中
- file: src/server/actions/mcp-versions.ts
why: 包含需要修复的损坏同步函数(第 269-354 行)
critical: buildRemoteConfig 返回错误格式,syncSchemaMCPToAivoTable 使用错误的 legacyConfig
- file: src/server/models/aiai-mcp.ts
why: 目标表结构的数据库模型定义
critical: config 字段是 JSON 类型,comment 显示应包含 URL, protocol, endpoints 等
- file: src/lib/utils/mcp-schema-endpoints.ts
why: Schema MCP URL 生成工具,getSchemaEndpoints() 函数
critical: 返回 {url, connectivity, protocol} 格式,需要映射到 {url, type}
- file: src/types/mcp-enhanced.ts
why: 类型定义,包含 MCPConnectivity 枚举和 MCPRemoteEndpoint 接口
critical: protocol 字段类型是 'sse' | 'streamable_http'
- file: INITIAL.md
why: 包含确切的问题描述和预期格式示例
critical: 明确指出当前错误格式和目标格式
- url: https://modelcontextprotocol.io/specification/2025-06-18
why: 官方 MCP 规范文档
critical: 传输类型(SSE vs streamable_http)和配置标准
```
### 当前代码库结构(相关 MCP 文件)
```bash
src/server/
├── actions/
│ ├── mcp.ts # 旧版 MCP 操作
│ └── mcp-versions.ts # 包含损坏的同步函数(目标文件)
├── models/
│ ├── aiai-mcp.ts # 目标表模型
│ ├── mcp.ts # 新 MCP 模型
│ └── mcp-version-v2.ts # 包含配置数据的版本模型
├── db/
│ └── index.ts # 数据库初始化
└── lib/
├── utils/
│ └── mcp-schema-endpoints.ts # Schema URL 生成工具
└── types/
└── mcp-enhanced.ts # 类型定义
```
### 已知的代码库陷阱和库特性
```typescript
// 关键:生产数据库使用特定格式
// 目标格式(Remote 和 Schema 都相同):{"url": "https://example.com/sse", "type": "sse"}
// Schema MCP 只是 URL 生成规则不同,但格式结构完全一样
// 关键:当前错误的返回格式 - buildRemoteConfig 函数(第 313-319 行)
return {
url: primaryUrl,
protocol: config.protocol || 'sse', // ❌ 应该是 'type' 而不是 'protocol'
endpoints: config.endpoints, // ❌ 不应该包含在 aiai_mcp 表中
authentication: config.authentication, // ❌ 不应该包含在 aiai_mcp 表中
version: version.version, // ❌ 不应该包含在 aiai_mcp 表中
};
// 关键:当前错误的 Schema 配置(第 329-332 行)
const legacyConfig = {
type: 'schema', // ❌ 应该是传输类型,不是 MCP 类型
schema: version.config?.schemaConfig?.schema || {}, // ❌ 不应该包含 schema 内容
};
// 关键:版本配置结构是嵌套的
// Remote MCP 使用 version.config.remoteConfig.endpoints[]
// Schema MCP 使用 version.config.schemaConfig.schema
// 关键:URL 生成模式差异
// Remote MCP:从 remoteConfig.endpoints[] 中提取(优先 production 环境)
// Schema MCP:通过 getSchemaEndpoints(mcpId) 生成 Firefly 服务 URL
// 关键:协议类型映射
// MCPRemoteEndpoint.protocol: 'sse' | 'streamable_http'
// aiai_mcp.config.type: 'sse' | 'streamable_http' (相同值)
// 关键:MCPConnectivity 枚举值
// PRODUCTION = 'production' // 优先选择
// OFFICE = 'office' // 备选
// 关键:数据库事务处理
// publishMCPVersion 使用事务 - 同步函数必须参与
// 事务失败时必须正确回滚
// 关键:导入依赖
import { MCPConnectivity } from "@/types/mcp-enhanced";
import { getSchemaEndpoints } from "@/lib/utils/mcp-schema-endpoints";
```
## 实现蓝图
### 数据模型和结构
当前错误格式 vs 目标格式:
```typescript
// 当前错误 - Remote MCP 同步(buildRemoteConfig 返回)
{
"url": "https://example.com/sse",
"protocol": "sse", // ❌ 应该是 "type"
"endpoints": [...], // ❌ 不应该存在
"authentication": {...}, // ❌ 不应该存在
"version": "1.0.0" // ❌ 不应该存在
}
// 当前错误 - Schema MCP 同步(legacyConfig)
{
"type": "schema", // ❌ 应该是传输类型,不是 MCP 类型
"schema": {...} // ❌ 不应该存在
}
// 目标格式 - 两种类型都应该使用相同的格式
// Remote MCP 目标格式
{
"url": "https://aiai-boe.example.net/api/openapi/v1/mcp/mcp.builtin.pms/sse",
"type": "sse"
}
// Schema MCP 目标格式(格式相同,只是 URL 生成规则不同)
{
"url": "https://firefly-va.byteintl.net/api/firefly/mcp_server/dynamic/mcp.v2.builtin.pms/mcp/firefly.example.org",
"type": "streamable_http"
}
```
### 按顺序完成 PRP 的任务列表
```yaml
任务 1:验证生产数据库格式
查询生产数据库确认目标格式:
- 运行:APP_ENV=boei18n.ip npm run db:query -- 'select id, mcp_id, config from aiai_mcp limit 5'
- 分析:确认所有记录都使用 {url, type} 格式
- 记录:验证没有 protocol, endpoints, version, schema 等字段
任务 2:修复 buildRemoteConfig 函数
修改 src/server/actions/mcp-versions.ts(第 303-320 行):
- 查找:function buildRemoteConfig
- 修复:返回格式只包含 {url, type},移除其他字段
- 更新:从 config.remoteConfig.endpoints[] 正确提取 URL
- 映射:protocol 字段正确映射到 type 字段
任务 3:创建 buildSchemaConfig 函数
添加新函数到 src/server/actions/mcp-versions.ts:
- 位置:在 buildRemoteConfig 函数之后
- 功能:使用 getSchemaEndpoints(mcpId) 生成 Schema URL
- 格式:返回与 buildRemoteConfig 相同的 {url, type} 格式
- 导入:添加必要的 import 语句
任务 4:修复 syncSchemaMCPToAivoTable 函数
修改 src/server/actions/mcp-versions.ts(第 326-354 行):
- 替换:legacyConfig 改为调用 buildSchemaConfig(mcp, version)
- 移除:第 329-332 行的错误 legacyConfig 定义
- 保持:其他逻辑不变(更新插入逻辑等)
任务 5:添加错误处理
增强错误处理机制:
- Remote MCP:检查 endpoints 数组是否为空
- Schema MCP:检查 getSchemaEndpoints 返回值
- 事务:确保同步失败时正确回滚
任务 6:测试和验证
运行完整测试:
- 单元测试:验证新函数返回正确格式
- 集成测试:验证发布操作写入正确数据
- 回归测试:确保现有功能不受影响
```
### 每个任务的具体实现代码
```typescript
// 任务 2:修复 buildRemoteConfig 函数
function buildRemoteConfig(mcp: any, version: any): any {
const config = version.config as any;
// 从 remoteConfig.endpoints 中提取端点
const endpoints = config.remoteConfig?.endpoints || [];
if (endpoints.length === 0) {
throw new Error(`No endpoints found for Remote MCP: ${mcp.mcpId}`);
}
// 优先选择 production 环境的端点
const productionEndpoints = endpoints.filter(
(endpoint: any) => endpoint.connectivity === 'production'
);
// 使用第一个生产端点,如果没有则使用第一个可用端点
const primaryEndpoint = productionEndpoints[0] || endpoints[0];
if (!primaryEndpoint?.url) {
throw new Error(`No valid URL found for Remote MCP: ${mcp.mcpId}`);
}
// 映射协议类型:保持 sse 和 streamable_http,其他默认为 sse
const protocol = primaryEndpoint.protocol || 'sse';
const transportType = ['sse', 'streamable_http'].includes(protocol)
? protocol
: 'sse';
// 目标格式:只返回 {url, type}
return {
url: primaryEndpoint.url,
type: transportType
};
}
// 任务 3:创建 buildSchemaConfig 函数
function buildSchemaConfig(mcp: any, version: any): any {
// 使用现有的 getSchemaEndpoints 函数获取 URL
const schemaEndpoints = getSchemaEndpoints(mcp.mcpId);
if (schemaEndpoints.length === 0) {
throw new Error(`No schema endpoints found for Schema MCP: ${mcp.mcpId}`);
}
// 优先选择 production 环境端点
const productionEndpoint = schemaEndpoints.find(
endpoint => endpoint.connectivity === MCPConnectivity.PRODUCTION
) || schemaEndpoints[0];
if (!productionEndpoint?.url) {
throw new Error(`No valid schema URL found for Schema MCP: ${mcp.mcpId}`);
}
// 映射协议类型:Streamable HTTP -> streamable_http
const transportType = productionEndpoint.protocol === "Streamable HTTP"
? "streamable_http"
: "sse";
// 目标格式:与 Remote MCP 相同的 {url, type} 格式
return {
url: productionEndpoint.url,
type: transportType
};
}
// 任务 4:修复 syncSchemaMCPToAivoTable 函数
async function syncSchemaMCPToAivoTable(mcp: any, version: any): Promise<void> {
const { AivoMCP } = await initializeDatabase();
// 使用新的 buildSchemaConfig 函数,而不是错误的 legacyConfig
const config = buildSchemaConfig(mcp, version);
const syncData = {
mcp_id: mcp.mcpId,
name: mcp.name,
description: mcp.description || '',
config: config, // 现在使用正确的 {url, type} 格式
documentation: mcp.documentation || '',
updated_at: new Date(),
};
const existingRecord = await AivoMCP.findOne({
where: { mcp_id: mcp.mcpId }
});
if (existingRecord) {
await existingRecord.update(syncData);
console.log(`[syncSchemaMCPToAivoTable] Schema MCP 更新成功: ${mcp.mcpId}`);
} else {
await AivoMCP.create({ ...syncData, created_at: new Date() } as any);
console.log(`[syncSchemaMCPToAivoTable] Schema MCP 创建成功: ${mcp.mcpId}`);
}
}
// 必须添加的导入语句(文件顶部)
import { MCPConnectivity } from "@/types/mcp-enhanced";
import { getSchemaEndpoints } from "@/lib/utils/mcp-schema-endpoints";
```
### 集成点
```yaml
数据库:
- 表:aiai_mcp
- 列:config(JSON 类型)
- 约束:必须维护向后兼容性
发布:
- 触发器:publishMCPVersion 函数
- 事务:必须参与现有事务
- 回滚:如果同步失败,整个发布应该失败
测试:
- 命令:APP_ENV=local npm run db:query -- 'select * from aiai_mcp'
- 验证:比较本地 vs 生产格式
- 端点:测试 http://localhost:3000/mcps/1?tab=versions 和 /mcps/3?tab=versions
```
## 验证循环
### 第 1 级:语法和样式
```bash
# 首先运行这些 - 修复任何错误后再继续
npm run lint
npm run type-check
# 预期:无错误。如有错误,阅读错误并修复。
```
### 第 2 级:数据库查询验证
```bash
# 测试生产格式理解
APP_ENV=boei18n.ip npm run db:query -- 'select id, mcp_id, config from aiai_mcp limit 10'
# 测试本地发布 - 修复前
APP_ENV=local npm run db:query -- 'select id, mcp_id, config from aiai_mcp'
# 预期:显示错误格式(如果有记录)
# 清空测试数据
APP_ENV=local npm run db:query -- 'delete from aiai_mcp where mcp_id like "test.%"'
# 测试本地发布 - 修复后
# 1. 启动服务:PORT=3000 npm run dev
# 2. 发布 Remote MCP:访问 http://localhost:3000/mcps/3?tab=versions 点击发布
# 3. 发布 Schema MCP:访问 http://localhost:3000/mcps/1?tab=versions 点击发布
# 4. 查询验证:APP_ENV=local npm run db:query -- 'select id, mcp_id, config from aiai_mcp'
# 预期:显示正确的 {"url": "...", "type": "..."} 格式
```
### 第 3 级:集成测试
```typescript
// 创建测试文件:tests/integration/mcp-publishing-fix.test.ts
import { publishMCPVersion } from '@/server/actions/mcp-versions';
import { initializeDatabase } from '@/server/db';
import assert from 'assert';
async function testMCPPublishingFormat() {
console.log('🚀 开始 MCP 发布格式测试...\n');
const { AivoMCP } = await initializeDatabase();
// 测试 Remote MCP 发布格式
console.log('📝 测试 Remote MCP 发布格式...');
// 这里需要创建测试 Remote MCP 和版本
// 发布后查询数据库验证格式
const remoteMCPRecord = await AivoMCP.findOne({
where: { mcp_id: 'test.remote.mcp' }
});
if (remoteMCPRecord) {
const config = remoteMCPRecord.config as any;
assert(config.url, 'Remote MCP config 应该有 url 字段');
assert(config.type, 'Remote MCP config 应该有 type 字段');
assert(['sse', 'streamable_http'].includes(config.type), 'type 应该是 sse 或 streamable_http');
assert(!config.protocol, 'config 不应该包含 protocol 字段');
assert(!config.endpoints, 'config 不应该包含 endpoints 字段');
assert(!config.version, 'config 不应该包含 version 字段');
console.log('✅ Remote MCP 格式正确');
}
// 测试 Schema MCP 发布格式
console.log('📝 测试 Schema MCP 发布格式...');
const schemaMCPRecord = await AivoMCP.findOne({
where: { mcp_id: 'test.schema.mcp' }
});
if (schemaMCPRecord) {
const config = schemaMCPRecord.config as any;
assert(config.url, 'Schema MCP config 应该有 url 字段');
assert(config.type, 'Schema MCP config 应该有 type 字段');
assert(['sse', 'streamable_http'].includes(config.type), 'type 应该是 sse 或 streamable_http');
assert(!config.schema, 'config 不应该包含 schema 字段');
assert(config.type !== 'schema', 'type 不应该是 schema');
console.log('✅ Schema MCP 格式正确');
}
console.log('🎉 所有测试通过!');
}
// 运行测试
testMCPPublishingFormat().catch(console.error);
```
```bash
# 运行集成测试
npx tsx tests/integration/mcp-publishing-fix.test.ts
# 预期:所有断言通过,无错误输出
```
### 第 4 级:端到端验证
```bash
# 启动服务
PORT=3000 npm run dev
# 手动测试流程:
# 1. 访问 http://localhost:3000/mcps/3?tab=versions
# 2. 点击"发布"按钮
# 3. 验证无错误,发布成功
# 4. 访问 http://localhost:3000/mcps/1?tab=versions
# 5. 点击"发布"按钮
# 6. 验证无错误,发布成功
# 验证数据库格式
APP_ENV=local npm run db:query -- 'select id, mcp_id, config from aiai_mcp order by updated_at desc limit 5'
# 预期输出示例:
# | id | mcp_id | config |
# | 1 | mcp.builtin.pms | {"url":"https://aiai-boe.example.net/...","type":"sse"} |
# | 2 | mcp.v2.builtin.pms | {"url":"https://firefly-va.example.net/...","type":"streamable_http"} |
```
## 最终验证清单
- [ ] 语法检查通过:`npm run lint`
- [ ] 类型检查通过:`npm run type-check`
- [ ] 生产格式研究完成:确认目标 {url, type} 结构
- [ ] buildRemoteConfig 函数修复:只返回 {url, type}
- [ ] buildSchemaConfig 函数创建:使用 getSchemaEndpoints
- [ ] syncSchemaMCPToAivoTable 函数修复:使用新的配置函数
- [ ] 导入语句添加:MCPConnectivity 和 getSchemaEndpoints
- [ ] 错误处理添加:空数组和无效 URL 检查
- [ ] 集成测试通过:配置格式验证正确
- [ ] 手动测试通过:Remote 和 Schema MCP 发布成功
- [ ] 数据库验证通过:本地与生产格式匹配
- [ ] 现有功能无回归:其他 MCP 功能正常工作
- [ ] 事务处理正确:失败时能正确回滚
---
## 要避免的反模式
- ❌ 不要保留 protocol, endpoints, version 等字段在 aiai_mcp.config 中
- ❌ 不要使用 {"type": "schema"} 格式 - 这是 MCP 类型,不是传输类型
- ❌ 不要硬编码 URL - 必须从配置或 getSchemaEndpoints 中提取
- ❌ 不要破坏事务处理 - 确保同步函数正确参与事务
- ❌ 不要忽略错误情况 - 必须处理空数组、无效 URL 等边界情况
- ❌ 不要忘记添加导入语句 - MCPConnectivity 和 getSchemaEndpoints 是必需的
- ❌ 不要更改无关代码 - 只修改明确损坏的同步函数
## 信心等级:9/10
此 PRP 提供了全面的上下文,包括:
- 具体的代码位置和行号
- 真实的错误代码示例和正确的目标代码
- 完整的导入依赖和类型定义
- 可执行的验证命令和测试用例
- 详细的错误处理策略
- 明确的实现步骤和代码示例
唯一的不确定性是特定环境下的 URL 生成,但已通过现有的 `getSchemaEndpoints` 函数解决。
PRP 生成、PRP 执行通常有专门的任务说明(一个大 Prompt)来做,目前有很多开源方案,比如:
由于现在工具不原生支持 PRP,开发需要阅读挑选,将自己喜欢的生成 PRP 和 执行 PRP 的文件拷贝到自己的 Repo 之中,当需要执行或生成 PRP 时,将该任务说明发给 LLM 即可,类似于:
Plain
请根据 @generate-prp.md 的说明,为我的需求 @INITIAL.md 生成 PRP
本文末尾给出了完整的 Vibe Coding 模板,包含 PRP 相关的支持。读者可以开箱即用
PRP 生成后执行前,这里是绝佳的人工介入纠错时机。一定要彻底审查 PRP,纠正所有错误,保证 PRP 质量非常高。审查 PRP 可以最大程度避免:AI 花了几个小时,烧了上百刀,结果跑出来的完全没法用
第三方上下文
有时候 LLM 需要一些第三方的信息来辅助判断,比如查询 next.js 文档,理解公司某个私有基建。目前有如下方案
| 类型 | 说明 | 适用场景 |
|---|---|---|
| 本地管理 | 建立 ./vibe/external/ 文件夹,将需要的文档保存其中,并在 Rule 文件中说明 |
所有场景 |
| 文档 MCP | 一些 MCP Server 提供文档查询功能,比如 context 7 和 deepwiki | 通用第三方文档 |
推荐都用,需要哪个用哪个。常见的文档 MCP 如下:
同时也将你常用的文档,比如你项目中使用框架的文档,下载下来放到 ./vibe/external/ 之中,然后可以在 Rule 中明确要求 LLM 遇到框架相关的问题,首先差异文档。类似于:
Markdown
## 项目框架
本项目使用 Next.js 框架,如果你遇到任何的 Next.js 问题,你应该先去 vibe/external/nextjs 中查找对应的问题,然后再制定解决方案
反馈回路
当我们写代码的时候,通常会在功能完成后进行简单的自测,通常来说会发现很多问题,所以需要再迭代几轮,最终交付。同样的,LLM 编程也很难做到第一次就成功,我们需要保证 LLM 有丰富的自测手段,可以"看到"自己写代码的问题
而想办法能让 LLM 有办法自测,看到问题,自行迭代,这个方向的建设,我称为"反馈回路"
良好的反馈回路可以避免"Vibe Coding 十分钟,Debug 两小时",避免 Vibe Coding 实际上是把编码工作换成了 Code Review 和手动测试,确保交付产物可以在无人为干预情况下直接达到可用状态
接下来本文将列举几个常见的可以建设反馈回路的方向,读者可以根据自己使用的技术栈来思考------有哪些反馈回路可以建设,让 LLM 可以"看得见"
运行日志
不论你是什么方向的开发,通常会有一个地方可以拿到程序的运行日志,如:
- 前端开发:编译日志
- 后端开发:服务器进程 stdio
- 安卓开发:adb 日志
这些日志在开发过程中非常有用,需要让 LLM 也看得到。那么,我们其实可以修改启动命令,使用 tee 命令将所有的日志不仅仅在控制台输出,也收集到一个文件之中:
Bash
# 假设我们原本是使用 ./start-dev.bash 来启动开发环境
# 增加 tee 管道来收集开发环境的日志
./start-dev.bash 2>&1 | tee -a log/dev-console.log
然后在基础 rule 文件之中增加相关的描述:
Markdown
## 开发环境日志
- 开发服务器的控制台输出在 @/log/dev-console.log 里面
- @/log/dev-console.log 文件可能比较大,必要时使用 tail -100 命令来读取输出(不要使用 tail -f)
- 如果你需要观察一次的session的输出,记得首先使用 echo '' > @/log/dev-console.log 首先清空日志文件,然后再 cat
这样,LLM 就会在遇到问题时有能力自行捞日志定位修复问题了
静态分析
很多代码中的问题可以通过静态分析来自动找到问题。最常见的无疑是拥有静态类型系统的语言,比如C++、Java、TypeScript。如果你使用的编程语言支持类型系统,那么你可以在 Rule 文件中要求 LLM:
- 定义和使用严格的类型
- 在任务完成前运行类型检查,确保没有类型错误
除了类型检查,你可以手动的去构建一些静态分析。比如要求 LLM 编写一个脚本,解析所有代码文件的
AST(Abstract Syntax Tree,抽象语法树),找到可以静态扫描的点,将运行时错误改造成编译错误。比如说:
- 在多语言支持中,解析 key,然后检查是否在多语言翻译中包含这个 key、
- 让代码符合项目某些约定俗成,比如不要使用某个第三方包的某个函数,使用项目自己包装过的函数。很多 lint 工具都支持开箱即用的配置,无需自己写脚本分析
这些都是可以通过静态分析 AST 来完成,中心思想也很简单:尽可能将运行时错误转变为编译时错误
关于静态分析这个其实是一个大话题,常见的就只有本文提到的类型检查、i18n键、lint。这里就不进一步展开了
集成 / E2E 测试
在软件开发中,通常来说,自动化测试的建设不是一个必选项,项目可以通过 RD 自测、QA 测试来保证质量。但是在 Vibe Coding 场景下,建设自动化测试成了一个必选项。LLM 需要在每个功能开发时,充分考虑到如何测试,将测试方案作为技术方案的一部分来进行
E2E 测试最好,集成测试也可以,但是单元测试不行(容易先射箭后画靶)
你可以根据自己项目的自动化测试方案,在 Rule 文件中要求 LLM 每次都要为功能编写测试,验证测试通过后才能交付,各个 PRP 的模板大多也会包含渐进式测试的方案部分
如果仅仅想关注一部分的代码,在 Vibe Coding 场景下,Review 测试的方案和代码比 Review 功能实现更有价值
团队接入
文件目录
统一组内 Vibe Coding 工具和 Rule 文件就像统一组内代码编辑器一样困难,因此不建议在项目内提交任何 Vibe Coding 工具相关的文件,以防冲突。但是项目将 Vibe Coding 的模板和通用文件提交到仓库并维护,以下是建议的目录结构:
Markdown
vibe/ # 存放所有 vibe coding 相关文件
├── externals/ # 第三方文档
├── prompts # 沉淀常用的任务说明
│ ├── update-getting-started.md # 更新/生成项目总览文档
│ ├── execute-prp.md # 执行 PRP
│ └── generate-prp.md # 生成 PRP
├── PRPs # 存放 PRP 模板和所有历史 PRP
│ └── template.md
├── getting-started.md # 项目总览文档
├── initial.example.md # 生成 PRP 的需求文档的模板
├── mcp.example.json # MCP 服务器配置模板
├── rules.example.md # 工具基础 Rule 规则模板
└── READMD.md # 目录说明
我在 Github 上创建了一个中文版模板,读者可以直接下载,然后将 vibe/ 目录拷贝到自己的项目之中即可~
Vibe Coding 流程
当开始一个任务时,如果是一个非常小的修改,可以直接通过对话的方式来进行沟通,而如果是一个稍大的改动,或者完整功能开发,则需要
- 撰写需求说明 Initial.md
- 使用
./vibe/prompts/generate-prp.md来生成 PRP - 理解 PRP,必要时迭代
- 使用
./vibe/prompts/execute-prp.md来执行 PRP
而关于如何接受代码,我观察到其实有很多人喜欢手动的 Review 然后接受每个 LLM 提交的改动,这个其实是一个非常糟糕的 Vibe Coding 方式,很容易导致"要不是用 Vibe Coding,我功能早开发完了"
正确的打开方式是:让 LLM 自动、无监督、纯脱手的方式完成大部分工作,然后再切换到手动模式,与 LLM 完成"最后一公里"
个人,团队和协作
使用 Vibe Coding 后,由于"写代码"资源不再稀缺,且可以并行开发,传统的"先迭代出完美的产品和技术方案,后续不再修改"的方式已经不再适用。LLM 可以快速的理解需求,根据项目现状生成完整的 PRP 包含PRD、TRD、测试方案。因此,针对需求开发流程我觉得可以有如下修改:
- 使用 PRP 代替技术方案的流程
- 在 PRP 完成后,直接拉业务方进行一轮原型展示,对齐基本功能和交互
- 大大简化 Code Review,仅 Review 跨模块的修改,模块内部的修改和新增不再 Code Review
而各个开发团队也可以着重建设下面几方面来增强 Vibe Coding 体验:
- 全栈项目 / Monorepo:LLM 对于全栈修改的能力极强,使用 Monorepo 可大大减少上下文沟通
- PE 能力培训:Vibe Coding 水平与 PE 水平强相关,建议团队对于开发者进行 PE 培训。可以参考 juejin.cn/post/728860...
中文模板
笔者为大家创建了一个中文版 Vibe Coding 模板,可以开箱即用。包含了基本 Prompt 和文件目录,后续也会将笔者的各种方法论继续沉淀于此,欢迎 Star:
其中关于 PRP 的生成和执行也有很多第三方的选择,读者可以自行阅读:
后记
这里是【花生派】,可以在这里找到我:
- 个人资料网站:
pea3nut.info - 个人博客:
pea3nut.blog - 掘金:花生Peadar
转载请随意,但需保留此小节,感谢理解~