# 如何写高质量开发Prompt

同一个需求,交给 AI 编程助手,出来的代码质量可能天差地别。一个人拿到的是可以直接合入主干的整洁实现,另一个人拿到的却是需要推倒重写的「能跑就行」。差距不在模型能力,而在 Prompt 的质量。

开发场景下的 Prompt 编写,本质上是一种「面向机器的需求沟通」。它不是自然语言聊天,也不是写注释。它的核心目标是:让 AI 在最少追问的情况下,产出符合你预期的代码。

本文系统梳理开发 Prompt 的编写原则、结构化方法、常见场景模板、典型错误和优化策略,附带可直接复用的模板和对比案例。

---

## 开发 Prompt 的四个核心原则

### 原则一:明确(Clarity)

「明确」意味着 AI 不需要猜你的意图。每一个动词都应该指向具体的动作,每一个名词都应该有可识别的对象。

差的写法:

```
帮我优化这段代码
```

好的写法:

```
将下面这段 TypeScript 函数从嵌套回调改为 async/await 风格,
保留原有的错误处理逻辑,不改变函数签名。
```

差在哪里?「优化」是一个没有边界的词。是性能优化?可读性优化?类型安全优化?好的写法把「优化」拆解为具体的动作:改风格、保留逻辑、不变签名。

### 原则二:具体(Specificity)

具体性体现在三个维度:技术栈具体、行为具体、边界具体。

```
用 React 18 + TypeScript 写一个用户搜索组件
```

这比「写个搜索组件」好得多,但仍然不够具体。更具体的写法:

```
用 React 18 + TypeScript 写一个用户搜索组件:
- 使用 <input> 实时搜索,输入防抖 300ms
- 调用 GET /api/users?q=keyword 接口
- 下拉展示最多 10 条结果,显示用户名和邮箱
- 选中后触发 onSelect 回调,参数为用户对象
- 无结果时显示「未找到匹配用户」
- 不使用任何第三方 UI 库
```

### 原则三:有上下文(Context)

AI 不了解你的项目。你需要主动提供它需要的上下文:技术栈版本、现有代码结构、项目约定、相关文件的关键信息。

```
我们的项目使用 Next.js 15 App Router,数据库用 PostgreSQL,
ORM 是 Prisma。现有的用户模型定义在 prisma/schema.prisma 中,
包含 id、email、name、createdAt 四个字段。
现在需要在用户模型上新增一个 phone 字段(可选),
并实现一个 API route 来更新用户的手机号。
```

没有上下文的 Prompt,AI 只能凭训练数据中的「最大概率」来猜测。它可能给你写一个 Class Component,可能用 REST 而你的项目是 GraphQL,可能引入你项目里不存在的依赖。

### 原则四:有约束(Constraints)

约束是 Prompt 中被忽略最多的部分,也是产出质量差异最大的来源。约束包括:

- **风格约束**:「遵循项目现有的 Biome 配置,使用单引号、无尾分号」
- **依赖约束**:「只使用已有的依赖,不引入新包」
- **架构约束**:「业务逻辑放 service 层,不要在 route handler 里直接写」
- **兼容性约束**:「需要兼容 Node.js 20+」
- **安全约束**:「SQL 查询必须使用参数化查询,禁止字符串拼接」

没有约束的代码生成,就像让一个不知道公司规范的新人写代码——能用,但不一定能融入。

---

## 结构化 Prompt 的写法

一个完整的开发 Prompt 可以拆解为五个模块。不是每次都需要全部模块,但模块越完整,输出质量越稳定。

### RTCO 结构

| 模块 | 说明 | 示例 |
|------|------|------|
| **Role(角色)** | 让 AI 扮演特定身份 | 「你是一个有 10 年经验的 Go 后端工程师」 |
| **Task(任务)** | 要做什么,用动词开头 | 「实现一个带重试逻辑的 HTTP 客户端」 |
| **Context(上下文)** | 技术栈、现有代码、项目约定 | 「项目使用 Go 1.22,标准库 net/http,错误处理用 sentinel error」 |
| **Output(输出)** | 期望的输出格式和内容 | 「只输出代码,不需要解释;每个函数加 GoDoc 注释」 |
| **Constraints(约束)** | 风格、依赖、安全、性能等限制 | 「不使用第三方库;超时 30s;最多重试 3 次,指数退避」 |

