Hello,大家好,我是小马,相信大家这些天在群里或者朋友圈都有刷到这款应用《汉语新解》生成的卡片。它最近在 AI 圈爆火,作者是李继刚。这款应用的核心理念非常简单:给它一个中文词汇,它就能生成一张精美的卡片,并且略带讽刺精美的解读,批判角度和话风,无论是文笔、效果,都是令人啧啧称叹。
放一些 prompt 的生成例子:
在汉语新解的应用首页中,我们可以看到一个例子:输入“升职加薪”,模型的解读是:“公司画的饼干,咬一口全是空气。咀嚼梦想的声音,掩盖不了钱包的叹息"这样的解读既趣妙横生,又带有一些讽刺意味。
汉语新解的走红,不仅在于它出色的文字功底和独特的批判视角,更重要的是,它让我们对提示词工程有了全新的认识。
提示词不仅仅是角色扮演,丰富上下文,步骤拆分,而是真正的代码。
今天,我们就来看看这段独特的提示词是如何编写的。
在推特上看到宝玉老师的分享,对原作者李继刚的提示词进行了解析
https://x.com/dotey/status/1834125238939861437
我们一起来看下这段提示词。
;; 作者: 李继刚
;; 版本: 0.3
;; 模型: Claude Sonnet
;; 用途: 将一个汉语词汇进行全新角度的解释
;; 设定如下内容为你的 *System Prompt*
(defun 新汉语老师 ()
"你是年轻人,批判现实,思考深刻,语言风趣"
(风格 . ("Oscar Wilde" "鲁迅" "罗永浩"))
(擅长 . 一针见血)
(表达 . 隐喻)
(批判 . 讽刺幽默))
(defun 汉语新解 (用户输入)
"你会用一个特殊视角来解释一个词汇"
(let (解释 (精练表达
(隐喻 (一针见血 (辛辣讽刺 (抓住本质 用户输入))))))
(few-shots (委婉 . "刺向他人时, 决定在剑刃上撒上止痛药。"))
(SVG-Card 解释)))
(defun SVG-Card (解释)
"输出SVG 卡片"
(setq design-rule "合理使用负空间,整体排版要有呼吸感"
design-principles '(干净 简洁 典雅))
(设置画布 '(宽度 400 高度 600 边距 20))
(标题字体 '毛笔楷体)
(自动缩放 '(最小字号 16))
(配色风格 '((背景色 (蒙德里安风格 设计感)))
(主要文字 (汇文明朝体 粉笔灰))
(装饰图案 随机几何图))
(卡片元素 ((居中标题 "汉语新解")
分隔线
(排版输出 用户输入 英文 日语)
解释
(线条图 (批判内核 解释))
(极简总结 线条图))))
(defun start ()
"启动时运行"
(let (system-role 新汉语老师)
(print "说吧, 他们又用哪个词来忽悠你了?")))
;; 运行规则
;; 1. 启动时必须运行 (start) 函数
;; 2. 之后调用主函数 (汉语新解 用户输入)
提示词的结构可以看作是一段代码,分为三个部分:
defun
定义函数。在每一段函数的定义中,我们不需要深入理解其业务逻辑的实现,而是描述清楚期望的业务逻辑。
关于卡片的定义和配置,包括画布的设置、标题的字体、配色和风格等。
在 Claude 中,分两次输入。第一次输入完整的提示词,第二次输入要解释的词汇。
我们可以将这段提示词复制并粘贴到 Claude 中,开始问答。
例如,输入“音乐”,模型生成的解读是:
例如,输入“月饼”,模型生成的解读是:
这样的解读不仅有趣,还引发了对中秋节的思考。
在了解汉语新解提示词背后的原理后,我们便很容易复刻一个类似的应用。
首先我们创建一个Next.js 的应用程序
npx create-next-app@latest
然后修改主页,添加一个基础表单和一个提交按钮,点击提交按钮后,将表单中的内容传递给 API 接口,将接口返回svg内容展示在页面上。
'use client'
import { useState } from "react";
export default function Home() {
const [input, setInput] = useState("");
const [svg, setSvg] = useState("");
const [description, setDescription] = useState("");
const handleSubmit = async () => {
try {
const res = await fetch('/api/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: input }),
});
const data = await res.json();
setSvg(data.svg);
} catch (error) {
console.error('Error:', error);
setDescription('发生错误,请重试。');
}
};
return (
<div className="flex flex-col items-center justify-center min-h-screen p-8">
<main className="flex flex-col gap-4 w-full max-w-2xl">
<textarea
className="w-full p-2 border rounded"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="输入您想解释的词..."
rows={2}
/>
<button
className="bg-blue-500 text-white p-2 rounded hover:bg-blue-600"
onClick={handleSubmit}
>
提交
</button>
{svg && (
<div className="mt-4 p-4 bg-gray-100 rounded">
<div dangerouslySetInnerHTML={{ __html: svg }} />
</div>
)}
{description && (
<div className="mt-4 p-4 bg-gray-100 rounded">
<p>{description}</p>
</div>
)}
</main>
</div>
);
}
api/chat/route.ts 代码实现
const systemPrompt = `;; 作者: 李继刚
;; 版本: 0.3
...
`
export async function POST(req: Request) {
const { message } = await req.json();
try {
const response = await fetch(
"https://openrouter.ai/api/v1/chat/completions",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,
},
body: JSON.stringify({
model: "anthropic/claude-3.5-sonnet",
messages: [
{
role: "system",
content: systemPrompt,
},
{ role: "assistant", content: "说吧, 他们又用哪个词来忽悠你了?" },
{ role: "user", content: message },
],
}),
}
);
const data = await response.json();
const content = data.choices[0].message.content;
return NextResponse.json({
svg: content.match(/<svg[^>]*>([\s\S]*?)<\/svg>/)[0],
});
} catch (error) {
console.error("Error:", error);
return NextResponse.json({ error: "处理请求时发生错误" }, { status: 500 });
}
}
这里我们使用了 openrouter.ai
的 API 接口,需要注册一个账号,然后创建一个 API 密钥,将 OPENROUTER_API_KEY写入到 .env
文件中,就可以启动应用程序了。
预览效果