entari-plugin-hyw 4.0.0rc12__py3-none-any.whl → 4.0.0rc13__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 entari-plugin-hyw might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: entari_plugin_hyw
3
- Version: 4.0.0rc12
3
+ Version: 4.0.0rc13
4
4
  Summary: Use large language models to interpret chat messages
5
5
  Author-email: kumoSleeping <zjr2992@outlook.com>
6
6
  License: MIT
@@ -5,10 +5,10 @@ entari_plugin_hyw/history.py,sha256=0XJwbfvXH5T1EPt4G1J5wWMJsKi0FfmajY5cvw8CQWE,
5
5
  entari_plugin_hyw/misc.py,sha256=5IqF5Z2C_6Ufy5TI89uX5hX5fVYcXOTZIQUIu_tvf54,6855
6
6
  entari_plugin_hyw/search_cache.py,sha256=7MIhTm5_YnZjc0aBaX7AE4AJp0VT8eU6ObR6mTkoerc,4285
7
7
  hyw_core/__init__.py,sha256=Jlr9Ic-BLOPTnff6OctUCdjDMdK4nssTF_vHie4QKTo,1958
8
- hyw_core/agent.py,sha256=lztXefMYjDedeTlTulEXq-5S5_D0BcQp3YVgFnNOX4E,26893
8
+ hyw_core/agent.py,sha256=xKvO9CIo0MX7mBQ6DuLDaDLvREyRIZMNXKk4tpOMi1U,29496
9
9
  hyw_core/config.py,sha256=DHxwToUVLm1nT88gG05e3hVzSLxXMk9BjgjAnhGCADk,4918
10
10
  hyw_core/core.py,sha256=_jN4831OeHQ_aM7sIlzcwYb5_Lp82kp2XmqpJD_tsLA,16097
11
- hyw_core/definitions.py,sha256=SrIYngRELNSsWlM4wDKdlAmy_kPwGX2onlkYF9Ti0aM,4137
11
+ hyw_core/definitions.py,sha256=pH46L-N25pSuPaIiN7l7yfoD6oHK6BLHigE0eYLFmJQ,4270
12
12
  hyw_core/image_cache.py,sha256=t8pr1kgH2ngK9IhrBAhzUqhBWERNztUywMzgCFZEtQk,9899
13
13
  hyw_core/pipeline.py,sha256=ZWwF0DHa29-65lUMU1_Fem3xQmxl7X_vgeni0ErOb8Q,22826
14
14
  hyw_core/search.py,sha256=VvfNSb9Hf7ZQWlNtnZfYe2eO9qPjYtwJxVlud6OdeCQ,7787
@@ -66,7 +66,7 @@ hyw_core/crawling/models.py,sha256=pCKe0k9xT3taSAlTlh0PazcLV0xYsm8p3XIkLHGf-LM,2
66
66
  hyw_core/stages/__init__.py,sha256=W89cWpq-HBLi2FprtJQSjQNLzpbhM8ZCkqPG61D_imE,521
67
67
  hyw_core/stages/base.py,sha256=EfnTkISXbBNxjARykqIhmMrVqw2tqZl7ozJbJEbRnhI,2806
68
68
  hyw_core/stages/summary.py,sha256=sgHCm_Leq_pkJ4YcgQuf8croiOP1oKz171TnzJwRwVs,7080
69
- entari_plugin_hyw-4.0.0rc12.dist-info/METADATA,sha256=R0rURsgj-bZ9i3MQrbP7txMhegpEthRncRZIOz4j6Nc,3845
70
- entari_plugin_hyw-4.0.0rc12.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
71
- entari_plugin_hyw-4.0.0rc12.dist-info/top_level.txt,sha256=ah76OrufRX0okOl4Fv8MO6PXiT0IaZ1oG0eDrdAPoNo,27
72
- entari_plugin_hyw-4.0.0rc12.dist-info/RECORD,,
69
+ entari_plugin_hyw-4.0.0rc13.dist-info/METADATA,sha256=NhbBeH0svhxJ6MvHl6vKF-BCzg-jRczAhlg_3fXD_lM,3845
70
+ entari_plugin_hyw-4.0.0rc13.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
71
+ entari_plugin_hyw-4.0.0rc13.dist-info/top_level.txt,sha256=ah76OrufRX0okOl4Fv8MO6PXiT0IaZ1oG0eDrdAPoNo,27
72
+ entari_plugin_hyw-4.0.0rc13.dist-info/RECORD,,
hyw_core/agent.py CHANGED
@@ -2,7 +2,7 @@
2
2
  Agent Pipeline
