优化重试机制

This commit is contained in:
xunbu
2025-09-04 15:10:42 +08:00
parent 4c5c6581b9
commit 45c6109c05
2 changed files with 23 additions and 28 deletions

View File

@@ -17,8 +17,8 @@ from docutranslate.global_values import USE_PROXY
from docutranslate.logger import global_logger from docutranslate.logger import global_logger
from docutranslate.utils.utils import get_httpx_proxies from docutranslate.utils.utils import get_httpx_proxies
MAX_RETRY_COUNT = 2 MAX_RETRY_COUNT = 3
MAX_REQUESTS_PER_ERROR = 15 MAX_REQUESTS_PER_ERROR = 10
ThinkingMode = Literal["enable", "disable", "default"] ThinkingMode = Literal["enable", "disable", "default"]
@@ -154,6 +154,7 @@ class Agent:
system_prompt = self.system_prompt system_prompt = self.system_prompt
if pre_send_handler: if pre_send_handler:
system_prompt, prompt = pre_send_handler(system_prompt, prompt) system_prompt, prompt = pre_send_handler(system_prompt, prompt)
# print(f"system_prompt:\n{system_prompt}")
headers, data = self._prepare_request_data(prompt, system_prompt) headers, data = self._prepare_request_data(prompt, system_prompt)
should_retry = False should_retry = False
@@ -183,7 +184,7 @@ class Agent:
except httpx.HTTPStatusError as e: except httpx.HTTPStatusError as e:
self.logger.error(f"AI请求HTTP状态错误 (async): {e.response.status_code} - {e.response.text}") self.logger.error(f"AI请求HTTP状态错误 (async): {e.response.status_code} - {e.response.text}")
print(f"prompt:\n{prompt}") # print(f"prompt:\n{prompt}")
should_retry = True should_retry = True
except httpx.RequestError as e: except httpx.RequestError as e:
self.logger.error(f"AI请求连接错误 (async): {repr(e)}") self.logger.error(f"AI请求连接错误 (async): {repr(e)}")
@@ -298,16 +299,14 @@ class Agent:
return result if result_handler is None else result_handler(result, prompt, self.logger) return result if result_handler is None else result_handler(result, prompt, self.logger)
# --- MODIFICATION START ---
except PartialTranslationError as e: except PartialTranslationError as e:
self.logger.error(f"收到部分翻译结果,将尝试重试: {e}") self.logger.error(f"收到部分翻译结果,将尝试重试: {e}")
current_partial_result = e.partial_result current_partial_result = e.partial_result
should_retry = True should_retry = True
# --- MODIFICATION END ---
except httpx.HTTPStatusError as e: except httpx.HTTPStatusError as e:
self.logger.error(f"AI请求HTTP状态错误 (sync): {e.response.status_code} - {e.response.text}") self.logger.error(f"AI请求HTTP状态错误 (sync): {e.response.status_code} - {e.response.text}")
print(f"prompt:\n{prompt}") # print(f"prompt:\n{prompt}")
should_retry = True should_retry = True
except httpx.RequestError as e: except httpx.RequestError as e:
self.logger.error(f"AI请求连接错误 (sync): {repr(e)}\nprompt:{prompt}") self.logger.error(f"AI请求连接错误 (sync): {repr(e)}\nprompt:{prompt}")
@@ -316,10 +315,8 @@ class Agent:
self.logger.error(f"AI响应格式或值错误 (sync), 将尝试重试: {repr(e)}") self.logger.error(f"AI响应格式或值错误 (sync), 将尝试重试: {repr(e)}")
should_retry = True should_retry = True
# --- MODIFICATION START ---
if current_partial_result: if current_partial_result:
best_partial_result = current_partial_result best_partial_result = current_partial_result
# --- MODIFICATION END ---
if should_retry and retry and retry_count < MAX_RETRY_COUNT: if should_retry and retry and retry_count < MAX_RETRY_COUNT:
if retry_count == 0: if retry_count == 0:
@@ -343,11 +340,9 @@ class Agent:
if should_retry: if should_retry:
self.logger.error(f"所有重试均失败,已达到重试次数上限。") self.logger.error(f"所有重试均失败,已达到重试次数上限。")
# --- MODIFICATION START ---
if best_partial_result: if best_partial_result:
self.logger.info("所有重试失败,但存在部分翻译结果,将使用该结果。") self.logger.info("所有重试失败,但存在部分翻译结果,将使用该结果。")
return best_partial_result return best_partial_result
# --- MODIFICATION END ---
return prompt if error_result_handler is None else error_result_handler(prompt, self.logger) return prompt if error_result_handler is None else error_result_handler(prompt, self.logger)

