04_note_tool_integration.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. """
  2. NoteTool 与 ContextBuilder 集成示例
  3. 展示如何将 NoteTool 与 ContextBuilder 集成,实现:
  4. 1. 长期项目追踪
  5. 2. 笔记检索与上下文注入
  6. 3. 基于历史笔记的连贯建议
  7. """
  8. from hello_agents import SimpleAgent, HelloAgentsLLM
  9. from hello_agents.context import ContextBuilder, ContextConfig, ContextPacket
  10. from hello_agents.tools import MemoryTool, RAGTool, NoteTool
  11. from hello_agents.core.message import Message
  12. from datetime import datetime
  13. from typing import List, Dict
  14. class ProjectAssistant(SimpleAgent):
  15. """长期项目助手,集成 NoteTool 和 ContextBuilder"""
  16. def __init__(self, name: str, project_name: str, **kwargs):
  17. # 配置 LLM
  18. from hello_agents.core.llm import HelloAgentsLLM
  19. llm = HelloAgentsLLM()
  20. super().__init__(name=name, llm=llm, **kwargs)
  21. self.project_name = project_name
  22. # 初始化工具
  23. self.memory_tool = MemoryTool(user_id=project_name)
  24. self.rag_tool = RAGTool(knowledge_base_path=f"./{project_name}_kb")
  25. self.note_tool = NoteTool(workspace=f"./{project_name}_notes")
  26. # 初始化上下文构建器
  27. self.context_builder = ContextBuilder(
  28. memory_tool=self.memory_tool,
  29. rag_tool=self.rag_tool,
  30. config=ContextConfig(max_tokens=4000)
  31. )
  32. self.conversation_history = []
  33. def run(self, user_input: str, note_as_action: bool = False) -> str:
  34. """运行助手,自动集成笔记"""
  35. # 1. 从 NoteTool 检索相关笔记
  36. relevant_notes = self._retrieve_relevant_notes(user_input)
  37. # 2. 将笔记转换为 ContextPacket
  38. note_packets = self._notes_to_packets(relevant_notes)
  39. # 3. 构建优化的上下文
  40. context = self.context_builder.build(
  41. user_query=user_input,
  42. conversation_history=self.conversation_history,
  43. system_instructions=self._build_system_instructions(),
  44. custom_packets=note_packets
  45. )
  46. # 4. 调用 LLM
  47. response = self.llm.invoke(context)
  48. # 5. 如果需要,将交互记录为笔记
  49. if note_as_action:
  50. self._save_as_note(user_input, response)
  51. # 6. 更新对话历史
  52. self._update_history(user_input, response)
  53. return response
  54. def _retrieve_relevant_notes(self, query: str, limit: int = 3) -> List[Dict]:
  55. """检索相关笔记"""
  56. try:
  57. # 优先检索 blocker 和 action 类型的笔记
  58. blockers = self.note_tool.run({
  59. "action": "list",
  60. "note_type": "blocker",
  61. "limit": 2
  62. })
  63. # 通用搜索
  64. search_results = self.note_tool.run({
  65. "action": "search",
  66. "query": query,
  67. "limit": limit
  68. })
  69. # 合并并去重
  70. all_notes = {note['note_id']: note for note in blockers + search_results}
  71. return list(all_notes.values())[:limit]
  72. except Exception as e:
  73. print(f"[WARNING] 笔记检索失败: {e}")
  74. return []
  75. def _notes_to_packets(self, notes: List[Dict]) -> List[ContextPacket]:
  76. """将笔记转换为上下文包"""
  77. packets = []
  78. for note in notes:
  79. content = f"[笔记:{note['title']}]\n{note['content']}"
  80. packets.append(ContextPacket(
  81. content=content,
  82. timestamp=datetime.fromisoformat(note['updated_at']),
  83. token_count=len(content) // 4, # 简单估算
  84. relevance_score=0.75, # 笔记具有较高相关性
  85. metadata={
  86. "type": "note",
  87. "note_type": note['type'],
  88. "note_id": note['note_id']
  89. }
  90. ))
  91. return packets
  92. def _save_as_note(self, user_input: str, response: str):
  93. """将交互保存为笔记"""
  94. try:
  95. # 判断应该保存为什么类型的笔记
  96. if "问题" in user_input or "阻塞" in user_input:
  97. note_type = "blocker"
  98. elif "计划" in user_input or "下一步" in user_input:
  99. note_type = "action"
  100. else:
  101. note_type = "conclusion"
  102. self.note_tool.run({
  103. "action": "create",
  104. "title": f"{user_input[:30]}...",
  105. "content": f"## 问题\n{user_input}\n\n## 分析\n{response}",
  106. "note_type": note_type,
  107. "tags": [self.project_name, "auto_generated"]
  108. })
  109. except Exception as e:
  110. print(f"[WARNING] 保存笔记失败: {e}")
  111. def _build_system_instructions(self) -> str:
  112. """构建系统指令"""
  113. return f"""你是 {self.project_name} 项目的长期助手。
  114. 你的职责:
  115. 1. 基于历史笔记提供连贯的建议
  116. 2. 追踪项目进展和待解决问题
  117. 3. 在回答时引用相关的历史笔记
  118. 4. 提供具体、可操作的下一步建议
  119. 注意:
  120. - 优先关注标记为 blocker 的问题
  121. - 在建议中说明依据来源(笔记、记忆或知识库)
  122. - 保持对项目整体进度的认识"""
  123. def _update_history(self, user_input: str, response: str):
  124. """更新对话历史"""
  125. self.conversation_history.append(
  126. Message(content=user_input, role="user", timestamp=datetime.now())
  127. )
  128. self.conversation_history.append(
  129. Message(content=response, role="assistant", timestamp=datetime.now())
  130. )
  131. # 限制历史长度
  132. if len(self.conversation_history) > 10:
  133. self.conversation_history = self.conversation_history[-10:]
  134. def main():
  135. print("=" * 80)
  136. print("NoteTool 与 ContextBuilder 集成示例")
  137. print("=" * 80 + "\n")
  138. # 使用示例
  139. assistant = ProjectAssistant(
  140. name="项目助手",
  141. project_name="data_pipeline_refactoring"
  142. )
  143. # 第一次交互:记录项目状态
  144. print("第一次交互:记录项目状态")
  145. response = assistant.run(
  146. "我们已经完成了数据模型层的重构,测试覆盖率达到85%。下一步计划重构业务逻辑层。",
  147. note_as_action=True
  148. )
  149. print(f"助手回答: {response}\n")
  150. # 第二次交互:提出问题
  151. print("第二次交互:提出问题")
  152. response = assistant.run(
  153. "在重构业务逻辑层时,我遇到了依赖版本冲突的问题,该如何解决?"
  154. )
  155. print(f"助手回答: {response}\n")
  156. # 查看笔记摘要
  157. print("查看笔记摘要:")
  158. summary = assistant.note_tool.run({"action": "summary"})
  159. import json
  160. print(json.dumps(summary, indent=2, ensure_ascii=False))
  161. print("\n" + "=" * 80)
  162. print("演示完成!")
  163. print("=" * 80)
  164. if __name__ == "__main__":
  165. main()