### Prompt 结构模板

```markdown
## 角色
你是一个资深的 [技术栈] 工程师,熟悉 [相关技术]。

## 任务
[用一句话描述要做什么]

## 上下文
- 技术栈:[语言、框架、版本]
- 现有代码:[贴出关键代码或描述结构]
- 项目约定:[命名规范、目录结构、编码风格]

## 约束
- [约束 1]
- [约束 2]
- [约束 3]

## 输出格式
- [期望输出什么,不要什么]
```

---

## 常见场景的 Prompt 模板

### 场景一:写新代码

```markdown
## 任务
用 TypeScript 实现一个函数 parseDuration,将人类可读的时间字符串
(如 "1h30m"、"2d"、"500ms")转换为毫秒数。

## 约束
- 支持的单位:ms、s、m、h、d
- 不支持的输入抛出 TypeError,message 为 "Invalid duration format: {input}"
- 输入为空字符串返回 0
- 使用正则匹配,不使用任何第三方库
- 需要完整的 JSDoc 注释
- 同时提供对应的单元测试(vitest)

## 输出
只输出代码和测试代码,不需要解释。
```

### 场景二:改代码 / 重构

```markdown
## 任务
将下面这段 Python 代码从同步风格改为异步风格。

## 当前代码
```python
def fetch_users():
    response = requests.get("https://api.example.com/users")
    data = response.json()
    return [User(**item) for item in data]
```

## 约束
- 使用 httpx.AsyncClient 替代 requests
- 保留 User 数据模型不变
- 添加异常处理:网络超时返回空列表,HTTP 错误抛出
- 函数签名改为 `async def fetch_users() -> list[User]`

## 输出
输出修改后的完整代码,用注释标注改动点。
```

### 场景三:解释代码

```markdown
## 任务
解释下面这段 Rust 代码的工作原理。

## 代码
```rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}
```

## 输出要求
- 分三部分说明:函数签名中的生命周期标注、函数体逻辑、调用时的约束
- 面向了解 Rust 基础语法但不熟悉生命周期的开发者
- 给出一个编译通过和一个编译失败的调用示例
```

### 场景四:调试代码

```markdown
## 任务
排查一个 React useEffect 导致的无限渲染问题。

## 技术栈
React 18 + TypeScript + Zustand

## 现象
组件挂载后 console.log 持续输出,页面卡死。

## 相关代码
```tsx
const [filters, setFilters] = useState<FilterState>(initialFilters)

useEffect(() => {
  const result = applyFilters(filters)
  setFilters(result) // 更新过滤结果
}, [filters])
```

## 我的判断
我怀疑是 setFilters 每次产生新引用导致 useEffect 重新触发,
但不确定 applyFilters 是否返回了新对象。

## 期望
1. 确认问题原因
2. 给出修复方案(至少两种)
3. 说明各自的 trade-off
```

### 场景五:Code Review

```markdown
## 任务
审查以下代码,关注安全、性能和可维护性。

## 代码
```typescript
app.post("/api/upload", async (req, res) => {
  const file = req.files.file
  const path = `./uploads/${file.name}`
  file.mv(path)
  await db.files.create({ name: file.name, path, userId: req.user.id })
  res.json({ ok: true })
})
```

## 上下文
- Express + express-file-upload
- 部署在 Linux 服务器,uploads 目录有执行权限

## 审查维度
1. 安全性(路径遍历、文件类型校验、权限)
2. 错误处理(文件操作失败、数据库失败)
3. 并发问题(同名文件)

## 输出格式
按严重程度排列:Critical > Warning > Suggestion,
每条给出问题描述、风险说明、修复建议。
```

---

## 好 Prompt vs 差 Prompt 对比

