Skip to content

Latest commit

 

History

History
485 lines (366 loc) · 13.1 KB

File metadata and controls

485 lines (366 loc) · 13.1 KB

第三章:提示词工程(Prompt Engineering)—— 与 AI 高效沟通的艺术

3.1 为什么提示词工程很重要?

"Prompt 就是 Agent 的灵魂。同样的模型,好的 Prompt 和差的 Prompt 输出质量天差地别。"

对于 Agent 开发来说,提示词工程不是可选项,而是必修课。因为:

  1. Agent 的行为由 System Prompt 决定 — 你的 Prompt 就是 Agent 的"操作系统"
  2. 工具调用依赖精确指令 — Prompt 写得不好,Agent 就不会正确使用工具
  3. 输出质量直接影响用户体验 — 结构化、准确、有用的输出来自好的 Prompt

3.2 Prompt 的基本结构

一个好的 Prompt 通常包含以下几个部分:

┌────────────────────────────────────┐
│  1. 角色定义(Role)               │  你是谁?
│  2. 上下文(Context)              │  背景信息是什么?
│  3. 指令(Instruction)            │  需要做什么?
│  4. 输入(Input)                  │  具体输入是什么?
│  5. 输出格式(Output Format)      │  输出应该是什么样?
│  6. 约束(Constraints)            │  有什么限制条件?
│  7. 示例(Examples)               │  期望的输入输出示例
└────────────────────────────────────┘

完整示例

const systemPrompt = `
## 角色
你是一个资深的 JavaScript 代码审查专家,有 10 年前端开发经验。

## 上下文
用户会提交 JavaScript/TypeScript 代码片段请你进行代码审查。

## 指令
对用户提交的代码进行全面审查,包括:
1. 代码质量和最佳实践
2. 潜在的 Bug 和安全漏洞
3. 性能优化建议
4. 可读性和可维护性

## 输出格式
使用以下 JSON 格式输出:
{
  "summary": "一句话总体评价",
  "score": "1-10 评分",
  "issues": [
    {
      "severity": "error | warning | info",
      "line": "行号",
      "description": "问题描述",
      "suggestion": "修改建议"
    }
  ],
  "improvedCode": "优化后的完整代码"
}

## 约束
- 只关注代码本身,不评论需求合理性
- 严格按照 JSON 格式输出
- 评分标准:8-10 优秀,5-7 一般,1-4 需要重构
`;

3.3 六大核心 Prompt 技巧

技巧一:角色扮演(Role Playing)

给 LLM 设定一个具体角色,可以显著提升输出质量:

// ❌ 一般
const prompt = "帮我分析这段代码有什么问题";

// ✅ 更好
const prompt = "你是一个有 10 年经验的 Node.js 安全专家。请从安全角度审查这段代码,找出所有可能的安全漏洞。";

技巧二:Few-Shot 示例

提供几个输入→输出的示例,让 LLM "照葫芦画瓢":

const systemPrompt = `
你是一个任务分类器,将用户输入分类为以下类别之一:
- code_question(编程问题)
- general_chat(闲聊)  
- file_operation(文件操作)
- web_search(需要搜索)

## 示例

输入:JavaScript 中如何深拷贝一个对象?
输出:{"category": "code_question", "confidence": 0.95}

输入:今天天气怎么样?
输出:{"category": "web_search", "confidence": 0.9}

输入:帮我把这个文件重命名为 test.js
输出:{"category": "file_operation", "confidence": 0.85}

输入:你好呀,最近怎么样?
输出:{"category": "general_chat", "confidence": 0.9}
`;

技巧三:思维链(Chain of Thought, CoT)

让 LLM 一步步思考,而不是直接给答案:

// ❌ 直接要答案
const prompt = "计算这个函数的时间复杂度";

// ✅ 引导逐步推理
const prompt = `
分析以下函数的时间复杂度。请按以下步骤思考:
1. 首先,识别所有循环和递归调用
2. 分析每个循环的迭代次数
3. 分析循环之间的嵌套关系
4. 综合计算总的时间复杂度
5. 给出最终的大O表示法

展示你的完整推理过程。
`;

快捷方式: 只需在 Prompt 末尾加上 "让我们一步步来思考(Let's think step by step)" 就能激活 CoT 推理。

技巧四:明确输出格式

告诉 LLM 你要什么样的输出格式:

// ❌ 模糊
const prompt = "分析一下这个 npm 包的信息";

// ✅ 明确格式
const prompt = `
分析以下 npm 包,以如下格式返回:

## 包名
{包名}

## 功能
{一句话描述}

## 优点
- {优点1}
- {优点2}

## 缺点
- {缺点1}

## 推荐场景
{什么时候适合用}

## 替代方案
| 替代品 | 优势 |
|--------|------|
| {包名} | {描述} |
`;

