【第二周】RAG与Agent实战04:OpenAI库附带历史消息调用模型

在构建智能对话系统(Chatbot)、RAG(检索增强生成)或 Agent(智能体) 时,上下文记忆(Context Memory)

是核心能力之一。模型本身是无状态的,它不知道您上一句说了什么,除非您显式地将历史对话记录传递给它们。

OpenAI 兼容接口(如阿里云 DashScope)的 messages

参数设计得非常巧妙,它是一个列表(List),天然支持传入多条历史消息。本文将演示如何通过维护这个列表,实现具备"记忆力"的多轮对话。

🧠 为什么需要历史消息?

大语言模型(LLM)本质上是基于概率预测下一个 token 的。如果没有历史信息,每一次请求对模型来说都是"初次见面"。

通过传入历史消息,我们可以实现:

  1. 指代消解:用户说"它多少钱?",模型知道"它"指的是上一轮提到的商品。
  2. 逻辑推理:用户先说"A有2个苹果",后说"B有3个苹果",最后问"一共几个?",模型能结合前文计算。
  3. 风格一致性:保持对话的人设和语气贯穿始终。

💡 核心机制:Messages 列表结构

messages 参数是一个字典列表,每个字典代表一条消息,包含 role(角色)和 content(内容)。

三种关键角色

角色 (Role) 作用 示例场景
system 系统指令 。设定助手的行为准则、人设、回答风格。通常放在列表最开头,权重最高。 "你是一个数学专家,回答要简洁。"
user 用户输入。代表用户的提问、指令或提供的信息。 "小明有2条狗。"
assistant 助手回复。代表模型之前的回答。将其回填到列表中,相当于告诉模型:"刚才我是这么回答的"。 "好的,记住了。"

📌 关键点 :要实现多轮对话,只需将之前的 user 提问和 assistant 回答按时间顺序依次加入 messages 列表即可。


📝 完整代码示例:多轮对话与逻辑推理

以下代码演示了一个经典的"累加推理"场景。我们将多轮对话历史硬编码在列表中,观察模型如何结合上下文回答问题。

python 复制代码
import os
from openai import OpenAI

# 1. 初始化客户端
# 建议将 API Key 放入环境变量,避免硬编码在代码中
client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"), 
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 2. 构建包含历史上下文的 messages 列表
# 注意:这里模拟了三轮对话的历史记录
messages_history = [
    # 第一轮:设定人设
    {"role": "system", "content": "你是AI助理,回答很简洁。"},
    
    # 第二轮:用户陈述事实 A
    {"role": "user", "content": "小明有2条宠物狗"},
    {"role": "assistant", "content": "好的"},
    
    # 第三轮:用户陈述事实 B
    {"role": "user", "content": "小红有3条宠物猫"},
    {"role": "assistant", "content": "好的"},
    
    # 第四轮:基于前文的事实进行提问
    {"role": "user", "content": "共有多少条宠物狗?"} 
    # ⚠️ 注意:这里问的是"狗",模型需要忽略"猫"的信息
]

print("🤖 正在结合上下文思考...\n")

# 3. 调用模型 (开启流式输出以获得更好体验)
response = client.chat.completions.create(
    model="qwen-plus",      # 使用 qwen-plus 模型
    messages=messages_history, # 传入完整的对话历史
    stream=True             # 开启流式输出
)

# 4. 处理流式结果
try:
    for chunk in response:
        # 提取增量内容并打印
        if chunk.choices and len(chunk.choices) > 0 and chunk.choices[0].delta.content:
            print(chunk.choices[0].delta.content, end="", flush=True)
    print("\n\n✅ 回答结束")
except Exception as e:
    print(f"\n❌ 发生错误:{e}")

🔍 代码运行逻辑解析

  1. System Prompt:首先告诉模型"回答要简洁",这会影响后续所有输出的风格。
  2. History Injection
    • 用户说"小明有2条狗" -> 模型回"好的"。
    • 用户说"小红有3条猫" -> 模型回"好的"。
    • 关键点 :我们将这两组问答都放入了 messages 列表。
  3. Final Query :最后用户问"共有多少条宠物 ?"。
    • 模型会扫描整个列表。
    • 它发现第一条信息提到"2条狗"。
    • 它发现第二条信息提到"3条"(与问题无关,忽略)。
    • 结论:模型会回答"2条"。

