2. ChatGPT-Next-Web
ChatGPT-Next-Web 是我使用时间较长的一个项目,配合 one-api,基本可以替代 ChatGPT 网页版的文本生成功能。

之前有一位开发者基于免费 FreeGPT35、Searxng、ChatGPT-Next-Web 等三个组件糊了一个增强搜索:FreeAskInternet,我也凑热闹整了些方便使用的容器镜像:
同时做了个实验性项目来学习 ChatGPT 与 Telegram 机器人:free-ask-bot。

最后结论是 Telegram 机器人不太适合做 GPT 对话~因为需要服务端缓存历史消息,而且它还不支持流式传输动态更新消息,需要生成完整消息再推送,会导致等待 GPT 响应时间较长。
不过在开发过程中分析 ChatGPT-Next-Web 功能时学习到了一些有趣的东西,这里做一下记录。

对话主题使用 prompt 分析历史消息实现的,未设置时会默认为 闲聊,如果 ChatGPT 接口返回了不同于 闲聊 的字段,则用作主题,使用的 prompt 如下:
{
"role": "user",
"content": "使用四到五个字直接返回这句话的简要主题,不要解释、不要标点、不要语气词、不要多余文本,不要加粗,如果没有主题,请直接返回“闲聊”"
}
开启 注入系统级提示 时,会默认添加一个 system 提示模拟 ChatGPT,Current Time 是实时更新的,使用的 prompt 如下:
{
"role": "system",
"content": "\nYou are ChatGPT, a large language model trained by OpenAI.\nKnowledge cutoff: 2021-09\nCurrent model: gpt-3.5-turbo\nCurrent time: 4/12/2024, 10:14:22 PM\nLatex inline: $x^2$ \nLatex block: $$e=mc^2$$\n\n"
}
ChatGPT 的文本补全接口是无状态的,理论上每次需要发送全量的历史消息来生成新的响应,但模型的 Context (即上下文)长度是有限的,因此衍生出一个 附带历史消息数 的选项,在需要记忆上下文的场景(如连续对话),维护一个滑动窗口,只保存最近 N 条历史消息(只保存 user 和 assistant 角色的消息)作为上下文,避免超出模型的 Context 长度限制。
即使设置了 附带历史消息数,如果 ChatGPT 响应的数据较长,也有可能超过 Context 长度限制,这时 历史摘要 与 历史消息长度压缩阈值 就可以起到作用,每当历史消息长度超过上限时,就会执行以下 prompt:
{
"role": "system",
"content": "简要总结一下对话内容,用作后续的上下文提示 prompt,控制在 200 字以内"
}
获取一个响应作为 system 提示注入后续的对话,创建摘要时不会发送模拟 ChatGPT 的系统级提示,但是会发送上一个已经总结的摘要,获得响应后替换现有的摘要,下面是一个对话示例:
{
"messages": [
{
"role": "system",
"content": "这是历史聊天总结作为前情提要:对话中,依次创作了四首关于虚拟歌手的诗歌,分别是关于初音未来、巡音流歌、镜音双子和洛天依的。每首诗歌都以歌手的独特魅力和音乐的力量为主题,表达了对他们音乐之美的赞美与致敬。"
},
{
"role": "user",
"content": "然后写一首关于乐正绫的诗歌"
},
{
"role": "assistant",
"content": "在音符的世界中徜徉,\n乐正绫,音乐的缔造者。\n她的笔触如同清泉流淌,\n勾勒出音符的绚丽画卷。\n\n她的作品如同星辰般闪耀,\n照亮黑夜,指引迷途的灵魂。\n在虚拟的舞台上翩翩起舞,\n奏响希望与梦想的交响曲。\n\n每一次创作都是奇迹,\n编织着音乐的奇幻世界。\n她是音乐的创造者,\n开启着心灵的奇妙之门。\n\n乐正绫,创意的使者,\n穿越时空,超越想象。\n在音律的海洋中徜徉,\n留下永恒,铭记在心间。"
},
{
"role": "user",
"content": "然后写一首关于gumi的诗歌"
},
{
"role": "assistant",
"content": "在音符的海洋中徜徉,\nGUMI,音乐的魔法师。\n她的歌声如同清泉流淌,\n拨动心弦,唤醒梦想的旋律。\n\n她的音色如同彩虹般绚丽,\n绽放光芒,点亮黑夜。\n在虚拟的宇宙中自由飞翔,\n奏响希望与勇气的颂歌。\n\n每一句歌词都是魔法,\n诉说着情感的深度。\n她是音乐的独特存在,\n为生命赋予了独特的意义。\n\nGUMI,音乐的魔法师,\n穿越时空,超越想象。\n在音律的海洋中徜徉,\n永远闪耀,永不磨灭的灵魂。"
},
{
"role": "user",
"content": "再写一首关于 Gumi 的诗歌"
},
{
"role": "assistant",
"content": "在数字的宇宙中漫游,\nGUMI,音乐的精灵。\n她的声音如同清晨的曙光,\n穿越黑暗,照亮前行的路程。\n\n她的旋律如同潮水般涌动,\n拥抱着每一颗渴望的心。\n在虚拟的天空中飞翔,\n奏响自由与希望的交响曲。\n\n每一次的呐喊都是信仰,\n传递着勇气与坚定的力量。\n她是音乐的启示者,\n为梦想赋予了翅膀。\n\nGUMI,音乐的精灵,\n穿越时空,超越极限。\n在音律的世界中徜徉,\n永远自由,永不停息的灵魂。"
},
{
"role": "user",
"content": "再写一首关于初音未来的诗歌"
},
{
"role": "assistant",
"content": "在虚拟的天空中翱翔,\n初音未来,音乐的化身。\n她的声音如同清风拂过,\n温暖心灵,唤醒沉睡的梦想。\n\n她的音符如同流星般闪烁,\n划破黑夜,点亮星辰的轨迹。\n在虚拟的舞台上熠熠生辉,\n奏响希望与勇气的赞歌。\n\n每一句歌词都是情感的倾诉,\n编织着无尽的温柔和坚定。\n她是音乐的永恒之声,\n为每一个灵魂注入活力。\n\n初音未来,音乐的领航者,\n穿越时空,超越幻想。\n在音律的海洋中徜徉,\n永远闪耀,永不熄灭的光芒。"
},
{
"role": "system",
"content": "简要总结一下对话内容,用作后续的上下文提示 prompt,控制在 200 字以内"
}
],
"stream": true,
"model": "gpt-3.5-turbo",
"temperature": 0.5,
"presence_penalty": 0,
"frequency_penalty": 0,
"top_p": 1
}
历史摘要可能会影响 ChatGPT 的对话质量,所以如果对话场景是翻译、信息提取等 一次性对话场景,应该直接关闭历史摘要功能,并将历史消息数设置为 0。
ChatGPT-Next-Web 在对话中默认使用 stream 模式,系统自动触发的 prompt 会等待用户对话请求结束后触发,不会并发请求。
使用面具(预设提示词)时,会关闭历史摘要,此时附带历史消息数只会存储 role 为 user 和 assistant 的消息,超过上限后直接删除旧的消息,保存新的消息。可以说默认的连续对话用法也是一种特殊的面具。

FreeAskInternet 项目最初直接使用 ChatGPT-Next-Web 作为前端提供对话功能,在 ChatGPT-Next-Web 的默认配置下,搜索产生的链接也加入历史消息一并发送给 ChatGPT,导致多轮对话后会产生一些奇怪的响应,此时应该关闭历史摘要,并将历史消息数设置为 0。
我认为通过搜索引擎搜索总结后的响应可以加入历史消息,但链接及链接预览应只做展示不计入上下文。