3
3
 
4
4
  Tool-calling agent that can autonomously use web_tool to search/screenshot.
5
- Maximum 2 tool calls, then forced summary.
5
+ Maximum 2 rounds of tool calls, up to 3 parallel calls per round.
6
6
  """
7
7
 
8
8
  import asyncio
@@ -31,6 +31,9 @@ class AgentSession:
31
31
  messages: List[Dict] = field(default_factory=list) # LLM conversation
32
32
  created_at: float = field(default_factory=time.time)
33
33
 
34
+ # Round tracking (each round can have up to 3 parallel tool calls)
35
+ round_count: int = 0
36
+
34
37
  # Image tracking
35
38
  user_image_count: int = 0 # Number of images from user input
36
39
  total_image_count: int = 0 # Total images including web screenshots
@@ -45,12 +48,13 @@ class AgentSession:
45
48
 
46
49
  @property
47
50
  def call_count(self) -> int:
51
+ """Total number of individual tool calls."""
48
52
  return len(self.tool_calls)
49
53
 
50
54
  @property
51
55
  def should_force_summary(self) -> bool:
52
- """第3次调用时强制总结"""
53
- return self.call_count >= 2
56
+ """Force summary after 2 rounds of tool calls."""
57
+ return self.round_count >= 2
54
58
 
55
59
 
56
60
  def parse_filter_syntax(query: str, max_count: int = 3):
@@ -130,12 +134,13 @@ class AgentPipeline:
130
134
 
131
135
  Flow:
132
136
  1. 用户输入 → LLM (with tools)
133
- 2. If tool_call: execute tool → notify user → loop
134
- 3. If call_count >= 2: force summary on next call
137
+ 2. If tool_call: execute all tools in parallel → notify user with batched message → loop
138
+ 3. If call_count >= 2 rounds: force summary on next call
135
139
  4. Return final content
136
140
  """
137
141
 
138
- MAX_TOOL_CALLS = 2
142
+ MAX_TOOL_ROUNDS = 2 # Maximum rounds of tool calls
143
+ MAX_PARALLEL_TOOLS = 3 # Maximum parallel tool calls per round
139
144
 
140
145
  def __init__(
141
146
  self,
@@ -183,7 +188,9 @@ class AgentPipeline:
183
188
 
184
189
  # Build initial messages
185
190
  language = getattr(self.config, "language", "Simplified Chinese")
186
- system_prompt = AGENT_SYSTEM_PROMPT + f"\n\n用户要求的语言: {language}"
191
+ from datetime import datetime
192
+ current_time = datetime.now().strftime("%Y-%m-%d %H:%M")
193
+ system_prompt = AGENT_SYSTEM_PROMPT + f"\n\n用户要求的语言: {language}\n当前时间: {current_time}"
187
194
 
188
195
  # Build user content with images if provided
189
196
  user_image_count = len(images) if images else 0
@@ -230,7 +237,7 @@ class AgentPipeline:
230
237
  while True:
231
238
  # Check if we need to force summary (no tools)
232
239
  if session.should_force_summary:
233
- logger.info(f"AgentPipeline: Max tool calls ({self.MAX_TOOL_CALLS}) reached, forcing summary")
240
+ logger.info(f"AgentPipeline: Max tool rounds ({self.MAX_TOOL_ROUNDS}) reached, forcing summary")
234
241
  # Add context message about collected info
235
242
  if context.web_results:
236
243
  context_msg = self._format_web_context(context)
@@ -307,7 +314,12 @@ class AgentPipeline:
307
314
  ]
