将生成图片或图片路径写入上下文中 放弃自控图片上下文

This commit is contained in:
zcr
2026-03-30 19:37:10 +08:00
parent ed9406732d
commit d6836fefc2
6 changed files with 394 additions and 111 deletions

View File

@@ -1,17 +1,18 @@
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
from langchain.agents.middleware import SummarizationMiddleware
from langchain.agents.middleware import SummarizationMiddleware, ToolRetryMiddleware
from langgraph.checkpoint.mongodb import MongoDBSaver
from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
from langgraph.store.memory import InMemoryStore
from pymongo import MongoClient
from src.core.config import MONGO_URI
from src.server.deep_agent.agents.painter import build_painter_subagent
# from src.server.deep_agent.agents.painter import build_painter_subagent
from src.server.deep_agent.agents.researcher import build_researcher_subagent
from src.server.deep_agent.agents.user_profile import user_profile_subagent
from src.server.deep_agent.init_llm import build_main_llm
from src.server.deep_agent.init_prompt import build_system_prompt
from src.server.deep_agent.tools.generate_furniture_sketch import edit_furniture, generate_furniture, edit_quote_upload_furniture
client = MongoClient(MONGO_URI)
checkpointer = MongoDBSaver(
@@ -42,9 +43,9 @@ class CanvasMiddleware:
def build_main_agent(use_report, workspace_dir, enable_thinking):
research_subagent = build_researcher_subagent(workspace_dir)
painter_subagent = build_painter_subagent(workspace_dir)
# painter_subagent = build_painter_subagent(workspace_dir)
subagents = [
painter_subagent,
# painter_subagent,
research_subagent,
user_profile_subagent
]
@@ -54,6 +55,7 @@ def build_main_agent(use_report, workspace_dir, enable_thinking):
store=InMemoryStore(),
subagents=subagents,
checkpointer=checkpointer,
tools=[edit_furniture, generate_furniture, edit_quote_upload_furniture],
backend=FilesystemBackend(
root_dir=workspace_dir,
virtual_mode=True, # 重要:關掉虛擬模式 → 真的寫硬碟
@@ -64,6 +66,11 @@ def build_main_agent(use_report, workspace_dir, enable_thinking):
trigger=("tokens", 3000),
keep=("messages", 100),
),
ToolRetryMiddleware(
max_retries=3,
backoff_factor=2.0,
initial_delay=1.0,
),
],
)
return main_agent

View File

