提示工程:从CRAC框架到思维链,掌握与大模型高效协作的核心技能
1. 项目概述:当“提示工程师”成为一项可复制的技能
最近在GitHub上看到一个挺有意思的项目,叫aptratcn/skill-prompt-engineer。光看这个名字,就挺能反映当下AI圈的一个热门趋势——提示工程(Prompt Engineering)正在从一种“玄学”或“经验”,逐渐沉淀为一套可学习、可复制、可系统化提升的“技能”。
我自己从GPT-3时代就开始折腾各种大语言模型,从早期的“调戏”式对话,到后来用API做应用开发,再到如今每天工作都离不开Copilot和各种AI助手。我深切地感受到,与AI高效协作的能力,已经和编程、数据分析一样,成为了一种核心的生产力技能。但问题是,这项技能怎么学?网上教程要么太零散,讲几个“魔法咒语”;要么太理论,离实际工作场景很远。
这个skill-prompt-engineer项目,在我看来,其核心价值就在于它试图提供一个结构化的“技能树”或“知识体系”。它不只是一个简单的提示词合集,而是想告诉你,要成为一个合格的“提示工程师”,你需要掌握哪些核心概念、遵循哪些设计原则、了解哪些高级技巧,以及如何将这些知识应用到具体的场景中(比如代码生成、内容创作、数据分析等)。这对于任何一个想系统提升自己AI使用效率的开发者、产品经理、运营,甚至是学生来说,都是一个非常棒的起点和路线图。
接下来,我就结合自己这几年的实操经验,对这个项目可能涵盖的核心内容进行一次深度拆解和扩展,希望能帮你不仅知道“用什么提示词”,更能理解“为什么这么写”以及“如何举一反三”。
2. 提示工程的核心思维模式转变
在深入具体技巧之前,我们必须先完成一次思维上的“升级”。很多人把提示工程理解为“寻找正确的魔法关键词”,这其实是最大的误区。高效的提示工程,本质上是清晰、结构化、可迭代的沟通设计。
2.1 从“命令式”到“协作式”思维
传统的计算机交互是“命令式”的:你输入一个精确指令,机器返回一个确定结果。代码print(“Hello”)一定会输出 “Hello”。但与大语言模型(LLM)交互是“协作式”的:你提供背景、约束和期望,模型基于其“理解”生成一个“合理”的回应。这里充满了不确定性。
注意:不要指望LLM像编译器一样“严格执行”你的每一个字。它是在“理解”你的意图,并基于海量训练数据生成“最可能”的回应。因此,你的提示词是在为这种“理解”创造最佳上下文。
我的实操心得:把LLM想象成一个能力超强但需要明确指引的实习生。你不能只说“写份报告”,而得说:“我们需要一份关于Q2社交媒体营销活动的复盘报告,面向部门总监。请包含以下部分:1. 核心目标达成情况(用数据);2. 主要渠道的ROI分析;3. 遇到的最大挑战及应对;4. 对Q3的3条具体建议。报告风格需专业、简洁,避免流水账。”
2.2 提示词的四大核心要素
一个高效的提示词,通常需要包含以下四个要素,我称之为“CRAC”框架:
- 上下文(Context):告诉模型它正在扮演什么角色、处于什么场景。这设定了模型的“身份”和知识边界。
- 示例:“你是一位资深的全栈开发工程师,精通Python和React。”
- 请求(Request):清晰、具体地说明你希望模型完成什么任务。这是提示词的核心。
- 示例:“请为以下用户需求编写一个Python函数。”
- 约束(Constraints):明确输出的格式、风格、长度、禁止事项等。这是控制输出质量的关键。
- 示例:“输出格式为JSON,包含
code和explanation两个字段。代码需包含详细的注释,解释部分不超过200字。”
- 示例:“输出格式为JSON,包含
- 示例(Examples):(可选但强烈推荐)提供一两个输入-输出的例子。这是让模型快速理解你意图的最有效方式,即“少样本学习”(Few-Shot Learning)。
- 示例:“例如,对于输入‘解析CSV文件并计算平均年龄’,输出应为:
{“code”: “import csv\n...”, “explanation”: “该函数使用csv模块...”}”
- 示例:“例如,对于输入‘解析CSV文件并计算平均年龄’,输出应为:
避坑指南:很多新手写的提示词只有“请求”,缺少其他要素,导致结果不稳定。下次写提示前,可以心里默念一遍CRAC,检查是否遗漏。
3. 结构化提示设计:从零构建一个可用的提示
理解了核心思维,我们来看如何一步步构建一个强健的提示。我们以一个常见的开发任务为例:“帮我写一个Python函数,从API获取天气数据并解析。”
3.1 基础版提示及其问题
新手可能会这样写:
写一个Python函数获取天气数据。这个提示的问题非常明显:过于模糊。哪个API?返回什么数据格式?函数签名是什么?如何处理错误?输出结果完全随机。
3.2 进阶版:应用CRAC框架进行迭代
让我们应用CRAC框架来迭代它。
第一轮迭代:增加上下文和约束
你是一位专业的Python后端工程师。请编写一个函数,从‘openweathermap.org’的公开API获取指定城市的当前天气数据。要求: 1. 函数名为 `get_current_weather`。 2. 参数为 `city_name` (字符串) 和 `api_key` (字符串)。 3. 使用 `requests` 库。 4. 处理网络请求异常(如超时、连接错误)和API返回错误(如城市不存在)。 5. 成功时返回一个包含 `temperature` (摄氏度)、`humidity` (百分比)、`description` (天气描述) 的字典。 6. 失败时返回 `None` 或抛出明确的异常。这个版本好多了,模型有了明确的角色、API端点、函数规范、错误处理和输出格式。
第二轮迭代:提供示例(Few-Shot),进一步提升稳定性
你是一位专业的Python后端工程师。请编写一个函数,从‘openweathermap.org’的公开API获取指定城市的当前天气数据。 【示例】 对于城市“London”和API密钥“your_key_here”,成功的响应解析后应返回类似: { “temperature”: 15.5, “humidity”: 72, “description”: “light rain” } 如果城市不存在,API返回 `{“cod”: “404”, “message”: “city not found”}`,则函数应抛出 `ValueError(“City not found”)`。 【任务】 请根据以上示例,编写函数 `get_current_weather(city_name: str, api_key: str) -> dict`。要求使用 `requests` 库,并妥善处理网络异常和API错误。通过提供一个成功输出的示例和一个错误处理的示例,我们极大地缩小了模型输出的不确定性范围,使其更贴近我们的真实需求。
我的实操心得:在编写复杂逻辑或特定格式的输出时,“示例”的力量远大于冗长的文字描述。给模型“打个样”,它能学得飞快。
3.3 使用分隔符和格式指令提升可读性
对于超长的提示,良好的结构能帮助模型(以及未来的你)更好地理解各部分内容。常用的分隔符有---、###、"""等。
### 角色设定 ### 你是一位经验丰富的系统架构师,擅长设计可扩展的微服务。 ### 任务背景 ### 我们正在开发一个电子商务平台,需要设计一个“订单服务”(Order Service)。 ### 具体请求 ### 请列出该订单服务的核心职责、必须对外提供的API接口(用HTTP方法和路径表示),以及它需要依赖的其他服务(如用户服务、库存服务、支付服务)。 ### 输出格式约束 ### 请严格按照以下Markdown表格格式输出: | 项目类别 | 具体内容 | 说明 | | :--- | :--- | :--- | | 核心职责 | 1. ...<br>2. ... | 用序号列表 | | 对外API | `POST /orders`<br>`GET /orders/{id}` | 用代码块样式 | | 依赖服务 | 服务名:交互方式(如RPC/消息) | 简要描述 |这种结构清晰的提示,不仅模型处理起来更准确,也便于你自己维护和修改。
4. 高级技巧与模式:超越基础对话
掌握了结构化设计,就可以探索一些更高级的模式,来解决复杂问题。
4.1 思维链(Chain-of-Thought, CoT)
对于需要推理、计算或多步骤的问题,直接问答案效果往往不好。引导模型“一步步思考”能极大提升复杂任务的准确性。
普通提问:
小明有5个苹果,他吃了2个,又买了3包苹果,每包4个,他现在一共有几个苹果?模型可能直接给出一个答案,但过程是黑箱的,错了也不知道哪一步有问题。
使用CoT提示:
请逐步推理以下问题: 小明最初有5个苹果。 第一步:他吃了2个,那么他还剩下几个苹果?请先计算这一步。 第二步:他买了3包苹果,每包有4个,那么他新买了多少个苹果?请计算这一步。 第三步:将第一步剩下的苹果和第二步新买的苹果加起来,总数是多少? 请按照以上三步,一步步给出你的推理过程和最终答案。通过强制模型展示中间步骤,我们不仅能得到更可靠的答案,还能在模型推理出错时精准定位问题所在。在代码调试、数学计算、逻辑分析等场景下,CoT是必备技巧。
4.2 自动提示优化(Self-Refine)
有时候,我们不确定自己的提示词是否最优。可以让模型自己评估和优化提示词。
你可以设计这样一个工作流:
- 任务:给模型一个初始提示和其生成的输出。
- 分析:要求模型以提示工程师的身份,分析这个初始提示有哪些可以改进的地方(如:是否模糊?缺少约束?示例不足?)。
- 优化:让模型根据分析,输出一个优化后的、更好的提示词版本。
这相当于让AI成为了你的“提示词教练”,通过几轮迭代,你能快速学习到优化提示词的思路。
4.3 系统指令(System Prompt)与用户指令(User Prompt)的分离
在通过API使用模型时(如OpenAI API),通常可以设置一个“系统”消息来定义模型的长期行为和人设,而“用户”消息则是具体的本次请求。这比把所有东西都塞进一条用户消息更清晰、更强大。
- 系统提示(设定角色和全局规则):
你是一个乐于助人且严谨的代码助手。你总是用Python回答问题,除非特别要求使用其他语言。你输出的代码必须简洁、高效,并包含必要的错误处理。在解释概念时,优先使用类比的方式让初学者理解。 - 用户提示(本次具体请求):
请解释一下Python中的装饰器(Decorator)是什么,并给一个记录函数运行时间的实用例子。
这种分离使得“角色设定”可以在一段对话中持续生效,而不需要在每个问题中都重复。
5. 场景化实战:提示工程在具体领域的应用
理论说再多,不如看实战。我们选取几个典型场景,看看如何应用上述原则。
5.1 场景一:代码生成与调试
目标:生成一个Flask API端点,接收用户ID,从数据库查询用户信息并返回。
初级提示:
写一个Flask的GET接口查用户。优化后的提示:
你是一位精通Flask和SQLAlchemy的后端工程师。请创建一个Flask应用的API端点。 ### 背景 ### - 项目使用 Flask + SQLAlchemy (ORM) 连接 PostgreSQL 数据库。 - 已存在一个 `User` 模型,包含 `id` (Integer, Primary Key), `username` (String), `email` (String) 字段。 - 数据库会话对象为 `db.session`。 ### 任务 ### 1. 编写一个名为 `get_user_by_id` 的视图函数。 2. 路由为 `GET /api/users/<int:user_id>`。 3. 功能:根据传入的 `user_id` 查询对应用户。 4. 如果用户存在,返回JSON格式:`{“id”: …, “username”: …, “email”: …}`,HTTP状态码200。 5. 如果用户不存在,返回JSON格式:`{“error”: “User not found”}`,HTTP状态码404。 6. 必须包含基本的异常处理(如数据库查询异常),异常时返回500状态码和通用错误信息。 7. 请为关键步骤添加简要的代码注释。 请直接输出完整的Python函数代码。要点分析:
- 明确技术栈:指定了Flask、SQLAlchemy、PostgreSQL,避免了模型猜测成Django或纯SQL。
- 设定前提:说明了已有的
User模型和db.session,让生成的代码能无缝嵌入假设的项目环境。 - 详细约束:明确了路由、函数名、成功/失败的不同响应格式和状态码,这是生成生产可用代码的关键。
- 要求健壮性:明确要求异常处理,这是新手容易忽略而资深工程师一定会考虑的点。
5.2 场景二:内容创作与润色
目标:将一篇生硬的技术博客草稿,润色得更加生动、易懂。
初级提示:
把这篇文章写得好读一点。优化后的提示:
你是一位科技专栏的资深编辑,擅长将深奥的技术内容转化为普通开发者感兴趣且能读懂的文章。 ### 原文片段 ### “本文阐述了在分布式系统中实现最终一致性的方法论。通过采用异步复制与冲突解决机制,系统能够在网络分区的情形下维持数据可用性,并保障在未来的某个时间点达成数据状态的一致性。” ### 润色要求 ### 1. **风格**:口语化、生动,可以适当使用比喻。目标读者是有一到三年经验的软件工程师。 2. **结构**:保留原意,但改变句式,避免长句和被动语态。 3. **目的**:让读者一眼就明白“最终一致性”解决了什么问题,以及它的核心思路是什么。 4. **输出**:请直接输出润色后的段落。 例如,你可以这样开头:“想象一下,在一个遍布全球的电商系统里,你在北京下单买了一本书,而库存数据却更新在美国的服务器上...”要点分析:
- 定义编辑角色:“科技专栏资深编辑”设定了专业的润色视角。
- 明确目标读者:“一到三年经验的软件工程师”,这决定了用词的深浅和解释的详细程度。
- 给出具体修改方向:“口语化、生动、用比喻、避免长句和被动语态”,这是可执行的指令,而不是模糊的“好读一点”。
- 提供启发式示例:给出的例子不仅展示了风格,更展示了一种“从场景切入”的叙述方式,能有效引导模型的输出方向。
5.3 场景三:数据分析与洞察
目标:给出一组销售数据,让AI帮忙分析趋势并提出建议。
初级提示:
分析一下这份销售数据。优化后的提示:
你是一位数据分析专家,擅长从数据中发现商业洞察。请分析以下2023年Q1的月度销售数据: | 月份 | 产品A销售额(万) | 产品B销售额(万) | 线上渠道占比 | 客单价(元) | | :--- | :--- | :--- | :--- | :--- | | 1月 | 120 | 80 | 65% | 450 | | 2月 | 115 | 85 | 68% | 440 | | 3月 | 130 | 75 | 72% | 460 | ### 分析要求 ### 1. **趋势描述**:用一两句话概括Q1的整体销售趋势。产品A和产品B的表现有何不同? 2. **关联洞察**:观察“线上渠道占比”和“客单价”的变化,它们之间或与销售额之间是否存在值得注意的关联? 3. **问题诊断**:基于数据,指出一个可能存在的潜在问题或风险。 4. **行动建议**:针对你诊断出的问题,提出1-2条具体、可操作的下季度(Q2)业务建议。 请将分析结果组织成一份简短的报告,分点陈述,语言精炼。要点分析:
- 结构化输入数据:使用表格清晰地提供数据,便于模型解析。
- 分步骤提问:将复杂的“分析”拆解成“趋势描述”、“关联洞察”、“问题诊断”、“行动建议”四个子任务,引导模型进行系统性的思考,避免回答笼统。
- 要求输出格式:“简短的报告,分点陈述”,这确保了回答的可用性,可以直接粘贴到邮件或文档中。
6. 工具、评估与迭代:构建你的提示工作流
掌握了方法,还需要好的工具和习惯来落地。
6.1 工具推荐
- 提示词管理:不要只用聊天窗口。使用笔记软件(如Notion、Obsidian)、专门的提示词管理工具(如PromptSource、自己建的数据库)或代码编辑器来保存和分类你的优质提示词模板。为它们打上标签,如
#代码生成、#内容润色、#CoT。 - 版本控制:像管理代码一样管理你的重要提示词。使用Git来记录提示词的迭代过程,备注每次修改的原因和效果。
skill-prompt-engineer这类项目本身就是一个开源的知识库。 - API Playground:OpenAI Playground、Claude Console等平台提供了更丰富的参数调整(如温度Temperature、最大生成长度Max tokens)和对比测试功能,是优化提示词的绝佳实验场。
6.2 如何评估提示词的好坏?
没有一个绝对标准,但可以从以下几个维度考量:
| 评估维度 | 说明 | 检查方法 |
|---|---|---|
| 清晰度 | 提示词本身是否无歧义,易于理解? | 让一个同事看你的提示词,看他是否能准确说出你想要什么。 |
| 可靠性 | 在相同提示下,模型多次输出的结果是否一致、稳定? | 用同样的提示词(温度设为0)运行3-5次,观察输出变化。 |
| 有效性 | 输出结果是否高质量地完成了任务? | 对照你的原始需求,检查输出内容的准确性、完整性和实用性。 |
| 效率 | 是否用最精炼的语言传达了必要信息? | 尝试删减提示词中的冗余词句,看输出质量是否下降。 |
| 可复用性 | 这个提示词模板是否能稍作修改就应用到类似任务上? | 尝试替换其中的关键参数(如主题、格式),看是否依然有效。 |
6.3 迭代流程:持续改进你的提示
提示工程是一个动态过程。我常用的迭代闭环是:
- 起草:根据CRAC框架写出第一版提示。
- 测试:在Playground或实际场景中运行,获取输出。
- 评估:对照“评估维度”检查输出,找出不足(如格式不对、缺少细节、有幻觉)。
- 归因:分析是提示词的哪个部分导致了问题(是上下文不清?约束不足?还是示例有误导?)。
- 修正:有针对性地修改提示词,回到第2步。
- 归档:将最终稳定的提示词版本和评估结果保存到你的知识库中。
例如,如果模型生成的代码没有错误处理,我就在“约束”部分加上“必须包含异常处理”。如果模型总是忽略我要求的Markdown格式,我就提供一个更清晰的格式示例,甚至把部分格式用“占位符”先写出来。
7. 常见问题与避坑指南
在实际操作中,你会遇到各种各样的问题。这里记录一些我踩过的坑和解决方案。
7.1 模型“幻觉”与事实错误
这是最常见的问题,模型会自信地生成错误信息或编造不存在的引用。
- 应对策略:
- 要求提供来源/依据:在提示中要求“基于已知的公开信息”或“如果你不确定,请说明”。
- 分步验证:对于关键事实或数据,让模型先输出其“推理依据”或“查找步骤”,你再进行人工核查。
- 外部知识库:对于专业领域,先将相关知识喂给模型(通过长上下文或检索增强生成RAG),再让它基于这些材料回答。
- 终极方案:永远不要完全信任AI生成的事实性内容,尤其是法律、医疗、金融等关键领域。AI是强大的协作者,而非权威的信息源。
7.2 输出格式不听话
明明要求了JSON,模型却输出了一段文字。
- 应对策略:
- 强化格式指令:在提示的开头和结尾都强调格式要求。例如:“你的输出必须是且仅是一个合法的JSON对象,不要有任何额外的解释文字。”
- 提供结构化示例:这是最有效的方法。在提示中给出一个完整的、符合格式要求的输入-输出示例。
- 使用系统提示:在API调用中,将严格的格式要求放在
system消息中,作为模型的“基础设定”。 - 后处理:对于简单格式,可以编写一个后处理脚本,用正则表达式从输出中提取所需结构。但这只是补救措施。
7.3 提示词过于冗长导致性能下降或遗忘
上下文长度有限,过长的提示词可能导致模型忘记开头的内容,或者响应速度变慢、成本增高。
- 应对策略:
- 精简语言:删除所有不必要的客套话、重复解释。
- 使用缩写和符号:在模型能理解的前提下,用更简洁的方式表达。例如用“->”表示映射关系。
- 分步对话:将复杂任务拆分成多个子任务,通过多轮对话完成。上一轮的输出可以作为下一轮的上下文。
- 总结长文档:如果需要基于长文档分析,先让模型对文档进行摘要,再基于摘要进行后续操作。
7.4 如何应对“我不知道”或拒绝回答
有时模型会因安全策略或知识盲区而拒绝回答。
- 应对策略:
- 重构问题:将敏感或可能被拒绝的问题,转化为一个假设性的、学术性的或类比性的问题。例如,不问“如何破解某个系统”,而是问“从网络安全教育角度,常见的系统身份验证机制有哪些脆弱性?”
- 赋予专业角色:让模型扮演一个允许讨论该话题的角色,如“作为一位网络安全研究员,在学术讨论的范围内...”。
- 分解问题:将一个大问题分解成几个中性、技术性的子问题。
说到底,aptratcn/skill-prompt-engineer这个项目指向的,不是一堆死记硬背的“咒语”,而是一种全新的、与智能体协作的思维方式和工作方法。它要求我们像产品经理一样思考需求,像老师一样设计教学大纲,像架构师一样规划交互流程。这项技能的核心不是“操控”AI,而是如何最清晰、最无歧义地表达人类的意图,并引导AI将其转化为高质量的创造。随着模型能力的进化,提示工程本身也在变化,但底层这种“结构化沟通”和“迭代优化”的能力,将会长期成为人机协同时代的核心竞争力。