308
315
  })
309
316
 
310
- # Execute tool calls
317
+ # Execute all tool calls in parallel
318
+ tool_tasks = []
319
+ tool_call_ids = []
320
+ tool_call_names = []
321
+ tool_call_args_list = []
322
+
311
323
  for tool_call in message.tool_calls:
312
324
  tc_id = tool_call.id
313
325
  func_name = tool_call.function.name
@@ -317,17 +329,22 @@ class AgentPipeline:
317
329
  except json.JSONDecodeError:
318
330
  args = {}
319
331
 
320
- logger.info(f"AgentPipeline: Executing tool '{func_name}' with args: {args}")
321
-
332
+ tool_call_ids.append(tc_id)
333
+ tool_call_names.append(func_name)
334
+ tool_call_args_list.append(args)
335
+ logger.info(f"AgentPipeline: Queueing tool '{func_name}' with args: {args}")
336
+
337
+ # Check for refuse_answer first (handle immediately)
338
+ for idx, func_name in enumerate(tool_call_names):
322
339
  if func_name == "refuse_answer":
323
- # Handle refusal
340
+ args = tool_call_args_list[idx]
324
341
  reason = args.get("reason", "Refused")
325
342
  context.should_refuse = True
326
343
  context.refuse_reason = reason
327
344
 
328
345
  session.messages.append({
329
346
  "role": "tool",
330
- "tool_call_id": tc_id,
347
+ "tool_call_id": tool_call_ids[idx],
331
348
  "content": f"已拒绝回答: {reason}"
332
349
  })
333
350
 
@@ -339,23 +356,50 @@ class AgentPipeline:
339
356
  "stats": {"total_time": time.time() - start_time},
340
357
  "usage": usage_totals,
341
358
  }
359
+
360
+ # Execute web_tool calls in parallel
361
+ search_start = time.time()
362
+ tasks_to_run = []
363
+ task_indices = []
364
+
365
+ for idx, func_name in enumerate(tool_call_names):
366
+ if func_name == "web_tool":
367
+ tasks_to_run.append(self._execute_web_tool(tool_call_args_list[idx], context))
368
+ task_indices.append(idx)
369
+
370
+ # Run all web_tool calls in parallel
371
+ if tasks_to_run:
372
+ results = await asyncio.gather(*tasks_to_run, return_exceptions=True)
373
+ else:
374
+ results = []
375
+
376
+ session.search_time += time.time() - search_start
377
+
378
+ # Process results and collect notifications
379
+ notifications = []
380
+ result_map = {} # Map task index to result
381
+
382
+ for i, result in enumerate(results):
383
+ task_idx = task_indices[i]
384
+ if isinstance(result, Exception):
385
+ result_map[task_idx] = {"summary": f"执行失败: {result}", "results": []}
386
+ else:
387
+ result_map[task_idx] = result
388
+
389
+ # Add all tool results to messages and collect notifications
390
+ for idx, func_name in enumerate(tool_call_names):
391
+ tc_id = tool_call_ids[idx]
392
+ args = tool_call_args_list[idx]
342
393
 
343
- elif func_name == "web_tool":
344
- # Execute web tool with time tracking
345
- search_start = time.time()
346
- result = await self._execute_web_tool(args, context)
347
- session.search_time += time.time() - search_start
394
+ if func_name == "web_tool":
395
+ result = result_map.get(idx, {"summary": "未执行", "results": []})
348
396
 
349
397
  # Track tool call
350
398
  session.tool_calls.append({"name": func_name, "args": args})
351
399
  session.tool_results.append(result)
352
400
 
353
- # Send IM notification with search result (NOT "正在搜索...")
354
- if self.send_func:
355
- try:
356
- await self.send_func(f"🔍 {result['summary']}")
357
- except Exception as e:
358
- logger.warning(f"AgentPipeline: Failed to send notification: {e}")
401
+ # Collect notification
402
+ notifications.append(f"🔍 {result['summary']}")
359
403
 
