Procházet zdrojové kódy

fix: address reviewed issues 642 through 745

jjyaoao před 1 týdnem
rodič
revize
6acdf9527f

+ 9 - 3
code/chapter10/14_weather_agent.py

@@ -16,14 +16,21 @@ def create_weather_assistant():
         name="天气助手",
         llm=llm,
         system_prompt="""你是天气助手,可以查询城市天气。
-使用 get_weather 工具查询天气,支持中文城市名。
+使用 mcp_get_weather 工具查询天气,支持中文城市名。
 """
     )
 
     # 添加天气 MCP 工具
     server_script = os.path.join(os.path.dirname(__file__), "14_weather_mcp_server.py")
     weather_tool = MCPTool(server_command=["python", server_script])
-    assistant.add_tool(weather_tool)
+
+    # 显式展开并注册 MCP 子工具
+    expanded_tools = weather_tool.get_expanded_tools()
+    if not expanded_tools:
+        raise RuntimeError("未发现天气 MCP 子工具,请检查服务脚本、依赖和启动日志。")
+
+    for expanded_tool in expanded_tools:
+        assistant.add_tool(expanded_tool)
 
     return assistant
 
@@ -55,4 +62,3 @@ if __name__ == "__main__":
         demo()
     else:
         interactive()
-

+ 10 - 0
code/chapter13/helloagents-trip-planner/frontend/src/env.d.ts

@@ -0,0 +1,10 @@
+/// <reference types="vite/client" />
+
+interface ImportMetaEnv {
+  readonly VITE_API_BASE_URL?: string
+  readonly VITE_AMAP_WEB_JS_KEY: string
+}
+
+interface ImportMeta {
+  readonly env: ImportMetaEnv
+}

+ 6 - 1
code/chapter13/helloagents-trip-planner/frontend/src/views/Home.vue

@@ -216,7 +216,12 @@ const loading = ref(false)
 const loadingProgress = ref(0)
 const loadingStatus = ref('')
 
