loom-agent 0.0.2__py3-none-any.whl → 0.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of loom-agent might be problematic. Click here for more details.

@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import time
5
6
  from typing import TYPE_CHECKING, Any, Optional, Dict, List
6
7
 
7
8
  from pydantic import BaseModel, Field
@@ -36,6 +37,12 @@ class TaskTool(BaseTool):
36
37
  Task 工具 - 启动 SubAgent 执行专项任务
37
38
 
38
39
  对应 Claude Code 的 Task 工具和 SubAgent 机制
40
+
41
+ 新特性 (Loom 0.0.3):
42
+ - 子代理池管理
43
+ - 性能监控和指标收集
44
+ - 智能负载均衡
45
+ - 资源使用优化
39
46
  """
40
47
 
41
48
  name = "task"
@@ -56,14 +63,35 @@ class TaskTool(BaseTool):
56
63
  self,
57
64
  agent_factory: Optional[callable] = None,
58
65
  max_iterations: int = 20,
66
+ enable_pooling: bool = True,
67
+ pool_size: int = 5,
68
+ enable_monitoring: bool = True,
59
69
  ) -> None:
60
70
  """
61
71
  Parameters:
62
72
  - agent_factory: 创建 SubAgent 的工厂函数
63
73
  - max_iterations: SubAgent 最大迭代次数
