12.20 豆包大模型bug解决
This commit is contained in:
parent
5dcbaa5eb5
commit
de254d3c29
@ -205,9 +205,56 @@ def count_tokens(text):
|
|||||||
tokens = re.findall(token_pattern, text)
|
tokens = re.findall(token_pattern, text)
|
||||||
return len(tokens)# 返回 tokens 数量和匹配的 token 列表
|
return len(tokens)# 返回 tokens 数量和匹配的 token 列表
|
||||||
|
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def get_total_tokens(text):
|
||||||
|
"""
|
||||||
|
调用 API 计算给定文本的总 Token 数量。
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- text (str): 需要计算 Token 的文本。
|
||||||
|
- model (str): 使用的模型名称,默认值为 "ep-20241119121710-425g6"。
|
||||||
|
|
||||||
|
返回:
|
||||||
|
- int: 文本的 total_tokens 数量。
|
||||||
|
"""
|
||||||
|
# API 请求 URL
|
||||||
|
url = "https://ark.cn-beijing.volces.com/api/v3/tokenization"
|
||||||
|
|
||||||
|
# 获取 API 密钥
|
||||||
|
doubao_api_key = os.getenv("DOUBAO_API_KEY")
|
||||||
|
if not doubao_api_key:
|
||||||
|
raise ValueError("DOUBAO_API_KEY 环境变量未设置")
|
||||||
|
|
||||||
|
# 请求头
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": "Bearer " + doubao_api_key
|
||||||
|
}
|
||||||
|
model = "ep-20241119121710-425g6"
|
||||||
|
# 请求体
|
||||||
|
payload = {
|
||||||
|
"model": model,
|
||||||
|
"text": [text] # API 文档中要求 text 是一个列表
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(url, headers=headers, json=payload)
|
||||||
|
response.raise_for_status()
|
||||||
|
response_data = response.json()
|
||||||
|
total_tokens=response_data["data"][0]["total_tokens"]
|
||||||
|
return total_tokens
|
||||||
|
except Exception as e:
|
||||||
|
print(f"获取 Token 数量失败:{e}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
@sleep_and_retry
|
@sleep_and_retry
|
||||||
@limits(calls=10, period=1) # 每秒最多调用10次
|
@limits(calls=10, period=1) # 每秒最多调用10次
|
||||||
def doubao_model(full_user_query):
|
def doubao_model(full_user_query, need_extra=False):
|
||||||
print("call doubao...")
|
print("call doubao...")
|
||||||
# 相关参数
|
# 相关参数
|
||||||
url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
|
url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
|
||||||
@ -220,8 +267,8 @@ def doubao_model(full_user_query):
|
|||||||
}
|
}
|
||||||
|
|
||||||
# 判断用户查询字符串的长度
|
# 判断用户查询字符串的长度
|
||||||
token_count = count_tokens(full_user_query)
|
token_count = get_total_tokens(full_user_query)
|
||||||
if token_count > 35000:
|
if token_count > 31500:
|
||||||
selected_model = models["pro_128k"] # 如果长度超过32k,直接使用128k模型
|
selected_model = models["pro_128k"] # 如果长度超过32k,直接使用128k模型
|
||||||
else:
|
else:
|
||||||
selected_model = models["pro_32k"] # 默认使用32k模型
|
selected_model = models["pro_32k"] # 默认使用32k模型
|
||||||
@ -252,14 +299,29 @@ def doubao_model(full_user_query):
|
|||||||
try:
|
try:
|
||||||
response = requests.post(url, headers=headers, json=data) # 设置超时时间为10秒
|
response = requests.post(url, headers=headers, json=data) # 设置超时时间为10秒
|
||||||
response.raise_for_status() # 如果响应状态码不是200,将引发HTTPError
|
response.raise_for_status() # 如果响应状态码不是200,将引发HTTPError
|
||||||
# 返回模型的回复内容
|
|
||||||
return response.json()["choices"][0]["message"]["content"]
|
# 获取响应 JSON
|
||||||
|
response_json = response.json()
|
||||||
|
|
||||||
|
# 获取返回内容
|
||||||
|
content = response_json["choices"][0]["message"]["content"]
|
||||||
|
|
||||||
|
# 获取 completion_tokens
|
||||||
|
completion_tokens = response_json["usage"].get("completion_tokens", 0)
|
||||||
|
|
||||||
|
# 根据 need_extra 返回不同的结果
|
||||||
|
if need_extra:
|
||||||
|
return content, completion_tokens
|
||||||
|
else:
|
||||||
|
return content
|
||||||
|
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
# 获取状态码并处理不同的重试逻辑
|
# 获取状态码并处理不同的重试逻辑
|
||||||
status_code = response.status_code if response is not None else None
|
status_code = response.status_code if response is not None else None
|
||||||
print(f"请求失败,状态码: {status_code}")
|
print(f"请求失败,状态码: {status_code}")
|
||||||
print("请求失败,完整的响应内容如下:")
|
print("请求失败,完整的响应内容如下:")
|
||||||
print(response.text) # 打印原始的响应内容,可能是 JSON 格式,也可能是其他格式
|
if response is not None:
|
||||||
|
print(response.text) # 打印原始的响应内容,可能是 JSON 格式,也可能是其他格式
|
||||||
|
|
||||||
# 如果是 429 错误
|
# 如果是 429 错误
|
||||||
if status_code == 429:
|
if status_code == 429:
|
||||||
@ -283,7 +345,11 @@ def doubao_model(full_user_query):
|
|||||||
|
|
||||||
# 如果到这里,说明所有尝试都失败了
|
# 如果到这里,说明所有尝试都失败了
|
||||||
print(f"请求失败,已达到最大重试次数。")
|
print(f"请求失败,已达到最大重试次数。")
|
||||||
return None
|
if need_extra:
|
||||||
|
return None, 0
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def generate_full_user_query(file_path, prompt_template):
|
def generate_full_user_query(file_path, prompt_template):
|
||||||
"""
|
"""
|
||||||
@ -357,8 +423,9 @@ if __name__ == "__main__":
|
|||||||
# processed_filepath = convert_pdf_to_markdown(pdf_path_2) # 转markdown格式
|
# processed_filepath = convert_pdf_to_markdown(pdf_path_2) # 转markdown格式
|
||||||
# processed_filepath = pdf2txt(pdf_path_2) #纯文本提取
|
# processed_filepath = pdf2txt(pdf_path_2) #纯文本提取
|
||||||
# user_query=generate_full_user_query(processed_filepath,prompt_template)
|
# user_query=generate_full_user_query(processed_filepath,prompt_template)
|
||||||
user_query="一年有多少天?"
|
# user_query="一年有多少天?"
|
||||||
res=doubao_model(user_query)
|
# res=doubao_model(user_query)
|
||||||
|
res=get_total_tokens("hh我是天才")
|
||||||
print(res)
|
print(res)
|
||||||
# print("--------------------")
|
# print("--------------------")
|
||||||
# print(user_query)
|
# print(user_query)
|
@ -101,7 +101,7 @@ def replace_latex_expressions(json_str):
|
|||||||
return fixed_str
|
return fixed_str
|
||||||
|
|
||||||
|
|
||||||
def extract_content_from_json(string, length_threshold=5000):
|
def extract_content_from_json(string):
|
||||||
"""
|
"""
|
||||||
输入字符串,尝试解析 JSON 数据:
|
输入字符串,尝试解析 JSON 数据:
|
||||||
1. 如果成功解析,返回字典。
|
1. 如果成功解析,返回字典。
|
||||||
@ -115,8 +115,6 @@ def extract_content_from_json(string, length_threshold=5000):
|
|||||||
match = re.search(r'\{[\s\S]*\}', string)
|
match = re.search(r'\{[\s\S]*\}', string)
|
||||||
if not match:
|
if not match:
|
||||||
print("未找到有效的 JSON 内容。")
|
print("未找到有效的 JSON 内容。")
|
||||||
if len(string) > length_threshold:
|
|
||||||
return string # 返回原始字符串
|
|
||||||
return {} # 返回空字典
|
return {} # 返回空字典
|
||||||
|
|
||||||
original_json = match.group(0)
|
original_json = match.group(0)
|
||||||
@ -157,15 +155,9 @@ def extract_content_from_json(string, length_threshold=5000):
|
|||||||
|
|
||||||
# 如果所有方法都失败,检查字符串长度
|
# 如果所有方法都失败,检查字符串长度
|
||||||
print("所有修复方法均失败。")
|
print("所有修复方法均失败。")
|
||||||
if len(string) > length_threshold:
|
return {} # 返回空字典
|
||||||
print("字符串长度超过阈值,返回原始字符串。")
|
|
||||||
return string # 返回原始字符串
|
|
||||||
else:
|
|
||||||
print("字符串长度不超过阈值,返回空字典。")
|
|
||||||
return {} # 返回空字典
|
|
||||||
|
|
||||||
def clean_json_string(json_string):
|
def clean_json_string(json_string):
|
||||||
# print(json_string)
|
|
||||||
"""清理JSON字符串,移除多余的反引号并解析为字典"""
|
"""清理JSON字符串,移除多余的反引号并解析为字典"""
|
||||||
return extract_content_from_json(json_string)
|
return extract_content_from_json(json_string)
|
||||||
|
|
||||||
|
@ -82,11 +82,6 @@ def continue_answer(original_query, original_answer, model_type=1, file_id=None)
|
|||||||
# 尝试解析为 JSON
|
# 尝试解析为 JSON
|
||||||
try:
|
try:
|
||||||
json_data = clean_json_string(full_answer)
|
json_data = clean_json_string(full_answer)
|
||||||
if not isinstance(json_data, dict):
|
|
||||||
print(f"警告: clean_json_string 返回的类型为 {type(json_data)},预期为 dict。")
|
|
||||||
print(json_data)
|
|
||||||
return {}
|
|
||||||
print("JSON 拼接成功且有效!")
|
|
||||||
return json_data
|
return json_data
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
print("JSON 拼接失败,错误信息如下:")
|
print("JSON 拼接失败,错误信息如下:")
|
||||||
@ -101,7 +96,7 @@ def process_continue_answers(questions_to_continue, model_type, file_id):
|
|||||||
- questions_to_continue (list of tuples): 需要继续回答的问题,每个元素是 (original_query, parsed_answer)。
|
- questions_to_continue (list of tuples): 需要继续回答的问题,每个元素是 (original_query, parsed_answer)。
|
||||||
- model_type (int): 指定使用的模型类型。
|
- model_type (int): 指定使用的模型类型。
|
||||||
- file_id (str): 可选的文件 ID,默认为 None。
|
- file_id (str): 可选的文件 ID,默认为 None。
|
||||||
|
- model_type
|
||||||
返回:
|
返回:
|
||||||
- dict: 继续回答后的结果合并。
|
- dict: 继续回答后的结果合并。
|
||||||
"""
|
"""
|
||||||
|
@ -3,6 +3,8 @@ import re
|
|||||||
import time
|
import time
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from flask_app.general.doubao import get_total_tokens
|
||||||
|
from flask_app.general.model_continue_query import process_continue_answers
|
||||||
from flask_app.general.通义千问long import upload_file, qianwen_long
|
from flask_app.general.通义千问long import upload_file, qianwen_long
|
||||||
|
|
||||||
|
|
||||||
@ -136,7 +138,7 @@ def parse_json_with_duplicates(raw_string):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
"""输入字符串,提取 { 和 } 之间的内容,并将其解析为字典"""
|
"""输入字符串,提取 { 和 } 之间的内容,并将其解析为字典"""
|
||||||
if not raw_string.strip():
|
if not raw_string or not raw_string.strip():
|
||||||
return {}
|
return {}
|
||||||
match = re.search(r'\{[\s\S]*\}', raw_string)
|
match = re.search(r'\{[\s\S]*\}', raw_string)
|
||||||
if match:
|
if match:
|
||||||
@ -147,8 +149,8 @@ def parse_json_with_duplicates(raw_string):
|
|||||||
print(f"json_utils: extract_content_from_json: JSON decode error: {e}")
|
print(f"json_utils: extract_content_from_json: JSON decode error: {e}")
|
||||||
return {}
|
return {}
|
||||||
else:
|
else:
|
||||||
print("json_utils: extract_content_from_json: No valid JSON content found.")
|
print("未找到有效的 JSON 内容。")
|
||||||
return {}
|
return {} # 返回空字典
|
||||||
|
|
||||||
# 防止外键只有一个'一包'的情况
|
# 防止外键只有一个'一包'的情况
|
||||||
def process_data_based_on_key(data):
|
def process_data_based_on_key(data):
|
||||||
@ -375,11 +377,23 @@ def combine_evaluation_standards(evaluation_method_path,invalid_path,zb_type):
|
|||||||
)
|
)
|
||||||
# 执行第二个查询
|
# 执行第二个查询
|
||||||
user_query = user_query_1 if zb_type == 1 else user_query_2
|
user_query = user_query_1 if zb_type == 1 else user_query_2
|
||||||
evaluation_res = qianwen_long(file_id, user_query) # 有些重复的键名,只有qianwen_long_text能保留
|
questions_to_continue = []
|
||||||
|
temp_final={}
|
||||||
|
evaluation_res = qianwen_long(file_id, user_query,2,1,True) # 有些重复的键名,只有qianwen_long_text能保留
|
||||||
|
message = evaluation_res[0]
|
||||||
|
total_tokens = evaluation_res[1]
|
||||||
# print(evaluation_res)
|
# print(evaluation_res)
|
||||||
# 清理和处理响应
|
# 清理和处理响应
|
||||||
cleaned_evaluation_res = parse_json_with_duplicates(evaluation_res) # 处理重复键名的情况
|
cleaned_evaluation_res = parse_json_with_duplicates(message) # 处理重复键名的情况
|
||||||
result_data = process_data_based_on_key(cleaned_evaluation_res) # 处理不知名外键的情况
|
# print(json.dumps(cleaned_evaluation_res,ensure_ascii=False,indent=4))
|
||||||
|
if not cleaned_evaluation_res and total_tokens > 5900:
|
||||||
|
questions_to_continue.append((user_query, evaluation_res))
|
||||||
|
else:
|
||||||
|
temp_final.update(cleaned_evaluation_res)
|
||||||
|
if questions_to_continue:
|
||||||
|
continued_results = process_continue_answers(questions_to_continue, 0, file_id)
|
||||||
|
temp_final.update(continued_results)
|
||||||
|
result_data = process_data_based_on_key(temp_final) # 处理不知名外键的情况
|
||||||
include = ['一包', '二包', '三包', '四包', '五包']
|
include = ['一包', '二包', '三包', '四包', '五包']
|
||||||
target_values = ['技术', '设计', '实施']
|
target_values = ['技术', '设计', '实施']
|
||||||
updated_jsons = {}
|
updated_jsons = {}
|
||||||
@ -423,8 +437,8 @@ def combine_evaluation_standards(evaluation_method_path,invalid_path,zb_type):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
start_time=time.time()
|
start_time=time.time()
|
||||||
# truncate_file=r"C:\Users\Administrator\Desktop\招标文件-采购类\tmp2\2024-新疆-塔城地区公安局食药环分局快检实验室项目_evaluation_method.pdf"
|
# truncate_file=r"C:\Users\Administrator\Desktop\招标文件-采购类\tmp2\2024-新疆-塔城地区公安局食药环分局快检实验室项目_evaluation_method.pdf"
|
||||||
evaluation_method_path = r'C:\Users\Administrator\Desktop\fsdownload\aba81749-5986-4492-8b4b-16db9c69a09d\ztbfile_evaluation_method.pdf'
|
evaluation_method_path = r'C:\Users\Administrator\Desktop\文件解析问题\文件解析问题\be901ea0-adc9-47b8-9ada-5c3bc0dd9434\ztbfile_evaluation_method.pdf'
|
||||||
invalid_path=r'C:\Users\Administrator\Desktop\fsdownload\91399aa4-1ee8-447d-a05b-03cd8d15ced5\ztbfile_invalid.pdf'
|
invalid_path=r'C:\Users\Administrator\Desktop\文件解析问题\文件解析问题\be901ea0-adc9-47b8-9ada-5c3bc0dd9434\ztbfile.docx'
|
||||||
# truncate_file = "C:\\Users\\Administrator\\Desktop\\货物标\\output2\\2-招标文件(统计局智能终端二次招标)_evaluation_method.pdf"
|
# truncate_file = "C:\\Users\\Administrator\\Desktop\\货物标\\output2\\2-招标文件(统计局智能终端二次招标)_evaluation_method.pdf"
|
||||||
# truncate_file="C:\\Users\\Administrator\\Desktop\\货物标\\output2\\广水市妇幼招标文件最新(W改)_evaluation_method.pdf"
|
# truncate_file="C:\\Users\\Administrator\\Desktop\\货物标\\output2\\广水市妇幼招标文件最新(W改)_evaluation_method.pdf"
|
||||||
# truncate_file = "C:\\Users\\Administrator\\Desktop\\fsdownload\\2d481945-1f82-45a5-8e56-7fafea4a7793\\ztbfile_evaluation_method.pdf"
|
# truncate_file = "C:\\Users\\Administrator\\Desktop\\fsdownload\\2d481945-1f82-45a5-8e56-7fafea4a7793\\ztbfile_evaluation_method.pdf"
|
||||||
|
@ -237,7 +237,7 @@ def pure_assistant():
|
|||||||
)
|
)
|
||||||
return assistant
|
return assistant
|
||||||
|
|
||||||
def llm_call(question, knowledge_name,file_id, result_queue, ans_index, llm_type):
|
def llm_call(question, knowledge_name,file_id, result_queue, ans_index, llm_type,need_extra=False):
|
||||||
"""
|
"""
|
||||||
调用不同的 LLM 模型并将结果放入结果队列。
|
调用不同的 LLM 模型并将结果放入结果队列。
|
||||||
"""
|
"""
|
||||||
@ -251,14 +251,14 @@ def llm_call(question, knowledge_name,file_id, result_queue, ans_index, llm_type
|
|||||||
elif llm_type==2:
|
elif llm_type==2:
|
||||||
print(f"qianwen_long! question:{question}")
|
print(f"qianwen_long! question:{question}")
|
||||||
# qianwen_res,usage = qianwen_long(file_id,question) #有bug
|
# qianwen_res,usage = qianwen_long(file_id,question) #有bug
|
||||||
qianwen_res = qianwen_long(file_id, question)
|
qianwen_res = qianwen_long(file_id, question,2,1,need_extra)
|
||||||
if not qianwen_res:
|
if not qianwen_res:
|
||||||
result_queue.put((ans_index, None)) # 如果为空字符串,直接返回 None
|
result_queue.put((ans_index, None)) # 如果为空字符串,直接返回 None
|
||||||
else:
|
else:
|
||||||
result_queue.put((ans_index, (question, qianwen_res)))
|
result_queue.put((ans_index, (question, qianwen_res)))
|
||||||
elif llm_type==3:
|
elif llm_type==3:
|
||||||
# print(f"doubao! question:{question}")
|
# print(f"doubao! question:{question}")
|
||||||
doubao_res=doubao_model(question)
|
doubao_res=doubao_model(question,need_extra)
|
||||||
if not doubao_res:
|
if not doubao_res:
|
||||||
result_queue.put((ans_index, None)) # 如果为空字符串,直接返回 None
|
result_queue.put((ans_index, None)) # 如果为空字符串,直接返回 None
|
||||||
else:
|
else:
|
||||||
@ -271,14 +271,14 @@ def llm_call(question, knowledge_name,file_id, result_queue, ans_index, llm_type
|
|||||||
print(f"LLM 调用失败,查询索引 {ans_index},错误:{e}")
|
print(f"LLM 调用失败,查询索引 {ans_index},错误:{e}")
|
||||||
result_queue.put((ans_index, None)) # 使用 None 作为失败的占位符
|
result_queue.put((ans_index, None)) # 使用 None 作为失败的占位符
|
||||||
|
|
||||||
def multi_threading(queries, knowledge_name="", file_id="", llm_type=1):
|
def multi_threading(queries, knowledge_name="", file_id="", llm_type=1,need_extra=False):
|
||||||
if not queries:
|
if not queries:
|
||||||
return []
|
return []
|
||||||
print("多线程提问:starting multi_threading...")
|
print("多线程提问:starting multi_threading...")
|
||||||
result_queue = queue.Queue()
|
result_queue = queue.Queue()
|
||||||
with concurrent.futures.ThreadPoolExecutor(max_workers=60) as executor:
|
with concurrent.futures.ThreadPoolExecutor(max_workers=60) as executor:
|
||||||
future_to_index = {
|
future_to_index = {
|
||||||
executor.submit(llm_call, query, knowledge_name, file_id, result_queue, index, llm_type): index
|
executor.submit(llm_call, query, knowledge_name, file_id, result_queue, index, llm_type,need_extra): index
|
||||||
for index, query in enumerate(queries)
|
for index, query in enumerate(queries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import re
|
|||||||
from functools import cmp_to_key
|
from functools import cmp_to_key
|
||||||
|
|
||||||
from flask_app.general.json_utils import clean_json_string
|
from flask_app.general.json_utils import clean_json_string
|
||||||
|
from flask_app.general.model_continue_query import process_continue_answers
|
||||||
from flask_app.general.通义千问long import upload_file, qianwen_long_stream
|
from flask_app.general.通义千问long import upload_file, qianwen_long_stream
|
||||||
|
|
||||||
|
|
||||||
@ -459,9 +460,18 @@ def get_requirements_with_gpt(merged_baseinfo_path, selection):
|
|||||||
return {"error": f"无效的 selection 值: {selection}. 请选择 1、2 或 3。"}
|
return {"error": f"无效的 selection 值: {selection}. 请选择 1、2 或 3。"}
|
||||||
# 调用大模型并处理响应
|
# 调用大模型并处理响应
|
||||||
try:
|
try:
|
||||||
res = qianwen_long_stream(file_id, user_query,2)
|
questions_to_continue = []
|
||||||
cleaned_res = clean_json_string(res)
|
qianwen_res = qianwen_long_stream(file_id, user_query,2,1,True)
|
||||||
return cleaned_res
|
message = qianwen_res[0]
|
||||||
|
parsed = clean_json_string(message)
|
||||||
|
total_tokens = qianwen_res[1]
|
||||||
|
if not parsed and total_tokens >5900:
|
||||||
|
questions_to_continue.append((user_query, message))
|
||||||
|
if questions_to_continue:
|
||||||
|
continued_results = process_continue_answers(questions_to_continue, 0, file_id)
|
||||||
|
return continued_results
|
||||||
|
else:
|
||||||
|
return parsed
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"error": "调用大模型失败"}
|
return {"error": "调用大模型失败"}
|
||||||
|
|
||||||
|
@ -79,9 +79,8 @@ def extract_error_details(error_message):
|
|||||||
|
|
||||||
return error_code, error_code_string
|
return error_code, error_code_string
|
||||||
@shared_rate_limit
|
@shared_rate_limit
|
||||||
def qianwen_long(file_id, user_query, max_retries=2, backoff_factor=1.0):
|
def qianwen_long(file_id, user_query, max_retries=2, backoff_factor=1.0, need_extra=False):
|
||||||
logger = logging.getLogger('model_log') # 通过日志名字获取记录器
|
logger = logging.getLogger('model_log') # 通过日志名字获取记录器
|
||||||
# logger.info(f"Received query: {user_query}")
|
|
||||||
"""
|
"""
|
||||||
基于上传的文件 ID 和用户查询生成响应,并在失败时自动重试。
|
基于上传的文件 ID 和用户查询生成响应,并在失败时自动重试。
|
||||||
参数:
|
参数:
|
||||||
@ -89,14 +88,12 @@ def qianwen_long(file_id, user_query, max_retries=2, backoff_factor=1.0):
|
|||||||
- user_query: 用户查询
|
- user_query: 用户查询
|
||||||
- max_retries: 最大重试次数(默认 2 次)
|
- max_retries: 最大重试次数(默认 2 次)
|
||||||
- backoff_factor: 指数退避的基础等待时间(默认 1.0 秒)
|
- backoff_factor: 指数退避的基础等待时间(默认 1.0 秒)
|
||||||
|
- need_extra: 是否需要返回额外数据(默认 False)
|
||||||
"""
|
"""
|
||||||
# print("call qianwen_long...")
|
|
||||||
|
|
||||||
client = OpenAI(
|
client = OpenAI(
|
||||||
api_key=os.getenv("DASHSCOPE_API_KEY"),
|
api_key=os.getenv("DASHSCOPE_API_KEY"),
|
||||||
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
|
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
for attempt in range(1, max_retries + 2): # +1 是为了包括初始调用
|
for attempt in range(1, max_retries + 2): # +1 是为了包括初始调用
|
||||||
try:
|
try:
|
||||||
# 调用 API
|
# 调用 API
|
||||||
@ -115,15 +112,19 @@ def qianwen_long(file_id, user_query, max_retries=2, backoff_factor=1.0):
|
|||||||
],
|
],
|
||||||
stream=False
|
stream=False
|
||||||
)
|
)
|
||||||
|
token_usage = completion.usage.completion_tokens
|
||||||
# 如果调用成功,返回响应内容
|
# 如果调用成功,返回响应内容
|
||||||
return completion.choices[0].message.content
|
if need_extra:
|
||||||
|
return completion.choices[0].message.content, token_usage
|
||||||
|
else:
|
||||||
|
return completion.choices[0].message.content
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
# 提取错误代码
|
# 提取错误代码
|
||||||
error_code, error_code_string = extract_error_details(str(exc))
|
error_code, error_code_string = extract_error_details(str(exc))
|
||||||
logger.error(f"第 {attempt} 次尝试失败,查询:'{user_query}',错误:{exc}", exc_info=True)
|
logger.error(f"第 {attempt} 次尝试失败,查询:'{user_query}',错误:{exc}", exc_info=True)
|
||||||
|
|
||||||
if error_code == 429: #超qps/qpm
|
if error_code == 429: # 超 qps/qpm
|
||||||
if attempt <= max_retries:
|
if attempt <= max_retries:
|
||||||
sleep_time = backoff_factor * (2 ** (attempt - 1)) # 指数退避
|
sleep_time = backoff_factor * (2 ** (attempt - 1)) # 指数退避
|
||||||
logger.warning(f"错误代码为 429,将在 {sleep_time} 秒后重试...")
|
logger.warning(f"错误代码为 429,将在 {sleep_time} 秒后重试...")
|
||||||
@ -131,11 +132,22 @@ def qianwen_long(file_id, user_query, max_retries=2, backoff_factor=1.0):
|
|||||||
else:
|
else:
|
||||||
print(f"查询 '{user_query}' 的所有 {max_retries + 1} 次尝试均失败(429 错误)。")
|
print(f"查询 '{user_query}' 的所有 {max_retries + 1} 次尝试均失败(429 错误)。")
|
||||||
break
|
break
|
||||||
elif error_code == 400 and error_code_string in ['data_inspection_failed', 'ResponseTimeout','DataInspectionFailed','response_timeout','request_timeout',"RequestTimeOut"]:
|
elif error_code == 400 and error_code_string in [
|
||||||
|
'data_inspection_failed', 'ResponseTimeout', 'DataInspectionFailed',
|
||||||
|
'response_timeout', 'request_timeout', "RequestTimeOut"
|
||||||
|
]:
|
||||||
logger.warning(f"错误代码为 400 - {error_code_string},将调用 qianwen_long_stream 执行一次...")
|
logger.warning(f"错误代码为 400 - {error_code_string},将调用 qianwen_long_stream 执行一次...")
|
||||||
try:
|
try:
|
||||||
# 超时就调用 qianwen_long_stream
|
# 超时就调用 qianwen_long_stream
|
||||||
return qianwen_long_stream(file_id, user_query, max_retries=0) # 禁用内部重试
|
stream_result = qianwen_long_stream(file_id, user_query, max_retries=0) # 禁用内部重试
|
||||||
|
if need_extra:
|
||||||
|
if isinstance(stream_result, tuple) and len(stream_result) == 2:
|
||||||
|
return stream_result[0], stream_result[1] # 返回内容和默认的 token_usage=0
|
||||||
|
else:
|
||||||
|
logger.error(f"qianwen_long_stream 返回值不符合预期(需要元组)。返回值: {stream_result}")
|
||||||
|
return "", 0 # 处理异常返回
|
||||||
|
else:
|
||||||
|
return stream_result # need_extra=False,直接返回内容
|
||||||
except Exception as stream_exc:
|
except Exception as stream_exc:
|
||||||
logger.error(f"调用 qianwen_long_stream 时出错:{stream_exc}", exc_info=True)
|
logger.error(f"调用 qianwen_long_stream 时出错:{stream_exc}", exc_info=True)
|
||||||
break # 跳出循环,不再重试
|
break # 跳出循环,不再重试
|
||||||
@ -144,12 +156,15 @@ def qianwen_long(file_id, user_query, max_retries=2, backoff_factor=1.0):
|
|||||||
logger.error(f"遇到非 429 或非 'data_inspection_failed' 的 400 错误(错误代码:{error_code}),不进行重试。")
|
logger.error(f"遇到非 429 或非 'data_inspection_failed' 的 400 错误(错误代码:{error_code}),不进行重试。")
|
||||||
break
|
break
|
||||||
|
|
||||||
return ""
|
# 在所有重试失败的情况下返回
|
||||||
|
if need_extra:
|
||||||
|
return "", 0 # 返回默认值和 token_usage = 0
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
@shared_rate_limit
|
@shared_rate_limit
|
||||||
def qianwen_long_stream(file_id, user_query, max_retries = 2, backoff_factor = 1.0):
|
def qianwen_long_stream(file_id, user_query, max_retries=2, backoff_factor=1.0, need_extra=False):
|
||||||
logger = logging.getLogger('model_log') # 通过日志名字获取记录器
|
logger = logging.getLogger('model_log') # 通过日志名字获取记录器
|
||||||
# logger.info(f"Received query: {user_query}")
|
|
||||||
"""
|
"""
|
||||||
使用之前上传的文件,根据用户查询生成响应,并实时显示流式输出。
|
使用之前上传的文件,根据用户查询生成响应,并实时显示流式输出。
|
||||||
参数:
|
参数:
|
||||||
@ -157,8 +172,10 @@ def qianwen_long_stream(file_id, user_query, max_retries = 2, backoff_factor = 1
|
|||||||
- user_query: 用户查询
|
- user_query: 用户查询
|
||||||
- max_retries: 最大重试次数(默认 2 次)
|
- max_retries: 最大重试次数(默认 2 次)
|
||||||
- backoff_factor: 指数退避的基础等待时间(默认 1.0 秒)
|
- backoff_factor: 指数退避的基础等待时间(默认 1.0 秒)
|
||||||
|
- need_extra: 是否需要返回额外数据(默认 False)
|
||||||
返回:
|
返回:
|
||||||
- Optional[str]: 成功时返回响应内容,失败时返回空字符串
|
- 当 need_extra=False 时: 返回响应内容 (str)
|
||||||
|
- 当 need_extra=True 时: 返回 (响应内容, token_usage)
|
||||||
"""
|
"""
|
||||||
print("调用 qianwen-long stream...")
|
print("调用 qianwen-long stream...")
|
||||||
|
|
||||||
@ -166,14 +183,13 @@ def qianwen_long_stream(file_id, user_query, max_retries = 2, backoff_factor = 1
|
|||||||
api_key=os.getenv("DASHSCOPE_API_KEY"),
|
api_key=os.getenv("DASHSCOPE_API_KEY"),
|
||||||
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
|
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
for attempt in range(1, max_retries + 2): # +1 是为了包括初始调用
|
for attempt in range(1, max_retries + 2): # +1 是为了包括初始调用
|
||||||
try:
|
try:
|
||||||
|
completion_tokens = 0 # 初始化 completion_tokens 为 0
|
||||||
# 生成基于文件ID的响应
|
# 生成基于文件ID的响应
|
||||||
completion = client.chat.completions.create(
|
completion = client.chat.completions.create(
|
||||||
model="qwen-long",
|
model="qwen-long",
|
||||||
temperature=0.4,
|
temperature=0.4,
|
||||||
max_tokens=5000,
|
|
||||||
messages=[
|
messages=[
|
||||||
{
|
{
|
||||||
'role': 'system',
|
'role': 'system',
|
||||||
@ -184,16 +200,21 @@ def qianwen_long_stream(file_id, user_query, max_retries = 2, backoff_factor = 1
|
|||||||
'content': user_query
|
'content': user_query
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
stream=True # 启用流式响应
|
stream=True, # 启用流式响应
|
||||||
|
stream_options={"include_usage": True}
|
||||||
)
|
)
|
||||||
|
|
||||||
full_response = "" # 用于存储完整的响应内容
|
full_response = "" # 用于存储完整的响应内容
|
||||||
|
|
||||||
for chunk in completion:
|
for chunk in completion:
|
||||||
|
print(chunk)
|
||||||
if hasattr(chunk, 'to_dict'):
|
if hasattr(chunk, 'to_dict'):
|
||||||
chunk_data = chunk.to_dict()
|
chunk_data = chunk.to_dict()
|
||||||
else:
|
else:
|
||||||
chunk_data = json.loads(chunk.model_dump_json())
|
chunk_data = json.loads(chunk.model_dump_json())
|
||||||
|
usage = chunk_data.get('usage')
|
||||||
|
if usage is not None:
|
||||||
|
completion_tokens = usage.get('completion_tokens', 0)
|
||||||
choices = chunk_data.get('choices', [])
|
choices = chunk_data.get('choices', [])
|
||||||
if not choices:
|
if not choices:
|
||||||
continue
|
continue
|
||||||
@ -202,11 +223,15 @@ def qianwen_long_stream(file_id, user_query, max_retries = 2, backoff_factor = 1
|
|||||||
content = delta.get('content', '')
|
content = delta.get('content', '')
|
||||||
if content:
|
if content:
|
||||||
full_response += content
|
full_response += content
|
||||||
# print(content, end='', flush=True) # 实时打印内容
|
# 实时打印内容(可以取消注释下面一行以实时输出)
|
||||||
|
# print(content, end='', flush=True)
|
||||||
if choice.get('finish_reason'):
|
if choice.get('finish_reason'):
|
||||||
break
|
break
|
||||||
|
|
||||||
return full_response # 返回完整的响应内容
|
if need_extra:
|
||||||
|
return full_response, completion_tokens
|
||||||
|
else:
|
||||||
|
return full_response
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
# 提取错误代码
|
# 提取错误代码
|
||||||
@ -221,8 +246,10 @@ def qianwen_long_stream(file_id, user_query, max_retries = 2, backoff_factor = 1
|
|||||||
else:
|
else:
|
||||||
logger.error(f"查询 '{user_query}' 的所有 {max_retries + 1} 次尝试均失败(429 错误)。")
|
logger.error(f"查询 '{user_query}' 的所有 {max_retries + 1} 次尝试均失败(429 错误)。")
|
||||||
break
|
break
|
||||||
elif error_code == 400 and error_code_string in ['data_inspection_failed', 'ResponseTimeout',
|
elif error_code == 400 and error_code_string in [
|
||||||
'DataInspectionFailed', 'response_timeout','RequestTimeOut','request_timeout']:
|
'data_inspection_failed', 'ResponseTimeout', 'DataInspectionFailed',
|
||||||
|
'response_timeout', 'request_timeout', "RequestTimeOut"
|
||||||
|
]:
|
||||||
if attempt == 1: # 只重试一次
|
if attempt == 1: # 只重试一次
|
||||||
logger.warning(f"错误代码为 400 - {error_code_string},将立即重试...")
|
logger.warning(f"错误代码为 400 - {error_code_string},将立即重试...")
|
||||||
continue # 直接跳到下一次循环(即重试一次)
|
continue # 直接跳到下一次循环(即重试一次)
|
||||||
@ -235,7 +262,10 @@ def qianwen_long_stream(file_id, user_query, max_retries = 2, backoff_factor = 1
|
|||||||
break
|
break
|
||||||
|
|
||||||
# 如果所有尝试都失败了,返回空字符串
|
# 如果所有尝试都失败了,返回空字符串
|
||||||
return ""
|
if need_extra:
|
||||||
|
return "", 0
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
@shared_rate_limit
|
@shared_rate_limit
|
||||||
def qianwen_long_text(file_id, user_query):
|
def qianwen_long_text(file_id, user_query):
|
||||||
@ -276,8 +306,13 @@ if __name__ == "__main__":
|
|||||||
file_path = r"C:\Users\Administrator\Desktop\货物标\截取test\2-招标文件_before.pdf"
|
file_path = r"C:\Users\Administrator\Desktop\货物标\截取test\2-招标文件_before.pdf"
|
||||||
file_id = upload_file(file_path)
|
file_id = upload_file(file_path)
|
||||||
print(file_id)
|
print(file_id)
|
||||||
|
user_query1 = "该招标文件前附表中的项目名称是什么,请以json格式返回给我"
|
||||||
|
# res1,res2=qianwen_long_stream(file_id,user_query1,2,1,True)
|
||||||
|
res1,res2= qianwen_long_stream(file_id, user_query1, 2, 1,True)
|
||||||
|
print(res1)
|
||||||
|
print(res2)
|
||||||
|
#
|
||||||
#
|
#
|
||||||
# user_query1 = "该招标文件前附表中的项目名称是什么,请以json格式返回给我"
|
|
||||||
# user_query2 = ("请提供文件中关于资格审查的具体内容和标准。")
|
# user_query2 = ("请提供文件中关于资格审查的具体内容和标准。")
|
||||||
# start_time=time.time()
|
# start_time=time.time()
|
||||||
# # First query
|
# # First query
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@ import json
|
|||||||
import re
|
import re
|
||||||
from PyPDF2 import PdfReader
|
from PyPDF2 import PdfReader
|
||||||
import textwrap
|
import textwrap
|
||||||
from flask_app.general.doubao import read_txt_to_string, pdf2txt
|
from flask_app.general.doubao import read_txt_to_string, pdf2txt, get_total_tokens
|
||||||
from flask_app.general.json_utils import combine_json_results, clean_json_string
|
from flask_app.general.json_utils import combine_json_results, clean_json_string
|
||||||
from flask_app.general.model_continue_query import continue_answer, process_continue_answers
|
from flask_app.general.model_continue_query import continue_answer, process_continue_answers
|
||||||
from flask_app.general.通义千问long import upload_file, qianwen_long_stream
|
from flask_app.general.通义千问long import upload_file, qianwen_long_stream
|
||||||
@ -303,7 +303,7 @@ def get_business_requirements(procurement_path, processed_filepath, model_type):
|
|||||||
future = executor.submit(doubao_model, busi_user_query)
|
future = executor.submit(doubao_model, busi_user_query)
|
||||||
else:
|
else:
|
||||||
# 使用 qianwen_long_stream 并传入 file_id
|
# 使用 qianwen_long_stream 并传入 file_id
|
||||||
future = executor.submit(qianwen_long_stream, file_id, busi_user_query, 2, 1)
|
future = executor.submit(qianwen_long_stream, file_id, busi_user_query, 2, 1,True)
|
||||||
futures.append(future)
|
futures.append(future)
|
||||||
future_to_query[future] = busi_user_query # 映射 future 到 busi_user_query
|
future_to_query[future] = busi_user_query # 映射 future 到 busi_user_query
|
||||||
|
|
||||||
@ -313,24 +313,25 @@ def get_business_requirements(procurement_path, processed_filepath, model_type):
|
|||||||
future = executor.submit(doubao_model, tech_user_query)
|
future = executor.submit(doubao_model, tech_user_query)
|
||||||
else:
|
else:
|
||||||
# 使用 qianwen_long_stream 并传入 file_id
|
# 使用 qianwen_long_stream 并传入 file_id
|
||||||
future = executor.submit(qianwen_long_stream, file_id, tech_user_query, 2, 1)
|
future = executor.submit(qianwen_long_stream, file_id, tech_user_query, 2, 1,True)
|
||||||
futures.append(future)
|
futures.append(future)
|
||||||
future_to_query[future] = tech_user_query # 映射 future 到 tech_user_query
|
future_to_query[future] = tech_user_query # 映射 future 到 tech_user_query
|
||||||
# 收集需要继续回答的问题
|
# 收集需要继续回答的问题
|
||||||
initial_results = {}
|
initial_results = {}
|
||||||
|
max_tokens = 3900 if model_type == 1 else 5900
|
||||||
# 获取结果
|
# 获取结果
|
||||||
for future in concurrent.futures.as_completed(futures):
|
for future in concurrent.futures.as_completed(futures):
|
||||||
original_query = future_to_query[future] # 获取对应的 original_query
|
original_query = future_to_query[future] # 获取对应的 original_query
|
||||||
try:
|
try:
|
||||||
result = future.result()
|
result = future.result()
|
||||||
if result: # 确保结果不为空
|
if result: # 确保结果不为空
|
||||||
parsed = clean_json_string(result)
|
message = result[0]
|
||||||
if isinstance(parsed, str): # flag为截断标记,如果不能解析且len(response)>5000,执行继续问答!
|
parsed = clean_json_string(message)
|
||||||
questions_to_continue.append((original_query, parsed))
|
total_tokens = result[1]
|
||||||
elif isinstance(parsed, dict):
|
if not parsed and total_tokens > max_tokens:
|
||||||
initial_results.update(parsed)
|
questions_to_continue.append((original_query, message))
|
||||||
else:
|
else:
|
||||||
print(f"Parsed result is not a dict or str: {parsed}")
|
initial_results.update(parsed)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"An error occurred: {e}")
|
print(f"An error occurred: {e}")
|
||||||
# 处理需要继续回答的问题
|
# 处理需要继续回答的问题
|
||||||
|
@ -485,20 +485,23 @@ def get_technical_requirements(invalid_path,processed_filepath,model_type=1):
|
|||||||
modified_grouped_key, "")
|
modified_grouped_key, "")
|
||||||
queries.append(new_query)
|
queries.append(new_query)
|
||||||
if model_type:
|
if model_type:
|
||||||
results = multi_threading(queries, "", "", 3) # 豆包
|
results = multi_threading(queries, "", "", 3,True) # 豆包
|
||||||
else:
|
else:
|
||||||
results = multi_threading(queries, "", file_id, 2) # 豆包
|
results = multi_threading(queries, "", file_id, 2,True) # 豆包
|
||||||
temp_final={}
|
temp_final={}
|
||||||
if not results:
|
if not results:
|
||||||
print("errror!未获得大模型的回答!")
|
print("errror!未获得大模型的回答!")
|
||||||
else:
|
else:
|
||||||
# 第一步:收集需要调用 `continue_answer` 的问题和解析结果
|
# 第一步:收集需要调用 `continue_answer` 的问题和解析结果
|
||||||
questions_to_continue = [] # 存储需要调用 continue_answer 的 (question, parsed)
|
questions_to_continue = [] # 存储需要调用 continue_answer 的 (question, parsed)
|
||||||
|
max_tokens=3900 if model_type==1 else 5900
|
||||||
for question, response in results:
|
for question, response in results:
|
||||||
parsed=clean_json_string(response)
|
message=response[0]
|
||||||
if isinstance(parsed, str): #flag为截断标记,如果不能解析且len(response)>5000,执行继续问答!
|
parsed = clean_json_string(message)
|
||||||
questions_to_continue.append((question, parsed))
|
total_tokens=response[1]
|
||||||
elif isinstance(parsed, dict):
|
if not parsed and total_tokens>max_tokens:
|
||||||
|
questions_to_continue.append((question, message))
|
||||||
|
else:
|
||||||
temp_final.update(parsed)
|
temp_final.update(parsed)
|
||||||
# 第二步:多线程处理需要调用 `continue_answer` 的问题
|
# 第二步:多线程处理需要调用 `continue_answer` 的问题
|
||||||
if questions_to_continue:
|
if questions_to_continue:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user