360
404
  # Add tool result to messages
361
405
  result_content = f"搜索完成: {result['summary']}\n\n找到 {len(result.get('results', []))} 个结果"
@@ -368,15 +412,15 @@ class AgentPipeline:
368
412
  # Add image source hint for web screenshots
369
413
  screenshot_count = result.get("screenshot_count", 0)
370
414
  if screenshot_count > 0:
371
- start_idx = session.total_image_count + 1
372
- end_idx = session.total_image_count + screenshot_count
373
- session.total_image_count = end_idx
415
+ start_idx_img = session.total_image_count + 1
416
+ end_idx_img = session.total_image_count + screenshot_count
417
+ session.total_image_count = end_idx_img
374
418
 
375
419
  source_desc = result.get("source_desc", "网页截图")
376
- if start_idx == end_idx:
377
- hint = f"第{start_idx}张图片来自{source_desc},作为查询的参考资料"
420
+ if start_idx_img == end_idx_img:
421
+ hint = f"第{start_idx_img}张图片来自{source_desc},作为查询的参考资料"
378
422
  else:
379
- hint = f"第{start_idx}-{end_idx}张图片来自{source_desc},作为查询的参考资料"
423
+ hint = f"第{start_idx_img}-{end_idx_img}张图片来自{source_desc},作为查询的参考资料"
380
424
  session.messages.append({"role": "system", "content": hint})
381
425
  else:
382
426
  # Unknown tool
@@ -385,6 +429,19 @@ class AgentPipeline:
385
429
  "tool_call_id": tc_id,
386
430
  "content": f"Unknown tool: {func_name}"
387
431
  })
432
+
433
+ # Send batched notification (up to 3 lines)
434
+ if self.send_func and notifications:
435
+ try:
436
+ # Join notifications with newlines, max 3 lines
437
+ notification_msg = "\n".join(notifications[:3])
438
+ await self.send_func(notification_msg)
439
+ except Exception as e:
440
+ logger.warning(f"AgentPipeline: Failed to send notification: {e}")
441
+
442
+ # Increment round count after processing all tool calls in this round
443
+ if tasks_to_run:
444
+ session.round_count += 1
388
445
 
389
446
  # Build final response
390
447
  total_time = time.time() - start_time
hyw_core/definitions.py CHANGED
@@ -46,6 +46,7 @@ def get_web_tool() -> Dict[str, Any]:
46
46
  "function": {
47
47
  "name": "web_tool",
48
48
  "description": """搜索网页或截图指定URL。用于获取最新信息、查找资料。
49
+ ## 使用方式
49
50
  网页搜索(大部分问题优先使用此方法):
50
51
  直接传入搜索词如 "python async" 会返回搜索结果列表
51
52
 
@@ -84,10 +85,12 @@ AGENT_SYSTEM_PROMPT = """# 你是一个智能助手 (Agent), 你的职责是使
84
85
  最小限度使用自身知识, 尽可能使用 web_tool 获取信息.
85
86
 
86
87
  ## 工具使用指南
88
+ - 并行调用工具
89
+ - 网页搜索: 可以同时调用3次, 其中URL截图消耗较大, 最多同时调用1个
87
90
  - 积极使用 web_tool 获取信息
88
91
  - 搜索时, 关键词保证简单、指向准确、利于传统搜索引擎.
89
92
  - 获取页面截图时, 只使用官方性较强的 wiki、官方网站、资源站等等, 不使用第三方转载新闻网站.
90
- - 最多可调用2次工具, 之后必须给出最终回答
93
+ - 最多可调用2轮工具, 之后必须给出最终回答
91
94
  - 适当时候调用 `refuse_answer`
92
95
  - 对于具体任务, 如果是转述、格式化、翻译等, 请直接给出最终回答, 不再调用工具
93
96