74
+ - enable_pooling: 启用子代理池管理
75
+ - pool_size: 子代理池大小
76
+ - enable_monitoring: 启用性能监控
64
77
  """
65
78
  self.agent_factory = agent_factory
66
79
  self.max_iterations = max_iterations
80
+
81
+ # Performance optimizations
82
+ self.enable_pooling = enable_pooling
83
+ self.pool_size = pool_size
84
+ self.enable_monitoring = enable_monitoring
85
+
86
+ # Sub-agent pool management
87
+ self._agent_pool: Dict[str, Any] = {}
88
+ self._pool_stats = {
89
+ "total_created": 0,
90
+ "total_executed": 0,
91
+ "average_execution_time": 0.0,
92
+ "cache_hits": 0,
93
+ "cache_misses": 0
94
+ }
67
95
 
68
96
  async def run(
69
97
  self,
@@ -136,7 +164,33 @@ class TaskTool(BaseTool):
136
164
  )
137
165
 
138
166
  # 运行子任务(系统提示已注入到 sub_agent,输入仍为原始 prompt)
167
+ start_time = time.time() if self.enable_monitoring else None
168
+
169
+ # Check pool for reusable agent
170
+ agent_key = self._get_agent_key(subagent_type, effective_model, permission_policy)
171
+ if self.enable_pooling and agent_key in self._agent_pool:
172
+ sub_agent = self._agent_pool[agent_key]
173
+ self._pool_stats["cache_hits"] += 1
174
+ else:
175
+ self._pool_stats["cache_misses"] += 1
176
+ self._pool_stats["total_created"] += 1
177
+
178
+ # Add to pool if enabled and not at capacity
179
+ if self.enable_pooling and len(self._agent_pool) < self.pool_size:
180
+ self._agent_pool[agent_key] = sub_agent
181
+
139
182
  result = await sub_agent.run(prompt)
183
+
184
+ # Update performance metrics
185
+ if self.enable_monitoring and start_time:
186
+ execution_time = time.time() - start_time
187
+ self._pool_stats["total_executed"] += 1
188
+ # Update running average
189
+ current_avg = self._pool_stats["average_execution_time"]
190
+ total_executed = self._pool_stats["total_executed"]
191
+ self._pool_stats["average_execution_time"] = (
192
+ (current_avg * (total_executed - 1) + execution_time) / total_executed
193
+ )
140
194
 
141
195
  # 格式化返回结果
142
196
  return f"**SubAgent Task: {description}**\n\nResult:\n{result}"
@@ -161,3 +215,49 @@ class TaskTool(BaseTool):
161
215
  subagent_type=subagent_type,
162
216
  model_name=model_name,
163
217
  )
218
+
219
+ def _get_agent_key(
220
+ self,
221
+ subagent_type: Optional[str],
222
+ model_name: Optional[str],
223
+ permission_policy: Optional[Dict[str, str]]
224
+ ) -> str:
225
+ """Generate unique key for agent pool"""
226
+ import hashlib
227
+
228
+ key_parts = [
229
+ subagent_type or "default",
230
+ model_name or "default",
231
+ str(sorted(permission_policy.items())) if permission_policy else "default"
232
+ ]
233
+
234
+ key_string = "|".join(key_parts)
235
+ return hashlib.md5(key_string.encode()).hexdigest()
236
+
237
+ def get_pool_stats(self) -> Dict[str, Any]:
238
+ """Get sub-agent pool statistics"""
239
+ return {
240
+ **self._pool_stats,
241
+ "pool_size": len(self._agent_pool),
242
+ "max_pool_size": self.pool_size,
243
+ "pool_utilization": len(self._agent_pool) / self.pool_size if self.pool_size > 0 else 0,
244
+ "cache_hit_rate": (
245
+ self._pool_stats["cache_hits"] /
246
+ (self._pool_stats["cache_hits"] + self._pool_stats["cache_misses"])
247
+ if (self._pool_stats["cache_hits"] + self._pool_stats["cache_misses"]) > 0 else 0
248
+ )
249
+ }
250
+
251
+ def clear_pool(self) -> None:
252
+ """Clear the sub-agent pool"""
253
+ self._agent_pool.clear()
254
+
255
+ def reset_stats(self) -> None:
256
+ """Reset performance statistics"""
257
+ self._pool_stats = {
258
+ "total_created": 0,
259
+ "total_executed": 0,
260
+ "average_execution_time": 0.0,
261
+ "cache_hits": 0,
262
+ "cache_misses": 0
263
+ }
@@ -11,7 +11,7 @@ import asyncio
11
11
  import json
12
12
  import time
13
13
  from pathlib import Path
14
- from typing import AsyncGenerator, Dict, List, Optional
14
+ from typing import AsyncGenerator, Dict, List, Optional, Any
15
15
  from uuid import uuid4
16
16
 
17
17
  from loom.callbacks.base import BaseCallback
@@ -37,6 +37,52 @@ try:
37
37
  except ImportError:
38
38
  ContextRetriever = None # type: ignore
39
39
 
40
+ # Unified coordination support
41
+ try:
42
+ from loom.core.unified_coordination import UnifiedExecutionContext, IntelligentCoordinator
43
+ except ImportError:
44
+ UnifiedExecutionContext = None # type: ignore
45
+ IntelligentCoordinator = None # type: ignore
46
+
47
+
48
+ class TaskHandler:
49
+ """
50
+ 任务处理器基类
51
+
52
+ 开发者可以继承此类来实现自定义的任务处理逻辑
53
+ """
54
+
55
+ def can_handle(self, task: str) -> bool:
56
+ """
57
+ 判断是否能处理给定的任务
58
+
59
+ Args:
60
+ task: 任务描述
61
+
62
+ Returns:
63
+ bool: 是否能处理此任务
64
+ """
65
+ raise NotImplementedError
66
+
67
+ def generate_guidance(
68
+ self,
69
+ original_task: str,
70
+ result_analysis: Dict[str, Any],
71
+ recursion_depth: int
72
+ ) -> str:
73
+ """
74
+ 生成递归指导消息
75
+
76
+ Args:
77
+ original_task: 原始任务
78
+ result_analysis: 工具结果分析
79
+ recursion_depth: 递归深度
80
+
81
+ Returns:
82
+ str: 生成的指导消息
83
+ """
84
+ raise NotImplementedError
85
+
40
86
 
41
87
  class AgentExecutor:
42
88
  """
@@ -78,6 +124,9 @@ class AgentExecutor:
78
124
  system_instructions: Optional[str] = None,
79
125
  callbacks: Optional[List[BaseCallback]] = None,
80
126
  enable_steering: bool = False,
127
+ task_handlers: Optional[List[TaskHandler]] = None,
128
+ unified_context: Optional["UnifiedExecutionContext"] = None,
129
+ enable_unified_coordination: bool = True,
81
130
  ) -> None:
82
131
  self.llm = llm
83
132
  self.tools = tools or {}
@@ -94,6 +143,15 @@ class AgentExecutor:
94
143
  self.system_instructions = system_instructions
95
144
  self.callbacks = callbacks or []
96
145
  self.enable_steering = enable_steering
