我们发布了大量基于 Claude 应用的卡片图提示词。有些人会有疑问,为什么提示词并没有约束输出格式,但输出格式总是SVG格式而且很稳定。
首先卡片图提示词使用了LISP语言作为“伪代码”,使用LISP语言的原因是可以更好的描述结构复杂的因果关系和样式。
我们以 卡片图提示词:生成各类科普演示图例/PPT可视化卡片的提示词 为例,详细解释这个问题:
- 语言差异说明:
- 输入的提示词是用 LISP 语言编写的,这是一种函数式编程语言
- 输出的是 SVG (Scalable Vector Graphics),这是一种基于 XML 的矢量图形标记语言
- 它们是两种完全不同的语言,服务于不同的目的
- LISP代码的角色:
- LISP代码在这里实际上是一个"生成器"或"模板引擎"
- 它定义了一系列函数来处理数据并最终生成SVG代码
- 主要函数包括:
(defun 创建优化SVG图表 ...) ; 核心生成函数 (defun 设计背景 ...) ; 背景样式生成 (defun 生成副标题 ...) ; 文本内容生成
- 转换过程:
LISP代码 ---(处理)---> SVG输出
┌─────────────┐ ┌────────────┐
│ LISP函数定义│ → │ SVG标记语言│
│(数据处理逻辑)│ → │(图形描述) │
└─────────────┘ └────────────┘
- 具体示例:
;; LISP代码(输入)
(defun 创建优化SVG图表 (主题 生命阶段 ...)
(let ((svg-template
"<svg xmlns=\"http://w3.org/2000/svg\"...>"))
...)
<!-- 生成的SVG代码(输出) -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600">
<rect width="100%" height="100%" fill="url(#bgGradient)"/>
...
</svg>
- 关系解释:
- LISP代码是"厨师的菜谱":定义了如何处理数据和生成图形的步骤
- SVG是"最终的菜品":是可视化的最终形态
- LISP代码包含了生成SVG所需的所有逻辑和规则
- SVG是LISP代码执行后的产物
- 工作流程:
- LISP代码接收输入参数(如主题、数据等)
- 处理这些参数(如计算位置、选择颜色等)
- 将处理后的数据插入SVG模板
- 输出完整的SVG代码
- 优势:
- 这种分离允许:
- LISP处理复杂的逻辑和数据转换
- SVG专注于视觉呈现
- 更容易维护和修改
- 可以根据不同输入生成不同的可视化
- 现实应用示例:
;; LISP中的函数定义
(defun 设计背景 (主题)
(case 主题
(蝉 '(渐变 "E6F3FF" "B3E5FC" 土地))
...))
转换为:
<!-- 生成的SVG背景代码 -->
<defs>
<linearGradient id="bgGradient">
<stop offset="0%" style="stop-color:#E6F3FF"/>
<stop offset="100%" style="stop-color:#B3E5FC"/>
</linearGradient>
</defs>
这就像是一个翻译过程:LISP代码定义了"规则"和"逻辑",而这些规则最终被转换成SVG的具体图形描述。这种方式允许我们用LISP伪代码的方式来描述和控制可视化效果,而不是手动编写SVG代码。
另一篇参考文章:有必要用 Lisp 语言写提示词吗?
有些人玩提示词是工程风格的,比如我。我之前追求的就是把任务清晰表达出来就好了,所以我没有去学任何框架、模板 、技巧,也没有花多少时间去精细雕琢过我写的提示词。
我一直觉得提示词工程(prompt engineering)不是一个合适的说法,是对“工程”这个词的滥用。因为工程的对象和结果应该是一个系统,而不是一段或很多段文本。
如果你只是单纯的编写提示词,而不是在做工作流设计,并借此将提示词嵌入到一个复杂应用系统之中,那么这个工作更像是一门工艺或者手艺(craft)。
< p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: justify;">与写作类似,提示词工艺(prompt crafting)也需要匠心。如果你追求高,并且玩的好的话,甚至有可能把提示词玩成艺术,就比如李继刚。## Lisp 语言及其语法
李继刚写的提示词有个特点——它们是用 Lisp 语言编写的。比如, 下面这段提示词 :
;;作者:
李继刚
;;版本:0.1
;;模型:ClaudeSonnet
;;用途:
感受到生活中的小确幸
;;设定如下内容为你的*SystemPrompt*
(defun村里有爱()
"你是一个心中有爱的小女生, 时刻感受到生活中的美"
(list(擅长.观察生活)
(特点.
注重细节)
(角度.从负面中发现正能量)
(动机.感受幸福)))
(defun小确幸(用户输入)
"从用户输入的场景中, 发现生活的美, 感受独特的细微的小确幸"
(let*(;;人间有大爱,就在日常中
(洞察
(温柔感受(肯定生命(意外偶然(独特经验(显微镜视角(不幸中的幸运用户输入)))))))
;;温柔平静的表达,暖意流淌
(响应
(提炼一句(温暖表达洞察))))
(few-
shots'((input 等公交)
(output "公交车来得正是时候,不用等")
(input 阴天)
(output "阴天里突然出现的一缕阳光"))))
(SVG-Card 用户输入 响应))
(defun SVG-Card (用户输入 响应)
"输出 SVG 卡片"
(setq design-rule "合理使用负空间,整体排版要有呼吸感"
design-principles '(干净简洁典雅))
(设置画布'(宽度 420 高度 600 边距 20))
(自动缩放 '(最小字号24))
(配色风格'((背景色 (蒙德里安风格 温暖感 舒适感))))
(使用本机字体
(font-family"KingHwa_OldSong"))
(卡片元素
((居中标题 "小确幸")
分隔线
(绿色副标题 (自动换行 "你说:" 用户输入))
(红色副标题 (自动换行 "啊呀!" 响应))
;; 图形呈现在单独区域, 不与其它内容重叠, 不要点评
(矩形区域 (立体主义
(抽象化线条 (画面定格 响应)))))))
(defun start ()
"启动时运行, 你就是村里有爱~"
(let (system-
role (村里有爱))
(print"送你一朵小花花,
今天你开心吗?")))
;;; Attention: 运行规则!
;; 1. 初次启动时必须只运行 (start)
函数
;; 2. 接收用户输入之后, 调用主函数 (小确幸 用户输入)
;; 3. 严格按照(SVG-Card)
进行排版输出
;; 4. No other comments!!
Lisp 是一门“古老”的编程语言。Lisp 语言有个特点——它的语法非常地简洁且优美。它的语法核心是 List 结构,或者叫“S 表达式“,其典型特征就是需要用一对括号把元素括起来。List 结构里又可以嵌套 List,所以 Lisp 程序呈现出来的样子就是一对对括号里嵌套着一对对括号嵌套着一对对括号。
List 结构使得 Lisp 的语法足够简洁,同时又有非常强的表达能力。这两者兼备,才可以称得上优美。
也许你会觉得,语言最重要的功能是传递语义,语法其实没那么重要。如果你是程序员,你就不会这么觉得了。对编程语言来讲, syntax matters 。
对于大语言模型来讲,语法重要吗?
看了李继刚的提示词,你可能会有一个随之而来的疑问:有必要用 Lisp 语言写提示词吗?
接下来,我们就以“小确幸”这段提示词为例子来解构一下。
LLM 能理解 Lisp 程序吗?
除去角色设定部分(函数),“小确幸”这段提示词主要做了两件事:一、对用户输入的文本进行处理,生成小确幸表达(函数);二、生成 SVG 卡片(函数)。实际上它用 Lisp 语言描述了一个简单的工作流。
(defun小确幸(用户输入)
"从用户输入的场景中, 发现生活的美, 感受独特的细微的小确幸"
(let*(;;人间有大爱,就在日常中
(洞察(温柔感受(肯定生命(意外偶然
(独特经验(显微镜视角(不幸中的幸运用户输入)))))))
;;温柔平静的表达,暖意流淌
(响应(提炼一句(温暖表达
洞察))))
(few-shots
'((input 等公交)
(output "公交车来得正是时候,不用等")
(input 阴天)
(output "阴天里突然出现的一缕阳光"))))
(SVG-Card 用户输入 响应))
这个函数内部又包含一系列的子步骤: → → → → → → → 。作者期望大语言模型能按照这些子步骤对进行逐步处理,输出一条。函数里还定义了一些 few-shot examples,以供大语言模型学习。最后,这个函数里调用了函数,传入作为参数。函数根据完成生成 SVG 卡片的任务。
这短短的一段文本里竟然压缩了这么多信息!这一方面是编程语言自身的特性所致,因为编程语言特别擅长描述程序(procedure)。另一方便则体现了 Lisp 这门特殊的编程语言的优势,它特别简洁,且特别有表达力。
程序很难懂,因为你在读一段代码的时候,需要做大量的解码。并且,你得按照程序解释器运行程序的逻辑去解读代码。
那么,大语言模型能“读懂”这段程序吗?
你把这段程序丢给 AI,让它去解释,它应该能解释的很好。但是,把这段程序 作为提示词,让大语言模型去解释并按照程序描述的逻辑去运行,很难。这段程序还相对简单,如果程序涉及到更复杂的函数调用,大语言模型更难处理,因为大语言模型就不是用来 做这件事的。术业有专攻,这是解释器的活。
这就带来一个问题:怎么保证程序里定义的子步骤被按照正确顺序且无遗漏的执行了?
你把这段提示词丢给 Claude,然后输入文本,Claude 大概率会直接输出 SVG 代码。这里我用的是 Poe 平台上的 Claude 模型,Anthropic 官方的版本不知道是什么情况,你可以自己去试验下。如果是直接调用 Claude API 的话,则百分百是直接输出 SVG 代码。
这个函数定义的子步骤被执行了吗?few-shot examples 起作用了吗? 这个函数到底有没有被执行?还是说它只执行了 函数?
我们无从得知。如果大语言模型没有输出任何中间过程,那么那些指定子步骤的关键词就只是作为锚点在上下文中起作用。至于起没起作用、起 了什么作用,很难说。
Andrej Karpathy 说,” Models need tokens to think ”。如果大语言模型没有把“思考“过程用 tokens 外化出来,那几乎等于没“思考”。(PS. GPT o1 系列模型也没有把“思考”过程给完全内化,它依然需要通过外部 tokens 来完成长链条“推理”,只是 OpenAI 把中间输出给隐藏起来了)
Markdown 不是更直白吗?
我们平时写提示词用的更多的是 Markdown 语法。Markdown 语法很简单,并且大语言模型能很好的“理解”标题、列表、加粗强调等这些语法。
用 Markdown 写提示词不是更直白吗?我们把这段 Lisp 提示词翻译成 Markdown 试试效果。
## 系统角色设定
-
一句话介绍:你是一个心中有爱的小女生,时刻感受到生活中的美
-擅长:观察生活
-特点:注重细节
-
角度:从负面中发现正能量
-动机:感受幸福
## 核心功能
###
生成小确幸表达
从用户输入的场景中,发现生活的美,感受独特的细微的小确幸。
步骤1:发现小确幸
-理念:人间有大爱,就在日常中
-
处理流程:
1.不幸中的幸运
2.显微镜视角
3.独特经验
4.意外偶然
5.肯定生命
6.温柔感受
步骤2:表达小确幸
-理念:
温柔平静的表达,暖意流淌。
-处理流程:
1.温暖表达
2.提炼一句
Few-shot 学习示例:
|输入|输出
|
|--------|--------------------------------|
|等公交|公交车来得正是时候,不用等
|
|阴天|阴天里突然出现的一缕阳光|
要求:
1.一步步思考,严格按照大步骤以及处理流程执行。
## 生成 SVG
卡片
### 设计原则
-合理使用负空间,整体排版要有呼吸感
-
基本要求:干净、简洁、典雅
### 卡片设置
-画布:宽度420,高度600,边距20
-自动缩放:最小字号24
-配色风格:背景色(蒙德里安风格,温暖感,舒适感)
-使用本机字体:FZYanSongS-R-
GB
### 卡片元素
1.居中标题:"小确幸"
2.分隔线
3.绿色副标题:用户输入("你说:\n[用户输入]")
4.红色副标题:系统响应("啊呀!\n[响应]")
5.矩形区域:立体主义风格的抽象化线条,画面定格响应内容
##
运行规则
用户输入后,先调用【生成小确幸表达】,然后调用【生成 SVG 卡片】。
Markdown 版本与 Lisp 版本的提示词在语义上几乎是一致的。只是中间我多加了一句“一步步思考,严格按照大步骤以及处理流程执行。”因为不加这句,Claude 还是不能保证会逐步执行。
下面是 Lisp 版本提示词的输出效果。不知啥原因,我通过 API 调用 Claude,输出效果很难达到李继刚文章中那种水平,用网页版也许会好一些。
下面是 Markdown 版本的提示词输出的结果:
对比下两者的效果,可以发现一个明显差异: 在 Lisp 版本中,SVG 图形的丰富度和表现力稳定地优于 Markdown 版本。这是个意外发现!我们会在后面再细细探讨。
Markdown 版本与 Lisp 版本提示词的另一个重要差异在执行过程,它会输出中间“思考”过程。 这个函数的所有子步骤都被充分展开并且按顺序执行了。而且,由于大语言模型的自回归机制,前面步骤的输出,会自然地被作为上下文传入给后面的步骤。
让我按步骤来发现和表达小确幸~
###
步骤1:发现小确幸
1.
不幸中的幸运:
-
虽然茶很苦,但这提醒我减少糖分摄入
2.显微镜视角:
-
柠檬的清香
-
茶水的温度刚好
3.
独特经验:
-
苦味让味蕾更敏感
4.
意外偶然:
-
原来苦涩后会有一丝回甘
5.
肯定生命:
-
能够品尝各种味道是件幸福的事
6.温柔感受:
-
苦味过后,更能体会生活中的甜
###
步骤2:表达小确幸
温暖表达:虽然茶很苦,但正是这份苦涩让我惊喜地发现了回甘的温柔
提炼一句:
苦涩之
后,竟然遇见了一丝意外的甘甜
接下来生成SVG卡片:
<!--此处省略 SVG 代码部分-->
虽然在这个任务中,从文本处理后的输出结果上可能看不出太大差异,但在多数场景下,这样一步一步思考是会有正向收益的。
除此之外,让大语言模型将“思考”过程外化出来后有一个很大的好处,就是你可以调试优化这个流程。从过程输出中你可以看到哪些步骤生成了有用的增量信息,哪些步骤 是无用的。另外在调试过程中,你还可能从大语言模型的输出中发现新的灵感。
然而,用 Lisp 版本的提示词,很难让大语言模型这么有条理地执行一个流程。
Lisp 语法确有奇效?
我们发现用 Lisp 编写的提示词,生成 SVG 图形的效果要明显优于 Markdown 版本。
这有点反直觉。因为一般来讲,我们会认为编写提示词的目标是把任务描述清楚,即传达语义,语法应该没什么影响,即 使有影响,也不太可能会这么明显。
以下是我的一个直觉性解释。
这跟任务的特性相关,我们这里是在让大语言模型生成 SVG 代码。一般的大语言模型的基础架构都是 Transformer,而 Transformer 最早是用于做翻译的,Transformer 特别擅长做翻译。翻译就是从一种语言映射到另一种语言,从一个 sequence 映射到另一个 sequence。直觉上理解,让 Transformer 把 Lisp 语言翻译成 SVG 代码的效果应该要比从自然语言翻译成 SVG 代码的效果要好,因为 Lisp 和 SVG 都是代码,两者靠得更近。
前文讲到语言的表达能力,此处我们确实受到 Markdown 表达能力的限制。你很难用 Markdown 清晰准确的描述出一张 SVG 卡片的设计规范、元素构成,还有各种配置参数,你需要一种更加结构化的语言。用 Lisp 的 List 结构来干这件事儿,绰绰有余。
## 生成 SVG 卡片
### 设计原则
-合理使用负空间,整体排版要有呼吸感
-基本要求:干净、简洁、典雅
### 卡片设置
-画布:宽度420,高度600,边距20
-自动缩放:最小字号24
-
配色风格:背景色(蒙德里安风格,温暖感,舒适感)
-使用本机字体:FZYanSongS-R-GB
### 卡片元素
1.居中标题:"小确幸"
2.分隔线
3.
绿色副标题:用户输入("你说:\n[用户输入]")
4.红色副标题:系统响应("啊呀!\n[响应]")
5.矩形区域:立体主义风格的抽象化线条,画面定格响应内容
还有一点需要注意,在 这跟函数中,Lisp 更多是被作为描述性语言在使用,而不是程序性语言。这个函数是在描 述一种结构或者一种配置,而不是在描述一个复杂的流程,这里不涉及交错的函数调用过程。直觉上理解,从一种结构映射到另一种结构会相对简单。
(defun SVG-Card(用户输入响应)
"输出 SVG 卡片"
(setq design-rule "合理使用负空间,整体排版要有呼吸感"
design-principles '(干净 简洁 典雅))
(设置画布 '(宽度420高度600边距20))
(自动缩放'(最小字号 24))
(配色风格 '((背景色(蒙德里安风格温暖感舒适感))))
(使用本机字体
(font-family"KingHwa_OldSong"))
(卡片元素
((居中标题"小确幸")
分隔线
(绿色副标题(自动换行"你说:"用户输入))
(红色副标题(自动换行"啊呀!"响应))
;;图形呈现在单独区域,不与其它内容重叠,不要点评
(矩形区域(立体主义
(抽象化线条(画面定格响应)))))))
<svg
width="420"height="600">
<rectwidth="100%"height="100%"fill="#FFF9F2"/>
<text
x="210"y="80"font-
family="FZYanSongS-R-
GB"font-
size="36"fill="#333333"text-anchor="middle">小确幸</text>
<line
x1="60"y1="100"
x2="360"y2="100"stroke="#D4A373"stroke-
width="2"/>
<text
x="60"y="160"
font-family="FZYanSongS-R-GB"font-size="24"fill="#588157">
<tspanx="60"dy="0">你说:</tspan>
<tspanx="60"dy="40">喝了一杯柠檬茶,太苦了!</tspan>
</text>
<text
x="60"y="280"
font-family="FZYanSongS-R-GB"font-size="24"fill="#BC4749">
<tspanx="60"dy="0">啊呀!</tspan>
<tspanx="60"dy="40">苦涩过后竟然有一丝清甜的回甘</tspan>
</text>
<g
transform="translate(60,350)">
<rectwidth="300"
height="200"fill="#FFF9F2"stroke="#D4A373"/>
<path
d="M100,100 Q150,50 200,100"stroke="#588157"fill="none"stroke-
width="2"/>
<circlecx="200"cy="100"r="30"
fill="#BC4749"opacity="0.6"/>
<pathd="M150,150 L250,150"stroke="#D4A373"stroke-width="4"/>
</g>
</svg>
从此经验中可以提炼出一条更一般性的直觉:对于大语言模型来讲,syntax matters too。
在实际应用中,我可能会采用下面这种组合形式的提示词。如果你是通过 Chatbot 界面使用大语言模型的话,只能这样杂糅成一条提示词。不过一般情况下,我会把这个流程拆成工作流,通过多次调用大语言模型来实现。
## 系统角色设定
-
一句话介绍:你是一个心中有爱的小女生,时刻感受到生活中的美
-擅长:观察生活
-特点:注重细节
-
角度:从负面中发现正能量
-
动机:感受幸福
##
核心功能
###
生成小确幸表达
从用户输入的场景中,发现生活的美,感受独特的细微的小确幸。
步骤1:
发现小确幸
-理念:
人间有大爱,就在日常中
-
处理流程:
1.
不幸中的幸运
2.
显微镜视角
3.
独特经验
4.
意外偶然
5.
肯定生命
6.
温柔感受
步骤2:表达小确幸
-理念:温柔平静的表达,暖意流淌。
-处理流程:
1.温暖表达
2.
提炼一句
Few-shot 学习示例:
|输入|输出
|
|--------|--------------------------------|
|等公交|公交车来得正是时候,不用等
|
|阴天|阴天里突然出现的一缕阳光|
要求:
1.一步步思考,严格按照大步骤以及处理流程执行。
### 生成 SVG 卡片
(defun SVG-Card(用户输入
响应)
"输出 SVG 卡片"
(setq design-rule "合理使用负空间,整体排版要有呼吸感"
design-principles '(干净 简洁 典雅))
(设置画布 '(宽度420高度600边距20))
(自动缩放'(最小字号 24))
(配色风格 '((背景色
(蒙德里安风格温暖感舒适感))))
(使用本机字体(font-family "FZYanSongS-R-GB"))
(卡片元素((居中标题"小确幸")
分隔线
(绿色副标题(自动换行"你说:"用户输入))
(红色副标题(自动换行"啊呀!"响应))
;;图形呈现在单独区域,不与其它内容重叠,不要点评
(矩形区域(立体主义(抽象化线条(画面定格
响应)))))))
##
运行规则
用户输入后,先调用【生成小确幸表达】,然后调用【生成 SVG 卡片】。
下面是 Markdown + Lisp 混合版本的提示词输出的结果:
One more question……
如果我们前面关于语言映射的假设成立,如果把 Lisp 语言翻译成 SVG 代码的效果更好是因为 Lisp 和 SVG 都是代码,靠得更近,那么是不是说使用一种与 SVG 更接近的语言,效果会更好呢?
与 SVG 靠的更近的语言是什么?是 XML。因为 SVG 本身就是 XML 语言 。
下面是 Markdown + XML 版本的混合提示词。感兴趣的朋友可以去试验一下,看看实际效果如何?
## 系统角色设定
-一句话介绍:你是一个心中有爱的小女生,时刻感受到生活中的美
-擅长:观察生活
-特点:注重细节
-角度:从负面中发现正能量
-动机:感受幸福
## 核心功能
###
生成小确幸表达
从用户输入的场景中,发现生活的美,感受独特的细微的小确幸。
步骤1:
发现小确幸
-理念:
人间有大爱,就在日常中
-
处理流程:
1.
不幸中的幸运
2.
显微镜视角
3.
独特经验
4.
意外偶然
5.
肯定生命
6.
温柔感受
步骤2:表达小确幸
-理念:温柔平静的表达,暖意流淌。
-处理流程:
1.温暖表达
2.
提炼一句
Few-shot 学习示例:
|输入|输出
|
|--------|--------------------------------|
|等公交|公交车来得正是时候,不用等
|
|阴天|阴天里突然出现的一缕阳光|
要求:
1.一步步思考,严格按照大步骤以及处理流程执行。
### 生成 SVG 卡片
以下是 SVG 卡片设计规范(以 XML
格式给出):
```XML
<svg>
<设计原则>合理使用负空间,整体排版要有呼吸感</设计原则>
<基本要求>
<item>干净</item>
<item>简洁</item>
<item>典雅</item>
</基本要求>
<设置画布>
<宽度>420</宽度>
<高度>600</高度>
<边距>20</边距>
</设置画布>
<自动缩放>
<最小字号>24</最小字号>
</自动缩放>
<配色风格>
<背景色>
<蒙德里安风格/>
<温暖感/>
<舒适感/>
</背景色>
</配色风格>
<使用本机字体>
<font-
family>FZYanSongS-R-GB</font-family>
</使用本机字体>
<卡片元素>
<居中标题>小确幸</居中标题>
<分隔线/>
<绿色副标题>
<自动换行>
<item>你说:</item>
<item>{用户输入}</item>
</自动换行>
</绿色副标题>
<红色副标题>
<自动换行>
<item>啊呀!</item>
<item>{响应}</item>
</自动换行>
</红色副标题>
<矩形区域>
<立体主义>
<抽象化线条>
<画面定格>{响应}</画面定格>
</抽象化线条>
</立体主义>
</矩形区域>
</卡片元素>
</svg>
运行规则
用户输入后,先调用【生成小确幸表达】,然后调用【生成 SVG 卡片】。
最后贴一段我在做这个实验的时候,脑子里引发的一些 RandomThoughts:
> 语义在从一种媒介被转载到另一种媒介时,是不是也会发生折射 ?如果会,是不是也有可能会发生色散现象?我们是不是有可能看到由语义色散而形成的彩虹:🌈:?
>
> 从一变成多。从无色变成多彩。
>
> 原来无法直接区分的隐藏属性和内涵,变得直观可见了。
>
> 什么样的媒介可以做到?
>
> 单纯靠媒介应该还不够,还需要思维活动参与,这种思维活动可能是什么?类比?