更新.gitignore文件以忽略日志文件,新增api_key.txt文件以管理API密钥,并在对话转工单和查询完整性判断模块中添加日志记录和堆栈跟踪功能,提升错误处理和调试能力。

This commit is contained in:
2025-06-20 09:43:37 +08:00
parent e0e893eb09
commit 450a632ad2
6 changed files with 196 additions and 127 deletions
+24 -10
View File
@@ -11,10 +11,23 @@ from langchain.output_parsers import PydanticOutputParser
from rag2_0.tool.ModelTool import OpenAiLLM
from dotenv import load_dotenv
import httpx
import traceback
import re
import logging
load_dotenv()
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(),
logging.FileHandler('dialogue_to_workorder.log', encoding='utf-8')
]
)
logger = logging.getLogger("dialogue_to_workorder")
# ================ 模型定义 ================
class UserQuestionAndSolution(BaseModel):
user_question: str = Field(description="用户的核心问题")
@@ -60,12 +73,13 @@ def retry_llm_call(max_retries=3, delay=2):
except Exception as e:
last_exception = e
retries += 1
print(f"LLM调用失败,正在进行第{retries}次重试: {str(e)}")
logger.warning(f"LLM调用失败,正在进行第{retries}次重试: {str(e)}")
if retries < max_retries:
time.sleep(delay*retries)
stack_trace = traceback.format_exc()
logger.error(f"LLM调用失败,堆栈跟踪信息:\n{stack_trace}")
# 所有重试都失败后,抛出最后一次的异常
print(f"LLM调用失败,已达到最大重试次数{max_retries}")
logger.error(f"LLM调用失败,已达到最大重试次数{max_retries}")
raise last_exception
return wrapper
@@ -472,8 +486,8 @@ class DialogueToWorkorder:
df = pd.read_excel(conversation_excel_path)
# 检查数据框的列
print(f"Excel文件列名: {df.columns.tolist()}")
print(f"数据总行数: {len(df)}")
logger.info(f"Excel文件列名: {df.columns.tolist()}")
logger.info(f"数据总行数: {len(df)}")
# 解析产品详情
product_detail_dict = self.parse_product_detail_excel(product_detail_excel_path)
@@ -482,7 +496,7 @@ class DialogueToWorkorder:
conversation_dict = self.group_conversations_by_id(df)
# 限制处理的会话数量为前2000个
if len(conversation_dict) > 2000:
print(f"会话总数为 {len(conversation_dict)},限制处理前2000个会话")
logger.info(f"会话总数为 {len(conversation_dict)},限制处理前2000个会话")
# 获取所有会话ID
conversation_ids = list(conversation_dict.keys())
# 只保留前2000个会话
@@ -492,7 +506,7 @@ class DialogueToWorkorder:
}
conversation_dict = limited_conversation_dict
else:
print(f"会话总数为 {len(conversation_dict)},处理全部会话")
logger.info(f"会话总数为 {len(conversation_dict)},处理全部会话")
# 使用线程池处理每个会话
workorder_dict_list = []
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
@@ -509,9 +523,9 @@ class DialogueToWorkorder:
result_workorders = future.result()
# 将每个会话的所有工单添加到总列表中
workorder_dict_list.extend(result_workorders)
print(f"完成处理会话ID: {conversation_id},生成工单数量: {len(result_workorders)}")
logger.info(f"完成处理会话ID: {conversation_id},生成工单数量: {len(result_workorders)}")
except Exception as exc:
print(f"处理会话ID: {conversation_id} 时发生错误: {exc}")
logger.error(f"处理会话ID: {conversation_id} 时发生错误: {exc}")
return workorder_dict_list
@@ -581,7 +595,7 @@ class DialogueToWorkorder:
col_letter = chr(64 + i // 26) + chr(65 + i % 26)
worksheet.column_dimensions[col_letter].width = column_widths[column]
print(f"结果已保存到 {output_file}")
logger.info(f"结果已保存到 {output_file}")
return output_file
+4 -1
View File
@@ -22,6 +22,7 @@ import os
import time
import re
import argparse
import traceback
from pathlib import Path
from rag2_0.tool.ModelTool import OpenAiLLM
from rag2_0.tool.APIKeyManager import APIKeyManager
@@ -194,8 +195,10 @@ class QueryCompletenessJudge:
retry_delay *= 2
else:
# 已达到最大重试次数,返回错误
stack_trace = traceback.format_exc()
print(f"错误: 经过 {max_retries} 次重试后仍然失败: {str(e)}")
return False, f"错误: 经过 {max_retries} 次重试后仍然失败: {str(e)}"
print(f"堆栈跟踪信息:\n{stack_trace}")
return False, f"错误: 经过 {max_retries} 次重试后仍然失败: {str(e)}\n堆栈摘要: {str(e).__class__.__name__}"
# 不应该到达这里,但为了代码完整性添加
return False, "未知错误:重试机制逻辑错误"
+45 -111
View File
@@ -17,104 +17,6 @@ import requests
# sk-bdagppigfxexcofiossccywvcqggbpywjapkdbtqycbgvqpz
# sk-dvbaktabkdwdpjgxyoozlwnejosjyhdgqwllfeborqahndxs
API_KEY_LIST=[
"sk-kvgfuqeqvpmfsccykyoohheshclcrtvjlnewratvrjpkpbkc",
"sk-kzhxlqvqcxlnbdgnpalqnzumkmspepkttkgbophnkqanainw",
"sk-tovmogiablsoeabwgqyvevpcfichyjpuzqdymmvksspdrtqt",
"sk-yvyvoiegjrdlgihnxlaaznzdhnvpmfowtwmofomcodaoeaqs",
"sk-vccwuaomxhjcszjhheipoqqmsnuetasiveombkyrptstesbi",
"sk-mxbapcczwjsyrictwgigxgcvdgptyfrlynrewqioegqwrggv",
"sk-dujjzxrknevesbagqgqmuffxsosjoueviubnmodoormlmlzt",
"sk-rpptsvdeifcnyfkkrwnphgkrlchrqrbkglxrmztdzvfutdor",
"sk-lsukfggzghmdhtfhqcbmlfqabbtapwpuxnvtwshqqqlaesie",
"sk-aulumxzhvaladchcwgmsxidtdsvzytbpvzqgfuvcxlwbwcgl",
"sk-tzdqzroakecvseclcmrbhdnepveatybhhzxfpzxzgirpqcdy",
"sk-otxxemniwhxkdvroszmmkitswwuykosnqoldrkzdoflqpgvw",
"sk-zlruqobfdbjebyyvkmehakpcvfgnlfbdlbfrepusazzckbnv",
"sk-zryimztrlkgvcaiolarhvbcewmhwruhqfcndbylonzlqvdox",
"sk-rczjqufgdisqplkrmvhaxmdgcboluvxympvzljlreuqeeviq",
"sk-xfnvcksdgwufsktvmhpqrwpgovsxxtaeehtxnaqjtxmubqzl",
"sk-gcostftlutooxzsnqefcgyfxqytidvfjpxhbuxppgatwczoq",
"sk-wwonvjnowbcxmoyoluynnkjwerghspzdulyidskunkordaft",
"sk-rbuykocecbdoqteveeggwzvrhbvisgaerffexjsnyvjefhdk",
"sk-qmrkfvvbbfssuoreyvwqawoveyowuvxviqzqknotyweqmuog",
"sk-nprpuknjmikvoaxnwgyshwwwtnifvixpuqtzkzmcacdnvoib",
"sk-xanwnicepdxfqrfejzuxjcrhdsglfypkoxlcmmtamrtjkork",
"sk-lvtdgodiaurqyiwdxtdrgxifguychhccqlqkhqctscvqbfgi",
"sk-aedlbtlmqcttxwnvlfmxzaysamocamqxjceoyqjfgpcowybw",
"sk-fahdvndjblyjlizamvwcrxnilsgmbgbvwssxgquhkezgpqne",
"sk-tzludgttzxvpvwayazdbppbauvathdtccafjrhojpemucgyi",
"sk-hrbroidbfusidwnsmxenuzljxgdzzxiimlezygxplavnxjik",
"sk-ylgoiqxmtxeojdnonthxtweungyzldaqarvjxlqyztlvyrff",
"sk-asuqbqwdhjcqnvtjlwufyrkrwkobnrbmukzarvcctsgjipdp",
"sk-dpgpymiydutoexgvkajwgahagnfmcqzafwulccudnzvleifz",
"sk-nbksjgcngsayoumnsdbkcpnqivnvxjenwpzuazzrkhnsgeoo",
"sk-iaafvpjyqiocgzchbdldbkgcffqniahkcbgoviuevuogulcm",
"sk-muvjguqeshyimzowqnqgxwpsgujlpkqgrisxsimthtyrpypx",
"sk-jgybgyayxlwoxeijgrjcneqlyusleohgbliuwpsuhocrjsmk",
"sk-wzjsmwxcbbpcrqivqfzjwufqqjtlwejtncnvbpeicznkwiuh",
"sk-izdjicdoyillktsihkiapuvwebisehtlgykozrvzfkgncwsc",
"sk-fcsfmyivfuojsqsditvobfqprdpeunukycpcfnoxkraqevpx",
"sk-szyjgyxrcvyxpvzfwgmbxnflxngxvcplitcctsdvvrqjgftk",
"sk-jzbodthsnvjwbyrnynsxrudtqfnbdbrcxebjwjgajocnzqse",
"sk-fxepossfzpmccibfwqpkluorzqlbtcaplepeugtfzfsctcbl",
"sk-ympnflocrkxjrbubsxqdjqwicuyavvvysctlpfhunkcrzxjx",
"sk-flhqvziknntednkcgjaxlyzzsrfzjhrzrmteqonajpbiinni",
"sk-xfregpbbquqbxpiobjzanydsjivrjrnbokzxcqtnhxhyghhe",
"sk-jrdzerhmvrtvzawkksowbgkggkubwfquplmrxbdhespqgtis",
"sk-jjbpnkbeupsxyclcivbhizcfpfjrppddunbqynyjkqhtmpwu",
"sk-oqehupcveovkjqqtxypqyifidcdissuyehwrkdwgruoyjkpq",
"sk-jnnmltwtqwuoyagoogzzeraczmyfxhoairiddgayksqdfnbr",
"sk-eghuepxnbcollzrjwbzqvbnhiiwagkejaclyhvaodeqgwrog",
"sk-poszkbjdmamimconjustnrxxqusuzlryxkrzkpronlenrmen",
"sk-zolvcegarsrwqhwgvwzgtqupodsdmckjiocyvoyldbkusbzc",
"sk-ywfafulcniaqdgdcsnbtqquaqeuiqlkcnknkaflwxyuemcow",
"sk-hhedmocgtfpywbbpwamgfkygrahiqsuurntlbqqbmjwfipmm",
"sk-gzdqfoyvulrqscdpjlwlufdecrsyjpmwpkknuhnjsvtyftox",
"sk-bkcufidsebujopqqwexwxwpmevrpelmvxzdymncvllcyojce",
"sk-olabhscekudzkyudypkcjvehwqunagubwdmtppugrjmcptwv",
"sk-zpdqyocliebhqpkuwvebpgcnfjdkvavdltimllmgkthwnwph",
"sk-gvhchlfelocjniuydusyhhwacnomxnvucjonzkhtqoplnbcr",
"sk-lzneagvdxhisodndnxnpkntghpkimjmjsebiqdzaoqzuhbla",
"sk-xotcfdkigykevngedupitbcatjqppxmcibjtcebyoglykuxz",
"sk-ufydqsdqnwsegaqwtappzwdyzqnoblyunfvslomnnmykedgk",
"sk-jwasykftbkyjzdqlwcxuicrwzxsbhttilxfefbrozrznpwlv",
"sk-xngteojwkxmftyaabjdwwgyoadspsowmcpcqobteutdcfmnr",
"sk-akzkgniebruqrtuqskvlibkpcxjuazhcatysptkfyqivldfn",
"sk-vpqkxtmcgkggllexchzysuewyfaoexzasoumxngdplzgwksw",
"sk-fvcsqdbqmdlwxzjyofrilusqcypbfyczogaqwqrjrwvojmer",
"sk-htjprscvfgskjtjzpxxxjhyymshagogykpawxekrrfbgftyx",
"sk-vtlgznzanapdinnlpuuygdidejxvlvlziolumckxwntzvxcn",
"sk-npwaykpqovytxhihxepeaxuwgnzvqvscirwbiffudyebibwe",
"sk-kduozskjwsbxytajaedemqemimflzoqdrfbbuopuogbxptos",
"sk-cmyaaofqksxeccaiswwholizxtdhqoszpilbqhmwwablwtwl",
"sk-zltwpyvzodfszrvqcmjxycfshshmqmgtpghiaulcrtrilqup",
"sk-svpublkronxohqmjvtdemtyqopuwyknynmxvurkmmoqsfesk",
"sk-tindfeurhffenyetmblplxgytcgqnbbruatchpddtstnajvq",
"sk-ttwgosljnfdqbevvfkjewspoasountxeyiesuuwwpuoseatw",
"sk-ikzwjzfxxnocfqvkhbolmmnojdqfvdxzmlruynfoneahgraq",
"sk-zomatpdlatgtzvuoeabofeandiiespmsinosenjlmdigkvjk",
"sk-xypcnibvqgebhwvsudjuxlycwombofshifpltstezflountf",
"sk-vhtbtqsdyhwsrsitpppjpulbpnwlhjeeaujccggdjvjtucru",
"sk-ygqyapfsdpxxzihadttugvofrcgwlnsrqjvrqfrsrlgqidch",
"sk-cczurtilupwyrnqnjpdjltzamjbpyaxnwgzqoykvfwecezhs",
"sk-hsalsmvnzmvlknpzldciqligtjrvhcjdbjtcqhorptsgoisr",
"sk-ierjdccoycmipzflzvzjqdmjazmbczxhwejwrjmuouxpcqev",
"sk-rlthtjzrxkdlfsemijumxiwwsoxkwnjovobtqwprhnducsqi",
"sk-doocedvfjsmwysxyzpptojnwwuoucxvrknlrdsnomffljems",
"sk-tgxnplgtwawfnnetghvlzoinksickbynzsgovbrmlrkzzrsy",
"sk-aggkwzdteggbcgyzimiivphpzibnpmqfbkmgtwyggsjfdymg",
"sk-tzynazayiglcpzejedpfdftqvtuqynygluorjbbcvrnmsbfl",
"sk-mcaqucnscgznmtvtzhooxbdafzhcgjgpusnjfbftroedzujk",
"sk-mlfnxfmwwhturavnsbyedkgrhxfspbfitfyqkllezgdtzhwn",
"sk-meaqykzphtqqoiswdtbxfqndddmtgvwrdnzdxprozlumrojs",
"sk-vrtfrtioocmrylrrqpburhkgwmyxfipnissumgufmjpzjqoo",
"sk-rhknxievjjjzcmkvfwqosnaiglsoufytfkbhhgconiibwriy",
"sk-wzsvxjfnvpceeoapbzdimpwecckpmcsqmflocttnqyvqvrsr",
"sk-lflfajincbdljrfwkhrjunyolzuxtcfdbzmspyfizpbdwvvh",
"sk-siujvueppvhhokbofqlcdxudpswdfwpetvxyittpbarddhsu",
"sk-avpyhivanabvdyorypdmlqnfetivlwoovfvgsgklumxeqgzy",
]
class APIKeyManager:
"""
@@ -129,6 +31,8 @@ class APIKeyManager:
_key_usage: Dict[str, Dict] = {}
# 当前正在使用的密钥索引
_current_index = 0
api_file_path = "api_key.txt"
@classmethod
def get_instance(cls, env_var_name: str = "OPENAI_API_KEY", separator: str = ";"):
@@ -193,7 +97,8 @@ class APIKeyManager:
],
"max_tokens": 1
}
for key in API_KEY_LIST:
instance = cls.get_instance()
for key in instance.api_keys:
headers = headers_template.copy()
headers["Authorization"] = f"Bearer {key}"
try:
@@ -233,6 +138,24 @@ class APIKeyManager:
return response.json()
@classmethod
def remove_invalid_api_keys(cls, api_keys: List[str]):
"""
移除无效的API密钥
"""
instance = cls.get_instance()
for key in api_keys:
instance.api_keys.remove(key)
@classmethod
def save_api_keys(cls):
"""
保存API密钥到文件
"""
instance = cls.get_instance()
with open(cls.api_file_path, "w") as f:
f.write("\n".join(instance.api_keys))
def __init__(self, env_var_name: str = "OPENAI_API_KEY", separator: str = ";"):
"""
初始化API密钥管理器
@@ -260,17 +183,15 @@ class APIKeyManager:
Returns:
API密钥列表
"""
# api_keys = []
# env_value = os.environ.get(self.env_var_name)
# if env_value:
# # 分割环境变量并移除空白字符
# keys = [key.strip() for key in env_value.split(self.separator)]
# # 过滤掉空字符串
# api_keys = [key for key in keys if key]
# return api_keys
return API_KEY_LIST
# 从文件中读取api_key
with open(self.api_file_path, "r") as f:
api_keys = f.readlines()
# 移除空白字符
api_keys = [key.strip() for key in api_keys if key.strip()]
return api_keys
def _get_next_api_key(self) -> Optional[str]:
"""
@@ -349,6 +270,19 @@ if __name__ == "__main__":
# 获取实例并查看使用统计
instance = APIKeyManager.get_instance()
stats = instance.get_usage_stats()
all_balance=0.0
buy_balance=16 * 10 * 14 # 购买16次,一次10条api_key,每个api_key有14元
invalid_api_keys = []
for key, data in stats.items():
usage_stats = APIKeyManager.get_key_usage_stats(key)
print(f"api key:{key}---赠送余额:{usage_stats['data']['balance']}")
all_balance+=float(usage_stats['data']['balance'])
print(f"api key:{key}---赠送余额:{usage_stats['data']['balance']}")
if float(usage_stats['data']['balance']) == 0:
invalid_api_keys.append(key)
print(f"剩余总赠送余额:{all_balance}元,累计消耗:{buy_balance-all_balance}")
print(f"无效的API密钥:{invalid_api_keys}")
print(f"开始移除无效的API密钥,并重新保存")
APIKeyManager.remove_invalid_api_keys(invalid_api_keys)
APIKeyManager.save_api_keys()
print(f"移除无效的API密钥,并重新保存完成")
+4 -4
View File
@@ -144,7 +144,10 @@ class OpenAiLLM:
max_retries = 3
retry_count = 0
if "timeout" not in self._kwargs:
timeout = httpx.Timeout(300.0)
self._kwargs["timeout"] = timeout
if need_retry:
while retry_count < max_retries:
try:
@@ -154,7 +157,6 @@ class OpenAiLLM:
completion = client.chat.completions.create(
model=self._model,
messages=[{'role': 'user', 'content': user_prompt}],
timeout=httpx.Timeout(300.0),
**self._kwargs
)
return completion.choices[0].message
@@ -162,7 +164,6 @@ class OpenAiLLM:
except Exception as e:
retry_count += 1
if retry_count == max_retries:
logging.error(f"LLM 重试{max_retries}次后仍然失败: {e}")
raise e
else:
time.sleep(5*retry_count) # 重试前等待1秒
@@ -173,7 +174,6 @@ class OpenAiLLM:
completion = client.chat.completions.create(
model=self._model,
messages=[{'role': 'user', 'content': user_prompt}],
timeout=httpx.Timeout(300.0),
**self._kwargs
)
return completion.choices[0].message