146
+ self.task_handlers = task_handlers or []
147
+
148
+ # Unified coordination
149
+ self.unified_context = unified_context
150
+ self.enable_unified_coordination = enable_unified_coordination
151
+
152
+ # Initialize unified coordination if enabled
153
+ if self.enable_unified_coordination and UnifiedExecutionContext and IntelligentCoordinator:
154
+ self._setup_unified_coordination()
97
155
 
98
156
  # Tool execution (legacy pipeline for backward compatibility)
99
157
  self.tool_pipeline = ToolExecutionPipeline(
@@ -102,6 +160,80 @@ class AgentExecutor:
102
160
  metrics=self.metrics,
103
161
  )
104
162
 
163
+ def _setup_unified_coordination(self):
164
+ """设置统一协调机制"""
165
+ if not self.unified_context:
166
+ # 创建默认的统一执行上下文
167
+ from loom.core.unified_coordination import CoordinationConfig
168
+ self.unified_context = UnifiedExecutionContext(
169
+ execution_id=f"exec_{int(time.time())}",
170
+ config=CoordinationConfig() # 使用默认配置
171
+ )
172
+
173
+ # 集成四大核心能力
174
+ self._integrate_core_capabilities()
175
+
176
+ # 创建智能协调器
177
+ self.coordinator = IntelligentCoordinator(self.unified_context)
178
+
179
+ # 设置跨组件引用
180
+ self._setup_cross_component_references()
181
+
182
+ def _integrate_core_capabilities(self):
183
+ """集成四大核心能力到统一上下文"""
184
+
185
+ config = self.unified_context.config
186
+
187
+ # 1. 集成 ContextAssembler
188
+ if not self.unified_context.context_assembler:
189
+ from loom.core.context_assembly import ContextAssembler
190
+ self.unified_context.context_assembler = ContextAssembler(
191
+ max_tokens=self.max_context_tokens,
192
+ enable_caching=True,
193
+ cache_size=config.context_cache_size
194
+ )
195
+
196
+ # 2. 集成 TaskTool
197
+ if "task" in self.tools and not self.unified_context.task_tool:
198
+ task_tool = self.tools["task"]
199
+ # 使用配置更新 TaskTool
200
+ task_tool.pool_size = config.subagent_pool_size
201
+ task_tool.enable_pooling = True
202
+ self.unified_context.task_tool = task_tool
203
+
204
+ # 3. 集成 EventProcessor
205
+ if not self.unified_context.event_processor:
206
+ from loom.core.events import EventFilter, EventProcessor, AgentEventType
207
+
208
+ # 创建智能事件过滤器,使用配置值
209
+ llm_filter = EventFilter(
210
+ allowed_types=[
211
+ AgentEventType.LLM_DELTA,
212
+ AgentEventType.TOOL_RESULT,
213
+ AgentEventType.AGENT_FINISH
214
+ ],
215
+ enable_batching=True,
216
+ batch_size=config.event_batch_size,
217
+ batch_timeout=config.event_batch_timeout
218
+ )
219
+
220
+ self.unified_context.event_processor = EventProcessor(
221
+ filters=[llm_filter],
222
+ enable_stats=True
223
+ )
224
+
225
+ # 4. 集成 TaskHandlers
226
+ if not self.unified_context.task_handlers:
227
+ self.unified_context.task_handlers = self.task_handlers or []
228
+
229
+ def _setup_cross_component_references(self):
230
+ """
231
+ 设置跨组件引用(已简化)
232
+
233
+ 移除了魔法属性注入,改为通过协调器处理所有跨组件通信
234
+ """
235
+ pass # 跨组件通信现在通过 IntelligentCoordinator 处理
236
+
105
237
  # Tool orchestration (Loom 2.0 - intelligent parallel/sequential execution)
106
238
  self.tool_orchestrator = ToolOrchestrator(
107
239
  tools=self.tools,
@@ -269,40 +401,50 @@ class AgentExecutor:
269
401
  },
270
402
  )
271
403
 
