models.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. """
  2. Tool Models - 工具系统核心数据模型
  3. 定义:
  4. 1. ToolResult: 工具执行结果(支持双层记忆管理)
  5. 2. ToolContext: 工具执行上下文(依赖注入)
  6. """
  7. import json
  8. from dataclasses import dataclass, field
  9. from typing import Any, Dict, List, Optional, Protocol
  10. @dataclass
  11. class ToolResult:
  12. """
  13. 工具执行结果
  14. 支持双层记忆管理(参考 Browser-Use 的 ActionResult):
  15. - output: 主要输出,可能很长,可以配置只给 LLM 看一次
  16. - long_term_memory: 简短摘要,永久保存在对话历史中
  17. 这种设计避免了大量临时内容占用 context。
  18. """
  19. # 主要输出(临时内容)
  20. title: str # 简短标题,用于展示
  21. output: str # 主要输出内容
  22. # 记忆管理
  23. long_term_memory: Optional[str] = None # 永久记忆(简短摘要)
  24. include_output_only_once: bool = False # output 是否只给 LLM 看一次
  25. # 元数据
  26. metadata: Dict[str, Any] = field(default_factory=dict)
  27. # 状态标志
  28. truncated: bool = False # 输出是否被截断
  29. error: Optional[str] = None # 错误信息(如果执行失败)
  30. # 附件支持(用于浏览器自动化等场景)
  31. attachments: List[str] = field(default_factory=list) # 文件路径列表
  32. images: List[Dict[str, Any]] = field(default_factory=list) # 图片列表
  33. # Token追踪(用于工具内部LLM调用)
  34. tool_usage: Optional[Dict[str, Any]] = None # 格式:{"model": "...", "prompt_tokens": 100, "completion_tokens": 50, "cost": 0.0}
  35. # 默认 metadata 不会进入对话;对批量画像等工具设为 True,将 metadata 序列化后追加给 LLM
  36. include_metadata_in_llm: bool = False
  37. def to_llm_message(self, first_time: bool = True) -> str:
  38. """
  39. 转换为给 LLM 的消息
  40. Args:
  41. first_time: 是否第一次展示(影响 include_output_only_once 的行为)
  42. Returns:
  43. 给 LLM 的消息字符串
  44. """
  45. # 如果有错误,优先返回错误
  46. if self.error:
  47. return f"Error: {self.error}"
  48. # 构建消息
  49. parts = []
  50. # 标题
  51. if self.title:
  52. parts.append(f"# {self.title}")
  53. # 主要输出
  54. if first_time or not self.include_output_only_once:
  55. if self.output:
  56. parts.append(self.output)
  57. if self.truncated:
  58. parts.append("(Output truncated)")
  59. # 长期记忆(永远包含)
  60. if self.long_term_memory:
  61. parts.append(f"\nSummary: {self.long_term_memory}")
  62. # 附件信息
  63. if self.attachments:
  64. parts.append(f"\nAttachments: {', '.join(self.attachments)}")
  65. if self.include_metadata_in_llm and self.metadata:
  66. parts.append(
  67. "## metadata (JSON)\n"
  68. + json.dumps(self.metadata, ensure_ascii=False)
  69. )
  70. return "\n\n".join(parts)
  71. class ToolContext(Protocol):
  72. """
  73. 工具执行上下文(依赖注入)
  74. 工具函数可以声明需要哪些上下文字段,框架自动注入。
  75. 使用 Protocol 允许不同实现提供不同的上下文字段。
  76. """
  77. # 基础字段(所有工具都可用)
  78. trace_id: str
  79. step_id: str
  80. uid: Optional[str]
  81. # 浏览器相关(Browser-Use 集成)
  82. browser_session: Optional[Any] # BrowserSession 实例
  83. page_url: Optional[str] # 当前页面 URL
  84. file_system: Optional[Any] # FileSystem 实例
  85. sensitive_data: Optional[Dict[str, Any]] # 敏感数据字典
  86. # 其他可扩展字段
  87. context: Optional[Dict[str, Any]] # 额外上下文数据
  88. @dataclass
  89. class ToolContextImpl:
  90. """ToolContext 的默认实现"""
  91. # 基础字段
  92. trace_id: str
  93. step_id: str
  94. uid: Optional[str] = None
  95. # 浏览器相关
  96. browser_session: Optional[Any] = None
  97. page_url: Optional[str] = None
  98. file_system: Optional[Any] = None
  99. sensitive_data: Optional[Dict[str, Any]] = None
  100. # 额外上下文
  101. context: Optional[Dict[str, Any]] = None