|
|
|
|
|
import json
|
|
|
|
|
|
def run_agent_safely(agent, max_retries=3, return_string=False, **kwargs):
|
|
|
"""
|
|
|
Безопасно запускает агента с повторными попытками при ошибках.
|
|
|
|
|
|
Args:
|
|
|
agent: Экземпляр агента smolagents
|
|
|
max_retries: Максимальное количество попыток
|
|
|
return_string: Если True, возвращает строку вместо парсинга JSON
|
|
|
**kwargs: Аргументы для передачи агенту
|
|
|
|
|
|
Returns:
|
|
|
dict или str: Распарсенный JSON ответ или строка (в зависимости от return_string)
|
|
|
"""
|
|
|
last_error = None
|
|
|
|
|
|
for attempt in range(1, max_retries + 1):
|
|
|
try:
|
|
|
result = agent.run(**kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
raw = None
|
|
|
|
|
|
|
|
|
|
|
|
if hasattr(result, 'to_string'):
|
|
|
raw = result.to_string()
|
|
|
elif hasattr(result, 'text'):
|
|
|
raw = result.text
|
|
|
elif hasattr(result, '__getitem__'):
|
|
|
|
|
|
try:
|
|
|
if 'answer' in result:
|
|
|
raw = result['answer']
|
|
|
except:
|
|
|
pass
|
|
|
elif isinstance(result, dict):
|
|
|
|
|
|
if "answer" in result:
|
|
|
raw = result["answer"]
|
|
|
elif "content" in result:
|
|
|
raw = result["content"]
|
|
|
elif "text" in result:
|
|
|
raw = result["text"]
|
|
|
elif "response" in result:
|
|
|
raw = result["response"]
|
|
|
else:
|
|
|
|
|
|
raw = json.dumps(result, ensure_ascii=False)
|
|
|
elif isinstance(result, str):
|
|
|
raw = result
|
|
|
else:
|
|
|
|
|
|
raw = str(result)
|
|
|
|
|
|
if not isinstance(raw, str):
|
|
|
raise ValueError(f"Agent output is not a string, got {type(raw)}")
|
|
|
|
|
|
|
|
|
raw = raw.strip()
|
|
|
|
|
|
|
|
|
|
|
|
if raw.startswith('{"') and '"answer"' in raw:
|
|
|
try:
|
|
|
|
|
|
outer_json = json.loads(raw)
|
|
|
if isinstance(outer_json, dict) and "answer" in outer_json:
|
|
|
raw = outer_json["answer"]
|
|
|
except json.JSONDecodeError:
|
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
if return_string:
|
|
|
return raw
|
|
|
|
|
|
|
|
|
|
|
|
if raw.startswith('{') and not raw.rstrip().endswith('}'):
|
|
|
|
|
|
last_brace = raw.rfind('}')
|
|
|
if last_brace > len(raw) * 0.8:
|
|
|
raw = raw[:last_brace + 1]
|
|
|
|
|
|
|
|
|
try:
|
|
|
return json.loads(raw)
|
|
|
except json.JSONDecodeError as e:
|
|
|
|
|
|
|
|
|
start_idx = raw.find('{')
|
|
|
end_idx = raw.rfind('}')
|
|
|
if start_idx >= 0 and end_idx > start_idx:
|
|
|
json_part = raw[start_idx:end_idx + 1]
|
|
|
try:
|
|
|
return json.loads(json_part)
|
|
|
except:
|
|
|
pass
|
|
|
|
|
|
|
|
|
if not raw.startswith('{') and not raw.startswith('['):
|
|
|
raise ValueError(f"Expected JSON but got plain text (first 200 chars): {raw[:200]}")
|
|
|
raise ValueError(f"JSON decode error: {e}. Raw (first 500 chars): {raw[:500]}")
|
|
|
|
|
|
except Exception as e:
|
|
|
last_error = e
|
|
|
print(f"[{agent.name}] Attempt {attempt}/{max_retries} failed: {e}")
|
|
|
|
|
|
raise ValueError(
|
|
|
f"[{agent.name}] Failed after {max_retries} attempts.\nLast error: {last_error}"
|
|
|
)
|
|
|
|