zbparse/flask_app/general/llm/qianwen_plus.py

154 lines
7.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
import logging
import os
import threading
import time
from itertools import cycle
from openai import OpenAI
from ratelimit import sleep_and_retry, limits
from flask_app.general.llm.qianwen_turbo import qianwen_turbo
from flask_app.general.llm.大模型通用函数 import extract_error_details, get_total_tokens
#若多账号实现分流那么就在这里添加不同的API_KEY这里要求每个模型的TPM都是一样的具体API_KEY写在.env文件中
api_keys = cycle([
os.getenv("DASHSCOPE_API_KEY"),
# os.getenv("DASHSCOPE_API_KEY_BACKUP1"),
# os.getenv("DASHSCOPE_API_KEY_BACKUP2")
])
api_keys_lock = threading.Lock()
def get_next_api_key():
with api_keys_lock:
return next(api_keys)
@sleep_and_retry
@limits(calls=7, period=1) # tpm是1000万稳定下每秒最多调用10次两个服务器分流就是5次 2.12日增加了turbo作为承载超限的部分可以适当扩大到calls=7
def qianwen_plus(user_query, need_extra=False):
logger = logging.getLogger('model_log') # 通过日志名字获取记录器
# print("call qianwen-plus...")
"""
使用之前上传的文件,根据用户查询生成响应,并实时显示流式输出。
目前命中缓存局限1.不足 256 Token 的内容不会被缓存。
2.上下文缓存的命中概率并不是100%,即使是上下文完全一致的请求,也存在无法命中的概率,命中概率依据系统判断而定。
3.若多线程同时请求,缓存无法及时更新!
参数:
- user_query: 用户查询
- need_extra: 是否需要返回额外数据(默认 False
返回:
- 当 need_extra=False 时: 返回响应内容 (str)
- 当 need_extra=True 时: 返回 (响应内容, token_usage)
"""
# 内部定义重试参数
max_retries = 2
backoff_factor = 2.0
api_key = get_next_api_key()
client = OpenAI(
api_key=api_key,
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
for attempt in range(1, max_retries + 2): # +1 是为了包括初始调用
try:
completion_tokens = 0 # 初始化 completion_tokens 为 0
# 生成基于用户查询的响应
completion = client.chat.completions.create(
model="qwen-plus",
temperature=0.5,
messages=[
{
'role': 'user',
'content': user_query
}
],
stream=True, # 启用流式响应
stream_options={"include_usage": True}
)
full_response = "" # 用于存储完整的响应内容
for chunk in completion:
# 解析 chunk 数据
if hasattr(chunk, 'to_dict'):
chunk_data = chunk.to_dict()
else:
chunk_data = json.loads(chunk.model_dump_json())
# 处理 usage 信息
usage = chunk_data.get('usage')
if usage is not None:
completion_tokens = usage.get('completion_tokens', 0)
# prompt_tokens_details = usage.get('prompt_tokens_details', {}) #命中tokens 取消注释可以print
# cache_hit = prompt_tokens_details.get('cached_tokens', 0)
# print("命中:"+str(cache_hit))
# 处理 choices 信息
choices = chunk_data.get('choices', [])
if choices:
choice = choices[0]
delta = choice.get('delta', {})
content = delta.get('content', '')
if content:
full_response += content
# 实时打印内容(可以取消注释下面一行以实时输出)
# print(content, end='', flush=True)
if choice.get('finish_reason'):
# 处理完成原因(如果需要)
pass # 或者记录 finish_reason 以供后续使用
if need_extra:
return full_response, completion_tokens
else:
return full_response
except Exception as exc:
# 提取错误代码
error_code, error_code_string = extract_error_details(str(exc))
logger.error(f"{attempt} 次尝试失败,查询:'{user_query}',错误:{exc}", exc_info=True)
if error_code == 429:
if attempt <= max_retries:
token_count = get_total_tokens(user_query)
if token_count < 90000: #如果超过plus的qpm tpm且user_query的tokens<90000那么就调用turbo过渡一下。
return qianwen_turbo(user_query,need_extra)
sleep_time = backoff_factor * (2 ** (attempt - 1)) # 指数退避
logger.warning(f"错误代码为 429将在 {sleep_time} 秒后重试...")
time.sleep(sleep_time)
else:
logger.error(f"查询 '{user_query}' 的所有 {max_retries + 1} 次尝试均失败429 错误)。")
break
else:
# 针对非 429 错误,只重试一次
if attempt <= 1:
sleep_time = backoff_factor # 固定等待时间
logger.warning(f"遇到非 429 错误(错误代码:{error_code} - {error_code_string}),将等待 {sleep_time} 秒后重试...")
time.sleep(sleep_time)
continue # 直接跳到下一次循环(即重试一次)
else:
logger.error(f"查询 '{user_query}' 的所有 {max_retries + 1} 次尝试均失败(错误代码:{error_code} - {error_code_string})。")
break
# 如果所有尝试都失败了,返回空字符串或默认值
if need_extra:
return "", 0
else:
return ""
if __name__ == "__main__":
user_query1 = """该招标文件对响应文件投标文件偏离项的要求或内容是怎样的请不要回答具体的技术参数也不要回答具体的评分要求。请以json格式给我提供信息外层键名为'偏离',若存在嵌套信息,嵌套内容键名为文件中对应字段或是你的总结,键值为原文对应内容。若文中没有关于偏离项的相关内容,在键值中填'未知'
禁止内容:
确保键值内容均基于提供的实际招标文件内容,禁止使用任何预设的示例作为回答。
禁止返回markdown格式请提取具体的偏离相关内容。
示例1嵌套键值对情况
{
"偏离":{
"技术要求":"以★标示的内容不允许负偏离",
"商务要求":"以★标示的内容不允许负偏离"
}
}
示例2无嵌套键值对情况
{
"偏离":"所有参数需在技术响应偏离表内响应,如应答有缺项,且无有效证明材料的,评标委员会有权不予认可,视同负偏离处理"
}
"""
res,a = qianwen_plus(user_query1,True)
print(res)
print(a)