| 维度 | 差 Prompt | 好 Prompt | 差距在哪里 |
|------|----------|----------|-----------|
| 写新功能 | 「帮我写一个登录页面」 | 「用 Next.js 15 App Router 写登录页:email + password 表单,调用 POST /api/auth/login,成功后跳转 /dashboard,失败显示错误 Toast,表单用 react-hook-form + zod 校验」 | 技术栈、交互、错误处理、依赖 |
| 改 Bug | 「这段代码有 bug,帮我修」 | 「下面这段 Go 代码在并发调用时出现 data race,go test -race 报错在第 42 行。代码如下:[贴代码]。我怀疑是 map 的并发读写,请确认并给出修复方案」 | 复现步骤、错误信息、自己的判断 |
| 重构 | 「让这段代码更干净」 | 「将下面的 200 行函数拆分为多个子函数,每个子函数不超过 30 行。提取公共逻辑到工具函数。保持对外接口不变,所有现有测试通过」 | 可量化的目标、约束、验证标准 |
| 解释代码 | 「解释这段代码」 | 「解释下面这段 TypeScript 类型体操代码。面向中级开发者,分步说明每个类型的作用,给出每个中间类型的计算结果示例」 | 目标受众、输出结构、具体程度 |

---

## 不同工具的 Prompt 差异

| 工具 | Prompt 入口 | 上下文获取 | 适合的 Prompt 风格 | 注意事项 |
|------|------------|-----------|-------------------|---------|
| **Cursor(Composer)** | Chat / Cmd+K / Composer | 自动读取当前文件和 @ 引用的文件 | 在 Prompt 中引用 `@file` 提供上下文,任务描述要具体 | Composer 支持多文件编辑,适合跨文件重构任务 |
| **Claude Code** | 终端 CLI 对话 | 自动搜索和读取项目文件 | 可以用自然语言描述需求,Claude 会主动搜索相关代码 | 配合 CLAUDE.md 写项目级约定,减少重复上下文 |
| **GitHub Copilot** | 行内补全 / Chat | 基于当前文件和打开的 tab | 用注释描述意图,让 Copilot 根据注释补全代码 | 行内补全适合短函数和单文件操作,复杂任务用 Chat |
| **ChatGPT / Claude 对话** | Web / API 对话 | 需要手动贴代码 | 必须显式提供所有上下文:技术栈、代码、约束 | 没有项目感知能力,上下文越全越好 |

---

## 常见错误和优化方法

### 错误清单

| 编号 | 常见错误 | 问题表现 | 优化方法 |
|------|---------|---------|---------|
| 1 | 需求模糊 | AI 反复追问或给出无关代码 | 用「动词 + 具体对象 + 期望结果」描述任务 |
| 2 | 缺少技术栈 | 生成的代码用了你没安装的库 | 明确声明语言版本、框架、已有依赖 |
| 3 | 一次给太大任务 | 代码不完整、逻辑断裂 | 拆解为子任务,逐步生成 |
| 4 | 没有贴错误信息 | AI 瞎猜问题原因 | 直接贴完整的错误堆栈和复现步骤 |
| 5 | 不给现有代码 | AI 重写而非修改 | 贴出相关代码,说明哪些可以改、哪些不能动 |
| 6 | 没有输出约束 | 输出一大堆解释,代码反而难找 | 明确「只输出代码」「用 Markdown 代码块」 |
| 7 | 重复提交相同 Prompt | 得到不同的随机结果 | 换一种描述方式,增加约束条件 |
| 8 | 忽略项目约定 | 代码能跑但不符合项目风格 | 在 Prompt 中引用项目的规范文件或 CLAUDE.md |

### 错误示例拆解

**示例 A:模糊的调试请求**

```
我的 API 返回 500 了,帮我修一下
```

AI 不知道你的 API 是什么、用的什么框架、请求参数是什么。它只能给出通用的排查建议。

**优化后:**

```
Express + TypeScript 项目,POST /api/orders 接口返回 500。
请求体:{ "productId": "abc", "quantity": 2 }
错误日志:TypeError: Cannot read properties of undefined (reading 'price')
  at calculateTotal (src/services/order.ts:42:18)
相关代码:[贴出 order.ts 的 calculateTotal 函数]
数据库查询返回的 Product 对象结构:{ id, name, price, stock }
```

