entari-plugin-hyw 4.0.0rc12__tar.gz → 4.0.0rc14__tar.gz
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.
- {entari_plugin_hyw-4.0.0rc12/src/entari_plugin_hyw.egg-info → entari_plugin_hyw-4.0.0rc14}/PKG-INFO +1 -1
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/pyproject.toml +1 -1
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14/src/entari_plugin_hyw.egg-info}/PKG-INFO +1 -1
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/agent.py +176 -56
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/definitions.py +4 -1
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/MANIFEST.in +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/README.md +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/setup.cfg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw/Untitled-1 +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw/__init__.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw/filters.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw/history.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw/misc.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw/search_cache.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw.egg-info/SOURCES.txt +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw.egg-info/dependency_links.txt +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw.egg-info/requires.txt +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw.egg-info/top_level.txt +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/__init__.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/__init__.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/index.html +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/anthropic.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/cerebras.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/deepseek.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/gemini.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/google.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/grok.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/huggingface.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/microsoft.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/minimax.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/mistral.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/nvida.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/openai.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/openrouter.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/perplexity.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/qwen.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/xai.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/xiaomi.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/logos/zai.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/card-dist/vite.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/index.html +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/anthropic.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/cerebras.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/deepseek.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/gemini.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/google.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/grok.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/huggingface.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/microsoft.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/minimax.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/mistral.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/nvida.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/openai.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/openrouter.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/perplexity.svg +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/qwen.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/xai.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/xiaomi.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/assets/logos/zai.png +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/engines/__init__.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/engines/base.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/engines/default.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/engines/duckduckgo.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/landing.html +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/manager.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/renderer.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/service.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/config.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/core.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/crawling/__init__.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/crawling/completeness.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/crawling/models.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/image_cache.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/pipeline.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/search.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/stages/__init__.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/stages/base.py +0 -0
- {entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/stages/summary.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "entari_plugin_hyw"
|
|
7
|
-
version = "4.0.0-
|
|
7
|
+
version = "4.0.0-rc14"
|
|
8
8
|
description = "Use large language models to interpret chat messages"
|
|
9
9
|
authors = [{name = "kumoSleeping", email = "zjr2992@outlook.com"}]
|
|
10
10
|
dependencies = [
|
|
@@ -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,
|
|
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
|
-
"""
|
|
53
|
-
return self.
|
|
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,15 @@ class AgentPipeline:
|
|
|
130
134
|
|
|
131
135
|
Flow:
|
|
132
136
|
1. 用户输入 → LLM (with tools)
|
|
133
|
-
2. If tool_call: execute
|
|
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
|
-
|
|
142
|
+
MAX_TOOL_ROUNDS = 2 # Maximum rounds of tool calls
|
|
143
|
+
MAX_PARALLEL_TOOLS = 3 # Maximum parallel tool calls per round
|
|
144
|
+
MAX_LLM_RETRIES = 3 # Maximum retries for empty API responses
|
|
145
|
+
LLM_RETRY_DELAY = 1.0 # Delay between retries in seconds
|
|
139
146
|
|
|
140
147
|
def __init__(
|
|
141
148
|
self,
|
|
@@ -183,7 +190,9 @@ class AgentPipeline:
|
|
|
183
190
|
|
|
184
191
|
# Build initial messages
|
|
185
192
|
language = getattr(self.config, "language", "Simplified Chinese")
|
|
186
|
-
|
|
193
|
+
from datetime import datetime
|
|
194
|
+
current_time = datetime.now().strftime("%Y-%m-%d %H:%M")
|
|
195
|
+
system_prompt = AGENT_SYSTEM_PROMPT + f"\n\n用户要求的语言: {language}\n当前时间: {current_time}"
|
|
187
196
|
|
|
188
197
|
# Build user content with images if provided
|
|
189
198
|
user_image_count = len(images) if images else 0
|
|
@@ -230,7 +239,7 @@ class AgentPipeline:
|
|
|
230
239
|
while True:
|
|
231
240
|
# Check if we need to force summary (no tools)
|
|
232
241
|
if session.should_force_summary:
|
|
233
|
-
logger.info(f"AgentPipeline: Max tool
|
|
242
|
+
logger.info(f"AgentPipeline: Max tool rounds ({self.MAX_TOOL_ROUNDS}) reached, forcing summary")
|
|
234
243
|
# Add context message about collected info
|
|
235
244
|
if context.web_results:
|
|
236
245
|
context_msg = self._format_web_context(context)
|
|
@@ -240,38 +249,88 @@ class AgentPipeline:
|
|
|
240
249
|
})
|
|
241
250
|
|
|
242
251
|
|
|
243
|
-
# Final call without tools
|
|
244
|
-
response =
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
252
|
+
# Final call without tools (with retry)
|
|
253
|
+
response = None
|
|
254
|
+
for retry in range(self.MAX_LLM_RETRIES):
|
|
255
|
+
try:
|
|
256
|
+
response = await client.chat.completions.create(
|
|
257
|
+
model=model,
|
|
258
|
+
messages=session.messages,
|
|
259
|
+
temperature=self.config.temperature,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
if response.usage:
|
|
263
|
+
usage_totals["input_tokens"] += response.usage.prompt_tokens or 0
|
|
264
|
+
usage_totals["output_tokens"] += response.usage.completion_tokens or 0
|
|
265
|
+
|
|
266
|
+
# Check for valid response
|
|
267
|
+
if response.choices:
|
|
268
|
+
break # Success, exit retry loop
|
|
269
|
+
|
|
270
|
+
# Empty choices, retry
|
|
271
|
+
logger.warning(f"AgentPipeline: Empty choices in force-summary (attempt {retry + 1}/{self.MAX_LLM_RETRIES}): {response}")
|
|
272
|
+
if retry < self.MAX_LLM_RETRIES - 1:
|
|
273
|
+
await asyncio.sleep(self.LLM_RETRY_DELAY)
|
|
274
|
+
except Exception as e:
|
|
275
|
+
logger.warning(f"AgentPipeline: LLM error (attempt {retry + 1}/{self.MAX_LLM_RETRIES}): {e}")
|
|
276
|
+
if retry < self.MAX_LLM_RETRIES - 1:
|
|
277
|
+
await asyncio.sleep(self.LLM_RETRY_DELAY)
|
|
278
|
+
else:
|
|
279
|
+
return {
|
|
280
|
+
"llm_response": f"Error: {e}",
|
|
281
|
+
"success": False,
|
|
282
|
+
"error": str(e),
|
|
283
|
+
"stats": {"total_time": time.time() - start_time}
|
|
284
|
+
}
|
|
249
285
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
286
|
+
# Final check after all retries
|
|
287
|
+
if not response or not response.choices:
|
|
288
|
+
logger.error(f"AgentPipeline: All retries failed for force-summary")
|
|
289
|
+
return {
|
|
290
|
+
"llm_response": "抱歉,AI 服务返回了空响应,请稍后重试。",
|
|
291
|
+
"success": False,
|
|
292
|
+
"error": "Empty response from API after retries",
|
|
293
|
+
"stats": {"total_time": time.time() - start_time},
|
|
294
|
+
"usage": usage_totals,
|
|
295
|
+
}
|
|
253
296
|
|
|
254
297
|
final_content = response.choices[0].message.content or ""
|
|
255
298
|
break
|
|
256
299
|
|
|
257
|
-
# Normal call with tools
|
|
300
|
+
# Normal call with tools (with retry)
|
|
258
301
|
llm_start = time.time()
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
302
|
+
response = None
|
|
303
|
+
|
|
304
|
+
for retry in range(self.MAX_LLM_RETRIES):
|
|
305
|
+
try:
|
|
306
|
+
response = await client.chat.completions.create(
|
|
307
|
+
model=model,
|
|
308
|
+
messages=session.messages,
|
|
309
|
+
temperature=self.config.temperature,
|
|
310
|
+
tools=tools,
|
|
311
|
+
tool_choice="auto",
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Check for valid response
|
|
315
|
+
if response.choices:
|
|
316
|
+
break # Success, exit retry loop
|
|
317
|
+
|
|
318
|
+
# Empty choices, retry
|
|
319
|
+
logger.warning(f"AgentPipeline: Empty choices (attempt {retry + 1}/{self.MAX_LLM_RETRIES}): {response}")
|
|
320
|
+
if retry < self.MAX_LLM_RETRIES - 1:
|
|
321
|
+
await asyncio.sleep(self.LLM_RETRY_DELAY)
|
|
322
|
+
except Exception as e:
|
|
323
|
+
logger.warning(f"AgentPipeline: LLM error (attempt {retry + 1}/{self.MAX_LLM_RETRIES}): {e}")
|
|
324
|
+
if retry < self.MAX_LLM_RETRIES - 1:
|
|
325
|
+
await asyncio.sleep(self.LLM_RETRY_DELAY)
|
|
326
|
+
else:
|
|
327
|
+
logger.error(f"AgentPipeline: All retries failed: {e}")
|
|
328
|
+
return {
|
|
329
|
+
"llm_response": f"Error: {e}",
|
|
330
|
+
"success": False,
|
|
331
|
+
"error": str(e),
|
|
332
|
+
"stats": {"total_time": time.time() - start_time}
|
|
333
|
+
}
|
|
275
334
|
|
|
276
335
|
llm_duration = time.time() - llm_start
|
|
277
336
|
session.llm_time += llm_duration
|
|
@@ -280,6 +339,17 @@ class AgentPipeline:
|
|
|
280
339
|
if session.call_count == 0 and session.first_llm_time == 0:
|
|
281
340
|
session.first_llm_time = llm_duration
|
|
282
341
|
|
|
342
|
+
# Final check after all retries
|
|
343
|
+
if not response or not response.choices:
|
|
344
|
+
logger.error(f"AgentPipeline: All retries failed, empty choices")
|
|
345
|
+
return {
|
|
346
|
+
"llm_response": "抱歉,AI 服务返回了空响应,请稍后重试。",
|
|
347
|
+
"success": False,
|
|
348
|
+
"error": "Empty response from API after retries",
|
|
349
|
+
"stats": {"total_time": time.time() - start_time},
|
|
350
|
+
"usage": usage_totals,
|
|
351
|
+
}
|
|
352
|
+
|
|
283
353
|
if response.usage:
|
|
284
354
|
usage_totals["input_tokens"] += response.usage.prompt_tokens or 0
|
|
285
355
|
usage_totals["output_tokens"] += response.usage.completion_tokens or 0
|
|
@@ -307,7 +377,12 @@ class AgentPipeline:
|
|
|
307
377
|
]
|
|
308
378
|
})
|
|
309
379
|
|
|
310
|
-
# Execute tool calls
|
|
380
|
+
# Execute all tool calls in parallel
|
|
381
|
+
tool_tasks = []
|
|
382
|
+
tool_call_ids = []
|
|
383
|
+
tool_call_names = []
|
|
384
|
+
tool_call_args_list = []
|
|
385
|
+
|
|
311
386
|
for tool_call in message.tool_calls:
|
|
312
387
|
tc_id = tool_call.id
|
|
313
388
|
func_name = tool_call.function.name
|
|
@@ -317,17 +392,22 @@ class AgentPipeline:
|
|
|
317
392
|
except json.JSONDecodeError:
|
|
318
393
|
args = {}
|
|
319
394
|
|
|
320
|
-
|
|
321
|
-
|
|
395
|
+
tool_call_ids.append(tc_id)
|
|
396
|
+
tool_call_names.append(func_name)
|
|
397
|
+
tool_call_args_list.append(args)
|
|
398
|
+
logger.info(f"AgentPipeline: Queueing tool '{func_name}' with args: {args}")
|
|
399
|
+
|
|
400
|
+
# Check for refuse_answer first (handle immediately)
|
|
401
|
+
for idx, func_name in enumerate(tool_call_names):
|
|
322
402
|
if func_name == "refuse_answer":
|
|
323
|
-
|
|
403
|
+
args = tool_call_args_list[idx]
|
|
324
404
|
reason = args.get("reason", "Refused")
|
|
325
405
|
context.should_refuse = True
|
|
326
406
|
context.refuse_reason = reason
|
|
327
407
|
|
|
328
408
|
session.messages.append({
|
|
329
409
|
"role": "tool",
|
|
330
|
-
"tool_call_id":
|
|
410
|
+
"tool_call_id": tool_call_ids[idx],
|
|
331
411
|
"content": f"已拒绝回答: {reason}"
|
|
332
412
|
})
|
|
333
413
|
|
|
@@ -339,23 +419,50 @@ class AgentPipeline:
|
|
|
339
419
|
"stats": {"total_time": time.time() - start_time},
|
|
340
420
|
"usage": usage_totals,
|
|
341
421
|
}
|
|
422
|
+
|
|
423
|
+
# Execute web_tool calls in parallel
|
|
424
|
+
search_start = time.time()
|
|
425
|
+
tasks_to_run = []
|
|
426
|
+
task_indices = []
|
|
427
|
+
|
|
428
|
+
for idx, func_name in enumerate(tool_call_names):
|
|
429
|
+
if func_name == "web_tool":
|
|
430
|
+
tasks_to_run.append(self._execute_web_tool(tool_call_args_list[idx], context))
|
|
431
|
+
task_indices.append(idx)
|
|
432
|
+
|
|
433
|
+
# Run all web_tool calls in parallel
|
|
434
|
+
if tasks_to_run:
|
|
435
|
+
results = await asyncio.gather(*tasks_to_run, return_exceptions=True)
|
|
436
|
+
else:
|
|
437
|
+
results = []
|
|
438
|
+
|
|
439
|
+
session.search_time += time.time() - search_start
|
|
440
|
+
|
|
441
|
+
# Process results and collect notifications
|
|
442
|
+
notifications = []
|
|
443
|
+
result_map = {} # Map task index to result
|
|
444
|
+
|
|
445
|
+
for i, result in enumerate(results):
|
|
446
|
+
task_idx = task_indices[i]
|
|
447
|
+
if isinstance(result, Exception):
|
|
448
|
+
result_map[task_idx] = {"summary": f"执行失败: {result}", "results": []}
|
|
449
|
+
else:
|
|
450
|
+
result_map[task_idx] = result
|
|
451
|
+
|
|
452
|
+
# Add all tool results to messages and collect notifications
|
|
453
|
+
for idx, func_name in enumerate(tool_call_names):
|
|
454
|
+
tc_id = tool_call_ids[idx]
|
|
455
|
+
args = tool_call_args_list[idx]
|
|
342
456
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
search_start = time.time()
|
|
346
|
-
result = await self._execute_web_tool(args, context)
|
|
347
|
-
session.search_time += time.time() - search_start
|
|
457
|
+
if func_name == "web_tool":
|
|
458
|
+
result = result_map.get(idx, {"summary": "未执行", "results": []})
|
|
348
459
|
|
|
349
460
|
# Track tool call
|
|
350
461
|
session.tool_calls.append({"name": func_name, "args": args})
|
|
351
462
|
session.tool_results.append(result)
|
|
352
463
|
|
|
353
|
-
#
|
|
354
|
-
|
|
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}")
|
|
464
|
+
# Collect notification
|
|
465
|
+
notifications.append(f"🔍 {result['summary']}")
|
|
359
466
|
|
|
360
467
|
# Add tool result to messages
|
|
361
468
|
result_content = f"搜索完成: {result['summary']}\n\n找到 {len(result.get('results', []))} 个结果"
|
|
@@ -368,15 +475,15 @@ class AgentPipeline:
|
|
|
368
475
|
# Add image source hint for web screenshots
|
|
369
476
|
screenshot_count = result.get("screenshot_count", 0)
|
|
370
477
|
if screenshot_count > 0:
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
session.total_image_count =
|
|
478
|
+
start_idx_img = session.total_image_count + 1
|
|
479
|
+
end_idx_img = session.total_image_count + screenshot_count
|
|
480
|
+
session.total_image_count = end_idx_img
|
|
374
481
|
|
|
375
482
|
source_desc = result.get("source_desc", "网页截图")
|
|
376
|
-
if
|
|
377
|
-
hint = f"第{
|
|
483
|
+
if start_idx_img == end_idx_img:
|
|
484
|
+
hint = f"第{start_idx_img}张图片来自{source_desc},作为查询的参考资料"
|
|
378
485
|
else:
|
|
379
|
-
hint = f"第{
|
|
486
|
+
hint = f"第{start_idx_img}-{end_idx_img}张图片来自{source_desc},作为查询的参考资料"
|
|
380
487
|
session.messages.append({"role": "system", "content": hint})
|
|
381
488
|
else:
|
|
382
489
|
# Unknown tool
|
|
@@ -385,6 +492,19 @@ class AgentPipeline:
|
|
|
385
492
|
"tool_call_id": tc_id,
|
|
386
493
|
"content": f"Unknown tool: {func_name}"
|
|
387
494
|
})
|
|
495
|
+
|
|
496
|
+
# Send batched notification (up to 3 lines)
|
|
497
|
+
if self.send_func and notifications:
|
|
498
|
+
try:
|
|
499
|
+
# Join notifications with newlines, max 3 lines
|
|
500
|
+
notification_msg = "\n".join(notifications[:3])
|
|
501
|
+
await self.send_func(notification_msg)
|
|
502
|
+
except Exception as e:
|
|
503
|
+
logger.warning(f"AgentPipeline: Failed to send notification: {e}")
|
|
504
|
+
|
|
505
|
+
# Increment round count after processing all tool calls in this round
|
|
506
|
+
if tasks_to_run:
|
|
507
|
+
session.round_count += 1
|
|
388
508
|
|
|
389
509
|
# Build final response
|
|
390
510
|
total_time = time.time() - start_time
|
|
@@ -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
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw/Untitled-1
RENAMED
|
File without changes
|
{entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw/__init__.py
RENAMED
|
File without changes
|
{entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw/filters.py
RENAMED
|
File without changes
|
{entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw/history.py
RENAMED
|
File without changes
|
|
File without changes
|
{entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/entari_plugin_hyw/search_cache.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/manager.py
RENAMED
|
File without changes
|
{entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/renderer.py
RENAMED
|
File without changes
|
{entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/browser_control/service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/crawling/__init__.py
RENAMED
|
File without changes
|
{entari_plugin_hyw-4.0.0rc12 → entari_plugin_hyw-4.0.0rc14}/src/hyw_core/crawling/completeness.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|