加了一堆模型和一堆功能
This commit is contained in:
@@ -11,7 +11,7 @@ from openai import AsyncOpenAI
|
||||
|
||||
from app.agent.tools import TOOL_DEFINITIONS, execute_tool
|
||||
from app.config import (
|
||||
get_llm_model,
|
||||
get_llm_model_config,
|
||||
get_llm_max_iterations,
|
||||
get_image_model_config,
|
||||
get_max_recent_turns,
|
||||
@@ -22,9 +22,19 @@ from app.services.image_gen import to_data_uri
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_client() -> AsyncOpenAI:
|
||||
base_url = os.getenv("OPENAI_BASE_URL")
|
||||
return AsyncOpenAI(base_url=base_url) if base_url else AsyncOpenAI()
|
||||
def _get_client(provider: str) -> AsyncOpenAI:
|
||||
"""按 provider 创建对应的 OpenAI 兼容客户端。"""
|
||||
if provider == "vectorengine":
|
||||
return AsyncOpenAI(
|
||||
api_key=os.getenv("VECTORENGINE_API_KEY"),
|
||||
base_url=os.getenv("VECTORENGINE_BASE_URL", "https://api.vectorengine.ai/v1"),
|
||||
)
|
||||
if provider == "deepseek":
|
||||
return AsyncOpenAI(
|
||||
api_key=os.getenv("DEEPSEEK_API_KEY"),
|
||||
base_url=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
|
||||
)
|
||||
return AsyncOpenAI()
|
||||
|
||||
SYSTEM_PROMPT = """\
|
||||
你是一个专业的游戏美术 AI 助手。你的工作是帮助美术人员通过对话生成游戏美术资源。
|
||||
@@ -33,7 +43,8 @@ SYSTEM_PROMPT = """\
|
||||
- 根据用户的文字描述生成图片(UI图标、按钮、插画、立绘、概念图等)
|
||||
- 理解用户的审美意图,将中文描述转化为高质量的英文生成 prompt
|
||||
- 根据用户反馈迭代修改(调整颜色、风格、构图等)
|
||||
- 如果用户提供了参考图,将参考图的风格元素融入生成 prompt
|
||||
- 如果用户提供了参考图(支持多张),将参考图的风格元素融入生成 prompt
|
||||
- 理解多张参考图各自的角色(如"图1的主体 + 图2的风格/视角"),并在 prompt 中准确传达
|
||||
- 理解用户在图片上的标注(框选区域 + 文字批注),精准定位需要修改的部分
|
||||
|
||||
## 工作流程
|
||||
@@ -54,23 +65,25 @@ SYSTEM_PROMPT = """\
|
||||
- 必须使用英文
|
||||
- 尽量详细描述:主体内容、颜色方案、光照、构图、材质等
|
||||
- 如果用户要求游戏 UI 元素,添加相关关键词如 "game UI", "icon", "button" 等
|
||||
- 如果你能直接看到参考图(图片内容),可以在 prompt 中描述参考图的风格特征
|
||||
- 如果你无法看到参考图(只收到了文字提示说有参考图),参考图会由生图工具的 IP-Adapter 自动处理风格融合。此时你不要自行猜测画风/艺术风格关键词(如 pixel art、watercolor、oil painting 等),把风格交给参考图来决定。但如果用户在消息中明确指定了风格(如"赛博朋克风"、"水彩风"等),应保留并翻译到 prompt 中——尊重用户的主动意图
|
||||
- 如果你能直接看到参考图(图片内容),可以在 prompt 中描述参考图的风格特征。多张参考图时,理解用户对各图的定位(如"图1做主体参考、图2做风格参考"),将相应特征分别融入 prompt
|
||||
- 如果你无法看到参考图(只收到了文字提示说有参考图),参考图会由生图工具自动处理风格融合。此时你不要自行猜测画风/艺术风格关键词(如 pixel art、watercolor、oil painting 等),把风格交给参考图来决定。但如果用户在消息中明确指定了风格(如"赛博朋克风"、"水彩风"等),应保留并翻译到 prompt 中——尊重用户的主动意图
|
||||
|
||||
## 注意事项
|
||||
- 用中文和用户交流
|
||||
- 生成图片后简要说明你使用的 prompt 思路
|
||||
- 主动建议迭代方向
|
||||
- **禁止模拟工具调用**:生成图片时必须实际调用 generate_image 工具,绝不能用文字描述"已生成"或假装工具已执行。如果需要生成多张图片,每张都必须单独调用工具
|
||||
- **禁止在回复中嵌入图片链接**:不要在回复文字中使用 Markdown 图片语法(如 `` 或 `sandbox:` 链接)。图片展示由系统自动处理,你只需用文字描述结果即可
|
||||
"""
|
||||
|
||||
|
||||
async def run_agent_loop(
|
||||
messages: list[dict],
|
||||
ref_image_url: Optional[str] = None,
|
||||
ref_image_urls: Optional[list[str]] = None,
|
||||
image_model: Optional[str] = None,
|
||||
session_id: Optional[str] = None,
|
||||
user_id: str = "default_user",
|
||||
llm_model: Optional[str] = None,
|
||||
) -> AsyncGenerator[dict, None]:
|
||||
"""
|
||||
运行 Agent Loop,以 SSE 事件流形式 yield 结果。
|
||||
@@ -114,10 +127,16 @@ async def run_agent_loop(
|
||||
yield {"type": "error", "data": {"message": f"记忆系统检索失败: {e}"}}
|
||||
return
|
||||
|
||||
# ── 解析 LLM 模型配置 ──
|
||||
llm_config = get_llm_model_config(llm_model)
|
||||
llm_provider = llm_config["provider"]
|
||||
llm_model_id = llm_config["model_id"]
|
||||
vision_capable = llm_config.get("vision", False)
|
||||
|
||||
# ── 构建 system prompt ──
|
||||
model_config = get_image_model_config(image_model)
|
||||
current_model_name = model_config.get('name', '未知')
|
||||
current_model_id = model_config.get('id', '未知')
|
||||
img_model_config = get_image_model_config(image_model)
|
||||
current_model_name = img_model_config.get('name', '未知')
|
||||
current_model_id = img_model_config.get('id', '未知')
|
||||
model_hint = (
|
||||
f"\n\n## 当前生图模型(重要)\n"
|
||||
f"本次对话用户选择的生图模型是 **{current_model_name}**"
|
||||
@@ -127,43 +146,39 @@ async def run_agent_loop(
|
||||
)
|
||||
api_messages = [{"role": "system", "content": SYSTEM_PROMPT + model_hint + memory_block}]
|
||||
|
||||
# 检测当前 LLM 是否支持 vision(多模态图片输入)
|
||||
llm_model = get_llm_model().lower()
|
||||
vision_capable = any(kw in llm_model for kw in ("gpt-4o", "gpt-4-vision", "claude"))
|
||||
effective_refs = ref_image_urls or []
|
||||
|
||||
for msg in messages:
|
||||
if msg["role"] == "user" and ref_image_url and msg is messages[-1]:
|
||||
if msg["role"] == "user" and effective_refs and msg is messages[-1]:
|
||||
if vision_capable:
|
||||
api_messages.append({
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": msg["content"]},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {"url": to_data_uri(ref_image_url)},
|
||||
},
|
||||
],
|
||||
})
|
||||
content_parts: list[dict] = [{"type": "text", "text": msg["content"]}]
|
||||
for ref_url in effective_refs:
|
||||
content_parts.append({
|
||||
"type": "image_url",
|
||||
"image_url": {"url": to_data_uri(ref_url)},
|
||||
})
|
||||
api_messages.append({"role": "user", "content": content_parts})
|
||||
else:
|
||||
n_refs = len(effective_refs)
|
||||
hint = (
|
||||
f"{msg['content']}\n\n"
|
||||
"【系统提示:用户上传了一张参考图,已自动传递给图片生成工具的 IP-Adapter。"
|
||||
"IP-Adapter 会从参考图中提取风格并融合到生成结果中。"
|
||||
"你无法看到这张参考图,因此在生成 prompt 时:\n"
|
||||
f"【系统提示:用户上传了 {n_refs} 张参考图,已自动传递给图片生成工具。"
|
||||
"生图工具会根据模型能力自动处理参考图的风格融合。"
|
||||
"你无法看到这些参考图,因此在生成 prompt 时:\n"
|
||||
"1. 描述画面内容(主体、构图、光照、材质等)\n"
|
||||
"2. 不要自行猜测画风/艺术风格——但如果用户明确指定了风格,保留到 prompt 中\n"
|
||||
"3. 用户未指定风格时,风格完全由参考图通过 IP-Adapter 决定】"
|
||||
"3. 用户未指定风格时,风格完全由参考图决定】"
|
||||
)
|
||||
api_messages.append({"role": "user", "content": hint})
|
||||
continue
|
||||
api_messages.append({"role": msg["role"], "content": msg["content"]})
|
||||
|
||||
client = _get_client()
|
||||
client = _get_client(llm_provider)
|
||||
|
||||
for _ in range(get_llm_max_iterations()):
|
||||
try:
|
||||
response = await client.chat.completions.create(
|
||||
model=get_llm_model(),
|
||||
model=llm_model_id,
|
||||
messages=api_messages,
|
||||
tools=TOOL_DEFINITIONS,
|
||||
stream=True,
|
||||
@@ -219,7 +234,7 @@ async def run_agent_loop(
|
||||
"你刚才没有调用 generate_image 工具,只是用文字描述了生成过程。"
|
||||
"请立即调用 generate_image 工具来实际生成图片。"
|
||||
"不要解释,直接调用工具。"
|
||||
f"当前使用的生图模型是 {model_config.get('name', '未知')}。"
|
||||
f"当前使用的生图模型是 {img_model_config.get('name', '未知')}。"
|
||||
),
|
||||
})
|
||||
continue
|
||||
@@ -263,7 +278,7 @@ async def run_agent_loop(
|
||||
except json.JSONDecodeError:
|
||||
arguments = {}
|
||||
|
||||
result = await execute_tool(tool_name, arguments, ref_image_url, image_model)
|
||||
result = await execute_tool(tool_name, arguments, effective_refs or None, image_model)
|
||||
|
||||
used_model = result.get("model_name", "")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user