PatrickRedStar commited on
Commit
758bfb1
·
1 Parent(s): 9043954

Fix: Improve agent response extraction and add better error handling for truncated JSON

Browse files
agents/__init__.py CHANGED
@@ -23,7 +23,7 @@ if not hf_token:
23
  model = InferenceClientModel(
24
  model_id="deepseek-ai/DeepSeek-V3.1-Terminus",
25
  token=hf_token,
26
- max_tokens=2048
27
  )
28
 
29
  final_tool = FinalAnswerTool()
@@ -35,6 +35,8 @@ parser_agent = ToolCallingAgent(
35
  instructions="""
36
  Ты эксперт по анализу системных логов. Твоя задача - парсить сырые логи и преобразовывать их в структурированный JSON формат.
37
 
 
 
38
  Для каждой строки лога определи:
39
  - timestamp: временная метка (если есть)
40
  - level: уровень логирования (INFO, WARNING, ERROR, CRITICAL, DEBUG, TRACE)
@@ -48,7 +50,7 @@ parser_agent = ToolCallingAgent(
48
 
49
  Временной диапазон (time_range) должен содержать start и end - первую и последнюю временную метку.
50
 
51
- Ответ строго верни в JSON через final_answer в следующем формате:
52
  {
53
  "events": [{"line_number": int, "timestamp": "str|null", "level": "str", "message": "str", "type": "str"}, ...],
54
  "errors": [{"line_number": int, "timestamp": "str|null", "level": "str", "message": "str", "type": "str"}, ...],
@@ -221,17 +223,20 @@ def run_gpt_prompt_agent(structured_data: dict, anomaly_report: dict, recommenda
221
  }
222
  input_json = json.dumps(input_data, ensure_ascii=False, indent=2)
223
 
224
- result = run_agent_safely(gpt_prompt_agent, task=input_json)
225
 
226
- # Результат должен быть строкой (промпт), а не JSON
227
- if isinstance(result, dict) and "answer" in result:
228
- return result["answer"]
229
- elif isinstance(result, str):
230
  return result
231
- else:
232
- # Если агент вернул JSON, попробуем извлечь промпт
233
- if isinstance(result, dict) and "prompt" in result:
 
 
234
  return result["prompt"]
 
 
 
235
  return str(result)
236
 
237
  __all__ = [
 
23
  model = InferenceClientModel(
24
  model_id="deepseek-ai/DeepSeek-V3.1-Terminus",
25
  token=hf_token,
26
+ max_tokens=4096 # Увеличено для больших промптов и JSON
27
  )
28
 
29
  final_tool = FinalAnswerTool()
 
35
  instructions="""
36
  Ты эксперт по анализу системных логов. Твоя задача - парсить сырые логи и преобразовывать их в структурированный JSON формат.
37
 
38
+ ВАЖНО: Твой ответ должен быть ПОЛНЫМ и ЗАВЕРШЁННЫМ JSON объектом. Не обрезай ответ!
39
+
40
  Для каждой строки лога определи:
41
  - timestamp: временная метка (если есть)
42
  - level: уровень логирования (INFO, WARNING, ERROR, CRITICAL, DEBUG, TRACE)
 
50
 
51
  Временной диапазон (time_range) должен содержать start и end - первую и последнюю временную метку.
52
 
53
+ Ответ строго верни в JSON через final_answer в следующем формате (ОБЯЗАТЕЛЬНО ПОЛНЫЙ JSON):
54
  {
55
  "events": [{"line_number": int, "timestamp": "str|null", "level": "str", "message": "str", "type": "str"}, ...],
56
  "errors": [{"line_number": int, "timestamp": "str|null", "level": "str", "message": "str", "type": "str"}, ...],
 
223
  }
224
  input_json = json.dumps(input_data, ensure_ascii=False, indent=2)
225
 
226
+ result = run_agent_safely(gpt_prompt_agent, task=input_json, return_string=True)
227
 
228
+ # Результат должен быть строкой (промпт)
229
+ if isinstance(result, str):
 
 
230
  return result
231
+ elif isinstance(result, dict):
232
+ # Если вернулся словарь, попробуем извлечь текст
233
+ if "answer" in result:
234
+ return result["answer"]
235
+ elif "prompt" in result:
236
  return result["prompt"]
237
+ else:
238
+ return json.dumps(result, ensure_ascii=False, indent=2)
239
+ else:
240
  return str(result)
241
 
242
  __all__ = [
agents/__pycache__/__init__.cpython-314.pyc CHANGED
Binary files a/agents/__pycache__/__init__.cpython-314.pyc and b/agents/__pycache__/__init__.cpython-314.pyc differ
 
utils/agent_runner.py CHANGED
@@ -1,17 +1,18 @@
1
  # utils/agent_runner.py
2
  import json
3
 
4
- def run_agent_safely(agent, max_retries=3, **kwargs):
5
  """
6
  Безопасно запускает агента с повторными попытками при ошибках.
7
 
8
  Args:
9
  agent: Экземпляр агента smolagents
10
  max_retries: Максимальное количество попыток
 
11
  **kwargs: Аргументы для передачи агенту
12
 
13
  Returns:
14
- dict: Распарсенный JSON ответ от агента
15
  """
16
  last_error = None
17
 
@@ -19,21 +20,96 @@ def run_agent_safely(agent, max_retries=3, **kwargs):
19
  try:
20
  result = agent.run(**kwargs)
21
 
22
- # Извлекаем ответ из результата
23
- if isinstance(result, dict) and "answer" in result:
24
- raw = result["answer"]
25
- else:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  raw = result
 
 
 
27
 
28
  if not isinstance(raw, str):
29
- raise ValueError("Agent output is not a string")
30
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  # Парсим JSON
32
- return json.loads(raw)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  except Exception as e:
35
  last_error = e
36
- print(f"[{agent.name}] Attempt {attempt}/{max_retries} failed: {e}")
37
 
38
  raise ValueError(
39
  f"[{agent.name}] Failed after {max_retries} attempts.\nLast error: {last_error}"
 
1
  # utils/agent_runner.py
2
  import json
3
 
4
+ def run_agent_safely(agent, max_retries=3, return_string=False, **kwargs):
5
  """
6
  Безопасно запускает агента с повторными попытками при ошибках.
7
 
8
  Args:
9
  agent: Экземпляр агента smolagents
10
  max_retries: Максимальное количество попыток
11
+ return_string: Если True, возвращает строку вместо парсинга JSON
12
  **kwargs: Аргументы для передачи агенту
13
 
14
  Returns:
15
+ dict или str: Распарсенный JSON ответ или строка (в зависимости от return_string)
16
  """
17
  last_error = None
18
 
 
20
  try:
21
  result = agent.run(**kwargs)
22
 
23
+ # Извлекаем ответ из результата smolagents
24
+ # smolagents возвращает AgentText объект, который нужно правильно обработать
25
+ raw = None
26
+
27
+ # Если это AgentText объект (из smolagents), получаем полный текст
28
+ # AgentText может содержать обрезанный вывод при str(), но полный текст доступен через другие методы
29
+ if hasattr(result, 'to_string'):
30
+ raw = result.to_string()
31
+ elif hasattr(result, 'text'):
32
+ raw = result.text
33
+ elif hasattr(result, '__getitem__'):
34
+ # Если это последовательность или маппинг, пробуем извлечь
35
+ try:
36
+ if 'answer' in result:
37
+ raw = result['answer']
38
+ except:
39
+ pass
40
+ elif isinstance(result, dict):
41
+ # Ищем ответ в различных возможных полях
42
+ if "answer" in result:
43
+ raw = result["answer"]
44
+ elif "content" in result:
45
+ raw = result["content"]
46
+ elif "text" in result:
47
+ raw = result["text"]
48
+ elif "response" in result:
49
+ raw = result["response"]
50
+ else:
51
+ # Если словарь не содержит текстовых полей, преобразуем в JSON строку
52
+ raw = json.dumps(result, ensure_ascii=False)
53
+ elif isinstance(result, str):
54
  raw = result
55
+ else:
56
+ # Пробуем преобразовать в строку
57
+ raw = str(result)
58
 
59
  if not isinstance(raw, str):
60
+ raise ValueError(f"Agent output is not a string, got {type(raw)}")
61
+
62
+ # Удаляем возможные escape-последовательности и пробелы
63
+ raw = raw.strip()
64
+
65
+ # Если raw содержит JSON-строку внутри (например, {"answer": "{\"key\": \"value\"}"})
66
+ # Нужно распарсить внешний JSON, чтобы получить внутренний
67
+ if raw.startswith('{"') and '"answer"' in raw:
68
+ try:
69
+ # Пробуем распарсить как JSON
70
+ outer_json = json.loads(raw)
71
+ if isinstance(outer_json, dict) and "answer" in outer_json:
72
+ raw = outer_json["answer"]
73
+ except json.JSONDecodeError:
74
+ # Если не получается распарсить, возможно JSON обрезан
75
+ # Пробуем найти полный JSON внутри
76
+ pass
77
+
78
+ # Если нужно вернуть строку (для GPT Prompt агента)
79
+ if return_string:
80
+ return raw
81
+
82
+ # Для JSON агентов - пытаемся распарсить
83
+ # Если строка обрезана (начинается с { но не заканчивается }), пробуем исправить
84
+ if raw.startswith('{') and not raw.rstrip().endswith('}'):
85
+ # Пытаемся найти последнюю закрывающ��ю скобку
86
+ last_brace = raw.rfind('}')
87
+ if last_brace > len(raw) * 0.8: # Если скобка не слишком близко к началу
88
+ raw = raw[:last_brace + 1]
89
+
90
  # Парсим JSON
91
+ try:
92
+ return json.loads(raw)
93
+ except json.JSONDecodeError as e:
94
+ # Если это не JSON, пробуем найти JSON внутри строки
95
+ # Ищем первую { и последнюю }
96
+ start_idx = raw.find('{')
97
+ end_idx = raw.rfind('}')
98
+ if start_idx >= 0 and end_idx > start_idx:
99
+ json_part = raw[start_idx:end_idx + 1]
100
+ try:
101
+ return json.loads(json_part)
102
+ except:
103
+ pass
104
+
105
+ # Если всё равно не получается, это ошибка для JSON-агентов
106
+ if not raw.startswith('{') and not raw.startswith('['):
107
+ raise ValueError(f"Expected JSON but got plain text (first 200 chars): {raw[:200]}")
108
+ raise ValueError(f"JSON decode error: {e}. Raw (first 500 chars): {raw[:500]}")
109
 
110
  except Exception as e:
111
  last_error = e
112
+ print(f"[{agent.name}] Attempt {attempt}/{max_retries} failed: {e}")
113
 
114
  raise ValueError(
115
  f"[{agent.name}] Failed after {max_retries} attempts.\nLast error: {last_error}"