|
|
@@ -2018,12 +2018,54 @@ class AgentRunner:
|
|
|
# 深拷贝避免修改原始数据
|
|
|
import copy
|
|
|
import hashlib
|
|
|
+ import asyncio
|
|
|
messages = copy.deepcopy(messages)
|
|
|
|
|
|
# 统计优化情况
|
|
|
stats = {"kept": 0, "downscaled": 0, "described": 0, "cache_hit": 0}
|
|
|
|
|
|
- # 遍历最后一条 assistant 之前的所有 tool messages
|
|
|
+ # 收集需要降分辨率的图片(用于并发处理)
|
|
|
+ downscale_jobs = [] # [(msg_idx, block_idx, image_url, cache_key)]
|
|
|
+
|
|
|
+ # 第一遍:扫描并收集需要处理的图片
|
|
|
+ for i in range(last_assistant_idx):
|
|
|
+ msg = messages[i]
|
|
|
+ if msg.get("role") != "tool":
|
|
|
+ continue
|
|
|
+
|
|
|
+ content = msg.get("content")
|
|
|
+ if not isinstance(content, list):
|
|
|
+ continue
|
|
|
+
|
|
|
+ rounds_ago = assistant_count_after[i]
|
|
|
+
|
|
|
+ for block_idx, block in enumerate(content):
|
|
|
+ if isinstance(block, dict) and block.get("type") == "image_url":
|
|
|
+ image_url_obj = block.get("image_url", {})
|
|
|
+ image_url = image_url_obj.get("url", "")
|
|
|
+
|
|
|
+ if image_url.startswith("data:"):
|
|
|
+ cache_key = hashlib.md5(image_url[:200].encode()).hexdigest()
|
|
|
+ else:
|
|
|
+ cache_key = hashlib.md5(image_url.encode()).hexdigest()
|
|
|
+
|
|
|
+ # 3-5 轮需要降分辨率
|
|
|
+ if 2 < rounds_ago <= 5:
|
|
|
+ cached = self._image_opt_cache.get(cache_key, {})
|
|
|
+ if "downscaled" not in cached and image_url.startswith("data:"):
|
|
|
+ downscale_jobs.append((i, block_idx, image_url, cache_key))
|
|
|
+
|
|
|
+ # 并发处理所有降分辨率任务
|
|
|
+ if downscale_jobs:
|
|
|
+ downscale_results = await asyncio.gather(
|
|
|
+ *[self._downscale_image(url) for _, _, url, _ in downscale_jobs],
|
|
|
+ return_exceptions=True
|
|
|
+ )
|
|
|
+ for (_, _, _, cache_key), result in zip(downscale_jobs, downscale_results):
|
|
|
+ if not isinstance(result, Exception) and result is not None:
|
|
|
+ self._image_opt_cache.setdefault(cache_key, {})["downscaled"] = result
|
|
|
+
|
|
|
+ # 第二遍:应用处理结果
|
|
|
for i in range(last_assistant_idx):
|
|
|
msg = messages[i]
|
|
|
if msg.get("role") != "tool":
|
|
|
@@ -2105,7 +2147,7 @@ class AgentRunner:
|
|
|
new_content.append(block)
|
|
|
|
|
|
msg["content"] = new_content
|
|
|
-
|
|
|
+ print(f"[Image Opt Check] 扫描到 {stats['kept'] + stats['downscaled'] + stats['described']} 张图片上下文")
|
|
|
if stats["downscaled"] > 0 or stats["described"] > 0:
|
|
|
logger.info(
|
|
|
f"[Image Optimization] 保留 {stats['kept']} 张,"
|
|
|
@@ -2155,16 +2197,16 @@ class AgentRunner:
|
|
|
new_height = max_size
|
|
|
new_width = int(width * max_size / height)
|
|
|
|
|
|
- # 缩放图片
|
|
|
- img_resized = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
|
|
|
+ # 缩放图片(使用更快的 BILINEAR 算法)
|
|
|
+ img_resized = img.resize((new_width, new_height), Image.Resampling.BILINEAR)
|
|
|
|
|
|
# 转换为 RGB(如果是 RGBA)
|
|
|
if img_resized.mode == "RGBA":
|
|
|
img_resized = img_resized.convert("RGB")
|
|
|
|
|
|
- # 重新编码为 JPEG(压缩率更高)
|
|
|
+ # 重新编码为 JPEG(降低质量以加快速度)
|
|
|
buffer = io.BytesIO()
|
|
|
- img_resized.save(buffer, format="JPEG", quality=85, optimize=True)
|
|
|
+ img_resized.save(buffer, format="JPEG", quality=60, optimize=False)
|
|
|
new_data = base64.b64encode(buffer.getvalue()).decode("utf-8")
|
|
|
|
|
|
return f"data:image/jpeg;base64,{new_data}"
|