**示例 B:过于庞大的重构任务**

```
把这个 2000 行的文件重构一下
```

AI 面对 2000 行代码,要么截断输出,要么产出一个面目全非的重构。

**优化后:**

```
我需要重构 src/services/payment.ts 中的 processPayment 函数(约 300 行)。
第一步:分析这个函数做了哪些事情,列出职责清单。
不要改代码,只输出分析结果。

等我确认分析后,再进行下一步拆分。
```

分步执行,每一步验证,远比一次性扔过去效果好。

---

## 案例:同一需求的不同 Prompt 效果对比

### 案例一:实现数据缓存

**需求:给 API 接口加缓存**

Prompt A:

```
帮我给 API 加个缓存
```

AI 可能的输出:一段 Redis 缓存代码,或者内存缓存,或者 CDN 缓存——完全看运气。代码可能是 Python 的,而你的项目是 Node.js。

Prompt B:

```
## 任务
为 Express API 的 GET /api/products 接口添加内存缓存。

## 上下文
- Node.js 20 + Express 4 + TypeScript
- 接口当前直接查 PostgreSQL,响应时间约 200ms
- 产品数据变更频率低,每天最多更新 10 次
- 项目已有 node-cache 依赖

## 约束
- 缓存 TTL 5 分钟
- 缓存 key 按请求参数区分
- 产品更新时清除相关缓存(更新接口在 src/routes/products.ts 的 PUT /:id)
- 不引入新的依赖

## 输出
1. 缓存中间件代码
2. 在更新接口中清除缓存的代码
3. 不需要解释,只输出代码
```

Prompt A 的输出大概率不可用。Prompt B 的输出可以直接集成到项目中,只需要微调导入路径。

### 案例二:排查内存泄漏

**需求:Node.js 服务内存持续增长**

Prompt A:

```
我的 Node.js 服务内存泄漏了,怎么排查
```

AI 会给你一篇通用排查指南:用 heapdump、看事件监听器、检查全局变量……正确但无用,因为你需要的针对你代码的诊断。

Prompt B:

```
## 任务
诊断一个 Node.js 服务的内存泄漏问题。

## 上下文
- Node.js 20 + Fastify + Prisma
- 服务运行 48 小时后内存从 200MB 增长到 1.2GB
- 使用 pm2 管理进程
- 主要处理 WebSocket 连接,平均在线 500 个客户端

## 已排查
- 用 --inspect 抓了两次 heap snapshot,对比后发现
  Socket 对象和 callback 函数数量持续增长
- 客户端断开连接后,相关对象没有被回收

## 相关代码
```typescript
wss.on("connection", (ws) => {
  const handler = createMessageHandler(ws)
  ws.on("message", handler)
  // 没有 ws.on("close", ...) 清理逻辑
})
```

## 期望
1. 确认问题根因
2. 给出修复代码
3. 建议一个防止类似问题再次发生的模式
```

Prompt B 给出了精确的修复方案和防御性编程模式,因为它有了足够的信息来定位问题。

---

## 写 Prompt 的思考流程

用 Mermaid 流程图表示编写 Prompt 时的决策过程:

```mermaid
flowchart TD
    A[明确要做什么] --> B{AI 能否看到相关代码?}
    B -->|能| C[引用文件路径或 @file]
    B -->|不能| D[贴出关键代码片段]
    C --> E{任务复杂度如何?}
    D --> E
    E -->|单步可完成| F[写一个完整 Prompt]
    E -->|需要多步| G[拆分为子任务序列]
    F --> H{需要约束吗?}
    G --> H
    H -->|有风格/依赖/安全要求| I[添加约束条件]
    H -->|无特殊要求| J[直接发送]
    I --> J
    J --> K{输出满意吗?}
    K -->|满意| L[完成]
    K -->|不满意| M{问题是出在哪?}
    M -->|结果偏离| N[补充上下文或约束]
    M -->|格式不对| O[明确输出格式]
    M -->|代码有 bug| P[贴出错误信息]
    N --> J
    O --> J
    P --> J
```