View File

@@ -27,27 +27,28 @@ class SegmentsTranslateAgent(Agent):
super().__init__(config) super().__init__(config)
self.system_prompt = f""" self.system_prompt = f"""
# Role # Role
You are a professional machine translation engine. - You are a professional machine translation engine.
# Task # Task
You will receive a sequence of segments to be translated, represented in JSON format. The keys are the segment IDs, and the values are the segments for translation. - You will receive a sequence of segments to be translated, represented in JSON format. The keys are the segment IDs, and the values are the segments for translation.
You need to translate these segments into the target language. - You need to translate these segments into the target language.
Target language: {config.to_lang} - Target language: {config.to_lang}
# Requirements # Requirements
The translation must be professional and accurate. - The translation must be professional and accurate.
Do not output any explanations or annotations. - Do not output any explanations or annotations.
The format of the translated segments should be as close as possible to the source format. - The format of the translated segments should be as close as possible to the source format.
For personal names and proper nouns, use the most commonly used words for translation. If there are multiple common translations, choose the word that comes first in dictionary order. - For personal names and proper nouns, use the most commonly used words for translation.
For special tags or other non-translatable elements (like codes, brand names, specific jargon), keep them in their original form. - For special tags or other non-translatable elements (like codes, brand names, specific jargon), keep them in their original form.
If a segment is already in the target language, keep it as is. - If a segment is already in the target language({config.to_lang}), keep it as is.
# Output # Output
The translated sequence of segments, represented as JSON text (note: not a code block). The keys are the segment IDs, and the values are the translated segments. - The translated sequence of segments, represented as JSON text (note: not a code block). The keys are the segment IDs, and the values are the translated segments.
The returned JSON text must be parsable by json.loads into a dictionary of the form {r'{"segment_id": "translation"}'}. - The returned JSON text must be a dictionary of the form {{<segment_id>: <translation>}}.
- The segment IDs in the output must **exactly** match those in the input. And all segment IDs in input must appear in the output.
# Example # Example
## Input ## Input
{r'{"0":"hello","1":"apple","2":true,"3":"false"}'} {r'{"10":"hello","11":"apple","12":true,"13":"false","14":null}'}
## Output ## Output(Assuming the target language is Chinese)
{r'{"0":"你好","1":"苹果","2":true,"3":"错误"}'} {r'{"10":"你好","11":"苹果","12":true,"13":"错误","14":null}'}
Warning: Never wrap the entire JSON object in quotes to make it a single string. Never wrap the JSON text in ```. > Warning: Never wrap the JSON text in ```.
""" """
self.custom_prompt = config.custom_prompt self.custom_prompt = config.custom_prompt
if config.custom_prompt: if config.custom_prompt:
@@ -149,7 +150,6 @@ Warning: Never wrap the entire JSON object in quotes to make it a single string.
continue continue
for key, val in chunk.items(): for key, val in chunk.items():
if key in indexed_translated: if key in indexed_translated:
# 此处不再需要 str(val)
indexed_translated[key] = val indexed_translated[key] = val
else: else:
self.logger.warning(f"在结果chunk中发现未知键 '{key}',已忽略。") self.logger.warning(f"在结果chunk中发现未知键 '{key}',已忽略。")