Browse Source

fix: agent67 — smarter JSON block extraction for nested backticks

Added _extract_json_blocks() that handles content containing
markdown code blocks (``` inside ```json). Uses heuristic:
finds closing ``` by checking if content between markers
contains valid-looking JSON with "action" key.

Also improved parse order: tries first valid block instead of last.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
kenny67nju 7 months ago
parent
commit
91695887b4
1 changed files with 68 additions and 2 deletions
  1. 68 2
      agentexample/agent67/core/assistant.py

+ 68 - 2
agentexample/agent67/core/assistant.py

@@ -73,6 +73,70 @@ TOOL_REGISTRY = _build_tool_registry()
 # 解析 & 执行引擎 (v2)
 # ════════════════════════════════════════════════════════════
 
+def _extract_json_blocks(text: str) -> list[str]:
+    """Extract JSON code blocks, handling nested ``` in content.
+
+    Strategy: find ```json markers, then find the matching ``` closer
+    by looking for a ``` that is followed by a newline or end-of-string
+    (not inside a JSON string value).
+    """
+    blocks = []
+    i = 0
+    marker = "```json"
+    while i < len(text):
+        start = text.find(marker, i)
+        if start == -1:
+            break
+        # Skip past the marker and optional newline
+        content_start = start + len(marker)
+        if content_start < len(text) and text[content_start] == '\n':
+            content_start += 1
+
+        # Find the closing ``` — look for ``` at start of line or after newline
+        # that's NOT inside a JSON string
+        best_end = -1
+        j = content_start
+        while j < len(text):
+            pos = text.find("```", j)
+            if pos == -1:
+                break
+            # Check if this ``` is a real closer (not inside JSON string content)
+            # Heuristic: if the text between content_start and pos contains
+            # a valid-looking JSON with "action", it's the closer
+            candidate = text[content_start:pos].strip()
+            if candidate and ('"action"' in candidate or '"tool"' in candidate):
+                try:
+                    # Try to parse — if it works, this is the right closer
+                    _try_parse_json(candidate)
+                    best_end = pos
+                    break
+                except Exception:
+                    pass
+                # Even if parse fails, if it looks like JSON, take it
+                if candidate.startswith("{") and candidate.rstrip().endswith("}"):
+                    best_end = pos
+                    break
+            j = pos + 3
+
+        if best_end != -1:
+            block = text[content_start:best_end].strip()
+            if block:
+                blocks.append(block)
+            i = best_end + 3
+        else:
+            # Fallback: take everything until the next ```
+            end = text.find("```", content_start)
+            if end != -1:
+                block = text[content_start:end].strip()
+                if block:
+                    blocks.append(block)
+                i = end + 3
+            else:
+                break
+
+    return blocks
+
+
 def _try_parse_json(json_str: str) -> dict | None:
     """Try multiple strategies to parse potentially malformed JSON from LLM output."""
     # Strategy 1: direct parse
@@ -158,8 +222,10 @@ def parse_and_execute(llm_output: str, hooks: HookRegistry = None) -> tuple[str,
     v2: 统一 action/tool 格式,支持内置工具 + macOS 工具
     策略:尝试所有 ```json 块(从第一个开始),找到第一个可执行的
     """
-    # 尝试从 ```json ... ``` 代码块提取(可能有多个)
-    matches = re.findall(r'```json\s*\n?(.*?)\n?```', llm_output, re.DOTALL)
+    # 尝试从 ```json ... ``` 代码块提取
+    # 使用贪婪匹配找到最外层的 ```json...``` 对
+    # 注意:内容中可能含有 ``` 标记(如 markdown 代码块),需要特殊处理
+    matches = _extract_json_blocks(llm_output)
     if not matches:
         # fallback: 找内联 JSON
         matches = re.findall(r'\{[^{}]*(?:"action"|"tool")\s*:.*?\}', llm_output, re.DOTALL)