调用大模型API上下文关联

一、引言

在使用大模型API(如OpenAI GPT系列、DeepSeek、Anthropic Claude等)时,我们经常需要实现多轮对话------让模型记住前几句聊天内容,而不是每次都"失忆"。很多新手会疑惑:明明只是调了一个接口,模型怎么知道我上一句问了啥?

答案是:大语言模型本身是无状态的,它并不会自动保存任何历史记录。要实现上下文关联,我们必须自己在调用API时,把完整的对话历史"喂"给它。

二、核心原理:把对话历史拼成数组发给模型

目前主流的大模型Chat接口(Chat Completions API)都接收一个叫 messages 的数组参数。这个数组里存放的就是从对话开始到现在的所有消息

比如你第一次问"我叫小明",第二次问"我叫什么名字",如果不传历史,模型根本不知道你是谁。正确的做法是:

  • 第一次请求时,messages 里只有:

    • {"role": "user", "content": "我叫小明"}
  • 模型回答后,我们把模型的回答这个新问题 都追加到 messages 中。

  • 第二次请求时,messages 变成了:

    • {"role": "user", "content": "我叫小明"}

    • {"role": "assistant", "content": "好的小明,记住啦!"}

    • {"role": "user", "content": "我叫什么名字?"}

模型看到完整的列表,自然就能"回忆"起之前说过的话,从而给出正确回答:"你叫小明"。

一句话总结:上下文就是你自己维护的一个消息列表,每次请求都全量发给模型。

三、三个重要角色

messages 数组中,每条消息都有 role 属性,主要包含以下三种角色:

role 含义 示例
system 系统提示词,用于设定AI的人设、规则,通常放在数组最前面。 "你是一个专业的菜谱顾问,回答要简洁。"
user 用户说的每一句话。 "西红柿炒鸡蛋怎么做?"
assistant 模型之前的回答,需要手动追加回数组。 "先准备西红柿、鸡蛋、葱......"

保持这些角色的正确顺序,就能模拟真实、连续的对话。

四、动手实现:Python简易多轮对话

下面是一个完整的、可运行的控制台多轮对话程序(使用 OpenAI 风格接口,DeepSeek 等也可通用)。

复制代码
import openai

# 初始化客户端(请替换为你的API Key和Base URL)
client = openai.OpenAI(
    api_key="your-api-key",
    base_url="https://api.deepseek.com"  # 如果使用DeepSeek,这样配置
)

# 初始化上下文,system prompt 作为基础设定
messages = [
    {"role": "system", "content": "你是一个幽默风趣的菜谱顾问。"}
]

print("开始聊天!输入 quit 退出。\n")

while True:
    user_input = input("你: ")
    if user_input.lower() == "quit":
        break

    # 1. 把用户新问题加入上下文
    messages.append({"role": "user", "content": user_input})

    # 2. 带着完整的 messages 数组请求模型
    response = client.chat.completions.create(
        model="deepseek-chat",  # 改成你用的模型名
        messages=messages       # 关键:每次都传入全部历史
    )

    # 3. 获取模型回复
    assistant_reply = response.choices[0].message.content
    print(f"AI: {assistant_reply}\n")

    # 4. 把模型的回答也加入上下文,下一秒它会记住
    messages.append({"role": "assistant", "content": assistant_reply})

运行效果:

复制代码
你: 我叫小明,今天想吃鱼。
AI: 小明你好!吃鱼好啊,聪明人爱吃鱼。你想怎么吃?清蒸、红烧还是水煮?

你: 你刚才叫我什么?
AI: 我叫你小明呀,你自己说的,我可记着呢!

五、实际工程中的4个关键优化

上述方案简单直接,但对话一长,messages 会越来越庞大。每次请求都把几千字的聊天记录发过去,成本高、速度慢,还可能超过模型的最大上下文限制。所以真正用在产品里时,我们一定会做下面的优化。

1. 限制轮次(保留最近 N 轮)