-const formData = reactive<TripFormData & { start_date: Dayjs | null; end_date: Dayjs | null }>({
+type TripFormState = Omit<TripFormData, 'start_date' | 'end_date'> & {
+  start_date: Dayjs | null
+  end_date: Dayjs | null
+}
+
+const formData = reactive<TripFormState>({
   city: '',
   start_date: null,
   end_date: null,

+ 0 - 43
code/chapter13/helloagents-trip-planner/frontend/src/views/Result.vue

@@ -786,49 +786,6 @@ const exportAsPDF = async () => {
   }
 }
 
-// 截取地图图片
-const captureMapImage = async () => {
-  if (!map) return
-
-  try {
-    // 获取地图容器
-    const mapContainer = document.getElementById('amap-container')
-    if (!mapContainer) return
-
-    // 使用高德地图的截图功能
-    const mapCanvas = mapContainer.querySelector('canvas')
-    if (mapCanvas) {
-      // 创建一个img元素替换地图容器
-      const img = document.createElement('img')
-      img.src = mapCanvas.toDataURL('image/png')
-      img.style.width = '100%'
-      img.style.height = '500px'
-      img.style.objectFit = 'cover'
-      img.id = 'map-snapshot'
-
-      // 隐藏原地图,显示截图
-      mapContainer.style.display = 'none'
-      mapContainer.parentElement?.appendChild(img)
-    }
-  } catch (error) {
-    console.error('截取地图失败:', error)
-  }
-}
-
-// 恢复地图
-const restoreMap = () => {
-  const mapContainer = document.getElementById('amap-container')
-  const snapshot = document.getElementById('map-snapshot')
-
-  if (mapContainer) {
-    mapContainer.style.display = 'block'
-  }
-
-  if (snapshot) {
-    snapshot.remove()
-  }
-}
-
 // 初始化地图
 const initMap = async () => {
   try {

+ 1 - 1
code/chapter13/helloagents-trip-planner/frontend/tsconfig.json

@@ -27,6 +27,6 @@
       "@/*": ["src/*"]
     }
   },
-  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
+  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "src/**/*.d.ts"]
 }
 

+ 2 - 2
code/chapter8/03_WorkingMemory_Implementation.py

@@ -66,7 +66,7 @@ class WorkingMemoryDemo:
         print("-" * 40)
         
         print("混合检索策略包括:")
-        print("• TF-IDF向量化语义检索")
+        print("• TF-IDF 向量化词法检索")
         print("• 关键词匹配检索")
         print("• 时间衰减因子")
         print("• 重要性权重调整")
@@ -301,4 +301,4 @@ def main():
         traceback.print_exc()
 
 if __name__ == "__main__":
-    main()
+    main()

+ 11 - 13
code/chapter8/08_Agent_Tool_Integration.py

@@ -36,26 +36,24 @@ class AgentIntegrationDemo:
         
         print("✅ MemoryTool和RAGTool初始化完成")
         
+        # 注册工具
+        print("\n2. 注册工具...")
+        self.tool_registry = ToolRegistry()
+        self.tool_registry.register_tool(self.memory_tool)
+        self.tool_registry.register_tool(self.rag_tool)
+        print("✅ 工具注册完成")
+
         # 创建Agent
-        print("\n2. 创建Agent...")
+        print("\n3. 创建Agent...")
         self.llm = HelloAgentsLLM()
         self.agent = SimpleAgent(
             name="智能学习助手",
             llm=self.llm,
-            system_prompt="集成记忆和RAG功能的智能助手"
+            system_prompt="集成记忆和RAG功能的智能助手",
+            tool_registry=self.tool_registry
         )
-        
         print("✅ Agent创建完成")
         
-        # 注册工具
-        print("\n3. 注册工具...")
-        self.tool_registry = ToolRegistry()
-        self.tool_registry.register_tool(self.memory_tool)
-        self.tool_registry.register_tool(self.rag_tool)
-        self.agent.tool_registry = self.tool_registry
-        
-        print("✅ 工具注册完成")
-        
         # 显示Agent状态
         print(f"\n📊 Agent状态:")
         print(f"  名称: {self.agent.name}")
@@ -465,4 +463,4 @@ def main():
         traceback.print_exc()
 
 if __name__ == "__main__":
-    main()
+    main()

+ 6 - 6
code/chapter8/09_Memory_Types_Deep_Dive.py

@@ -149,35 +149,35 @@ class MemoryTypesDeepDive:
         learning_session = [
             {
                 "content": "开始学习Python机器学习",
-                "context": "学习开始",
+                "context": {"stage": "学习开始"},
                 "location": "家里书房",
                 "mood": "专注",
                 "importance": 0.7
             },
             {
                 "content": "学习了线性回归的数学原理",
-                "context": "理论学习",
+                "context": {"stage": "理论学习"},
                 "chapter": "第3章",
                 "difficulty": "中等",
                 "importance": 0.8
             },
             {
                 "content": "实现了第一个线性回归模型",
-                "context": "实践编程",
+                "context": {"stage": "实践编程"},
                 "code_lines": 45,
                 "bugs_fixed": 2,
                 "importance": 0.9
             },
             {
                 "content": "完成了课后练习题",
-                "context": "练习巩固",
+                "context": {"stage": "练习巩固"},
                 "exercises_completed": 5,
                 "accuracy": 0.8,
                 "importance": 0.6
             },
             {
                 "content": "总结今天的学习收获",
-                "context": "学习总结",
+                "context": {"stage": "学习总结"},
                 "key_concepts": ["线性回归", "梯度下降", "损失函数"],
                 "importance": 0.8
             }
@@ -741,4 +741,4 @@ def main():
         traceback.print_exc()
 
 if __name__ == "__main__":
-    main()
+    main()

+ 9 - 4
docs/chapter10/Chapter10-Agent-Communication-Protocols.md

@@ -2054,14 +2054,21 @@ def create_weather_assistant():
         name="Weather Assistant",
         llm=llm,
         system_prompt="""You are a weather assistant that can query city weather.
-Use the get_weather tool to query weather, supports Chinese city names.
+Use the mcp_get_weather tool to query weather, supports Chinese city names.
 """
     )
 
     # Add weather MCP tool
     server_script = os.path.join(os.path.dirname(__file__), "14_weather_mcp_server.py")
     weather_tool = MCPTool(server_command=["python", server_script])
-    assistant.add_tool(weather_tool)
+
+    # Explicitly expand and register MCP sub-tools
+    expanded_tools = weather_tool.get_expanded_tools()
+    if not expanded_tools:
+        raise RuntimeError("No weather MCP sub-tools were discovered. Check the server script, dependencies, and startup logs.")
+
+    for expanded_tool in expanded_tools:
+        assistant.add_tool(expanded_tool)
 
     return assistant
 
@@ -2102,7 +2109,6 @@ if __name__ == "__main__":
 ✅ Tool 'mcp_get_weather' registered.
 ✅ Tool 'mcp_list_supported_cities' registered.
 ✅ Tool 'mcp_get_server_info' registered.
-✅ MCP tool 'mcp' expanded into 3 independent tools
 
 You: I want to query Beijing's weather
 🔗 Connecting to MCP server...
@@ -2438,4 +2444,3 @@ You now have mastered the core knowledge of agent communication protocols. Keep
 [2] The A2A Project. (2025). *A2A Protocol: An open protocol for agent-to-agent communication*. Retrieved October 7, 2025, from https://a2a-protocol.org/
 
 [3] Chang, G., Lin, E., Yuan, C., Cai, R., Chen, B., Xie, X., & Zhang, Y. (2025). *Agent Network Protocol technical white paper*. arXiv. https://doi.org/10.48550/arXiv.2508.00007
-

+ 9 - 3
docs/chapter10/第十章 智能体通信协议.md

@@ -2054,14 +2054,21 @@ def create_weather_assistant():
         name="天气助手",
         llm=llm,
         system_prompt="""你是天气助手,可以查询城市天气。
-使用 get_weather 工具查询天气,支持中文城市名。
+使用 mcp_get_weather 工具查询天气,支持中文城市名。
 """
     )
 
     # 添加天气 MCP 工具
     server_script = os.path.join(os.path.dirname(__file__), "14_weather_mcp_server.py")
     weather_tool = MCPTool(server_command=["python", server_script])
