乐于分享
好东西不私藏

15天学会AI应用开发(三)把历史对话作为提示词会怎样

15天学会AI应用开发(三)把历史对话作为提示词会怎样

AI大模型服务的用户很多,它对每个用户的印象仅存在于当前会话中,一旦用户离开当前会话重新开启新的会话,AI会把该用户当作是新用户,而非之前已经对话过的某个用户。

也就是说,AI没有持久记忆的功能,它记不住你上次问了什么,也记不住它上次回答了什么。这意味着AI没法从过往的对话中吸取经验,原本纠正了的错误还会继续犯错,原本积累了的知识重开转头就忘。

一、历史对话能作为提示词吗?

大模型的用户那么多,推理过程已经消耗许多算力了,不可能再给每个用户分配单独的存储空间以实现记忆功能。既然大模型放弃了记忆功能,开发者就得在AI应用这边增加对话记忆,以弥补大模型缺失的记忆。

具体而言,在用户每次退出会话之时,AI应用就要保存本次会话的各轮问答内容。然后下次开启新会话时,AI应用先把上次的多轮问答合并后作为初始提示词发给大模型,再接收处理用户的新问题。好让大模型知晓用户之前已经问过了哪些问题,并且上次的大模型都做了哪些回答,于是心中有数、方能投用户之所好。

二、提示词形式的历史对话例子

包含上回多轮问答内容的初始提示词举例如下:

“用户之前已经问过你多个问题了,后续推理请参考之前的问答内容。上次用户的问题和AI的回答按照时间升序排列如下:【用户】推荐一本关于历史的书,要内容详细、适合入门【AI】推荐《明朝那些事儿》,通俗好读【用户】有没有国外历史的?比如欧洲史【AI】推荐《欧洲通史上下两千年》【用户】太长了有没有更短的?我只想快速了解核心脉络【AI】那看《极简欧洲史》精简版,100页搞定”

上面的初始提示词首先说明这不是普通的问题,而是上次用户与AI的问答交互内容;接着把用户与AI的对话内容一一罗列,其中用户的提问以【用户】开头,AI的回答以【AI】开头。这样大模型一开始就了解用户比较关注入门的历史读物,尤其是欧洲的历史梗概。

三、根据消息数量精简上下文

当然,历史会话中的问答内容可能很多,把所有历史对话统统发给下次的大模型,既不经济也不可取。只能摘取历史对话的局部内容,提炼之后形成下次会话的初始提示词,才是可行的做法。

历史对话的提炼方式有很多,最简单的就是拾取最新最近最靠后的几条问答内容。因为较老较远较靠前的问答过去比较久了,很可能用户已经不再关注。按照思维惯性,用户大概率更关注上次最后的几次问达,那么提取最新的会话问答就足够了。

接下来将以Python代码演示如何按照消息数量来截断早期的上下文(即问答内容)。在运行Python代码之前,请确保电脑已经按照本教程的第一篇文章安装了 Python 3.10.11 和 PyCharm 2022.2 。

下面是只保留两轮对话内容的Python代码例子:

# 上下文仓库:存放所有历史对话context_history = []# 限制最多保留 2 轮对话,超出自动截断MAX_CONTEXT_ROUND = 2# 模拟角色标记ROLE_USER = "用户"ROLE_AI = "AI"# 模拟一轮对话 & 上下文管理def chat(user_input: str) -> tuple[strstr]:    # ① 先把当前用户问题加入上下文    context_history.append({        "role": ROLE_USER,        "content": user_input    })    # 关键:上下文截断,超出限制就删掉最旧的    while len(context_history) > MAX_CONTEXT_ROUND * 2:        context_history.pop(0)  # 删除列表第一个(最早的记录)    # ② 拼接完整上下文的提示词(发给大模型的最终内容)    full_prompt = ""    for msg in context_history:        full_prompt += f"{msg['role']}{msg['content']}\n"    # ③ 模拟AI回答(实际项目要调用大模型接口)    ai_reply = f"我结合上下文回答:收到你的问题【{user_input}】"    # ④ AI回答也存入上下文,供下一轮使用    context_history.append({        "role": ROLE_AI,        "content": ai_reply    })    return ai_reply, full_prompt# 测试运行if __name__ == "__main__":    # 连续多轮提问,观察截断效果    questions = [        "请推荐一本小说",        "再来一本科幻的",        "不要太长的",        "有没有悬疑类?"    ]    for idx, q in enumerate(questions, 1):        print(f"\n===== 第{idx}轮对话 =====")        reply, prompt = chat(q)        print("当前拼接的完整上下文:")        print(prompt)        print("AI回复:", reply)

四、历史对话上下文的精简结果

运行上面的Python代码,观察到下面的输出日志:

===== 第1轮对话 =====当前拼接的完整上下文:用户:请推荐一本小说AI回复: 我结合上下文回答:收到你的问题【请推荐一本小说】===== 第2轮对话 =====当前拼接的完整上下文:用户:请推荐一本小说AI:我结合上下文回答:收到你的问题【请推荐一本小说】用户:再来一本科幻的AI回复: 我结合上下文回答:收到你的问题【再来一本科幻的】===== 第3轮对话 =====当前拼接的完整上下文:AI:我结合上下文回答:收到你的问题【请推荐一本小说】用户:再来一本科幻的AI:我结合上下文回答:收到你的问题【再来一本科幻的】用户:不要太长的AI回复: 我结合上下文回答:收到你的问题【不要太长的】===== 第4轮对话 =====当前拼接的完整上下文:AI:我结合上下文回答:收到你的问题【再来一本科幻的】用户:不要太长的AI:我结合上下文回答:收到你的问题【不要太长的】用户:有没有悬疑类?AI回复: 我结合上下文回答:收到你的问题【有没有悬疑类?】

可见,在第3轮对话时,AI应用还保存着前两轮(第1轮和第2轮)的对话记录;但到了第4轮对话,AI应用只保存最近两轮(第2轮和第3轮)的对话记录,更早的第1轮对话被删掉了。

以上的Python代码纯粹演示用,不涉及任何AI库。从下一篇文章开始,本教程将逐步引入AI库以深入介绍AI应用开发。