fix(agent): normalize fenced and flat-args LLM outputs in parser
核心算法 #10(Agent Loop):仅加固 _parse_response 解析路径—— 剥除 ```json 围栏 + 收拢 action 平铺参数(deepseek 稳定输出变体, 案例 637-3/615-3 三连拒 0 步阵亡)。Thinking+JSON 协议、json_repair 兜底链、hook 时序与步数语义均未改动。
This commit is contained in:
+27
-3
@@ -18,6 +18,7 @@ TRM4 → TRM5 有意变更(非简化):
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import pluggy
|
||||
@@ -31,6 +32,9 @@ if TYPE_CHECKING:
|
||||
from core.protocols import LLMProvider
|
||||
from core.types import LLMResponse
|
||||
|
||||
# deepseek 等模型稳定输出变体:```json 围栏包裹 JSON 体
|
||||
_CODE_FENCE_RE = re.compile(r"^\s*```(?:json)?\s*\n?|\n?\s*```\s*$")
|
||||
|
||||
|
||||
async def _call_hook(hook: Any, **kwargs: Any) -> list[Any]:
|
||||
"""调用 pluggy hook 并 await 异步返回值。
|
||||
@@ -267,7 +271,8 @@ class AgentLoop:
|
||||
) -> tuple[str, dict, dict, str, dict, str] | None:
|
||||
"""从 LLMResponse 中提取结构化决策数据。
|
||||
|
||||
解析流程: content → repair_json → json.loads → 校验 action/tool/args。
|
||||
解析流程: content → 剥除 ```json 围栏 → repair_json → json.loads
|
||||
→ 收拢 action 平铺参数 → 校验 action/tool/args。
|
||||
|
||||
参数:
|
||||
response: LLMResponse 实例。
|
||||
@@ -282,7 +287,7 @@ class AgentLoop:
|
||||
if not content.strip():
|
||||
return None
|
||||
|
||||
repaired = repair_json(content)
|
||||
repaired = repair_json(_CODE_FENCE_RE.sub("", content).strip())
|
||||
try:
|
||||
data = json.loads(repaired)
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
@@ -291,7 +296,7 @@ class AgentLoop:
|
||||
if not isinstance(data, dict) or "action" not in data:
|
||||
return None
|
||||
|
||||
action = data["action"]
|
||||
action = self._normalize_action(data["action"])
|
||||
if not isinstance(action, dict) or "tool" not in action or "args" not in action:
|
||||
return None
|
||||
|
||||
@@ -299,6 +304,25 @@ class AgentLoop:
|
||||
plan = data.get("plan", {})
|
||||
return thought, reflect, plan, content, action, response.call_id
|
||||
|
||||
@staticmethod
|
||||
def _normalize_action(action: Any) -> Any:
|
||||
"""收拢 deepseek 变体的 action 平铺参数。
|
||||
|
||||
deepseek 等模型稳定输出变体: 工具参数平铺在 action 下(缺 args
|
||||
嵌套),确定性收拢为标准 {"tool": ..., "args": {...}} 结构。
|
||||
标准嵌套结构与非法结构均原样返回,由调用方校验。
|
||||
|
||||
参数:
|
||||
action: 从 LLM 输出解析出的 action 字段(任意类型)。
|
||||
|
||||
返回:
|
||||
归一化后的 action(仅平铺变体被改写,其余原样返回)。
|
||||
"""
|
||||
if isinstance(action, dict) and "tool" in action and "args" not in action:
|
||||
flat_args = {k: v for k, v in action.items() if k != "tool"}
|
||||
return {"tool": action["tool"], "args": flat_args}
|
||||
return action
|
||||
|
||||
async def _execute_tool(
|
||||
self,
|
||||
dispatcher: ToolDispatcher,
|
||||
|
||||
Reference in New Issue
Block a user