-    assistant.add_tool(weather_tool)
+
+    # 显式展开并注册 MCP 子工具
+    expanded_tools = weather_tool.get_expanded_tools()
+    if not expanded_tools:
+        raise RuntimeError("未发现天气 MCP 子工具,请检查服务脚本、依赖和启动日志。")
+
+    for expanded_tool in expanded_tools:
+        assistant.add_tool(expanded_tool)
 
     return assistant
 
@@ -2102,7 +2109,6 @@ if __name__ == "__main__":
 ✅ 工具 'mcp_get_weather' 已注册。
 ✅ 工具 'mcp_list_supported_cities' 已注册。
 ✅ 工具 'mcp_get_server_info' 已注册。
-✅ MCP工具 'mcp' 已展开为 3 个独立工具
 
 你: 我想查询北京的天气
 🔗 连接到 MCP 服务器...

+ 25 - 28
docs/chapter8/Chapter8-Memory-and-Retrieval.md

@@ -229,13 +229,6 @@ from hello_agents.tools import MemoryTool, RAGTool
 # Create LLM instance
 llm = HelloAgentsLLM()
 
-# Create Agent
-agent = SimpleAgent(
-    name="Intelligent Assistant",
-    llm=llm,
-    system_prompt="You are an AI assistant with memory and knowledge retrieval capabilities"
-)
-
 # Create tool registry
 tool_registry = ToolRegistry()
 
@@ -247,8 +240,13 @@ tool_registry.register_tool(memory_tool)
 rag_tool = RAGTool(knowledge_base_path="./knowledge_base")
 tool_registry.register_tool(rag_tool)
 
-# Configure tools for Agent
-agent.tool_registry = tool_registry
+# Create Agent and configure tools
+agent = SimpleAgent(
+    name="Intelligent Assistant",
+    llm=llm,
+    system_prompt="You are an AI assistant with memory and knowledge retrieval capabilities",
+    tool_registry=tool_registry
+)
 
 # Start conversation
 response = agent.run("Hello! Please remember my name is Zhang San, I am a Python developer")