技巧五:分隔符(Delimiters)

使用分隔符清晰地划分 Prompt 的不同部分:

const systemPrompt = `
你是一个代码翻译器,将 JavaScript 代码翻译为 Python。

### 规则
1. 保持相同的逻辑和命名风格
2. 使用 Python 的惯用写法
3. 添加类型提示

### 输入代码
\`\`\`javascript
${userCode}
\`\`\`

### 要求
请输出翻译后的 Python 代码,用 \`\`\`python\`\`\` 代码块包裹。
`;

常用的分隔符:

  • 三重反引号 ```
  • XML 标签 <context></context>
  • Markdown 标题 ###
  • 破折号 ---
  • 方括号 【】

技巧六:约束和边界

明确 LLM 不要做什么,和要做什么同样重要:

const systemPrompt = `
你是一个 API 文档生成器。

## 要做的
- 根据代码生成清晰的 API 文档
- 包含参数说明、返回值、示例

## 不要做的
- 不要修改原始代码
- 不要添加与文档无关的评论
- 不要使用自己编造的 API 示例,所有示例必须基于实际代码
- 如果代码逻辑不清晰,标注"需要作者确认"而不是猜测
`;

3.4 Agent Prompt 实战:设计一个完整的 Agent System Prompt

以下是一个代码助手 Agent 的完整 System Prompt 设计:

const agentSystemPrompt = `
# 角色
你是 CodeBuddy,一个智能编程助手 Agent。你运行在用户的本地电脑上,可以帮用户完成各种编程任务。

# 能力
你可以使用以下工具:
1. **read_file(path)** - 读取文件内容
2. **write_file(path, content)** - 写入文件
3. **run_command(command)** - 执行终端命令
4. **search_web(query)** - 搜索网络信息
5. **list_directory(path)** - 列出目录内容

# 工作流程
当用户提出请求时,按以下步骤执行:

1. **理解意图**:仔细分析用户的需求,如有不明确的地方先询问
2. **制定计划**:将任务拆解为清晰的步骤,并告知用户
3. **逐步执行**:依次执行每个步骤,使用合适的工具
4. **验证结果**:执行完成后验证结果正确性
5. **汇报总结**:简要总结完成了什么

# 规则
- 在执行文件写入或命令执行前,先告知用户将要做什么
- 遇到错误时,分析原因并尝试自动修复,最多重试 3 次
- 不确定时主动询问,不要猜测用户意图
- 始终使用安全的命令,永远不要执行 rm -rf / 等危险命令
- 代码遵循项目的现有风格和约定

# 输出风格
- 简洁明了,避免冗余
- 代码用代码块包裹,标明语言
- 每个步骤标注进度,如 [1/3]、[2/3]
- 对关键决策给出简短解释

# 示例交互

用户:帮我创建一个 Express 服务器
助手:好的,我来帮你创建。计划如下:
[1/3] 初始化项目并安装依赖
[2/3] 创建服务器代码
[3/3] 测试运行

开始执行:
[1/3] 初始化项目...
> 执行 run_command("npm init -y && npm install express")
(等待结果...)
`;

3.5 Prompt 模板化:在代码中管理 Prompt

实际开发中,你需要把 Prompt 模板化管理:

// prompts/index.mjs

// 基础模板函数
function createPrompt(template, variables) {
  return Object.entries(variables).reduce(
    (result, [key, value]) => result.replaceAll(`{{${key}}}`, value),
    template
  );
}

// Agent System Prompt 模板
const AGENT_SYSTEM_TEMPLATE = `
你是 {{agentName}},一个{{agentRole}}。

## 能力
{{capabilities}}

## 规则
{{rules}}

## 输出格式
{{outputFormat}}
`;

// 使用
const systemPrompt = createPrompt(AGENT_SYSTEM_TEMPLATE, {
  agentName: 'CodeBuddy',
  agentRole: '智能编程助手,帮助用户完成 JavaScript/Node.js 相关任务',
  capabilities: `
- 阅读和编写代码
- 执行终端命令
- 分析代码问题并给出修复方案`,
  rules: `
