fix: 修复 'ToolResult' object has no attribute 'get'

根因: cli_tools.process_tool_call 在启用 UnifiedToolManager 时直接返回
ToolResult 对象,而 7 个调用点全部按 dict 用 result.get('result'),触发
AttributeError;FC 路径 _execute_tool_by_name 也可能把 ToolResult 透传出去,
后续 result[:5000] 会再炸 TypeError。

- tool_result.py: 新增 normalize_tool_text / normalize_tool_dict,
  统一收敛 ToolResult / dict / str / None 三种历史返回形态。
- cli_tools.process_tool_call: 统一返回 dict(附带 success/error_code/
  tool_result),回退 handler 路径同样归一。
- cli_core._execute_tool_by_name: handler / memory / scheduler 返回值
  归一成 str,保证 FC 路径可安全切片。
- openai/ollama/manual 引擎、websocket_server、cli_clawli: 取工具结果改用
  normalize_tool_text(带本地兜底实现,避免循环导入)。
- cli_clawli: 顺带修复只回传最后一个工具结果的 bug,改为汇总全部。
- 测试: 23 项全过;纯格式/讯飞 500 降级回归冒烟通过。
This commit is contained in:
shuiyu67 committed 2026-08-05 10:54:39 +08:00
1 parent dfc42d8583
commit a63b9d2f97
8 files changed
+129 -20

No files matched your search

+12 -1
View File
@@ -18,6 +18,17 @@ current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(current_dir)
sys.path.insert(0, project_root)
try:
from xcli_core.tool_result import normalize_tool_text
except Exception:
def normalize_tool_text(result, default="无结果"):
"""兜底归一化:ToolResult / dict / str 统一成字符串"""
if result is None:
return default
if isinstance(result, dict):
return str(result.get("result", default))
return str(result)
# 通信用的临时目录
COMM_DIR = os.path.join(tempfile.gettempdir(), "xiaoli_manual_engine")
@@ -246,7 +257,7 @@ while True:
# 把用户消息写入文件
msg = user_input
if tool_results:
msg = f"[工具结果] {tool_results.get('result', '无结果')}\n\n{user_input}"
msg = f"[工具结果] {normalize_tool_text(tool_results)}\n\n{user_input}"
to_human = paths["msg_to_human"]
with open(to_human, 'w', encoding='utf-8') as f:
+12 -1
View File
@@ -29,6 +29,17 @@ except Exception:
def resolve_input_value(value, field_name="", default_prompt=None):
return value
try:
from xcli_core.tool_result import normalize_tool_text
except Exception:
def normalize_tool_text(result, default="无结果"):
"""兜底归一化:ToolResult / dict / str 统一成字符串"""
if result is None:
return default
if isinstance(result, dict):
return str(result.get("result", default))
return str(result)
class OllamaAI:
"""基于Ollama的本地AI引擎"""
@@ -164,7 +175,7 @@ Ollama AI引擎插件帮助信息
# 如果有工具结果,将其作为上下文返回给AI处理
if tool_results:
tool_result_text = f"工具执行结果: {tool_results.get('result', '无结果')}"
tool_result_text = f"工具执行结果: {normalize_tool_text(tool_results)}"
messages = self._build_messages_with_history(system_prompt, tool_result_text)
else:
messages = self._build_messages_with_history(system_prompt, user_input)
+12 -1
View File
@@ -19,6 +19,17 @@ except Exception:
def get_system_config(key, default=None):
return default
try:
from xcli_core.tool_result import normalize_tool_text
except Exception:
def normalize_tool_text(result, default="无结果"):
"""兜底归一化:ToolResult / dict / str 统一成字符串"""
if result is None:
return default
if isinstance(result, dict):
return str(result.get("result", default))
return str(result)
# ── 常见模型的上下文窗口(token 数)──
# OpenAI /chat/completions 协议本身不返回模型窗口,需本地维护。
MODEL_CONTEXT_WINDOWS = {
@@ -172,7 +183,7 @@ OpenAI 兼容格式引擎帮助信息
# 处理工具结果
if tool_results:
tool_result_text = f"工具执行结果: {tool_results.get('result', '无结果')}"
tool_result_text = f"工具执行结果: {normalize_tool_text(tool_results)}"
messages = self._build_messages_with_history(system_prompt, tool_result_text)
else:
messages = self._build_messages_with_history(system_prompt, user_input)