🎯 测试建议:您可以尝试修改最后一句提问为"共有多少只宠物?",模型应该会回答"5只"(2狗+3猫),证明它真正理解了上下文。


🔄 进阶:如何在程序中动态维护历史消息?

上面的例子是静态的(写死的列表)。在实际的聊天机器人应用中,我们需要动态地往列表里添加消息。

简易聊天循环示例

python 复制代码
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 初始化历史列表,只包含 System Prompt
conversation_history = [
    {"role": "system", "content": "你是一个乐于助人的AI助手。"}
]

print("💬 开始聊天 (输入 'quit' 退出):")

while True:
    # 1. 获取用户输入
    user_input = input("👤 你: ")
    if user_input.lower() == 'quit':
        break
    
    # 2. 将用户输入加入历史记录
    conversation_history.append({"role": "user", "content": user_input})
    
    # 3. 调用模型
    try:
        response = client.chat.completions.create(
            model="qwen-plus",
            messages=conversation_history, # 发送包含最新问题的完整历史
            stream=True
        )
        
        print("🤖 AI: ", end="", flush=True)
        full_response = ""
        
        # 4. 流式接收并打印
        for chunk in response:
            if chunk.choices and len(chunk.choices) > 0 and chunk.choices[0].delta.content:
                content = chunk.choices[0].delta.content
                print(content, end="", flush=True)
                full_response += content
        
        print("\n") # 换行
        
        # 5. 【重要】将模型的回答也加入历史记录,以便下一轮对话能引用
        conversation_history.append({"role": "assistant", "content": full_response})
        
    except Exception as e:
        print(f"\n❌ 出错啦:{e}")

动态维护的关键点

  1. Append User : 每次用户说话后,立即 history.append({"role": "user", ...})
  2. Call Model : 发送整个 history 列表。
  3. Append Assistant : 收到模型回复后,必须 history.append({"role": "assistant", ...})如果不做这一步,模型就会"失忆",不知道刚才自己说了什么。

⚠️ 注意事项与最佳实践

1. Token 长度限制

messages 列表不能无限增长。每个模型都有最大上下文窗口(Context Window),例如 8k, 32k, 128k tokens。

  • 风险:如果列表太长,超过限制,API 会报错。
  • 对策 :当对话过长时,需要实施滑动窗口策略 (只保留最近 N 轮对话)或摘要策略(用模型总结之前的对话)。

2. System Prompt 的位置

虽然大多数模型对位置不敏感,但最佳实践是将 system 角色始终放在列表的第一个位置,以确保指令的全局有效性。

3. 数据清洗

在将用户输入加入 messages 之前,最好进行简单的清洗(如去除多余空白、过滤敏感词),以保证上下文质量。


🚀 总结

通过灵活运用 messages 列表,我们可以赋予无状态的大模型以"记忆"。

  • 基础用法 :按顺序填入 system, user, assistant 消息。
  • 核心价值:实现指代理解、逻辑推理和人设保持。
  • 工程落地 :在代码中使用列表 append 操作动态维护对话历史。

掌握这一技术,是开发 RAG 系统和自主 Agent 的基石。下一步,您可以尝试结合向量数据库,将外部知识库作为"长期记忆"注入到 messages 中!

相关推荐
deephub2 小时前
多智能体系统的三种编排模式:Supervisor、Pipeline 与 Swarm
人工智能·python·大语言模型·agent
Lw中2 小时前
提示词效果不稳定?
人工智能·rag·大模型应用基础
大模型真好玩2 小时前
一文详解2026年技术圈最火概念——Agent Engineering智能体工程
人工智能·langchain·agent
Jackson@ML4 小时前
自然语言处理概要
人工智能·自然语言处理·nlp
咚咚王者4 小时前
人工智能之语言领域 自然语言处理 第十一章 注意力机制
人工智能·自然语言处理
xier_ran5 小时前
【第二周】RAG与Agent实战07:提示词优化案例_金融信息抽取
自然语言处理·prompt·agent·rag
V搜xhliang02465 小时前
开发环境搭建(Ubuntu+ROS2+Isaac Sim)
大数据·人工智能·深度学习·机器学习·自然语言处理·机器人
熊猫钓鱼>_>6 小时前
使用阿里云轻量应用服务器OpenClaw丝滑接入飞书打造智能群聊总结助手
人工智能·阿里云·云计算·飞书·agent·skill·openclaw
闽农13 小时前
Cursor taking longer than expected 问题这样解决
agent·ai编程·cursor·taking longer