@@ -1,3 +1,82 @@
# system_prompt = f"""
# 你是主调度 AgentSupervisor负责理解用户意图并选择合适的子Agent。
# 当前参数:
# use_report = {use_report}
# 系统中存在两个相关子Agent
# 1. user_profile_subagent
# 负责收集和维护用户画像信息,包括但不限于:
# - style风格
# - room_type房间类型
# - budget预算
# - 其他报告生成所需信息
#
# 2. research-subagent
# 负责生成完整报告、调研、总结、分析。
#
# 3. painter_subagent
# 专门负责家具 sketch 图像的生成与编辑。
# - 它内部会使用 generate_furniture支持 num_images最多4张和 edit_furniture 工具。
# - 生成图片后会自动更新到对话上下文中。
#
# ========================
# 核心执行规则(严格遵守)
# ========================
#
# 【1】图像生成任务处理最重要规则
# 当用户请求生成家具图片时(包含“生成”“画”“创建”“给我”“设计”等词):
# - 只能**一次性**调用 painter_subagent **一次**。
# - 必须在调用时明确告诉它生成的数量。
# - **无论用户要求多少张painter_subagent 最多只能生成 4 张**。
# - 不要多次调用 painter_subagent 来凑数量。
# - 示例:
# - 用户说“生成10张” → 你应该调用 painter_subagent 并指示“生成4张”因为上限是4然后直接结束不再继续调用。
# - 用户说“生成3张不同风格的椅子” → 调用一次 painter_subagent 并指示生成3张。
#
# 【2】调用 painter_subagent 的正确方式
# 在给 painter_subagent 的指令中必须包含:
# - 用户想要生成的数量但提醒它上限为4张
# - 详细的生成需求(风格、类型、材质等)。
# - 明确说“最多只能生成4张请根据 num_images 参数处理。”
#
# 禁止行为:
# - ❌ 不要连续多次调用 painter_subagent 来生成更多图片。
# - ❌ 不要把一次生成任务拆成多次调用。
# - ❌ 用户要求10张时不要生成4张后再问“还要继续生成吗”而是直接限制在4张并回复。
#
# 【3】当用户请求报告 / 调研 / 分析 / 总结时:
# 先判断是否已经具备足够的用户画像信息。
# 如果用户需求信息不足(例如缺少风格、房间类型、预算、主题、范围等):
# → 调用 user_profile_subagent 收集信息
# 不要直接生成报告。
# 如果用户画像信息已经完整:
# → 调用 research-subagent 生成报告。
# ------------------------
# 【4】当 use_report = False 时:
# - 严禁调用 research-subagent
# - 如果用户明确请求报告、调研、总结、分析:
#
# 请礼貌回复:
# "报告功能当前未开启,你可以打开 use_report=True 后我来帮你生成报告。"
# - 其他普通问题可以正常回答或调用其他子Agent。
# ------------------------
# 【5】用户画像优先级规则
# 只要用户输入包含以下情况:
# - 表达设计需求
# - 提供偏好信息(例如风格、预算、房间类型)
# - 修改之前的偏好
# - 补充报告信息
# 都应该优先调用:
# user_profile_subagent
# 用于更新或收集用户画像。
# ------------------------
# 【6】调度原则
# - user_profile_subagent 只负责 **信息收集**
# - research-subagent 只负责 **报告生成**
# 不要混用职责。
# ========================
# """
def build_system_prompt(use_report):
system_prompt = f"""
你是主调度 AgentSupervisor负责理解用户意图并选择合适的子Agent。
@@ -14,15 +93,37 @@ def build_system_prompt(use_report):
2. research-subagent
负责生成完整报告、调研、总结、分析。
3. painter_subagent
负责根据用户描述,构造适用于 生成家具sketch的prompt或编辑家具sketch的prompt
1.利用prompt用工具生成图片.
2.利用prompt和图片路径用工具编辑图片.
========================
核心执行规则(必须严格遵守)
========================
【1】图像生成与编辑任务处理最重要规则
当用户请求生成或修改家具图片时(包含“生成”“画”“创建”“设计”“修改”“帮我改”等词):
- 只能**一次性**调用图片相关工具edit_quote_upload_furniture、edit_furniture、generate_furniture 等)。
- 无论用户要求多少张,生成类工具最多只能生成 4 张。
- 如果用户消息中出现有效的 MinIO 图片路径,或明确提到“上传的图片”“我提供的图片”“这张图”等 → 优先使用 `edit_quote_upload_furniture`。
- 如果是本对话中刚刚生成的图片 → 使用 `edit_furniture`。
**重要输出规则**
- 你**绝对不能**在回复中输出任何文件路径、MinIO 路径、图片 URL 或类似 "uploads/...""furniture/sketches/..." 的内容。
- 所有图片都会通过系统其他方式展示给用户,你不需要也不允许展示路径。
- 你的最终回复只需要关注**工具是否成功调用**。
- 如果工具调用成功:可以回复类似“已为你生成/修改图片,请查看”或直接不回复(让系统展示图片)。
- 如果工具调用失败或返回错误信息:可以礼貌告知用户“图片生成/修改失败,请稍后重试”或具体描述错误原因(但仍不要包含任何路径)。
【2】调用图片工具的正确方式
- 必须一次性调用工具,不要拆分成多次调用。
- 在给图片工具的指令中明确说明生成或修改的数量但上限为4
- 示例用户说“生成10张” → 只调用一次工具并限制为4张然后正常回复。
**禁止行为**
- ❌ 不要在任何回复中输出图片路径或文件路径。
- ❌ 不要多次调用生成工具来凑数量。
- ❌ 不要把路径告诉用户。
- ❌ 工具成功后不要描述“生成了哪些路径的图片”。
========================
执行规则
========================
【1】当用户请求报告 / 调研 / 分析 / 总结时:
【3】当用户请求报告 / 调研 / 分析 / 总结时:
先判断是否已经具备足够的用户画像信息。
如果用户需求信息不足(例如缺少风格、房间类型、预算、主题、范围等):
→ 调用 user_profile_subagent 收集信息
@@ -30,7 +131,7 @@ def build_system_prompt(use_report):
如果用户画像信息已经完整:
→ 调用 research-subagent 生成报告。
------------------------
2】当 use_report = False 时:
4】当 use_report = False 时:
- 严禁调用 research-subagent
- 如果用户明确请求报告、调研、总结、分析:
@@ -38,7 +139,7 @@ def build_system_prompt(use_report):
"报告功能当前未开启,你可以打开 use_report=True 后我来帮你生成报告。"
- 其他普通问题可以正常回答或调用其他子Agent。
------------------------
3】用户画像优先级规则
5】用户画像优先级规则
只要用户输入包含以下情况:
- 表达设计需求
- 提供偏好信息(例如风格、预算、房间类型)
@@ -48,11 +149,17 @@ def build_system_prompt(use_report):
user_profile_subagent
用于更新或收集用户画像。
------------------------
4】调度原则
6】调度原则
- user_profile_subagent 只负责 **信息收集**
- research-subagent 只负责 **报告生成**
不要混用职责。
========================
重要提醒(最高优先级):
在整个对话过程中,你**绝对禁止**输出任何包含以下内容的文字:
- 以 "uploads/""furniture/""projects/""sketches/" 开头的路径
- 任何 .png、.jpg 结尾的路径
- 任何 http 开头的图片链接(除非系统明确要求)
所有图片展示均由系统统一处理,你只需负责正确调用工具。
"""
return system_prompt
@@ -71,6 +178,7 @@ def build_painter_prompt():
- 或任何“基于多张图片做合并提取”的表达
👉 必须使用:
edit_furniture
👉 严格要求:
- 不允许调用 generate_furniture
- 不允许重新生成整张图
@@ -85,43 +193,53 @@ def build_painter_prompt():
如果用户输入不明确(例如:“改成绿色”):
👉 一律视为【编辑类】
👉 使用 edit_furniture
--------------------------------
【二、关于图片来源(关键规则)】
- 当前系统已经提供了一张“当前图片”(不需要生成 image_url
- ❗禁止你自行编造 image_url
- ❗禁止你猜测 image_url
- edit_furniture 会自动从上下文获取图片
【二、generate_furniture 参数规则(重要)】
需要生成多张图片时:
- prompt 必须始终描述 **单张家具**single furniture piece不要在 prompt 里写入 "Generate 4 different...""multiple chairs"、“4 variations”等数量相关的词。
- 正确的 prompt 风格示例(单张):
"A modern minimalist dining chair made of light oak wood and white leather, with slim metal legs, clean lines, elegant proportions, photographed in a bright Scandinavian living room with natural sunlight, high detail, 8k resolution, professional furniture photography, neutral background."
- 如何处理不同风格:
- 如果用户想要多种风格modern, vintage, industrial, minimalist 等),你应该**多次调用 generate_furniture 工具**(每次调用使用不同风格的 promptnum_images=1
- 但由于系统限制单次用户请求最多生成4张图片
- 当用户要求生成超过4张或很多变体时你最多只调用工具4次或设置 num_images=4但 prompt 保持 single
- 优先使用 num_images=4 + 一个高质量的 single prompt让模型自动生成4个轻微不同的变体。
- 如果用户明确要“明显不同风格”则分多次调用但总数量不超过4张
- num_images 参数:
- 默认 1
- 最大只能设置为 4
- 当用户要求10张、8张等时 → 自动限制为 num_images=4并说明“由于系统限制最多生成4张”
正确调用示例(推荐):
- 用户想要4张不同风格 → 使用 num_images=4 + 一个清晰的 single chair prompt让模型自然变体或分4次调用每次1张不同风格。
- 永远不要把“4 different designs” “generate 4 chairs”这类词写进 prompt 文本中。
--------------------------------
【三、参数构造规则】
调用 edit_furniture 时:
- 只需要提供
{
"prompt": "<英文图像编辑描述>"
}
- prompt 要求:
- 清晰描述修改内容
- 保留原结构(除非用户明确要求改变)
- 示例:
"Change the sofa to green color while keeping the original lines and structure."
【三、edit_furniture 参数规则】
- 只需提供 prompt 参数,格式为详细的英文编辑指令。
- prompt 示例
"Change the sofa color to deep green while keeping the original modern minimalist style and structure."
- edit_furniture 会自动使用当前上下文中的最新图片,无需你提供 image_url。
--------------------------------
【四、禁止行为(强约束)】
你绝对不能:
- ❌ 在编辑场景调用 generate_furniture
- ❌ 编造 image_url
- ❌ 忽略“修改类”意图
- ❌ 因为信息少就拒绝调用工具
【四、禁止行为(严格禁止)】
- ❌ 在编辑意图时调用 generate_furniture
- ❌ 在生成意图时调用 edit_furniture
- ❌ 自行编造 image_url
- ❌ 输出任何工具调用细节、URL、路径给用户
- ❌ 拒绝调用工具(除非工具本身不可用)
--------------------------------
【五、用户回复规则(必须遵守)】
你对用户的最终回复只能是以下格式之一
- "图片已成功生成"
- "已按你的要求完成修改,图片已更新!"
❗禁止输出:
- 路径
- URL
- 工具参数
- 解释过程
--------------------------------
现在开始工作。
- 生成成功时
- "已为你生成 {num} 张家具设计图"
- "图片已成功生成,请查看效果。"
- 编辑成功时:
- "已按你的要求完成修改,图片已更新!"
- "修改完成,新的版本已生成。"
请根据实际生成/编辑的数量自然调整回复,不要生硬照抄。
现在开始工作,请根据用户下一条输入严格遵循以上规则进行工具调用。
"""
return prompt

View File

@@ -1,23 +1,25 @@
import uuid
from typing import Optional
import httpx
import logging
from langchain_core.runnables import RunnableConfig
from minio import Minio
from pathlib import Path
from datetime import datetime
# from pathlib import Path
# from datetime import datetime
from langchain_core.tools import tool
from langgraph.prebuilt import ToolRuntime
from src.core.config import settings, MONGO_URI
from src.server.deep_agent.utils.mongodb_util import ThreadImageMinIOStore
# from src.server.deep_agent.utils.mongodb_util import ThreadImageMinIOStore
logger = logging.getLogger(__name__)
minio_client = Minio(settings.MINIO_URL, access_key=settings.MINIO_ACCESS, secret_key=settings.MINIO_SECRET, secure=settings.MINIO_SECURE)
image_store = ThreadImageMinIOStore(MONGO_URI, "agent_tool_generate_db")
# image_store = ThreadImageMinIOStore(MONGO_URI, "agent_tool_generate_db")
@tool
async def generate_furniture(prompt: str, runtime: ToolRuntime):
async def generate_furniture(runtime: ToolRuntime, prompts: list[str] = None, num_images: Optional[int] = 1):
"""
使用图像生成模型根据用户提供的详细英文提示词,从零生成一张全新的家具设计草图。
@@ -28,6 +30,7 @@ async def generate_furniture(prompt: str, runtime: ToolRuntime):
参数说明:
- prompt (str): **必须是详细的英文提示词**,越详细越好,包含家具类型、风格、颜色、材质、尺寸比例、背景、视角、光影等具体要求。
示例:"Generate a modern minimalist dining chair made of light oak wood and white leather, with slim metal legs, photographed in a bright Scandinavian living room with natural sunlight, high detail, 8k resolution."
- num_images (int, 可选): 要生成的图片数量,默认 1 张。最大只能是 4 张。如果输入超过 4会自动限制为 4。
返回值:
返回新生成家具图片的 image_url后续对话中 Agent 可直接引用该图片进行描述、进一步编辑或分析。
@@ -42,85 +45,232 @@ async def generate_furniture(prompt: str, runtime: ToolRuntime):
- 生成的图片会自动携带到整个对话上下文中,支持后续使用 edit_furniture 等工具进行迭代修改。
- 如果需要生成多个方案,可以多次调用本工具或在 prompt 中明确要求生成不同变体。
"""
current_checkpoint_id = runtime.store.get(namespace=("image_history",), key="checkpoint_id", ).value.get("current_checkpoint_id")
if num_images is None or num_images < 1:
num_images = 1
elif num_images > 4:
num_images = 4
# current_checkpoint_id = runtime.store.get(namespace=("image_history",), key="checkpoint_id", ).value.get("current_checkpoint_id")
logger.info(f"\n[系统日志] 正在调用 generate_furniture ...当前checkpoint_id={current_checkpoint_id}")
try:
image_url = await generate_or_edit_image(prompt=prompt)
bucket_name = "fida-public-bucket"
object_name = f"furniture/sketches/{uuid.uuid4()}"
image_urls = []
for i in range(num_images):
image_urls.append(await generate_or_edit_image(prompt=prompts[i], bucket_name=bucket_name, object_name=f"{object_name}-{i}.png"))
if image_url:
image_store.save_image_path(thread_id=current_checkpoint_id, object_path=image_url, metadata={"prompt": prompt, "generated_at": str(datetime.now())})
return image_url
else:
return "Image generation failed."
# if image_urls:
# image_store.save_image_path(thread_id=current_checkpoint_id, object_path=image_urls, metadata={"prompt": prompt, "generated_at": str(datetime.now())})
return image_urls
# else:
# return "Image generation failed."
except Exception as e:
logger.warning(f"绘图流程异常:{e}")
return "generate furniture error"
@tool
async def edit_furniture(prompt: str, runtime: ToolRuntime, config: RunnableConfig):
async def edit_furniture(runtime: ToolRuntime, config: RunnableConfig, input_image_paths: list[str] = None, prompts: list[str] = None, ):
"""
使用先进的图像编辑模型image editing model对家具设计草图进行精准修改。
使用先进的图像编辑模型对家具设计草图进行精准修改。
功能说明:
- 根据用户提供的**详细英文提示词**生成修改后的新家具图片。
- 支持批量处理多张家具图片,根据对应的提示词生成修改后的新图片。
- input_image_paths 和 prompts 必须一一对应,数量完全一致。
- 最多支持同时处理 4 对图片和提示词(即最多 4 张图片)。
参数说明:
- prompt (str): **必须是详细的英文提示词**,描述想要的具体修改(风格、颜色、材质、形状、添加/删除元素、比例等)。
示例:"Change the sofa to a modern minimalist style with dark gray fabric and metal legs, add a matching coffee table, make the overall lighting warmer and more luxurious."
- input_image_paths (list[str]):
输入图片在 MinIO 中的存储路径列表。
示例:["furniture/designs/sofa_concept_v1.png", "projects/room_2026/chair_v2.jpg"]
注意:路径必须是有效的 MinIO 对象路径,工具会自动下载对应图片。
- prompts (list[str]):
与图片一一对应的详细英文提示词列表。
每个提示词描述对对应图片的具体修改要求(风格、颜色、材质、形状、添加/删除元素等)。
示例:["Change the sofa to a modern minimalist style with dark gray fabric and metal legs, add a matching coffee table.",
"Convert the chair to Scandinavian Nordic style with light wood and soft beige upholstery."]
使用要求(重要):
- input_image_paths 和 prompts 的长度必须完全相同。
- 列表长度必须在 1 到 4 之间(最多 4 对)。
- input_image_paths[0] 对应 prompts[0],以此类推,一一对应进行编辑。
使用场景:
- 家具设计迭代
- 室内设计方案修改
- 风格转换(现代/北欧/工业风等)
- 材质/颜色调整
- 家具设计方案迭代
- 室内设计方案对比修改
- 批量风格转换(现代/北欧/工业/奢华风等)
- 材质颜色、细节批量调整
示例调用:
input_image_paths = ["designs/sofa1.png", "designs/chair1.png"]
prompts = [
"Make the sofa more luxurious with velvet fabric and gold accents.",
"Change the chair to a sleek modern design with black leather and chrome legs."
]
"""
image_history = runtime.store.get(namespace=("image_history",), key="checkpoint_id", )
last_checkpoint_id = image_history.value.get("last_checkpoint_id")
current_checkpoint_id = image_history.value.get("current_checkpoint_id")
logger.info(f"\n[系统日志] 正在调用 edit_furniture ...current_checkpoint_id={current_checkpoint_id} --- last_checkpoint_id={last_checkpoint_id}")
input_path = []
if image_store.get_image_path(last_checkpoint_id):
current_image_path = image_store.get_image_path(last_checkpoint_id).get("current_image_path", False)
else:
current_image_path = None
# image_history = runtime.store.get(namespace=("image_history",), key="checkpoint_id", )
# last_checkpoint_id = image_history.value.get("last_checkpoint_id")
# current_checkpoint_id = image_history.value.get("current_checkpoint_id")
#
# logger.info(f"\n[系统日志] 正在调用 edit_furniture ...current_checkpoint_id={current_checkpoint_id} --- last_checkpoint_id={last_checkpoint_id}")
#
# if image_store.get_image_path(last_checkpoint_id):
# current_image_path = image_store.get_image_path(last_checkpoint_id).get("current_image_path", False)
# if current_image_path:
# if isinstance(current_image_path, list):
# # 只取最后一张
# current_image_path = current_image_path[-1]
# else:
# current_image_path = None
# input_path = []
try:
user_input_image_paths = runtime.state.get("files").get("input_image", [])
user_quote_image_path = runtime.state.get("files").get("quote_image", "")
if current_image_path:
if len(user_input_image_paths) or current_image_path:
for path in user_input_image_paths:
input_path.append(path)
if user_quote_image_path:
input_path.append(user_quote_image_path)
if not len(user_input_image_paths) and not user_quote_image_path:
input_path = [current_image_path]
# user_input_image_paths = runtime.state.get("files").get("input_image", [])
# user_quote_image_path = runtime.state.get("files").get("quote_image", "")
result = []
if len(input_image_paths):
for i in range(len(input_image_paths)):
bucket_name = "fida-public-bucket"
object_name = f"furniture/sketches/{uuid.uuid4()}.png"
image_url = await generate_or_edit_image(input_path=input_path, prompt=prompt, bucket_name=bucket_name, object_name=object_name)
if image_url:
image_store.save_image_path(thread_id=current_checkpoint_id, object_path=image_url, metadata={"prompt": prompt, "generated_at": str(datetime.now())})
return image_url
else:
return "Image generation failed."
else:
return "The picture to be edited does not exist."
else:
return "No recent image found, please upload or cite it"
image_url = await generate_or_edit_image(input_path=[input_image_paths[i]], prompt=prompts[i], bucket_name=bucket_name, object_name=f"{object_name}-{i}.png")
result.append(image_url)
# image_url = await generate_or_edit_image(input_path=input_path, prompt=prompt, bucket_name=bucket_name, object_name=object_name)
# if image_url:
# image_store.save_image_path(thread_id=current_checkpoint_id, object_path=[image_url], metadata={"prompt": prompt, "generated_at": str(datetime.now())})
return result
# else:
# return "Image generation failed."
# else:
# return "The picture to be edited does not exist."
# else:
# return "No recent image found, please upload or cite it"
except Exception as e:
logger.warning(f"edit_furniture error {e}")
return "edit_furniture error"
@tool
async def edit_quote_upload_furniture(image_paths: list[str] = None, mode: str = "auto", prompts: list[str] = None, ):
"""
使用先进的图像编辑模型对家具图片进行精准批量修改。
支持四种模式:
- one_to_one最常用多张图片 + 多个提示词,一一对应编辑
- one_to_many多张图片 + 1个提示词所有图片统一修改
- many_to_one1张图片 + 多个提示词(同一张图生成多个不同变体,例如不同颜色)
- many_to_many新增多张图片 + 多个提示词,一一对应(多对多交叉编辑)
参数说明:
- image_paths (list[str]): MinIO 图片路径列表,长度建议 1~4
- prompts (list[str]): 详细英文提示词列表
- mode (str): "one_to_one", "one_to_many", "many_to_one", "many_to_many", "auto"(默认自动判断)
使用要求:
- image_paths 长度必须在 1~4 之间
- mode="auto" 时会根据长度智能判断
- many_to_many 模式下image_paths 和 prompts 的长度必须完全相同
示例:
示例1many_to_many多对多一一对应
image_paths = ["sofa1.png", "chair1.png", "table1.png"]
prompts = [
"Change to bright yellow modern style.",
"Change to deep green luxury style.",
"Change to soft beige Scandinavian style."
]
mode = "many_to_many"
示例2many_to_one同一张图多个颜色版本
image_paths = ["sofa_original.png"]
prompts = ["yellow version", "green version", "blue version", "black version"]
mode = "many_to_one"
"""
try:
# ====================== 参数校验(直接返回错误信息) ======================
if not image_paths or len(image_paths) < 1 or len(image_paths) > 4:
return f"参数错误image_paths 必须提供,且长度需要在 1 到 4 张之间。目前收到 {len(image_paths) if image_paths else 0} 张。"
if not prompts:
return "参数错误prompts 不能为空,请至少提供一个修改提示词。"
if mode not in ["one_to_one", "one_to_many", "many_to_one", "many_to_many", "auto"]:
return f"参数错误mode 参数无效。可用值one_to_one, one_to_many, many_to_one, many_to_many, auto。当前收到{mode}"
# Auto 模式智能判断
if mode == "auto":
if len(image_paths) == 1 and len(prompts) > 1:
mode = "many_to_one"
elif len(prompts) == 1:
mode = "one_to_many"
elif len(image_paths) == len(prompts):
mode = "many_to_many" # 新增:数量相等时优先 many_to_many
else:
mode = "one_to_one"
# 各模式严格校验
if mode == "many_to_one":
if len(image_paths) != 1:
return f"参数错误many_to_one 模式只能传入 1 张图片,当前传入了 {len(image_paths)} 张。"
if len(prompts) < 1:
return "参数错误many_to_one 模式下 prompts 至少需要 1 个。"
elif mode == "one_to_many":
if len(prompts) != 1:
return f"参数错误one_to_many 模式下 prompts 必须只有 1 个,当前有 {len(prompts)} 个。"
elif mode in ["one_to_one", "many_to_many"]:
if len(prompts) != len(image_paths):
return (f"参数错误:{mode} 模式下 image_paths 和 prompts 数量必须完全一致。\n"
f"当前 image_paths 有 {len(image_paths)}prompts 有 {len(prompts)} 个。")
# ====================== 执行编辑 ======================
result = []
bucket_name = "fida-public-bucket"
if mode == "many_to_one":
# 同一张图片 + 多个 prompt
base_image = image_paths[0]
for i, prompt in enumerate(prompts):
object_name = f"furniture/sketches/{uuid.uuid4()}.png"
image_url = await generate_or_edit_image(
input_path=[base_image],
prompt=prompt,
bucket_name=bucket_name,
object_name=f"{object_name}-var{i}.png"
)
result.append(image_url)
else:
# one_to_one、many_to_many、one_to_many 统一处理
for i in range(len(image_paths)):
# 根据模式决定当前使用的 prompt
if mode == "one_to_many":
current_prompt = prompts[0]
else:
current_prompt = prompts[i] # one_to_one 和 many_to_many 都用对应位置的 prompt
object_name = f"furniture/sketches/{uuid.uuid4()}.png"
image_url = await generate_or_edit_image(
input_path=[image_paths[i]],
prompt=current_prompt,
bucket_name=bucket_name,
object_name=f"{object_name}-{i}.png"
)
result.append(image_url)
return result
except Exception as e:
logger.error(f"edit_quote_upload_furniture 执行异常: {e}", exc_info=True)
return f"工具执行失败:{str(e)},请检查参数后重试。"
async def generate_or_edit_image(input_path=None, bucket_name="fida-public-bucket",
object_name=f"furniture/sketches/{uuid.uuid4()}.png",
prompt="Generate a modern minimalist dining chair made of light "

View File

@@ -53,7 +53,7 @@ class ThreadImageMinIOStore:
def save_image_path(
self,
thread_id: str,
object_path: str, # MinIO 中的相對路徑,例如 "test/123.png" 或 "images/20250320/abc.png"
object_path: list, # MinIO 中的相對路徑,例如 "test/123.png" 或 "images/20250320/abc.png"
metadata: Optional[dict] = None
) -> bool:
"""
@@ -137,7 +137,7 @@ if __name__ == '__main__':
image_store = ThreadImageMinIOStore(MONGO_URI, "agent_tool_generate_db")
success = image_store.save_image_path(
thread_id="121233",
object_path="test/123.png",
object_path=["test/123.png"],
metadata={"prompt": "prompt", "generated_at": str(datetime.now())})
print(success)
info = image_store.get_image_path("121233")

View File

@@ -182,10 +182,10 @@ def check_and_extract_minio_image(url: str) -> dict[str, str]:
if __name__ == '__main__':
url = 'fida-test/furniture/sketches/1b82b2db-8019-4796-b2cc-11fb24c7799d.png'
read_type = "2"
img = oss_get_image(oss_client=minio_client, bucket=url.split('/')[0], object_name=url[url.find('/') + 1:])
img.show()
img.save("result.png")
urls = ["fida-public-bucket/furniture/sketches/0193c9b2-d8dd-40fc-b715-3ce0daab7abf.png-0.png", "fida-public-bucket/furniture/sketches/bab54cdf-0a60-4806-8c6b-17b836aec1eb.png-1.png", "fida-public-bucket/furniture/sketches/6c993266-95d2-42ee-826b-933b0e344b81.png-2.png"]
# read_type = "2"
for url in urls:
img = oss_get_image(oss_client=minio_client, bucket=url.split('/')[0], object_name=url[url.find('/') + 1:])
img.show()
# img.save("result.png")
# get_presigned_url(oss_client=minio_client, bucket="fida-test", object_name="furniture/sketches/07bf4cfe-4502-4821-b78f-7727bf409498.png")
#