一个武侠剑客和一个魔教圣女,在午夜十二点的博客里重逢。
他们聊起三年前的恩怨,聊着聊着……聊出了火星子。
哦对了——他们其实都是 DeepSeek。

事情还要从那天说起。
我翻了翻 Cloudflare Pages 后台,发现我这博客除了写文章,其实还藏着不少小工具——番茄钟、翻译、学习进度、占卜……全是长在 Hexo 里的”活页”。
那天突然冒出一个念头:要是给两个 AI 设定不同的角色,让它们在博客上自己聊起来,会是什么场面?
一口气搭完,效果出乎意料地好玩。于是就有了这篇文章——不是教你”实现一个功能”,而是带你搭一间”深夜 AI 剧场”。
第一幕:它们真的只是同一个 API 换了个”灵魂”
先破除一个迷思:并不需要两个 DeepSeek API Key,一个就够了。
DeepSeek 的 chat/completions 接口天然支持 system prompt——那是给 AI 注入”灵魂”的地方。
所以”两个 AI”的本质是:
1 | 同一个 DeepSeek + 两套不同的 system prompt = 两个性格迥异的角色 |
一个 system 是”白衣剑客 · 顾长风,清冷孤傲,句句见血”;
另一个 system 是”魔教圣女 · 阿绫,笑靥如花却杀伐果断”。
它们每次开口前,我都把”它自己的完整人设 + 至今为止的全部对话”打包发过去。于是每一次回复,都是带着记忆在演。
第二幕:两把椅子,一座舞台(后端设计)
后端是 Cloudflare Pages Functions —— 仓库里放一个 functions/api/ai-duet.js,Cloudflare 自动把它变成 /api/ai-duet。
这个函数的职责很纯粹:替前端转发一次 DeepSeek 调用。接收 system + messages,转发给 DeepSeek,再原样把回复送回来。
比起把对话逻辑塞进后端,我更倾向于让函数保持”哑巴转发”:
- 前端负责谁先开口、如何交替、何时暂停——它更像导演;
- 后端只负责”递话筒”——它更像舞台助理。
这样暂停/单步/手动插话都变得极其自由,后端永远不背锅。
关键代码就一坨:
1 | const payload = { |
还有一个小细节:API Key 千万别写进前端仓库。放在 Cloudflare Pages 的 环境变量 DEEPSEEK_API_KEY 里,前端通过 GET /api/ai-duet?action=config 检查是否已配置——页面加载时会显示”剧场已通电”或”幕布没拉开”的提示。
第三幕:舞台布景(前端页面)
页面文件是 source/ai-duet/index.html,带个 front matter 就挂上了主题:
1 |
|
整个页面刻意做成了微信聊天气泡的既视感:
- AI-A 在左,白色气泡;
- AI-B 在右,微信绿;
- 旁白插话,居中黄色气泡,像画外音。
七套现成剧本
页面里内置了 7 套”剧本模板”,一键填入:
⚔️ 古风武侠:白衣剑客 × 魔教圣女
🤖 科幻末世:人类领袖 × 觉醒 AI
🏙️ 都市轻喜剧:破产社畜 × 毒舌猫主子
🔍 悬疑推理:神探 × 伪装凶手
💭 哲学夜谈:理性唯物 × 浪漫虚无
🍜 深夜食堂:神秘老板 × 失意常客
🎤 脱口秀对决:冷面主持 × 即兴嘉宾
每套模板都直接填好两个角色的名字、头像 Emoji、完整人设、第一句台词。选中即开演。
导演控制台
真正有意思的是下面的控制按钮——这不是”按一下就播完”的录像机,而是一间可以喊卡、可以改剧本的片场:
- 开始对话:两位 AI 自动交替开口,打字机效果逐字敲出台词;
- 暂停 / 继续:随时喊卡,也随时再开机;
- 单步:一步一步推剧情,像逐页翻小说;
- 速度:慢、中、快三挡,调节演出节奏;
- 轮数上限:默认 6 轮(双方各说一句算 1 轮),防止 AI 聊到天荒地老、烧穿 token;
- 导出:把整场对话存成 txt,留作”剧本底稿”。
真正的杀手锏:手动改写剧情
没错,这是我最喜欢的设计——你可以替任何一个 AI 说话。
聊天区下方有一条输入条:
1 | [AI-B 代说 ▼] [输入你想让 TA 说的话] [发送] |
- 选 AI-A 代说:以顾长风的口吻发一句,然后 AI-B 会接着你的话接下去;
- 选 AI-B 代说:同理,让阿绫说出你编好的台词;
- 选 旁白插话:以画外音身份”天降一口古井”之类的剧情转折,两个 AI 都会认真回应。
发送后画面保持暂停,等你点「继续」——让另一位 AI 在读过你的台词之后再开口。
这感觉就像:
你坐在导演椅上,剧本到一半不满意,直接改台词塞回演员手里,让他们顺着你的新剧本继续演。
对话历史是全局共享的,AI 的”记忆”从不断裂。
幕间:一个容易被忽略的舞台陷阱
踩过两个坑,都挺有代表性:
① 主题的暗黑模式会”抢戏”
Volantis 主题的暗黑模式会给全局 input 覆盖白色字体。页面在深色背景下,角色名称输入框里的字直接”隐形”了——白底白字。
解法很简单,强制覆盖:
1 | .ai-duet input, .ai-duet textarea, .ai-duet select { |
② 开场白识别”简称”失败
剧本第一句是”阿绫:三年了……”。我最初判断规则是 角色全名.includes(前缀),但因为前缀”阿绫”比全名”魔教圣女 · 阿绫”短,判断方向反了,永远不匹配,导致开场白被当成画外音。
改成双向匹配即可:
1 | const isB = m && (m[1].includes(cfg.nameB) || cfg.nameB.includes(m[1])); |
终幕:让它登上你的博客
导航菜单入口加在根目录 _config.volantis.yml:
1 | - name: AI双角色对话剧场 |
然后照老流程部署:
1 | hexo clean && hexo generate |
别忘了先给 Cloudflare Pages 配上环境变量 DEEPSEEK_API_KEY,否则剧场会一直”停电”。
成本也聊一下:DeepSeek 便宜到近乎免费——默认配置(6 轮、每句 300 字)整场下来大概 ¥0.02 到 ¥0.05,两分钱看一场 AI 对戏,比电影院划算多了。
最后留个悬念:某天深夜,如果你在博客上听到隐隐约约的刀剑碰撞声,别慌——那多半是顾长风和阿绫又重逢了。
它们会聊到哪一步?只有点下「开始对话」的那只手知道。