大家好,我是 lv (lv.liu)。
最近文章一直没有更新内容,没有失踪~
只是一直在打磨体系化的前端 AI 知识体系。
本篇文章,也是在整理知识体系的过程中,梳理出来的一些思考总结。
也是一个老生常谈的话题,尽管基础,但却蛮重要。
本篇,咱们来探索:面向前端人员的提示词工程(Prompt Engineering)。
提示词工程,本质上就是如何更好地利用大语言模型的这些能力,来完成我们的目标。
不同的模型,相同的提示词,效果可能不一样,但是提示词的设计思路是相通的。
从技术的角度看提示词的类别
我们以前端 Chat Bot 助手为例,整个对话的过程如下:
如上图所示,整个对话的过程中的信息可以分为 4 个类别:
System Prompt
-
定义 Assistant 的角色、行为、基础规则
-
例如:专注在前端问题的解答
User Prompt
-
用户输入问题/指令
-
例如:生成一个 Table 组件
Assistant Prompt
-
AI 回复内容
-
例如:Table 组件的代码
Tools Prompt
-
调用特定功能生成的内容
-
例如:调用代码生成器,生成 Table 组件的代码
站在技术的角度,我们用 OpenAI 的 SDK API 调用为例,伪代码如下:
import OpenAI from "openai";
const openai = new OpenAI();
const response = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "system",
content:
"你是一个前端问题解答助手,善于解答前端开发中遇到的问题,比如:组件的封装、代码的生成、库的安装、工具的使用等",
},
{
role: "user",
content: "生成一个 Table 组件",
},
{
role: "assistant",
tool_calls: [
{
id: "call_id_1",
type: "function",
function: {
name: "generateCode",
arguments: '{"userDemand": "生成一个 Table 组件"}',
},
},
],
},
{
role: "tool",
name: "generateCode",
tool_call_id: "call_id_1",
content: "// Table 组件的代码\n...\n",
},
{
role: "assistant",
content: "Table 组件的代码如下:```tsx\n...\n```",
},
],
tools: [
{
type: "function",
function: {
name: "generateCode",
description: "生成代码",
parameters: {
type: "object",
properties: {
userDemand: {
type: "string",
description: "用户需求详细描述",
},
},
},
},
},
],
});
市面上大家讨论的提示词工程,主要集中在如何编写 System Prompt,它决定了 AI 会用什么样的内容(Assistant Prompt)来回复用户的需求(User Prompt)。
因此后文我们本质上主要讨论 System Prompt 工程。
编写提示词的核心原则
在了解了提示词的类别之后,我们来探讨一下编写提示词的核心原则。
市面上的提示词框架(比如:BROKE 框架、结构化提示词)、技巧(比如:COT 思维链、Few-Shot 示例)等。
它们说白了就是一些工具或者技巧,帮助大家照葫芦画瓢,拿过来改一改就能跑起来。
把它们类比为前端项目的脚手架 CLI,对于前端新手来说,通过脚手架 CLI 确实可以快速上手搭建一个项目出来。
但是,对于整个前端项目来说,这还只是从0到1的起步阶段,要把项目做好,高质量、高性能、高体验等,这些需要前端专业知识来做保障。
回到提示词工程,核心不是掌握所有的框架和技巧,而是能够用专业的知识让 AI 来完成专业的需求。
举一个例子:AI 的数据集很庞大,就把 AI 当成一个图书馆,你需要有专业的知识,才能知道你需要什么类别的书,同时判断这本书是否有价值,是否能够解决你的问题。
专业程度越高,越能够知道需要什么类别的书,同时判断这本书是否有价值,是否能够解决你的问题。
基于此,编写提示词的核心原则有 2 点:
-
明确需求和目标,这要求你对业务场景有深刻的理解和专业度,如果连你自己都不清楚要处理什么场景下的什么问题,那么 AI 是无法帮到你的
-
清晰的表达需求和目标,在明确需求和目标的基础上,用专业、清晰、精准、AI 友好的语言表达出来
所有的提示词框架、技巧,都是为了增强核心原则的第 2 点:用专业、清晰、精准的语言表达出来来展开的。
编写提示词的基本步骤
了解了编写提示词的核心原则之后,我们来探讨一下编写提示词的基本步骤。👇
第一步:用专业的知识明确需求和目标
比如:让 AI 来生成业务组件的场景,那么你首先需要明确业务组件的定义、业务组件的组成结构、用什么样的技术栈来实现等。
这些专业知识,决定了你最终能够从 AI 那里得到什么样的答案。
第二步:考虑选择合适的模型
不同的模型,有不同的能力,比如说,对于编码领域来讲,Claude-3.5-Sonnet 的编码能力现阶段相对比较强,因此在条件允许的范围内,我们可能首选就是 Claude-3.5-Sonnet 模型来完成编码任务。
第三步:用专业、清晰、精准、AI 友好的语言编写提示词(借助一些提示词框架、技巧)
这些框架和技巧我们待会来看。
第四步:拆分提示词链,循环迭代
拆分提示词链这个部分,和我们之前探索的AI赋能金字塔模型是一样的道理 👇
其实核心就是跟人处理问题的思路一样,将复杂的问题拆分成多个简单的问题。
对 AI 来说,就是拆分成多个提示词,每个提示词解决一个小问题。
串联起来,就形成了完整的解决方案,也就是提示词链。
下面我们来看一下,市面上比较流行的提示词技巧。
提示词技巧
让 AI 来帮助我们写提示词
这个技巧是我们需要重点掌握的。
市面上有蛮多这种魔法生成魔法的AI 工具,比如说:
- LangGPT 的提示词生成器,它主要是可以让 AI 帮忙生成
结构化的提示词
- Claude 的提示词生成器,它主要是可以让 AI 帮忙生成
对于使用 Claude 模型来说,比较友好的提示词
因此,我们在编写提示词的时候,首先就可以尝试让 AI 来帮助我们生成提示词,其实 AI 生成的提示词,本身就遵循了很多下面要提到的其他技巧。
提示词框架
BROKE、ICIO ...
BROKE:B(Background)、R(Role:角色)、O(Objective:目标)、K(Key Result:关键结果)、E(Evolve:反馈迭代)
举个例子:
B:你是一个前端问题解答助手
R:你善于解答前端开发中遇到的问题,比如:组件的封装、代码的生成、库的安装、工具的使用等
O:你的目标是帮助用户解决前端开发中遇到的问题
K:每次回答问题,按照以下格式:
- 问题:用户的问题
- 回答:问题的答案
- 原因:产生回答的原因
- 代码:辅助理解(如果需要)
E:在 AI 给出输出的结果后,用户提供的一些反馈和优化建议
ICIO:I(Intruction:介绍)、C(Context:背景上下文)、I(Input:输入)、O(Output:输出)
举个例子:
I:你是一个前端业务组件生成助手
C:你善于根据用户的需求,生成对应的业务组件
I:用户会问你一些问题,比如:生成一个 Table 组件或者给一个设计稿图,让你生成对应的代码
O:生成的业务组件代码遵循的规范:
- 代码规范:遵循 Ant Design 的组件规范
- 技术栈:React、Typescript、Less
- 代码风格:函数式编程
市面上的这种通过名字命名的提示词框架有很多,其实大家都大差不差,在 AI 能力还没那么强的阶段,比如说 GPT3.5 之前,这种提示词框架确实能够更好地约束 AI 生成的内容。
现阶段,AI 的能力越来越强,用户提供很少的提示词,AI 也能给出不错的答案。
所以现在这种框架更大的价值在于:帮助用户更好地维护和迭代提示词,在不同场景下我们按照一定的规范来组织提示词的结构。类似于代码规范,提示词也有提示词规范。
一团乱的提示词,就好比一团乱的代码,尽管能正常运行,但是维护成本和迭代成本都非常高。
结构化提示词
前面有提到让 AI 来生成提示词的项目:LangGPT,它生成的提示词是结构化的提示词。
结构化提示词有一个很明显的特点:整体的提示词采用 Markdown 格式,看起来很清晰整洁。
下面是它的结构:
# Role: Your_Role_Name
## Profile
- Author: lv
- Version: 1.0
- Language: English or 中文 or Other language
- Description: Describe your role. Give an overview of the role's characteristics and skills
### Skill-1
1.skill description 1
2.skill description 2
### Skill-2
1.skill description 1
2.skill description 2
## Rules
1. Don't break character under any circumstance.
2. Don't talk nonsense and make up facts.
## Workflow
1. First, xxx
2. Then, xxx
3. Finally, xxx
## Tools
### Tool-1
- tool description 1
- tool description 2
### Tool-2
- tool description 1
- tool description 2
### More Tools
...
## Initialization
As a/an <Role>, you must follow the <Rules>, you must talk to user in default <Language>,you must greet the user. Then introduce yourself and introduce the <Workflow>.
举个例子,让 AI 生成业务组件代码,如果按照结构化提示词的格式来写,如下:
# Role: 前端业务组件生成助手
## Profile
- Author: lv
- Version: 1.0
- Language: 中文
- Description: 你是一个前端业务组件生成助手,善于根据用户的需求,生成对应的业务组件
## Rules
1. 不要打破角色设定,只能回答和组件生成相关的问题
2. 不要胡言乱语,不要编造 API
3. 使用 antd 组件库
## Workflow
1. 首先,理解用户的需求描述
2. 然后,根据用户的需求描述,分析需要用到的 antd 组件
3. 最后,根据分析的组件,生成对应的代码
## Initialization
做为<Role>,你必须遵循<Rules>,你必须用<Language>和用户交流,你必须问候用户,然后介绍自己,最后介绍<Workflow>。
伪代码框架
前一段时间,李继刚老师分享的 Claude 提示词:汉语新解,用的就是类似于伪代码框架的提示词。
作者是用 Lisp 语言写的,我把这段代码翻译成 JavaScript 伪代码,如下:
/**
* 作者: 李继刚
* 版本: 0.3
* 模型: Claude Sonnet
* 用途: 将一个汉语词汇进行全新角度的解释
*/
// System Prompt 设定
class 新汉语老师 {
constructor() {
this.persona = "你是年轻人,批判现实,思考深刻,语言风趣";
this.风格 = ["Oscar Wilde", "鲁迅", "罗永浩"];
this.擅长 = "一针见血";
this.表达 = "隐喻";
this.批判 = "讽刺幽默";
}
}
class 汉语新解 {
constructor(用户输入) {
this.描述 = "你会用一个特殊视角来解释一个词汇";
this.用户输入 = 用户输入;
}
解释() {
const 抓住本质 = (输入) => `分析${输入}的本质`;
const 辛辣讽刺 = (内容) => `讽刺性解读: ${内容}`;
const 一针见血 = (内容) => `直击要害: ${内容}`;
const 隐喻 = (内容) => `隐喻表达: ${内容}`;
const 精练表达 = (内容) => `精炼版本: ${内容}`;
const few_shots = {
委婉: "刺向他人时, 决定在剑刃上撒上止痛药。",
};
const 处理流程 = 精练表达(
隐喻(一针见血(辛辣讽刺(抓住本质(this.用户输入))))
);
return new SVG_Card(处理流程).生成();
}
}
class SVG_Card {
constructor(解释) {
this.解释 = 解释;
this.design_rule = "合理使用负空间,整体排版要有呼吸感";
this.design_principles = ["干净", "简洁", "典雅"];
this.画布设置 = {
宽度: 400,
高度: 600,
边距: 20,
};
this.样式 = {
标题字体: "毛笔楷体",
自动缩放: { 最小字号: 16 },
配色: {
背景色: "蒙德里安风格",
主要文字: "汇文明朝体 粉笔灰",
装饰图案: "随机几何图",
},
};
}
生成() {
// SVG 卡片生成逻辑
return {
标题: "汉语新解",
内容: this.解释,
图形: this.生成线条图(this.解释),
总结: this.生成极简总结(this.解释),
};
}
生成线条图(内容) {
return `线条图表达: ${内容}`;
}
生成极简总结(内容) {
return `极简总结: ${内容}`;
}
}
// 启动函数
function start() {
const systemRole = new 新汉语老师();
console.log("说吧, 他们又用哪个词来忽悠你了?");
return systemRole;
}
/**
* 运行规则:
* 1. 启动时必须运行 start() 函数
* 2. 之后调用主函数 new 汉语新解(用户输入).解释()
*/
复制到 Claude 中,帮我解读:牛马
这种伪代码框架,对于程序员来说其实蛮友好的,能够用代码的思维来抽象和维护提示词,真正意义上的提示词即代码,还有一点,代码相对文字的表达方式来说,更加精准和严谨。
比如用文字来描述一个嵌套的逻辑,其实很难精准描述,但是用代码的方式,其实就是嵌套函数,一目了然,AI 也更能够理解。
COT 思维链
COT(Chain of Thought)思维链的核心就是让大模型逐步思考、逐步回答,提升大模型的推理能力和回答的准确性。
举一个例子:很简单的就是在提示词中加上一句经典的Let's think step by step,就可以开启思维链模式。
再进阶一点,可以借助Few-Shot 示例或者提供思考的Workflow,结合COT来引导大模型思考。
不过随着大模型的发展,COT 的技巧可能会直接内置到模型中,如果你使用过 OpenAI 的 o1 模型,你会发现它其实内置一整套 COT,不需要你做任何配置。
XML 语法
我感触最深的就是 XML 语法,最典型的例子就是Bolt.new的提示词,通篇看过去,全是用 XML 包裹的各种提示词块。👇
Claude 官方的提示词工程指南也大力推荐 XML 语法 👇
Few-Shot 示例
Few-Shot 示例,其实就是提供一些示例,让 AI 学习,从而更好地理解用户的意图。
举个例子,在Bolt.new中,有大量的Few-Shot Examples,👇
善用 System 之外的 Prompt
前面我们一直在聊的是System Prompt,其实有些情况下,为了重点突出某个意图,可以把重点的需求注入到User Prompt中。
比如做 RAG 应用的时候,如果某些召回的知识特别重要,需要重点强调,就可以在User Prompt中注入这些知识,组合用户的问题成为新的User Prompt。
适合前端人的提示词调试工具
下面总结了一些我平时调试提示词的工具,供大家参考。
开源的 LLMOps 平台
Dify
对于开发人员来说,可以理解为一个可视化调节 Prompt 的 IDE,支持接入市面上几乎所有的 LLM 模型。👇
关键是可以私有化部署,也能定义自己的 AI Key,对于企业来说,也是个挺不错的 LLM 应用平台。
除此了 Dify 这种聚合型的 LLMOps 平台之外,各种 LLM 模型厂商也都有自己的调试平台,比如:
Anthropic:console.anthropic.com/
OpenAI:platform.openai.com/playground
如果你的提示词只针对某一个模型,可以考虑使用模型厂商的调试平台。
IDE 插件
对于前端开发来说,VSCode 或者 Cursor 等 IDE 是日常工作中使用最频繁的工具,所以如果能够利用 IDE 插件来调试提示词,那自然最方便的。
这里推荐一个 VSCode 插件:Continue,能够帮助你方便地在 IDE 中调试和管理提示词。👇
Continue 也支持市面上几乎所有的 LLM 模型,也支持本地化的模型,对于一些对于数据安全有要求的研发团队来说,可以考虑组合 Continue + 本地化的模型。
像我平常也通过 Continue 封装了很多通用类的提示词,比如:English to Chinese、Chinese to English、Code Review、Code Explain 等等。
有两种方式可以在 Continue 中封装提示词:
- 打开
~/.continue/config.json,添加如下配置:
{
"models": [
{
"model": "gpt-4o", // 模型名称
"contextLength": 128000, // 上下文长度
"title": "Custom-Code-Assistant", // Assistant 的名称
"systemMessage": "你是一个定制化的代码生成助手,善于根据用户的需求,生成对应的代码。", // 系统提示词
"provider": "openai", // 模型提供商
"apiKey": "***", // 模型 API Key
"apiBase": "***", // 模型 API Base
"completionOptions": {
"maxTokens": 16384, // 最大 Token 数
"temperature": 0 // 温度系数
}
}
]
}
其中,systemMessage 是我们需要着重调试的内容,还有model、contextLength、completionOptions 等参数,可以根据自己的需求进行调整。
然后就可以在 Continue 中使用这个提示词了。👇
- 通过 prompt files 来定义
步骤 1:在工作区的顶层创建一个名为 .prompts/ 的文件夹。
步骤 2:在这个文件夹中添加一个名为 test.prompt 的文件,这个文件的名称将作为你在 Continue 中调用提示词的命令名称。
步骤 3:将下面的内容写入 test.prompt 并保存。
步骤 4:在 Continue 中,输入/来查看斜杠命令列表,选择刚刚定义的test命令。按回车键,LLM 将根据提示文件中的指令进行响应。
prompt files 详见:docs.continue.dev/customize/d…
Deno
如果你是在为一个 AI 应用写提示词,那么可以考虑使用 Deno + jupyter + vscode 的组合,来调试提示词,同时用来做 AI 应用的 POC 验证。
Deno:deno.com/
jupyter:jupyter.org/
可以看 LangChain 官方文档的许多教程,都是使用 jupyter 来进行教学的,不过是用 Python 写的。
作为前端来说,可以基于 Deno(新一代 JavaScript 的运行时,类似 Node) 的内核来使用 jupyter。
如何搭建 Deno + jupyter 环境:
-
在 vscode 中安装 jupyter 插件
-
安装 Deno
curl -fsSL https://deno.land/x/install/install.sh | sh
- 安装 jupyter
deno jupyter --install
- 打开 vscode,新建一个 jupyter(.ipynb) 文件,选择 Deno 内核即可使用
我平常研发 AI 应用的时候,需要频繁调试提示词,同时还要对 AI 输出的数据进行很多逻辑判断,所以也比较喜欢用 Deno 脚本,结合 jupyter 来调试提示词。
最大的好处就是:借助 jupyter 的逐步调试能力,可以很方便地对 AI 输出的数据进行多次逻辑判断和数据处理。
其它好处:POC 验证完成了之后,可以让 AI 基于 Deno 和 jupyter 脚本,转换为正式的生产环境的代码,因为对于前端来说 Deno 的语法和 Node 的语法几乎完全一致。
参考资料
本次分享就先到这~
-
觉得有用的话,帮忙点个赞、也可以转发给更多的朋友看到。
-
如果你对本文有任何疑问,欢迎在评论区留言交流。
-
《AI 赋能前端研发从 0 ~ 1》电子书:ai.iamlv.cn/