@@ -326,15 +324,16 @@ Before diving into implementation details, let's quickly experience the basic fu
 from hello_agents import SimpleAgent, HelloAgentsLLM, ToolRegistry
 from hello_agents.tools import MemoryTool
 
-# Create Agent with memory capability
+# Create LLM instance
 llm = HelloAgentsLLM()
-agent = SimpleAgent(name="Memory Assistant", llm=llm)
 
 # Create memory tool
 memory_tool = MemoryTool(user_id="user123")
 tool_registry = ToolRegistry()
 tool_registry.register_tool(memory_tool)
-agent.tool_registry = tool_registry
+
+# Create Agent with memory capability
+agent = SimpleAgent(name="Memory Assistant", llm=llm, tool_registry=tool_registry)
 
 # Experience memory features
 print("=== Adding Multiple Memories ===")
@@ -664,7 +663,7 @@ class MemoryTool(Tool):
         )
 ````
 
-MemoryManager, as the core coordinator of the memory system, is responsible for managing different types of memory modules and providing a unified operation interface.
+MemoryManager, as the core coordinator of the memory system, is responsible for managing different types of memory modules and providing a unified operation interface. The concrete storage and retrieval capabilities are implemented internally by each memory type.
 
 ````python
 class MemoryManager:
@@ -682,24 +681,22 @@ class MemoryManager:
         self.config = config or MemoryConfig()
         self.user_id = user_id
 
-        # Initialize storage and retrieval components
-        self.store = MemoryStore(self.config)
-        self.retriever = MemoryRetriever(self.store, self.config)
+        # Storage and retrieval are implemented within each memory type
 
         # Initialize various types of memory
         self.memory_types = {}
 
         if enable_working:
-            self.memory_types['working'] = WorkingMemory(self.config, self.store)
+            self.memory_types['working'] = WorkingMemory(self.config)
 
         if enable_episodic:
-            self.memory_types['episodic'] = EpisodicMemory(self.config, self.store)
+            self.memory_types['episodic'] = EpisodicMemory(self.config)
 
         if enable_semantic:
-            self.memory_types['semantic'] = SemanticMemory(self.config, self.store)
+            self.memory_types['semantic'] = SemanticMemory(self.config)
 
         if enable_perceptual:
-            self.memory_types['perceptual'] = PerceptualMemory(self.config, self.store)
+            self.memory_types['perceptual'] = PerceptualMemory(self.config)
 ````
 
 ### 8.2.5 Four Types of Memory
@@ -718,7 +715,7 @@ class WorkingMemory:
     Features:
     - Limited capacity (default 50 items) + TTL automatic cleanup
     - Pure in-memory storage, extremely fast access
