1. 项目概述:当Ruby遇见ChatGPT
如果你是一位Ruby开发者,最近肯定被各种AI应用刷屏了。看着别人用Python、JavaScript轻松调用ChatGPT API,实现智能对话、代码生成,是不是心里也痒痒的?别急,今天要聊的这个项目rubyonai/chatgpt-ruby,就是专门为Ruby社区打造的“官方平替”。它不是一个简单的API封装,而是一个功能完整、设计优雅的Gem,让你能在熟悉的Ruby环境中,无缝集成OpenAI的ChatGPT模型能力。无论是想给你的Rails应用加个智能客服,还是写个命令行工具来辅助代码审查,甚至是构建一个复杂的AI工作流,这个Gem都能让你用最Ruby的方式(简洁、优雅、约定优于配置)来实现。接下来,我会带你从设计思路到实战踩坑,彻底拆解这个工具,让你不仅能上手,更能用好。
2. 核心设计思路与架构解析
2.1 为什么需要专门的Ruby SDK?
在chatgpt-ruby出现之前,Ruby开发者调用OpenAI API主要有两种方式:一是直接用Net::HTTP或Faraday等HTTP客户端手动构造请求,二是使用一些社区早期贡献的、功能不全的封装。前者繁琐且容易出错,需要自己处理认证、参数序列化、错误重试等琐事;后者则可能面临接口不完整、更新不及时、设计不符合Ruby习惯等问题。
rubyonai/chatgpt-ruby的出现,正是为了解决这些痛点。它的核心设计目标很明确:提供一套符合Ruby开发者直觉的、面向对象的、功能完整的官方级SDK。这意味着,你操作的不是原始的HTTP JSON数据,而是一个个Ruby对象(如ChatGPT::Client,ChatGPT::Message),方法调用链清晰可读,错误以异常形式抛出,配置管理方便。这种设计极大地降低了集成门槛,提升了开发体验和代码的可维护性。
2.2 核心架构与模块职责
这个Gem的架构清晰分层,主要包含以下几个核心部分:
- 客户端 (
ChatGPT::Client):这是入口点。它封装了HTTP连接、认证(使用你的OpenAI API Key)、请求发送和基础响应处理。你可以把它理解为与OpenAI服务通信的总机。 - 资源对象 (Resource Objects):这是面向对象设计的精髓。API返回的复杂JSON数据被映射成了Ruby对象。例如,一次聊天完成请求会返回一个
ChatGPT::Completion对象,这个对象包含了回复内容、使用的模型、token消耗等信息,你可以通过completion.choices.first.message.content这样直观的方式获取回复文本,而不是去解析哈希response[“choices”][0][“message”][“content”]。 - 服务端点 (Service Endpoints):对应OpenAI API的不同功能模块。目前核心是
ChatGPT::Chat,它提供了创建聊天补全(即对话)的接口。未来如果Gem扩展,可能会加入Images,Audio等端点。这种设计使得API扩展非常清晰。 - 配置与工具 (
ChatGPT::Configuration):集中管理API密钥、请求超时、自定义HTTP适配器等全局设置。支持通过代码块配置,也支持从环境变量(如OPENAI_API_KEY)读取,非常灵活。
这种架构的优势在于“关注点分离”。你作为使用者,大部分时间只需要和高级的、语义化的资源对象和方法打交道,底层网络通信、错误处理等脏活累活都被SDK默默处理好了。
3. 从零开始:环境配置与基础使用
3.1 安装与基础配置
首先,在你的Gemfile中添加这行代码,然后执行bundle install。
gem ‘chatgpt-ruby’, ‘~> 0.3.0’ # 请检查最新版本安装完成后,你需要一个OpenAI的API密钥。如果没有,去OpenAI平台注册并获取。切记,API密钥是私密信息,绝不能直接硬编码在代码中提交到版本库。
最推荐的方式是使用环境变量:
export OPENAI_API_KEY=‘sk-your-secret-key-here’然后在你的Ruby代码中,可以这样初始化客户端:
require ‘chatgpt’ # 方式一:最简单,自动从 ENV[‘OPENAI_API_KEY’] 读取密钥 client = ChatGPT::Client.new # 方式二:显式传入密钥 client = ChatGPT::Client.new(api_key: ‘sk-...’) # 方式三:进行更详细的全局配置(通常放在初始化文件中,如 config/initializers/chatgpt.rb) ChatGPT.configure do |config| config.api_key = ENV[‘OPENAI_API_KEY’] config.request_timeout = 120 # 超时时间,单位秒,处理长内容时可能需要调高 config.logger = Rails.logger if defined?(Rails) # 集成Rails日志 end # 配置后,直接使用 ChatGPT::Client.new注意:API密钥的保管是安全红线。在团队协作中,使用
.env文件配合dotenvgem 是常见做法,但务必确保.env文件在.gitignore中。对于生产环境,应使用云服务商提供的密钥管理服务(如AWS Secrets Manager, HashiCorp Vault)。
3.2 发起你的第一次对话
让我们从一个最简单的对话开始,感受一下SDK的流畅感。
client = ChatGPT::Client.new response = client.chat( model: “gpt-3.5-turbo”, # 指定模型 messages: [ {role: “user”, content: “用Ruby写一个方法,计算斐波那契数列的第n项。”} ] ) puts response.choices.first.message.content # 输出可能类似于: # def fibonacci(n) # return n if n <= 1 # fibonacci(n-1) + fibonacci(n-2) # end看,只需要一个client.chat方法调用,传入模型和消息数组,就能拿到结构化的响应对象。response.choices是一个数组,通常我们取第一个。这里的message对象包含了AI返回的content。
3.3 理解消息角色与对话上下文
ChatGPT的对话API是基于消息列表的。每条消息都有一个role(角色),主要分为三种:
system: 系统消息,用于设定AI助手的背景、行为准则或身份。它在对话开始时设定,对整个会话有全局性影响。user: 用户消息,代表我们人类用户的问题或指令。assistant: 助手消息,代表AI之前的回复。
维持对话上下文的关键,就在于在后续请求中,将之前所有的消息(包括你的问题和AI的回答)都包含在messages数组里。SDK让这变得很简单:
# 第一轮对话 messages = [ {role: “system”, content: “你是一位资深的Ruby代码审查专家,语气严谨但友好。”}, {role: “user”, content: “请审查这段代码:def calculate; total = items.sum(&:price); end”} ] response1 = client.chat(model: “gpt-3.5-turbo”, messages: messages) assistant_reply1 = response1.choices.first.message.content puts “AI: #{assistant_reply1}” # 将AI的回复也加入到消息历史中 messages << {role: “assistant”, content: assistant_reply1} # 接着提出后续问题 messages << {role: “user”, content: “如果items可能为nil,如何改进?”} # 第二轮对话,携带了完整上下文 response2 = client.chat(model: “gpt-3.5-turbo”, messages: messages) puts “AI: #{response2.choices.first.message.content}”通过这种方式,AI就能记住之前的对话内容,实现连贯的多轮对话。chatgpt-rubySDK本身不自动维护这个历史,这给了开发者最大的灵活性,你可以选择将历史存储在内存、数据库或会话中。
4. 高级功能与参数深度调优
4.1 核心参数详解与实战影响
chat方法除了model和messages,还支持一系列精细控制参数。理解它们对产出结果至关重要。
response = client.chat( model: “gpt-4”, # 更强大,也更贵 messages: [...], temperature: 0.7, # 核心创意度参数 max_tokens: 500, # 控制回复长度 top_p: 0.9, # 核心多样性参数 frequency_penalty: 0.5, # 减少重复 presence_penalty: 0.3, # 鼓励新话题 stream: false # 是否使用流式输出 )下面我们用一个表格来详细拆解这些参数:
| 参数 | 含义与作用 | 推荐范围 | 实战心得 |
|---|---|---|---|
temperature | 创意度/随机性。值越高(接近1),输出越随机、有创意、可能跑偏;值越低(接近0),输出越确定、保守、倾向于最高概率词。 | 代码生成/事实问答:0.1-0.3 创意写作/头脑风暴:0.7-0.9 | 这是最重要的参数之一。写代码、总结文档时用低值保证准确性;写诗、想点子时用高值激发创意。不建议设为0,会导致输出非常呆板重复。 |
max_tokens | 回复的最大token数(约等于单词数)。注意,这包括输入和输出的总和不能超过模型上下文长度(如gpt-3.5-turbo是16385)。 | 根据需求设定,预留足够余量。 | 务必设置。否则AI可能生成极长的回复,消耗大量token。估算方法:英文1token≈0.75单词,中文1token≈1-2汉字。你的问题长度+max_tokens应小于模型上限。 |
top_p(核采样) | 从概率质量前p%的候选词中随机选择。与temperature协同工作,通常只调整其中一个。 | 0.8-0.95 | 一种更智能的采样方式。top_p=0.9意味着只考虑概率累积和达到90%的那些词。与低temperature配合,可以在保持确定性的同时增加一点多样性。 |
frequency_penalty | 正值根据词频降低概率,惩罚重复用词。 | -2.0 到 2.0,常用0.5-1.0 | 在生成文章、长回答时非常有用,可以有效避免AI车轱辘话来回说。对于代码生成,通常设为0或很低,因为代码中重复关键字是正常的。 |
presence_penalty | 正值根据是否出现过降低概率,鼓励谈论新内容。 | -2.0 到 2.0,常用0.0-0.5 | 适合多轮对话中,当你希望AI引入新概念、新角度时使用。值太高可能导致话题跳跃。 |
stream | 是否启用流式响应。为true时,API会以Server-Sent Events形式返回数据块。 | true/false | 强烈建议在需要实时显示的场景(如聊天界面)启用。SDK会返回一个可枚举的对象,你可以逐块获取并显示内容,用户体验极佳。下文会详细讲。 |
4.2 流式输出:实现“打字机”效果
在Web应用或CLI工具中,让回复一个字一个字地“打”出来,体验好很多。chatgpt-ruby完美支持流式传输。
require ‘chatgpt’ client = ChatGPT::Client.new stream_response = client.chat( model: “gpt-3.5-turbo”, messages: [{role: “user”, content: “给我讲一个关于Ruby的小故事。”}], stream: true # 关键参数 ) # stream_response是一个可枚举对象 full_content = “” stream_response.each do |chunk| # chunk是一个哈希,结构类似 {“choices”=>[{“delta”=>{“content”=>”Once”}}]} delta_content = chunk.dig(“choices”, 0, “delta”, “content”) if delta_content print delta_content # 逐块打印,实现打字效果 STDOUT.flush full_content += delta_content end end puts “\n--- Stream Complete ---” puts “Full content: #{full_content}”实操心得:处理流式响应时,要注意网络中断和错误处理。一个健壮的做法是将流式逻辑包裹在begin…rescue块中,并考虑设置超时。另外,如果是在Rails的ActionController中,可以使用response.stream.write来直接向客户端流式输出。
4.3 函数调用(Function Calling)集成
这是ChatGPT API一个强大的功能,允许AI根据对话内容,请求调用你预先定义好的函数(工具),并将函数执行结果返回给AI,从而完成复杂任务(如查询数据库、调用外部API)。chatgpt-ruby从某个版本开始也支持了此功能。
其使用模式分为三步:
- 在请求中定义你可以提供的函数列表(
functions参数)。 - AI分析用户问题,如果觉得需要调用函数,会在回复中通过
finish_reason: “function_call”和function_call字段告知你该调用哪个函数、参数是什么。 - 你在本地执行该函数,将结果作为一条新的
role: “function”的消息,连同历史消息再次发送给AI,AI会基于函数结果生成最终回复给用户。
# 1. 定义函数(工具) tools = [ { type: “function”, function: { name: “get_current_weather”, description: “获取指定城市的当前天气”, parameters: { type: “object”, properties: { location: { type: “string”, description: “城市名,如‘北京’、‘San Francisco’。” }, unit: { type: “string”, enum: [“celsius”, “fahrenheit”], default: “celsius” } }, required: [“location”] } } } ] # 2. 发起对话,告知AI可用的工具 messages = [{role: “user”, content: “北京今天天气怎么样?”}] response = client.chat(model: “gpt-3.5-turbo”, messages: messages, tools: tools) message = response.choices.first.message # 3. 检查AI是否想调用函数 if message.tool_calls && message.tool_calls.any? tool_call = message.tool_calls.first if tool_call.function.name == “get_current_weather” # 解析AI提供的参数(JSON字符串) args = JSON.parse(tool_call.function.arguments) location = args[“location”] # 4. 模拟或真实调用你的天气API weather_result = “{ \“temperature\”: 22, \“condition\”: \“晴朗\”, \“unit\”: \“celsius\” }” # 5. 将函数执行结果作为新消息追加 messages << message # 先加入AI的请求消息 messages << { role: “tool”, tool_call_id: tool_call.id, # 必须对应之前的调用ID content: weather_result } # 6. 再次调用AI,让它根据天气结果生成面向用户的回复 second_response = client.chat(model: “gpt-3.5-turbo”, messages: messages) final_answer = second_response.choices.first.message.content puts “AI: #{final_answer}” # => “北京今天天气晴朗,气温22摄氏度,是个好天气。” end end重要提示:函数调用功能强大,但逻辑相对复杂。你需要仔细设计函数的描述和参数模式,这直接影响AI判断是否以及如何调用它的准确性。建议先从简单的函数开始测试。
5. 工程化实践:集成到Rails应用与错误处理
5.1 在Rails中构建一个简单的AI服务层
在真实的Rails项目中,我们不应该把AI调用逻辑散落在控制器或作业里。最佳实践是创建一个服务对象(Service Object)。
# app/services/ai_chat_service.rb class AIChatService class AIServiceError < StandardError; end def initialize(user, conversation = nil) @client = ChatGPT::Client.new(api_key: Rails.application.credentials.openai_api_key) @user = user @conversation = conversation # 可传入一个Conversation模型,用于持久化历史 end # 发送单条消息并获取回复 def send_message(content, options = {}) messages = build_messages(content) begin response = @client.chat({ model: options[:model] || “gpt-3.5-turbo”, messages: messages, temperature: options[:temperature] || 0.7, max_tokens: options[:max_tokens] || 1000 }.merge(options)) ai_message_content = response.choices.first.message.content # 可选:将用户消息和AI回复保存到数据库 save_to_conversation(content, ai_message_content) if @conversation return ai_message_content rescue ChatGPT::Error => e # 捕获SDK定义的错误 Rails.logger.error “OpenAI API Error: #{e.message}” raise AIServiceError, “AI服务暂时不可用:#{e.message}” rescue Net::OpenTimeout, Net::ReadTimeout => e Rails.logger.error “OpenAI API Timeout: #{e.message}” raise AIServiceError, “请求超时,请稍后重试” end end # 流式版本 def send_message_stream(content, &block) messages = build_messages(content) stream_response = @client.chat({ model: “gpt-3.5-turbo”, messages: messages, stream: true }) full_content = “” stream_response.each do |chunk| delta = chunk.dig(“choices”, 0, “delta”, “content”) if delta full_content += delta yield delta if block_given? # 将每个片段传递给调用方(如控制器) end end full_content end private def build_messages(new_user_content) # 基础系统提示词,可根据用户身份定制 base_messages = [ {role: “system”, content: “你是一个乐于助人的助手,回答请简洁明了。”} ] # 如果存在历史对话,加载进来 if @conversation base_messages += @conversation.messages.order(:created_at).map do |msg| {role: msg.role, content: msg.content} end end # 加入当前新消息 base_messages << {role: “user”, content: new_user_content} base_messages end def save_to_conversation(user_content, ai_content) @conversation.messages.create!(role: “user”, content: user_content) @conversation.messages.create!(role: “assistant”, content: ai_content) end end这样,在你的控制器中,调用就变得非常清晰:
# app/controllers/chat_controller.rb class ChatController < ApplicationController def create @conversation = current_user.conversations.find(params[:conversation_id]) service = AIChatService.new(current_user, @conversation) if params[:stream] # 流式响应 render stream: true service.send_message_stream(params[:message]) do |chunk| response.stream.write(chunk) end else # 普通响应 @reply = service.send_message(params[:message]) render :create end rescue AIChatService::AIServiceError => e render json: { error: e.message }, status: :service_unavailable end end5.2 全面的错误处理与重试机制
OpenAI API调用可能因网络、限流、令牌超限等原因失败。一个健壮的系统必须有完善的错误处理。
chatgpt-rubySDK会抛出它自己定义的异常,如ChatGPT::Error,以及一些标准的网络错误。我们可以根据不同的错误类型采取不同策略。
def robust_ai_call(messages, retries = 3) attempts = 0 begin @client.chat(model: “gpt-3.5-turbo”, messages: messages) rescue ChatGPT::RateLimitError => e attempts += 1 if attempts <= retries # 速率限制,指数退避重试 sleep_time = 2 ** attempts + rand(0.1..0.5) Rails.logger.warn “Rate limited, retrying in #{sleep_time}s (attempt #{attempts})” sleep(sleep_time) retry else raise “Rate limit exceeded after #{retries} retries.” end rescue ChatGPT::InvalidRequestError => e # 通常是参数错误,如消息太长(token超限),重试无意义 Rails.logger.error “Invalid request: #{e.message}” raise “请求参数错误:#{e.message}” rescue Net::OpenTimeout, Net::ReadTimeout => e attempts += 1 if attempts <= retries Rails.logger.warn “Timeout, retrying (attempt #{attempts})” retry else raise “Network timeout after #{retries} retries.” end rescue => e # 其他未知错误 Rails.logger.error “Unexpected AI API error: #{e.class} - #{e.message}” raise “AI服务发生未知错误” end end关键点:
- 速率限制:OpenAI对每分钟和每天的请求数、token数都有限制。遇到
429错误,必须实现指数退避重试,并考虑在应用层面做请求队列。 - 令牌超限:如果
messages总长度超过模型上下文窗口,会抛出InvalidRequestError。解决方案是实施“对话摘要”或“滑动窗口”,只保留最近N条消息或对历史消息进行总结压缩。 - 超时设置:对于生成长内容的请求,务必在客户端和SDK配置中设置合理的超时时间(如
request_timeout: 120)。
6. 性能优化、成本控制与监控
6.1 令牌计算与成本估算
OpenAI API按令牌数收费,输入和输出都算。控制成本的关键在于监控和优化令牌使用。
# 估算消息的token数(近似值,精确计算需使用tiktoken库) def estimate_tokens(messages) # 一个非常粗略的估算:英文1token≈4字符,中文1token≈1.5-2字符 total_chars = messages.to_json.size # 这是一个非常不精确的估算!生产环境请使用 `tiktoken` Ruby gem或调用OpenAI的tokenizer端点。 (total_chars / 4.0).ceil end # 在发送请求前检查 messages = build_messages(params[:message]) estimated_tokens = estimate_tokens(messages) if estimated_tokens > 4000 # 假设我们设定一个安全阈值 # 触发优化策略:如提示用户输入过长,或自动截断/总结历史消息 return { error: “输入内容过长,请简化您的问题。” } end # 实际使用后,从响应中获取精确的token消耗 response = client.chat(...) usage = response.usage puts “本次消耗:输入token-#{usage.prompt_tokens}, 输出token-#{usage.completion_tokens}, 总计-#{usage.total_tokens}” # 可以根据 usage.total_tokens 和模型单价(如gpt-3.5-turbo每千token$0.002)计算本次请求成本 cost = usage.total_tokens / 1000.0 * 0.002重要建议:对于生产系统,务必集成精确的令牌计数库(如通过FFI调用Python的tiktoken,或使用社区开发的Ruby版本),并在数据库记录每次请求的token消耗,以便进行成本分析和审计。
6.2 缓存策略:减少重复调用
对于某些相对静态或可重复的查询,引入缓存能大幅降低成本和延迟。
# 使用Rails.cache,以消息的MD5摘要为键 def cached_chat(messages, options = {}) cache_key = “chatgpt:#{Digest::MD5.hexdigest(messages.to_json + options.to_s)}” cached_response = Rails.cache.read(cache_key) if cached_response Rails.logger.info “Cache hit for AI chat” return cached_response end response = client.chat({model: “gpt-3.5-turbo”, messages: messages}.merge(options)) # 缓存响应内容,注意设置合理的过期时间,对于事实性内容可以长一些,时效性强的则短或不缓存 Rails.cache.write(cache_key, response.choices.first.message.content, expires_in: 1.hour) response.choices.first.message.content end注意事项:缓存AI回复需要谨慎。确保缓存的场景是合适的(例如,将常见技术问题“如何安装Ruby on Rails?”的答案缓存一天是合理的),而对于高度个性化或依赖实时数据的对话,则不能缓存。
6.3 异步处理与队列
AI API调用可能耗时数百毫秒甚至数秒,在Web请求中同步调用会导致用户体验卡顿。务必使用后台作业。
# app/jobs/process_chat_job.rb class ProcessChatJob < ApplicationJob queue_as :default retry_on ChatGPT::RateLimitError, wait: :exponentially_longer, attempts: 5 discard_on ChatGPT::InvalidRequestError # 参数错误,重试无意义 def perform(conversation_id, user_message) conversation = Conversation.find(conversation_id) service = AIChatService.new(nil, conversation) ai_reply = service.send_message(user_message) # 可能通过ActionCable或轮询通知前端任务完成 ConversationChannel.broadcast_to(conversation, {type: ‘message’, content: ai_reply}) end end # 在控制器中 ChatController < ApplicationController def create @conversation = current_user.conversations.find(params[:conversation_id]) @conversation.messages.create!(role: “user”, content: params[:message]) # 立即返回,告知用户请求已接收 ProcessChatJob.perform_later(@conversation.id, params[:message]) render json: { status: ‘processing’ } end end使用Sidekiq或GoodJob等队列后端,可以更好地管理重试、监控和扩展。
7. 常见问题排查与实战踩坑记录
即使有了好用的SDK,在实际集成中还是会遇到各种问题。下面是我总结的一些典型场景和解决方案。
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
报错ChatGPT::Error: Invalid API Key | 1. API密钥未设置或错误。 2. 密钥所属组织有权限问题。 3. 环境变量名不匹配(SDK默认找 OPENAI_API_KEY)。 | 1. 检查ENV[‘OPENAI_API_KEY’]是否存在且正确。可在Rails console中puts ENV[‘OPENAI_API_KEY’].inspect。2. 登录OpenAI平台,确认密钥有效且余额充足。 3. 初始化客户端时显式传入密钥 ChatGPT::Client.new(api_key: ‘sk-...’)进行测试。 |
错误ChatGPT::InvalidRequestError: This model’s maximum context length is ... | 输入的messages总token数超过了模型上下文上限。 | 1. 实现令牌估算,在请求前检查。 2. 实施“对话摘要”:当历史消息过长时,调用一次AI,让其将之前的对话总结成一段简短摘要,然后用“系统消息+摘要+最新问题”作为新上下文。 3. 使用“滑动窗口”:只保留最近N条消息,丢弃更早的历史。 |
| AI回复内容完全无关或胡言乱语 | 1.temperature参数设置过高。2. 系统提示词 ( systemmessage) 不明确或冲突。3. 消息历史混乱,包含了冲突的指令。 | 1. 将temperature调低至0.2-0.5范围。2. 检查并优化系统提示词,确保指令清晰、单一。例如,明确“你是一个Ruby专家,只回答技术问题”。 3. 清理消息历史,确保上下文连贯。对于新会话,从一个干净的系统提示词开始。 |
| 流式响应中途中断或内容不完整 | 1. 网络不稳定。 2. 服务器或客户端超时设置过短。 3. 处理流数据的代码有bug,未正确处理每个chunk。 | 1. 增加客户端超时config.request_timeout。2. 在流式处理循环中加入心跳或超时判断。 3. 确保你的代码正确处理了 chunk.dig(“choices”, 0, “delta”, “content”),有些chunk可能只包含role或finish_reason字段,content为nil是正常的。 |
函数调用 (tool_calls) 不生效 | 1. 函数定义(名称、描述、参数模式)不够清晰,AI无法理解何时调用。 2. 未将AI的 tool_calls消息加入历史就发送后续请求。3. 函数返回的结果格式不符合AI预期。 | 1. 详细编写函数描述,并确保参数模式定义准确。可以先用Playground测试。 2.严格按照流程:将AI的请求消息( role: ‘assistant’, tool_calls: [...])加入messages,再将函数执行结果作为role: ‘tool’消息加入,然后重新请求。3. 函数结果应是JSON字符串,且包含描述中约定的字段。 |
| Rails开发环境下请求很慢 | 1. 网络问题。 2. 没有使用连接池或HTTP客户端配置不当。 3. 同步调用阻塞了主线程。 | 1. 考虑使用httpclient或typhoeus替代默认的Net::HTTP,它们性能更好。可在SDK配置中通过http_client选项指定。2.务必使用后台作业进行异步处理,这是生产应用的基本要求。 |
我个人最常踩的一个坑是“上下文管理”。初期为了省事,我把整个对话历史(可能几十条)都塞进每次请求,很快触发了token超限错误。后来我实现了一个简单的策略:当历史消息估算token超过3000时,就用一条指令让AI自己总结之前的对话核心,然后用这个总结替换掉旧的历史消息。这个策略在平衡上下文记忆和成本控制上非常有效。另一个教训是关于错误处理,最初我只捕获了最通用的异常,后来发现速率限制错误需要特殊处理(重试),而令牌超限错误则不应该重试,必须调整请求内容。把这些错误细分处理,系统的健壮性才真正上来。