272
- # Assemble system prompt using ContextAssembler
273
- assembler = ContextAssembler(max_tokens=self.max_context_tokens)
274
-
275
- # Add base instructions (critical priority)
276
- if self.system_instructions:
277
- assembler.add_component(
278
- name="base_instructions",
279
- content=self.system_instructions,
280
- priority=ComponentPriority.CRITICAL,
281
- truncatable=False,
404
+ # 使用统一协调的智能上下文组装
405
+ if self.enable_unified_coordination and hasattr(self, 'coordinator'):
406
+ # 使用智能协调器进行上下文组装
407
+ execution_plan = self.coordinator.coordinate_tt_recursion(
408
+ messages, turn_state, context
282
409
  )
410
+ final_system_prompt = execution_plan.get("context", "")
411
+ # 使用统一协调器的 assembler
412
+ assembler = self.unified_context.context_assembler
413
+ else:
414
+ # 传统方式组装系统提示
415
+ assembler = ContextAssembler(max_tokens=self.max_context_tokens)
416
+
417
+ # Add base instructions (critical priority)
418
+ if self.system_instructions:
419
+ assembler.add_component(
420
+ name="base_instructions",
421
+ content=self.system_instructions,
422
+ priority=ComponentPriority.CRITICAL,
423
+ truncatable=False,
424
+ )
283
425
 
284
- # Add RAG context (high priority)
285
- if rag_context:
286
- assembler.add_component(
287
- name="retrieved_context",
288
- content=rag_context,
289
- priority=ComponentPriority.HIGH,
290
- truncatable=True,
291
- )
426
+ # Add RAG context (high priority)
427
+ if rag_context:
428
+ assembler.add_component(
429
+ name="retrieved_context",
430
+ content=rag_context,
431
+ priority=ComponentPriority.HIGH,
432
+ truncatable=True,
433
+ )
292
434
 
293
- # Add tool definitions (medium priority)
294
- if self.tools:
295
- tools_spec = self._serialize_tools()
296
- tools_prompt = f"Available tools:\n{json.dumps(tools_spec, indent=2)}"
297
- assembler.add_component(
298
- name="tool_definitions",
299
- content=tools_prompt,
300
- priority=ComponentPriority.MEDIUM,
301
- truncatable=False,
302
- )
435
+ # Add tool definitions (medium priority)
436
+ if self.tools:
437
+ tools_spec = self._serialize_tools()
438
+ tools_prompt = f"Available tools:\n{json.dumps(tools_spec, indent=2)}"
439
+ assembler.add_component(
440
+ name="tool_definitions",
441
+ content=tools_prompt,
442
+ priority=ComponentPriority.MEDIUM,
443
+ truncatable=False,
444
+ )
303
445
 
304
- # Assemble final system prompt
305
- final_system_prompt = assembler.assemble()
446
+ # Assemble final system prompt
447
+ final_system_prompt = assembler.assemble()
306
448
 
307
449
  # Inject system prompt into history
308
450
  if history and history[0].role == "system":
@@ -442,15 +584,20 @@ class AgentExecutor:
442
584
  # Prepare next turn state
443
585
  next_state = turn_state.next_turn(compacted=compacted_this_turn)
444
586
 
445
- # Prepare next turn messages (only new messages, not full history)
446
- next_messages = [
447
- Message(
448
- role="tool",
449
- content=r.content,
450
- tool_call_id=r.tool_call_id,
587
+ # Prepare next turn messages with intelligent context guidance
588
+ next_messages = self._prepare_recursive_messages(
589
+ messages, tool_results, turn_state, context
590
+ )
591
+
592
+ # Add tool results
593
+ for r in tool_results:
594
+ next_messages.append(
595
+ Message(
596
+ role="tool",
597
+ content=r.content,
598
+ tool_call_id=r.tool_call_id,
599
+ )
451
600
  )
452
- for r in tool_results
453
- ]
454
601
 
455
602
  # Emit recursion event
456
603
  yield AgentEvent(
@@ -466,6 +613,130 @@ class AgentExecutor:
466
613
  async for event in self.tt(next_messages, next_state, context):
467
614
  yield event
468
615
 
616
+ # ==========================================
617
+ # Intelligent Recursion Methods
618
+ # ==========================================
619
+
620
+ def _prepare_recursive_messages(
621
+ self,
622
+ messages: List[Message],
623
+ tool_results: List[ToolResult],
624
+ turn_state: TurnState,
625
+ context: ExecutionContext,
626
+ ) -> List[Message]:
627
+ """
628
+ 智能准备递归调用的消息
629
+
630
+ 基于工具结果类型、任务上下文和递归深度,生成合适的用户指导消息
631
+ """
632
+ # 分析工具结果
633
+ result_analysis = self._analyze_tool_results(tool_results)
634
+
635
+ # 获取原始任务
636
+ original_task = self._extract_original_task(messages)
637
+
638
+ # 生成智能指导消息
639
+ guidance_message = self._generate_recursion_guidance(
640
+ original_task, result_analysis, turn_state.turn_counter
641
+ )
642
+
643
+ return [Message(role="user", content=guidance_message)]
644
+
645
+ def _analyze_tool_results(self, tool_results: List[ToolResult]) -> Dict[str, Any]:
646
+ """分析工具结果类型和质量"""
647
+ analysis = {
648
+ "has_data": False,
649
+ "has_errors": False,
650
+ "suggests_completion": False,
651
+ "result_types": [],
652
+ "completeness_score": 0.0
653
+ }
654
+
655
+ for result in tool_results:
656
+ content = result.content.lower()
657
+
658
+ # 检查数据类型
659
+ if any(keyword in content for keyword in ["data", "found", "retrieved", "table", "schema", "获取到", "表结构", "结构"]):
660
+ analysis["has_data"] = True
661
+ analysis["result_types"].append("data")
662
+ analysis["completeness_score"] += 0.3
663
+
664
+ # 检查错误
665
+ if any(keyword in content for keyword in ["error", "failed", "exception", "not found"]):
666
+ analysis["has_errors"] = True
667
+ analysis["result_types"].append("error")
668
+
669
+ # 检查完成建议
670
+ if any(keyword in content for keyword in ["complete", "finished", "done", "ready"]):
671
+ analysis["suggests_completion"] = True
672
+ analysis["result_types"].append("completion")
673
+ analysis["completeness_score"] += 0.5
674
+
675
+ # 检查分析结果
676
+ if any(keyword in content for keyword in ["analysis", "summary", "conclusion", "insights"]):
677
+ analysis["result_types"].append("analysis")
678
+ analysis["completeness_score"] += 0.4
679
+
680
+ analysis["completeness_score"] = min(analysis["completeness_score"], 1.0)
681
+ return analysis
682
+
683
+ def _extract_original_task(self, messages: List[Message]) -> str:
684
+ """从消息历史中提取原始任务"""
685
+ # 查找第一个用户消息作为原始任务
686
+ for message in messages:
687
+ if message.role == "user" and message.content:
688
+ # 过滤掉系统生成的递归消息
689
+ if not any(keyword in message.content.lower() for keyword in [
690
+ "工具调用已完成", "请基于工具返回的结果", "不要继续调用工具"
691
+ ]):
692
+ return message.content
693
+ return "处理用户请求"
694
+
695
+ def _generate_recursion_guidance(
696
+ self,
697
+ original_task: str,
698
+ result_analysis: Dict[str, Any],
699
+ recursion_depth: int
700
+ ) -> str:
701
+ """生成递归指导消息"""
702
+
703
+ # 使用可扩展的任务处理器
704
+ if hasattr(self, 'task_handlers') and self.task_handlers:
705
+ for handler in self.task_handlers:
706
+ if handler.can_handle(original_task):
707
+ return handler.generate_guidance(original_task, result_analysis, recursion_depth)
708
+
709
+ # 默认处理
710
+ return self._generate_default_guidance(original_task, result_analysis, recursion_depth)
711
+
712
+
713
+ def _generate_default_guidance(
714
+ self,
715
+ original_task: str,
716
+ result_analysis: Dict[str, Any],
717
+ recursion_depth: int
718
+ ) -> str:
719
+ """生成默认的递归指导"""
720
+
721
+ if result_analysis["suggests_completion"] or recursion_depth >= 6:
722
+ return f"""工具调用已完成。请基于返回的结果完成任务:{original_task}
723
+
724
+ 请提供完整、准确的最终答案。"""
725
+
726
+ elif result_analysis["has_errors"]:
727
+ return f"""工具执行遇到问题。请重新尝试完成任务:{original_task}
728
+
729
+ 建议:
730
+ - 检查工具参数是否正确
731
+ - 尝试使用不同的工具或方法
732
+ - 如果问题持续,请说明具体错误"""
733
+
734
+ else:
735
+ return f"""继续处理任务:{original_task}
736
+
737
+ 当前进度:{result_analysis['completeness_score']:.0%}
738
+ 建议:使用更多工具收集信息或分析已获得的结果"""
739
+
469
740
  # ==========================================
470
741
  # Helper Methods
471
742
  # ==========================================