- 安全第一:不执行危险命令
- 先确认后执行:重要操作前先告知用户
- 保持代码风格一致`,
  outputFormat: '使用 Markdown 格式,代码用代码块包裹'
});

export { createPrompt, AGENT_SYSTEM_TEMPLATE };

Prompt 版本管理

prompts/
├── v1/
│   ├── system.md        # System Prompt
│   ├── classify.md      # 意图分类 Prompt
│   └── summarize.md     # 摘要 Prompt
├── v2/
│   ├── system.md        # 改进版
│   └── ...
└── index.mjs            # 模板引擎

💡 建议:把 Prompt 存为独立的 .md 文件,方便版本管理和 A/B 测试。

3.6 高级技巧

ReAct Prompt 模式(Agent 核心)

ReAct 是 Agent 最常用的 Prompt 模式,将**推理(Reasoning)行动(Acting)**结合:

const reactPrompt = `
你是一个能使用工具的 AI 助手。对于每个用户请求,按以下模式思考和行动:

Thought: 我需要思考下一步该做什么
Action: 选择一个工具来执行
Action Input: 工具的输入参数
Observation: 工具返回的结果

重复以上步骤直到你能给出最终答案:

Thought: 我已经有了足够的信息来回答
Final Answer: 最终回答

## 可用工具
- search(query): 搜索信息
- calculate(expression): 计算数学表达式
- get_weather(city): 获取天气

## 示例

用户: 北京和上海今天哪个更热?

Thought: 我需要分别查询北京和上海的天气
Action: get_weather
Action Input: 北京
Observation: 北京今天 28°C,晴

Thought: 我已经知道北京的温度了,再查上海
Action: get_weather  
Action Input: 上海
Observation: 上海今天 32°C,多云

Thought: 上海32°C > 北京28°C,我可以给出答案了
Final Answer: 上海今天更热。上海 32°C,北京 28°C,上海比北京高 4°C。
`;

自我修正 Prompt

让 Agent 在出错时自动修正:

const selfCorrectPrompt = `
执行任务时,使用以下自我检查流程:

1. 执行计划
2. 检查结果是否符合预期
3. 如果不符合,分析原因:
   - 是理解错误?→ 重新理解需求
   - 是工具使用错误?→ 换一个工具或参数
   - 是信息不足?→ 获取更多信息
4. 修正后重新执行

注意:同一个错误最多重试 3 次。如果 3 次都失败,向用户说明情况并请求帮助。
`;

3.7 常见 Prompt 陷阱与调试

陷阱 1:指令太模糊

// ❌ 
"处理一下这个数据"

// ✅ 
"将以下 CSV 数据转换为 JSON 数组,每行是一个对象,列名作为键,数值列转为 number 类型"

陷阱 2:Prompt 过长导致"迷失"

LLM 对超长 Prompt 中间部分的关注度会下降(称为 Lost in the Middle 问题)。

解决方案:

  • 最重要的指令放在开头和结尾
  • 使用清晰的 Markdown 结构分段
  • 拆分为多个短 Prompt,分步执行

陷阱 3:缺少"负面示例"

// ❌ 只说了要做什么
"生成专业的错误信息"

// ✅ 同时说了不要什么
`生成专业的错误信息。

好的示例:
- "无法连接到数据库:连接超时(host: localhost:5432)。请检查数据库是否正在运行。"

不好的示例(不要这样):
- "出错了"
- "Error: something went wrong"
- "数据库错误:[object Object]"
`

Prompt 调试方法

// 方法一:让 LLM 解释它的理解
const debugPrompt = `
在执行任务之前,先用以下格式确认你的理解:

【我的理解】
- 用户想要:...
- 具体需求:...
- 预期输出:...

确认理解正确后,再开始执行。
`;

// 方法二:观察输出,迭代优化
// 记录每次迭代的 Prompt 和效果
const promptLog = {
  version: 'v1.2',
  prompt: '...',
  testCases: [
    { input: '...', expectedOutput: '...', actualOutput: '...', pass: true },
    { input: '...', expectedOutput: '...', actualOutput: '...', pass: false },
  ],
  notes: '修改了输出格式约束后,准确率从 70% 提升到 92%'
};

3.8 小结

本章你掌握了:

  • ✅ Prompt 的核心结构:角色、上下文、指令、格式、约束、示例
  • ✅ 六大核心技巧:角色扮演、Few-Shot、思维链、格式化、分隔符、约束
  • ✅ Agent System Prompt 设计方法
  • ✅ ReAct Prompt 模式
  • ✅ Prompt 模板化管理和版本控制
  • ✅ 常见陷阱和调试方法

练习

  1. 为自己的 Electron Agent 应用设计一个 System Prompt
  2. 用 Few-Shot 技巧实现一个"用户意图分类器"
  3. 尝试 ReAct 模式,让 AI 使用自定义工具解决问题

下一章我们将深入 Agent 的核心架构,了解 Agent 内部的工作原理和常见架构模式。