如何为OpenAI模型配置对话记忆?Django+React聊天机器人开发疑问
解决方案
一、创建对话存储的数据库模型
先定义会话与消息的关联模型,用于存储对话历史:
# models.py from django.db import models from django.contrib.auth.models import User # 无用户系统可删除此关联 class Conversation(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Message(models.Model): ROLE_CHOICES = ( ('user', '用户'), ('assistant', 'AI助手'), ) conversation = models.ForeignKey(Conversation, on_delete=models.CASCADE, related_name='messages') role = models.CharField(max_length=10, choices=ROLE_CHOICES) content = models.TextField() created_at = models.DateTimeField(auto_now_add=True)
执行迁移命令:
python manage.py makemigrations python manage.py migrate
二、修改APIView集成对话记忆
推荐方案:切换到gpt-3.5-turbo模型(对话适配性更强、token成本更低)
# views.py from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status import openai from .models import Conversation, Message api_key = "你的OpenAI密钥" class ChatbotView(APIView): def post(self, request): if not api_key: return Response({'errors': {'api_key': ['未配置API密钥']}}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) openai.api_key = api_key user_input = request.data.get('user_input') conversation_id = request.data.get('conversation_id') if not user_input: return Response({'errors': {'user_input': ['输入为空']}}, status=status.HTTP_400_BAD_REQUEST) # 处理会话:新建或获取已有会话 if conversation_id: try: conversation = Conversation.objects.get(id=conversation_id) except Conversation.DoesNotExist: return Response({'errors': {'conversation_id': ['会话不存在']}}, status=status.HTTP_404_NOT_FOUND) else: conversation = Conversation.objects.create() # 有用户系统可改为 Conversation.objects.create(user=request.user) # 保存用户输入 Message.objects.create(conversation=conversation, role='user', content=user_input) # 构建对话历史 messages = [] for msg in conversation.messages.all().order_by('created_at'): messages.append({"role": msg.role, "content": msg.content}) # 调用OpenAI接口 try: response = openai.ChatCompletion.create( model='gpt-3.5-turbo', messages=messages, max_tokens=250, temperature=0.5 ) assistant_response = response['choices'][0]['message']['content'].strip() except Exception as e: return Response({'errors': {'openai': [str(e)]}}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) # 保存AI回复 Message.objects.create(conversation=conversation, role='assistant', content=assistant_response) return Response({ "response": assistant_response, "conversation_id": conversation.id }, status=status.HTTP_200_OK)
兼容原有方案:继续使用text-davinci-003
# views.py from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status import openai from .models import Conversation, Message api_key = "你的OpenAI密钥" class ChatbotView(APIView): def post(self, request): if not api_key: return Response({'errors': {'api_key': ['未配置API密钥']}}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) openai.api_key = api_key user_input = request.data.get('user_input') conversation_id = request.data.get('conversation_id') if not user_input: return Response({'errors': {'user_input': ['输入为空']}}, status=status.HTTP_400_BAD_REQUEST) # 处理会话 if conversation_id: try: conversation = Conversation.objects.get(id=conversation_id) except Conversation.DoesNotExist: return Response({'errors': {'conversation_id': ['会话不存在']}}, status=status.HTTP_404_NOT_FOUND) else: conversation = Conversation.objects.create() # 保存用户输入 Message.objects.create(conversation=conversation, role='user', content=user_input) # 构建prompt prompt_parts = [] for msg in conversation.messages.all().order_by('created_at'): role_text = "用户:" if msg.role == 'user' else "AI:" prompt_parts.append(f"{role_text}{msg.content}") prompt_parts.append("AI:") prompt = "\n".join(prompt_parts) # 调用OpenAI接口 try: response = openai.Completion.create( model='text-davinci-003', prompt=prompt, max_tokens=250, temperature=0.5, stop=["用户:"] ) assistant_response = response["choices"][0]["text"].strip() except Exception as e: return Response({'errors': {'openai': [str(e)]}}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) # 保存AI回复 Message.objects.create(conversation=conversation, role='assistant', content=assistant_response) return Response({ "response": assistant_response, "conversation_id": conversation.id }, status=status.HTTP_200_OK)
三、Token消耗优化方案
- 切换到gpt-3.5-turbo模型:token成本仅为text-davinci-003的1/10,对话场景适配性更强。
- 限制历史对话轮数:只保留最近5-10轮对话,减少上下文长度:
# 示例:只保留最近8条消息(4轮对话) recent_messages = conversation.messages.all().order_by('created_at')[-8:]
- 历史对话总结:当对话超过一定轮数时,调用API总结历史对话,用总结内容替代完整历史:
if conversation.messages.count() > 10: # 提取历史对话文本 history_text = "\n".join([f"{msg.role}: {msg.content}" for msg in conversation.messages.all().order_by('created_at')[:-2]]) summary_prompt = f"请总结以下对话关键信息:\n{history_text}" summary_response = openai.ChatCompletion.create( model='gpt-3.5-turbo', messages=[{"role": "user", "content": summary_prompt}], max_tokens=100 ) summary = summary_response['choices'][0]['message']['content'] # 重置会话,保留总结与最新对话 conversation.messages.all().delete() Message.objects.create(conversation=conversation, role='system', content=f"对话总结:{summary}") # 重新添加最新的用户输入 Message.objects.create(conversation=conversation, role='user', content=user_input)
- token数量校验:用
tiktoken库计算上下文token数,避免超过模型上限:
pip install tiktoken
import tiktoken def count_tokens(messages, model="gpt-3.5-turbo"): encoding = tiktoken.encoding_for_model(model) num_tokens = 0 for message in messages: num_tokens += 4 for key, value in message.items(): num_tokens += len(encoding.encode(value)) if key == "name": num_tokens -= 1 num_tokens += 2 return num_tokens # 构建messages后校验,超过限制则移除最早的消息 while count_tokens(messages) > 3500: messages.pop(0)
四、前端配合说明
前端首次对话无需传递conversation_id,后端返回新会话ID后,后续所有对话都携带该ID,即可关联同一会话的历史记录。
内容的提问来源于stack exchange,提问作者Salaodxgotico _LAS
相关产品推荐
相关产品推荐