-    - Hybrid retrieval: TF-IDF vectorization + keyword matching
+    - Combined lexical retrieval: TF-IDF term-weight similarity + keyword matching
     """
 
     def __init__(self, config: MemoryConfig):
@@ -737,10 +734,10 @@ class WorkingMemory:
         return memory_item.id
 
     def retrieve(self, query: str, limit: int = 5, **kwargs) -> List[MemoryItem]:
-        """Hybrid retrieval: TF-IDF vectorization + keyword matching"""
+        """Combined lexical retrieval: TF-IDF term-weight similarity + keyword matching"""
         self._expire_old_memories()
 
-        # Try TF-IDF vector retrieval
+        # Calculate TF-IDF term-weight similarity
         vector_scores = self._try_tfidf_search(query)
 
         # Calculate comprehensive score
@@ -762,7 +759,7 @@ class WorkingMemory:
         return [memory for _, memory in scored_memories[:limit]]
 ````
 
-Working memory retrieval adopts a hybrid retrieval strategy. It first attempts to use TF-IDF vectorization for semantic retrieval, and if that fails, it falls back to keyword matching. This design ensures reliable retrieval services in various environments. The scoring algorithm combines semantic similarity, time decay, and importance weight. The final score formula is: `(similarity × time decay) × (0.8 + importance × 0.4)`.
+Working memory uses a combined lexical retrieval strategy: TF-IDF represents text as sparse term vectors and calculates lexical similarity, while keyword matching provides an additional signal. When TF-IDF is unavailable or produces no effective score, the keyword score is used instead. TF-IDF vectorization is based on term frequency and inverse document frequency; it is not equivalent to semantic retrieval based on dense embeddings. The scoring algorithm combines lexical relevance, time decay, and importance weight. The final score formula is: `(relevance × time decay) × (0.8 + importance × 0.4)`.
 
 (2) Episodic Memory
 
@@ -1134,9 +1131,8 @@ Let's quickly experience the basic functions of the RAG system:
 from hello_agents import SimpleAgent, HelloAgentsLLM, ToolRegistry
 from hello_agents.tools import RAGTool
 
-# Create Agent with RAG capability
+# Create LLM instance
 llm = HelloAgentsLLM()
-agent = SimpleAgent(name="Knowledge Assistant", llm=llm)
 
 # Create RAG tool
 rag_tool = RAGTool(
@@ -1147,7 +1143,9 @@ rag_tool = RAGTool(
 
 tool_registry = ToolRegistry()
 tool_registry.register_tool(rag_tool)
-agent.tool_registry = tool_registry
+
+# Create Agent with RAG capability
+agent = SimpleAgent(name="Knowledge Assistant", llm=llm, tool_registry=tool_registry)
 
 # Experience RAG features
 # Add first knowledge
@@ -2080,4 +2078,3 @@ In the next chapter, we will continue to explore how to further improve the dial
 ## References
 
 [1] Atkinson, R. C., & Shiffrin, R. M. (1968). Human memory: A proposed system and its control processes. In *Psychology of learning and motivation* (Vol. 2, pp. 89-195). Academic press.
-

+ 25 - 27
docs/chapter8/第八章 记忆与检索.md

@@ -228,13 +228,6 @@ from hello_agents.tools import MemoryTool, RAGTool
 # 创建LLM实例
 llm = HelloAgentsLLM()
 
-# 创建Agent
-agent = SimpleAgent(
-    name="智能助手",
-    llm=llm,
-    system_prompt="你是一个有记忆和知识检索能力的AI助手"
-)
-
 # 创建工具注册表
 tool_registry = ToolRegistry()
 
@@ -246,8 +239,13 @@ tool_registry.register_tool(memory_tool)
 rag_tool = RAGTool(knowledge_base_path="./knowledge_base")
 tool_registry.register_tool(rag_tool)
 
-# 为Agent配置工具
-agent.tool_registry = tool_registry
+# 创建Agent并配置工具
+agent = SimpleAgent(
+    name="智能助手",
+    llm=llm,
+    system_prompt="你是一个有记忆和知识检索能力的AI助手",
+    tool_registry=tool_registry
+)
 
 # 开始对话
 response = agent.run("你好!请记住我叫张三,我是一名Python开发者")
@@ -327,15 +325,16 @@ INFO:hello_agents.memory.storage.qdrant_store:✅ 使用现有Qdrant集合: rag_
 from hello_agents import SimpleAgent, HelloAgentsLLM, ToolRegistry
 from hello_agents.tools import MemoryTool
 
-# 创建具有记忆能力的Agent
+# 创建LLM实例
 llm = HelloAgentsLLM()
-agent = SimpleAgent(name="记忆助手", llm=llm)
 
 # 创建记忆工具
 memory_tool = MemoryTool(user_id="user123")
 tool_registry = ToolRegistry()
 tool_registry.register_tool(memory_tool)
-agent.tool_registry = tool_registry
+
+# 创建具有记忆能力的Agent
+agent = SimpleAgent(name="记忆助手", llm=llm, tool_registry=tool_registry)
  
 # 体验记忆功能
 print("=== 添加多个记忆 ===")
@@ -664,7 +663,7 @@ class MemoryTool(Tool):
             enable_perceptual="perceptual" in self.memory_types
         )
 ````
-MemoryManager作为记忆系统的核心协调者,负责管理不同类型的记忆模块,并提供统一的操作接口。
+MemoryManager作为记忆系统的核心协调者,负责管理不同类型的记忆模块,并提供统一的操作接口。具体的存储与检索能力由各记忆类型在内部实现。
 
 ````python
 class MemoryManager:
@@ -682,24 +681,22 @@ class MemoryManager:
         self.config = config or MemoryConfig()
         self.user_id = user_id
 
-        # 初始化存储和检索组件
-        self.store = MemoryStore(self.config)
-        self.retriever = MemoryRetriever(self.store, self.config)
+        # 存储和检索功能由各记忆类型内部实现
 
         # 初始化各类型记忆
         self.memory_types = {}
 
         if enable_working:
-            self.memory_types['working'] = WorkingMemory(self.config, self.store)
+            self.memory_types['working'] = WorkingMemory(self.config)
 
         if enable_episodic:
-            self.memory_types['episodic'] = EpisodicMemory(self.config, self.store)
+            self.memory_types['episodic'] = EpisodicMemory(self.config)
 
         if enable_semantic:
-            self.memory_types['semantic'] = SemanticMemory(self.config, self.store)
+            self.memory_types['semantic'] = SemanticMemory(self.config)
 
         if enable_perceptual:
-            self.memory_types['perceptual'] = PerceptualMemory(self.config, self.store)
+            self.memory_types['perceptual'] = PerceptualMemory(self.config)
 ````
 ### 8.2.5 四种记忆类型
 
@@ -718,7 +715,7 @@ class WorkingMemory:
     特点:
     - 容量有限(默认50条)+ TTL自动清理
     - 纯内存存储,访问速度极快
-    - 混合检索:TF-IDF向量化 + 关键词匹配
+    - 组合词法检索:TF-IDF 词项权重相似度 + 关键词匹配
     """
     
     def __init__(self, config: MemoryConfig):
