OpenAI带对话历史的Token计数方法是否正确?求优化方案
代码正确性确认与优化建议
一、当前代码的正确性
你的代码核心逻辑是正确的:
- 通过OpenAI API返回的
usage字段分别统计prompt_tokens(输入)和completion_tokens(输出),这是官方提供的最准确的Token计数方式,比第三方工具更适配带对话历史的场景。 - 每次对话后将助理和用户消息加入
example_messages,维护对话内存的逻辑也没问题。
二、改进建议
1. 增强错误处理
当前代码未处理API调用可能出现的异常(如网络故障、限额超限、参数错误等),建议添加异常捕获:
def gpt_response_func(): try: response = client.chat.completions.create( model="gpt-3.5-turbo", messages=example_messages ) return response except Exception as e: print(f"API调用出错: {e}") return None
在循环中需判断gpt_response是否为None,避免后续代码报错。
2. 实时成本计算
既然要做全面成本分析,可以直接基于OpenAI定价计算实时成本(以GPT-3.5-turbo常规定价为例):
# 定价参考:输入$0.0015/1k tokens,输出$0.002/1k tokens PRICE_PER_1K_INPUT = 0.0015 PRICE_PER_1K_OUTPUT = 0.002 # 在统计Token后计算成本 cost_input = token_count['input'] * PRICE_PER_1K_INPUT / 1000 cost_output = token_count['output'] * PRICE_PER_1K_OUTPUT / 1000 total_cost = cost_input + cost_output print(f"累计成本: ${total_cost:.6f}")
3. 对话内存Token管控
当对话历史过长时,会导致输入Token过多、成本上升,还可能触发模型上下文限制。建议添加Token阈值管控:
def count_messages_tokens(messages, model="gpt-3.5-turbo"): encoding = tiktoken.encoding_for_model(model) num_tokens = 0 for message in messages: # 每条消息的Token计算规则:<|start|>role<|end|>content<|end|> num_tokens += 4 # 每条消息固定的起始/结束Token for key, value in message.items(): num_tokens += len(encoding.encode(value)) num_tokens += 2 # 对话结束的<|end|>Token return num_tokens # 添加新消息后检查总Token,超过阈值则截断历史(保留系统提示和最近消息) MAX_CONTEXT_TOKENS = 4096 # GPT-3.5-turbo默认上下文长度 while count_messages_tokens(example_messages) > MAX_CONTEXT_TOKENS: # 移除最早的非系统消息 for i in range(1, len(example_messages)): if example_messages[i]['role'] != 'system': example_messages.pop(i) break
三、带对话内存的Token计数更优方法
1. 优先使用API返回的usage字段
官方API返回的usage是最准确的,因为模型实际处理的Token数可能和本地计算存在细微差异(比如特殊字符、模型内部格式处理),你的当前做法已经是最优的实时计数方式。
2. 本地预计算用于阈值管控
如果需要在发送API请求前预估Token数(避免超出上下文限制),可以用tiktoken本地计算,上面的count_messages_tokens函数是OpenAI官方推荐的计算方式,能准确模拟模型对对话消息的Token统计逻辑。
3. 统计数据持久化
如果需要长期统计,可以把Token计数和成本数据持久化到文件或数据库,比如每次对话后写入CSV:
import csv def save_token_stats(token_count, cost_input, cost_output, total_cost): with open('token_stats.csv', 'a', newline='', encoding='utf-8') as f: writer = csv.writer(f) # 首次写入时添加表头 if f.tell() == 0: writer.writerow(['输入Token', '输出Token', '总Token', '输入成本($)', '输出成本($)', '总成本($)', '时间']) writer.writerow([ token_count['input'], token_count['output'], token_count['total'], f"{cost_input:.6f}", f"{cost_output:.6f}", f"{total_cost:.6f}", time.strftime("%Y-%m-%d %H:%M:%S") ])
内容的提问来源于stack exchange,提问作者Mubashir Ahmed Siddiqui
相关产品推荐
相关产品推荐

