write_json.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. """
  2. Write JSON Tool - 用于直接安全写入结构化 JSON 数据,避免大模型生成超长转义文本导致的截断和报错
  3. """
  4. import json
  5. from pathlib import Path
  6. from typing import Optional, Dict, Any
  7. from agent.tools import tool, ToolResult, ToolContext
  8. @tool(description="专门且唯一安全的 JSON 数据文件写入工具。传入 Python Dict/Object,自动为你生成格式化和转义无误的 JSON 文件。严禁使用普通的 write_file 写 JSON。参数 file_path 是文件绝对路径字符串,json_data 是要写入的原生 JSON 对象(直接传 dict,无需提前序列化)", hidden_params=["context"], groups=["core"])
  9. async def write_json(
  10. file_path: str = "",
  11. json_data: dict = None,
  12. context: Optional[ToolContext] = None
  13. ) -> ToolResult:
  14. # 参数防空保护 - 给出明确错误而非空报错
  15. if not file_path:
  16. return ToolResult(
  17. title="参数错误",
  18. output="请提供 file_path 参数:file_path 必须是一个有效的绝对文件路径字符串,例如 '/path/to/output.json'",
  19. error="Missing required argument: file_path"
  20. )
  21. if json_data is None:
  22. return ToolResult(
  23. title="参数错误",
  24. output="请提供 json_data 参数:json_data 必须是一个原生的 Python dict 对象,请将要写入的 JSON 结构直接作为对象传入,无需序列化为字符串",
  25. error="Missing required argument: json_data"
  26. )
  27. """
  28. 专门安全地写入结构化 JSON 数据到文件。
  29. Args:
  30. file_path: 要写入的绝对或相对文件路径。
  31. json_data: 要写入的数据对象 (object/dict)。
  32. context: 工具上下文
  33. Returns:
  34. ToolResult: 写入操作结果
  35. """
  36. path = Path(file_path)
  37. if not path.is_absolute():
  38. path = Path.cwd() / path
  39. if path.exists() and path.is_dir():
  40. return ToolResult(
  41. title="路径错误",
  42. output=f"路径是目录,不是文件: {file_path}",
  43. error="Path is a directory"
  44. )
  45. path.parent.mkdir(parents=True, exist_ok=True)
  46. # 自动检测并修正类型:如果模型传进来的是 JSON 字符串,先反序列化再写入
  47. # 这解决了模型把 dict 当成 JSON 字符串传入导致的双重序列化问题
  48. if isinstance(json_data, str):
  49. try:
  50. json_data = json.loads(json_data)
  51. except json.JSONDecodeError as e:
  52. return ToolResult(
  53. title="JSON 解析失败",
  54. output=f"json_data 参数作为 JSON 字符串解析失败,错误在这附近:{e}\n\n"
  55. f"[系统强制警告] 你的 JSON 生成存在语法错误(很可能是 description 中的未转义双引号、换行符,或者缺少逗号)。\n"
  56. f"⚠️ 严禁使用 write_file 回退硬写!用 write_file 强行写入坏 JSON 会导致整个流水线后续崩溃死锁!\n"
  57. f"你必须立即检查并修复字符串内的转义问题,然后重新调用 write_json!",
  58. error=str(e)
  59. )
  60. try:
  61. # 落盘前自动将 JSON 数据中的外站图片 URL 替换为自有 CDN 链接
  62. try:
  63. from agent.tools.builtin.file.image_cdn import replace_image_urls_in_obj
  64. json_data = await replace_image_urls_in_obj(json_data)
  65. except Exception as cdn_err:
  66. import logging
  67. logging.getLogger(__name__).warning("[write_json] CDN mirror step failed, writing original: %s", cdn_err)
  68. with open(path, 'w', encoding='utf-8') as f:
  69. json.dump(json_data, f, ensure_ascii=False, indent=2)
  70. json_str = json.dumps(json_data, ensure_ascii=False, indent=2)
  71. lines = len(json_str.split('\n'))
  72. return ToolResult(
  73. title=path.name,
  74. output=f"JSON 数据已安全且完美地格式化写入: {path.name}",
  75. metadata={
  76. "lines": lines,
  77. "existed": path.exists()
  78. },
  79. long_term_memory=f"安全覆盖写入了结构化 JSON 文件 {path.name}"
  80. )
  81. except Exception as e:
  82. return ToolResult(
  83. title="JSON 写入失败",
  84. output=f"无法写入 JSON 到文件: {str(e)}",
  85. error=str(e)
  86. )