@@ -737,10 +734,10 @@ class WorkingMemory:
         return memory_item.id
     
     def retrieve(self, query: str, limit: int = 5, **kwargs) -> List[MemoryItem]:
-        """混合检索:TF-IDF向量化 + 关键词匹配"""
+        """组合词法检索:TF-IDF 词项权重相似度 + 关键词匹配"""
         self._expire_old_memories()
         
-        # 尝试TF-IDF向量检索
+        # 计算 TF-IDF 词项权重相似度
         vector_scores = self._try_tfidf_search(query)
         
         # 计算综合分数
@@ -761,7 +758,7 @@ class WorkingMemory:
         scored_memories.sort(key=lambda x: x[0], reverse=True)
         return [memory for _, memory in scored_memories[:limit]]
 ````
-工作记忆的检索采用了混合检索策略,首先尝试使用TF-IDF向量化进行语义检索,如果失败则回退到关键词匹配。这种设计确保了在各种环境下都能提供可靠的检索服务。评分算法结合了语义相似度、时间衰减和重要性权重,最终得分公式为:`(相似度 × 时间衰减) × (0.8 + 重要性 × 0.4)`。
+工作记忆采用组合词法检索策略:使用 TF-IDF 将文本表示为稀疏词项向量并计算词法相似度,同时结合关键词匹配;当 TF-IDF 不可用或未产生有效得分时,则使用关键词得分。TF-IDF 的向量化表示来自词频和逆文档频率,并不等同于基于稠密嵌入的语义检索。评分算法结合了词法相关性、时间衰减和重要性权重,最终得分公式为:`(相关性 × 时间衰减) × (0.8 + 重要性 × 0.4)`。
 
 (2)情景记忆(EpisodicMemory)
 
@@ -1132,9 +1129,8 @@ def _calculate_recency_score(self, timestamp: str) -> float:
 from hello_agents import SimpleAgent, HelloAgentsLLM, ToolRegistry
 from hello_agents.tools import RAGTool
 
-# 创建具有RAG能力的Agent
+# 创建LLM实例
 llm = HelloAgentsLLM()
-agent = SimpleAgent(name="知识助手", llm=llm)
 
 # 创建RAG工具
 rag_tool = RAGTool(
@@ -1145,7 +1141,9 @@ rag_tool = RAGTool(
 
 tool_registry = ToolRegistry()
 tool_registry.register_tool(rag_tool)
-agent.tool_registry = tool_registry
+
+# 创建具有RAG能力的Agent
+agent = SimpleAgent(name="知识助手", llm=llm, tool_registry=tool_registry)
 
 # 体验RAG功能
 # 添加第一个知识