myagent-ai 1.14.1 → 1.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,7 @@ agents/main_agent.py - 主 Agent
6
6
  from __future__ import annotations
7
7
 
8
8
  import asyncio
9
+ import re
9
10
  from typing import Any, Callable, Dict, List, Optional
10
11
 
11
12
  from core.logger import get_logger
@@ -442,6 +443,8 @@ class MainAgent(BaseAgent):
442
443
  get_knowledge_content = ""
443
444
  # 追踪流式推送的 reasoning 文本(用于构建有意义的最终回复)
444
445
  _v2_reasoning_collected: List[str] = []
446
+ # XML 解析失败时的 LLM 修正重试计数
447
+ _xml_correction_retries: int = 0
445
448
 
446
449
  conversation_history = list(context.conversation_history or [])
447
450
 
@@ -587,31 +590,54 @@ class MainAgent(BaseAgent):
587
590
  "finish": parsed.finish,
588
591
  "finish_reason": truncate_str(parsed.finish_reason, 200),
589
592
  "next_step": truncate_str(parsed.next_step, 200),
593
+ "response": truncate_str(parsed.response, 500),
590
594
  "parse_success": parsed.parse_success,
595
+ "needs_correction": parsed.needs_correction,
591
596
  }},
592
597
  stream_callback,
593
598
  )
594
599
 
600
+ # Step 4.5: 解析失败处理 — 回退给 LLM 修正或提取周边文本
595
601
  if not parsed.parse_success:
596
- logger.warning(f"[{task_id}] XML 解析失败,尝试提取周边文本")
597
- before, after = extract_surrounding_text(llm_raw)
598
- if before.strip() or after.strip():
599
- final_text = (before + "\n" + after).strip()
600
- context.working_memory["final_response"] = final_text
601
- await self._emit_v2_event("v2_reasoning", {"content": final_text}, stream_callback)
602
- if self.memory:
603
- self.memory.add_session(
604
- session_id=context.session_id,
605
- role="assistant",
606
- content=final_text,
607
- )
608
- break
602
+ if parsed.needs_correction and _xml_correction_retries < 1:
603
+ # XML 完全无法解析,让 LLM 重新格式化输出
604
+ _xml_correction_retries += 1
605
+ logger.warning(
606
+ f"[{task_id}] XML 解析完全失败,回退给 LLM 修正 "
607
+ f"(重试 {_xml_correction_retries}/1)"
608
+ )
609
+ correction_prompt = (
610
+ "你上一次的输出格式有误,XML解析器无法识别。"
611
+ "请严格按照 <output>...</output> 格式重新输出你的回答。"
612
+ "注意:不要在 <output> 标签前后输出任何其他文字。\n\n"
613
+ f"你上一次的原始输出如下:\n{llm_raw}"
614
+ )
615
+ conversation_history.append(
616
+ Message(role="assistant", content=llm_raw)
617
+ )
618
+ conversation_history.append(
619
+ Message(role="user", content=correction_prompt)
620
+ )
621
+ await self._emit_v2_event(
622
+ "v2_reasoning",
623
+ {"content": "⚠️ 模型输出格式异常,正在自动修正..."},
624
+ stream_callback,
625
+ )
626
+ continue # 重新进入循环,让 LLM 重新生成
609
627
  else:
610
- # XML 解析失败且无法提取文本,发送原始输出作为备选
611
- logger.warning(f"[{task_id}] 无法提取文本,发送原始 LLM 输出")
612
- final_text = llm_raw.strip() if llm_raw.strip() else "处理完毕。"
628
+ # 已重试过或不需要修正,提取周边文本作为备选
629
+ logger.warning(f"[{task_id}] XML 解析失败,提取周边文本作为备选")
630
+ before, after = extract_surrounding_text(llm_raw)
631
+ if before.strip() or after.strip():
632
+ final_text = (before + "\n" + after).strip()
633
+ else:
634
+ # 清除残余 XML 标签后作为纯文本
635
+ final_text = re.sub(r"<[^>]+>", "", llm_raw).strip()
636
+ final_text = final_text if final_text else "处理完毕。"
613
637
  context.working_memory["final_response"] = final_text
614
- await self._emit_v2_event("v2_reasoning", {"content": final_text}, stream_callback)
638
+ await self._emit_v2_event(
639
+ "v2_reasoning", {"content": final_text}, stream_callback
640
+ )
615
641
  if self.memory:
616
642
  self.memory.add_session(
617
643
  session_id=context.session_id,
@@ -809,6 +835,7 @@ class MainAgent(BaseAgent):
809
835
  "v2_tool_start",
810
836
  {"tool": {
811
837
  "toolname": tool_name,
838
+ "beforecalltext": before_call,
812
839
  "parms": truncate_str(parms, 500),
813
840
  "timeout": timeout,
814
841
  "callback": should_callback,
@@ -1002,10 +1029,12 @@ class MainAgent(BaseAgent):
1002
1029
 
1003
1030
  # 保存工具调用到会话记忆
1004
1031
  if self.memory:
1032
+ # 构建工具调用记录,包含 beforecalltext 作为标题
1033
+ tool_call_title = before_call if before_call else f"调用工具: {tool_name}"
1005
1034
  self.memory.add_session(
1006
1035
  session_id=context.session_id,
1007
1036
  role="assistant",
1008
- content=f"调用工具: {tool_name}\n参数: {truncate_str(parms, 1000)}",
1037
+ content=f"{tool_call_title}\n调用工具: {tool_name}\n参数: {truncate_str(parms, 1000)}",
1009
1038
  key="tool_call",
1010
1039
  importance=0.4,
1011
1040
  )