models.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. """
  2. Memory 数据模型
  3. Experience: 经验规则(条件 + 规则 + 证据)
  4. Skill: 技能(从经验归纳的高层知识)
  5. """
  6. from dataclasses import dataclass, field
  7. from datetime import datetime
  8. from typing import Dict, Any, List, Optional, Literal
  9. import uuid
  10. @dataclass
  11. class Experience:
  12. """
  13. 经验规则
  14. 从执行过程或人工反馈中提取的规则,格式:
  15. - condition: 什么情况下适用
  16. - rule: 应该怎么做
  17. - evidence: 证据(step_ids)
  18. """
  19. exp_id: str
  20. scope: str # "agent:{type}" 或 "user:{uid}"
  21. # 核心三元组
  22. condition: str
  23. rule: str
  24. evidence: List[str] = field(default_factory=list) # step_ids
  25. # 元数据
  26. source: Literal["execution", "feedback", "manual"] = "feedback"
  27. confidence: float = 0.5
  28. usage_count: int = 0
  29. success_rate: float = 0.0
  30. # 时间
  31. created_at: datetime = field(default_factory=datetime.now)
  32. updated_at: datetime = field(default_factory=datetime.now)
  33. @classmethod
  34. def create(
  35. cls,
  36. scope: str,
  37. condition: str,
  38. rule: str,
  39. evidence: List[str] = None,
  40. source: Literal["execution", "feedback", "manual"] = "feedback",
  41. confidence: float = 0.5,
  42. ) -> "Experience":
  43. """创建新的 Experience"""
  44. now = datetime.now()
  45. return cls(
  46. exp_id=str(uuid.uuid4()),
  47. scope=scope,
  48. condition=condition,
  49. rule=rule,
  50. evidence=evidence or [],
  51. source=source,
  52. confidence=confidence,
  53. created_at=now,
  54. updated_at=now,
  55. )
  56. def to_dict(self) -> Dict[str, Any]:
  57. """转换为字典"""
  58. return {
  59. "exp_id": self.exp_id,
  60. "scope": self.scope,
  61. "condition": self.condition,
  62. "rule": self.rule,
  63. "evidence": self.evidence,
  64. "source": self.source,
  65. "confidence": self.confidence,
  66. "usage_count": self.usage_count,
  67. "success_rate": self.success_rate,
  68. "created_at": self.created_at.isoformat() if self.created_at else None,
  69. "updated_at": self.updated_at.isoformat() if self.updated_at else None,
  70. }
  71. def to_prompt_text(self) -> str:
  72. """转换为可注入 Prompt 的文本"""
  73. return f"当 {self.condition} 时,{self.rule}"
  74. @dataclass
  75. class Skill:
  76. """
  77. 技能 - 从经验归纳的高层知识
  78. 技能可以形成层次结构(通过 parent_id)
  79. """
  80. skill_id: str
  81. scope: str # "agent:{type}" 或 "user:{uid}"
  82. name: str
  83. description: str
  84. category: str # 分类,如 "search", "reasoning", "writing"
  85. # 层次结构
  86. parent_id: Optional[str] = None
  87. # 内容
  88. content: Optional[str] = None # 完整的 skill 内容(Markdown)
  89. guidelines: List[str] = field(default_factory=list)
  90. derived_from: List[str] = field(default_factory=list) # experience_ids
  91. # 版本
  92. version: int = 1
  93. # 时间
  94. created_at: datetime = field(default_factory=datetime.now)
  95. updated_at: datetime = field(default_factory=datetime.now)
  96. @classmethod
  97. def create(
  98. cls,
  99. scope: str,
  100. name: str,
  101. description: str,
  102. category: str = "general",
  103. content: Optional[str] = None,
  104. guidelines: List[str] = None,
  105. derived_from: List[str] = None,
  106. parent_id: Optional[str] = None,
  107. ) -> "Skill":
  108. """创建新的 Skill"""
  109. now = datetime.now()
  110. return cls(
  111. skill_id=str(uuid.uuid4()),
  112. scope=scope,
  113. name=name,
  114. description=description,
  115. category=category,
  116. parent_id=parent_id,
  117. content=content,
  118. guidelines=guidelines or [],
  119. derived_from=derived_from or [],
  120. created_at=now,
  121. updated_at=now,
  122. )
  123. def to_dict(self) -> Dict[str, Any]:
  124. """转换为字典"""
  125. return {
  126. "skill_id": self.skill_id,
  127. "scope": self.scope,
  128. "name": self.name,
  129. "description": self.description,
  130. "category": self.category,
  131. "parent_id": self.parent_id,
  132. "content": self.content,
  133. "guidelines": self.guidelines,
  134. "derived_from": self.derived_from,
  135. "version": self.version,
  136. "created_at": self.created_at.isoformat() if self.created_at else None,
  137. "updated_at": self.updated_at.isoformat() if self.updated_at else None,
  138. }
  139. def to_prompt_text(self) -> str:
  140. """
  141. 转换为可注入 Prompt 的文本
  142. 优先使用完整的 content(如果有),否则使用 description + guidelines
  143. """
  144. # 如果有完整的 content,直接使用
  145. if self.content:
  146. return self.content.strip()
  147. # 否则使用旧的格式(向后兼容)
  148. lines = [f"### {self.name}", self.description]
  149. if self.guidelines:
  150. lines.append("指导原则:")
  151. for g in self.guidelines:
  152. lines.append(f"- {g}")
  153. return "\n".join(lines)