只保留最近的几十条消息,旧消息直接丢弃。

复制代码
MAX_HISTORY = 20  # 保留最近20条消息(10轮对话)
if len(messages) > MAX_HISTORY:
    # 保留 system prompt,裁剪掉最早的用户和助手消息
    messages = [messages[0]] + messages[-(MAX_HISTORY-1):]

2. 滑动窗口 + Token 计数

更精确的做法是按 token 数量裁剪。确保总 token 数不超过模型上限(如 4K、8K、128K)和你设定的阈值。

复制代码
import tiktoken  # OpenAI 的 token 计算库

def count_tokens(messages):
    encoding = tiktoken.encoding_for_model("gpt-4")
    text = "".join([m["content"] for m in messages])
    return len(encoding.encode(text))

MAX_TOKENS = 3000
while count_tokens(messages) > MAX_TOKENS:
    # 保留 system prompt,从第二早的消息开始删除
    messages.pop(1)  # 删除第一条非system消息

3. 对话摘要压缩

当历史太长时,用模型把前面的对话总结成一段摘要,然后只保留摘要 + 最近的原始消息。

复制代码
# 伪逻辑:
if len(messages) > 20:
    # 将前15轮对话拿出来,让模型生成摘要
    old_messages = messages[:15]
    summary = summarize(old_messages)   # 调用模型总结
    # 新上下文 = system + 摘要 + 最近5轮原始对话
    messages = [system_msg, {"role": "system", "content": f"对话历史摘要:{summary}"}] + messages[-10:]

4. 关键信息外部持久化

不要依赖上下文记住用户的永久信息(如姓名、会员等级、地址)。第一次获取后就存入数据库,每次请求时动态注入到 system prompt 中。

复制代码
user_name = get_name_from_db(user_id)  # 从数据库取
messages = [
    {"role": "system", "content": f"当前用户叫{user_name},是金牌会员。"},
    {"role": "user", "content": "我的订单到哪了?"}
]

这样即使对话历史被清空,关键信息也不会丢失。

六、注意事项

  1. 模型的最大上下文长度:每个模型都有最大窗口(如 4k、128k),在裁剪历史时一定要留出模型输出的空间。

  2. system prompt 要保护:裁剪时永远保留第一条系统设定,否则人设会崩。

  3. 成本意识:每次全量发送历史会让 token 消耗翻倍增长,上生产一定要做裁剪或摘要。

  4. 同一请求可传多轮:有些时候为了省事,一次请求就塞入完整的几轮历史,这也是允许的,关键是你要自己维护。

七、总结

  • 直接API调用实现上下文关联,本质就是你在客户端维护一个 messages 数组,每次请求时全量(或截断后)发送。

  • 模型本身不存储任何会话信息,所有的"记忆"都靠你手动传递。

  • 实际应用时必须配合裁剪、摘要、持久化等策略,才能在保证对话连贯性的同时控制成本与性能。

相关推荐
sakiko_1 小时前
UIKit学习笔记3-布局、滚动视图、隐藏或显示视图
前端·笔记·学习·objective-c·swift·uikit
koo3641 小时前
周报5.3
笔记
xian_wwq3 小时前
【学习笔记】Harness到底是什么
笔记·学习·ai·harness
二哈赛车手3 小时前
新人笔记---项目中简易版的RAG检索后评测指标(@Recall ,Mrr..)实现
java·开发语言·笔记·spring·ai
是上好佳佳佳呀4 小时前
【前端(十二)】JavaScript 函数与对象笔记
前端·javascript·笔记
三块可乐两块冰5 小时前
机器学习三十八
笔记
TechMix6 小时前
【fkw学习笔记】Android 13 AOSP 源码添加系统预置应用实战指南
android·笔记·学习
阿Y加油吧6 小时前
二刷 LeetCode:62. 不同路径 & 64. 最小路径和 复盘笔记
笔记·算法·leetcode
2501_927168296 小时前
手机号测吉凶:尾数722手机号吉凶
笔记