在Ruby on Rails中使用ruby-openai API Gem实现流式对话与会话历史
解决ruby-openai的流式对话与会话历史问题
1. 手动维护会话历史
ruby-openai本身不追踪会话上下文,需自行按OpenAI API要求的格式维护对话消息数组,每次请求时将该数组传入messages参数,实现上下文延续。
实现思路
- 用会话存储(
session)或数据库模型持久化对话历史,每条消息需包含role(取值为system/user/assistant)和content字段。 - 用户发送新消息时,将其追加到历史数组;收到AI回复后,同样把回复存入数组,确保后续请求能携带完整对话上下文。
Rails控制器示例代码
# 初始化会话历史(首次请求时设置系统提示词) session[:chat_history] ||= [{ role: "system", content: "你是一名专业的Ruby on Rails技术顾问" }] # 追加用户新提问到历史 user_message = params[:user_input].strip session[:chat_history] << { role: "user", content: user_message } # 调用ruby-openai API,传入完整会话历史 client = OpenAI::Client.new(api_key: ENV["OPENAI_API_KEY"]) response = client.chat( parameters: { model: "gpt-3.5-turbo", messages: session[:chat_history], temperature: 0.7 } ) # 提取AI回复并追加到会话历史 assistant_response = response.dig("choices", 0, "message", "content") session[:chat_history] << { role: "assistant", content: assistant_response } # 返回回复给前端 render json: { reply: assistant_response }
持久化扩展(跨会话保存)
若需要永久保存对话记录,可创建数据库模型:
# 生成模型:rails generate model Conversation user:references # rails generate model ConversationMessage conversation:references role:string content:text # 保存对话消息的示例 conversation = current_user.conversations.find_or_create_by(title: "技术咨询") ConversationMessage.create!(conversation: conversation, role: "user", content: user_message) # 查询历史消息组成数组 chat_history = conversation.conversation_messages.order(created_at: :asc).map do |msg| { role: msg.role, content: msg.content } end
2. 实现流式对话
ruby-openai原生不支持流式响应,需手动调用OpenAI的HTTP API处理分块传输的响应,配合前端实现实时内容渲染。
实现思路
- 使用Ruby标准库
Net::HTTP发送带stream: true参数的请求,逐块读取响应内容。 - 解析Server-Sent Events(SSE)格式的响应块,提取AI的增量回复内容,可搭配Rails Action Cable实现前端实时接收。
流式请求示例代码
require 'net/http' require 'json' uri = URI("https://api.openai.com/v1/chat/completions") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Post.new(uri) request["Content-Type"] = "application/json" request["Authorization"] = "Bearer #{ENV['OPENAI_API_KEY']}" # 传入会话历史并开启流式 request.body = JSON.dump({ model: "gpt-3.5-turbo", messages: session[:chat_history], stream: true }) # 逐块处理流式响应 full_response = "" http.request(request) do |response| response.read_body do |chunk| chunk.split("\n").each do |line| next unless line.start_with?("data: ") data = line.gsub("data: ", "") next if data == "[DONE]" json_chunk = JSON.parse(data) delta_content = json_chunk.dig("choices", 0, "delta", "content") next unless delta_content full_response += delta_content # 若用Action Cable,此处可推送内容给前端: # ChatChannel.broadcast_to(current_user, { content: delta_content }) end end end # 最后将完整回复追加到会话历史 session[:chat_history] << { role: "assistant", content: full_response }
内容的提问来源于stack exchange,提问作者Olivier Girardot
相关产品推荐
相关产品推荐