这个流程的核心思路:先想清楚,再写下来;一次不行,针对性补充。

---

## Prompt 编写检查清单

每次编写 Prompt 时,对照以下清单逐项检查:

- [ ] **任务是否用动词开头**?「实现」「修改」「解释」「排查」比「弄一下」「看看」「搞搞」更有效
- [ ] **是否声明了技术栈和版本**?语言、框架、主要依赖的版本号
- [ ] **是否提供了相关代码**?不是「这段代码」,而是直接贴出代码或引用文件路径
- [ ] **是否说明了期望行为**?修改后应该怎样运行、返回什么结果
- [ ] **是否包含了错误信息**?排查问题时,完整的错误堆栈是必须的
- [ ] **是否列出了约束条件**?风格、依赖、安全、性能、兼容性
- [ ] **是否指定了输出格式**?只要代码?还是要带注释、带测试、带解释?
- [ ] **任务是否足够小**?一个 Prompt 解决一个问题,不要贪多
- [ ] **是否避免了主观词汇**?「更好的」「更干净的」→ 用可衡量的标准替代
- [ ] **是否考虑了 AI 的信息盲区**?它不知道你的项目结构、团队约定、历史决策
- [ ] **是否在 Prompt 结尾提出了明确的问题**?以问号结尾比句号更容易得到精准回答
- [ ] **是否做过至少一轮迭代**?第一版 Prompt  rarely 完美,补充和修正才是常态

---

## 高级技巧

### 用「反面示例」约束行为

当你发现 AI 总是犯某个特定错误时,直接在 Prompt 中列出「不要这样做」:

```
## 约束
- 不要使用 any 类型,必须给出具体类型
- 不要用 console.log 做日志,使用项目已有的 logger 模块
- 不要创建新的工具函数文件,除非逻辑在 3 个以上文件中使用
- 不要在组件内部定义样式对象,提取到独立的 styles 常量
```

### 用「分步对话」处理复杂任务

对于涉及多个文件的修改,不要试图在一个 Prompt 中完成:

```
第一轮:分析现有代码结构,输出依赖关系图
第二轮:确认分析后,给出重构方案
第三轮:确认方案后,逐文件输出修改后的代码
第四轮:输出需要新增的测试用例
```

每一轮你都有一次检查机会,避免最终结果偏离预期。

### 用「项目规则文件」减少重复

大多数 AI 编程工具都支持项目级配置文件(CLAUDE.md、.cursorrules、COPILOT_INSTRUCTIONS.md)。把以下内容写入配置文件,每次对话自动生效:

- 技术栈和版本
- 编码风格规范
- 目录结构约定
- 常用命令
- 禁止事项

这样你在每次 Prompt 中就不需要重复声明这些基础信息。

---

## 参考资料

1. OpenAI. [Best Practices for Prompt Engineering](https://help.openai.com/en/articles/6654000-best-practices-for-prompt-engineering-with-the-openai-api). OpenAI Help Center.
2. Prompt Engineering Guide. [Generating Code - Prompt Engineering Guide](https://www.promptingguide.ai/applications/coding). 2025.
3. JP Stevens. [The Developer's Guide to Prompt Engineering for AI Coding Assistants](https://dev.to/jps27cse/the-developers-guide-to-prompt-engineering-for-ai-coding-assistants-2089). dev.to, 2026.
4. 阿里云. [文生文 Prompt 指南](https://help.aliyun.com/zh/model-studio/prompt-engineering-guide). 阿里云帮助文档.
5. 火山引擎. [AI编程 Prompt Engineering:高效代码生成的核心技巧](https://www.volcengine.com/article/130). 火山引擎技术博客.
6. 少个分号. [如何写好提示词:AI 协同开发实用指南](https://shaogefenhao.com/posts/ai/better-prompt-for-ai-coding.html). 个人技术博客.
7. SSOJet. [25 Best Prompts for AI Coding Agents](https://ssojet.com/blog/best-coding-agent-prompts). 2026.
8. Anthropic. [Prompt Engineering Guide](https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview). Anthropic Docs.