Appearance
场景 1: 智能图片助手
模块:Tools + Conditional Edges优先级:🔴 P0(最高)业务价值:增强图片处理能力,提升用户体验
一、业务背景
1.1 当前痛点
当前项目已支持图片上传和图片生成,但存在以下问题:
具体痛点:
- AI 只能"看图说话",无法对图片执行操作
- 用户需要手动描述"帮我生成一张类似风格的图",无法自动识别
- 图片处理(裁剪、滤镜、格式转换)需要用户自己完成
- 搜索相似图片、识别图片中的文字等需要第三方工具
1.2 期望效果
二、工具设计
2.1 工具清单
基于项目实际需求,设计以下工具:
| 工具名称 | 功能 | 触发场景 |
|---|---|---|
image_crop | 图片裁剪 | "帮我裁剪这张图"、"把左边切掉" |
image_ocr | 图片文字识别 | "图片里写了什么"、"提取文字" |
image_style_extract | 提取图片风格 | "生成类似风格的图"、"模仿这个风格" |
image_similarity_search | 搜索相似图片 | "找类似的图"、"这张图像什么" |
get_weather | 获取天气 | "今天天气"、"北京天气" |
search_web | 联网搜索 | "搜索xxx"、"查一下xxx" |
2.2 工具流程
三、代码实现
3.1 工具定义
创建文件: services/tools/image_tools.py
python
"""图片处理相关工具
基于项目现有的图片处理能力,封装为 LangGraph 工具。
"""
from langchain.tools import tool
from typing import Optional, Dict, Any, List
import logging
logger = logging.getLogger(__name__)
@tool
def image_crop(image_url: str, x: int, y: int, width: int, height: int) -> Dict[str, Any]:
"""
裁剪图片。
当用户想要裁剪图片、截取图片某部分时使用此工具。
Args:
image_url: 图片 URL(支持 COS key 和完整 URL)
x: 裁剪起始 X 坐标
y: 裁剪起始 Y 坐标
width: 裁剪宽度
height: 裁剪高度
Returns:
裁剪后的图片信息
"""
from services.image_utils import download_image_as_base64
from PIL import Image
import io
import base64
try:
# 下载图片
if image_url.startswith("cos://"):
from services.image_storage import get_signed_url_from_key
image_url = get_signed_url_from_key(image_url)
image_data = download_image_as_base64(image_url)
# 解码 base64
image_bytes = base64.b64decode(image_data.split(",")[1])
img = Image.open(io.BytesIO(image_bytes))
# 裁剪
cropped = img.crop((x, y, x + width, y + height))
# 转回 base64
buffer = io.BytesIO()
cropped.save(buffer, format="PNG")
result_base64 = "data:image/png;base64," + base64.b64encode(buffer.getvalue()).decode()
return {
"success": True,
"cropped_image": result_base64,
"message": f"已裁剪图片,区域: ({x}, {y}) 到 ({x + width}, {y + height})"
}
except Exception as e:
logger.error(f"图片裁剪失败: {e}")
return {"success": False, "error": str(e)}
@tool
def image_ocr(image_url: str, language: Optional[str] = "chi_sim+eng") -> Dict[str, Any]:
"""
识别图片中的文字。
当用户想要提取图片中的文字、识别图片内容时使用此工具。
Args:
image_url: 图片 URL
language: 识别语言(默认中英文)
Returns:
识别出的文字内容
"""
# TODO: 集成 OCR 服务(如腾讯云 OCR、百度 OCR)
# 这里返回模拟结果
return {
"success": True,
"text": "(OCR 识别结果)图片中的文字内容...",
"confidence": 0.95
}
@tool
def image_style_extract(image_url: str) -> Dict[str, Any]:
"""
提取图片风格特征。
当用户想要生成类似风格的图片、模仿某个图片风格时使用此工具。
Args:
image_url: 参考图片 URL
Returns:
风格描述和提示词
"""
from services.image_utils import download_image_as_base64
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
import os
try:
# 下载图片
if image_url.startswith("cos://"):
from services.image_storage import get_signed_url_from_key
image_url = get_signed_url_from_key(image_url)
image_data = download_image_as_base64(image_url)
# 使用 LLM 分析风格
llm = ChatOpenAI(
model="openai/gpt-4o-mini",
api_key=os.getenv("OPENROUTER_API_KEY"),
base_url=os.getenv("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1")
)
prompt = """分析这张图片的视觉风格,生成一个用于 AI 图片生成的提示词。
请从以下维度描述:
1. 主题/内容
2. 艺术风格(如油画、水彩、像素风等)
3. 色调(如暖色调、冷色调、高饱和等)
4. 构图特点
5. 光影效果
输出格式:
{
"style_description": "风格描述",
"prompt_for_generation": "用于生成的英文提示词"
}"""
response = llm.invoke([
HumanMessage(content=[
{"type": "text", "text": prompt},
{"type": "image_url", "image_url": {"url": image_data}}
])
])
import json
result = json.loads(response.content)
return {
"success": True,
"style_description": result.get("style_description", ""),
"generation_prompt": result.get("prompt_for_generation", "")
}
except Exception as e:
logger.error(f"风格提取失败: {e}")
return {"success": False, "error": str(e)}
@tool
def get_weather(city: str) -> Dict[str, Any]:
"""
获取指定城市的天气信息。
当用户询问天气、温度、是否下雨等问题时使用此工具。
Args:
city: 城市名称(如"北京"、"上海")
Returns:
天气信息
"""
# TODO: 集成天气 API
# 示例返回
return {
"success": True,
"city": city,
"temperature": "25°C",
"condition": "晴",
"humidity": "60%",
"message": f"{city}今天天气晴朗,温度 25°C,湿度 60%"
}
@tool
def search_web(query: str) -> Dict[str, Any]:
"""
搜索网络获取实时信息。
当用户询问实时信息、新闻、最新数据时使用此工具。
Args:
query: 搜索关键词
Returns:
搜索结果摘要
"""
# TODO: 集成搜索 API(Tavily、SerpAPI 等)
return {
"success": True,
"query": query,
"results": [
{"title": "搜索结果 1", "snippet": "相关内容摘要...", "url": "https://example.com/1"},
{"title": "搜索结果 2", "snippet": "相关内容摘要...", "url": "https://example.com/2"},
],
"summary": f"关于「{query}」的搜索结果摘要..."
}3.2 Agent 实现
创建文件: services/tool_agent.py
python
"""带工具调用的智能 Agent
基于项目实际需求,实现图片智能处理等功能。
"""
from typing import Literal, Optional
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import ToolNode
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage
import os
import logging
from services.tools.image_tools import (
image_crop,
image_ocr,
image_style_extract,
get_weather,
search_web,
)
logger = logging.getLogger(__name__)
class ImageAssistantAgent:
"""智能图片助手 Agent"""
def __init__(self):
self.tools = [
image_crop,
image_ocr,
image_style_extract,
get_weather,
search_web,
]
self.tools_by_name = {tool.name: tool for tool in self.tools}
def _get_llm(self, model: Optional[str] = None):
"""获取绑定了工具的 LLM"""
llm = ChatOpenAI(
model=model or os.getenv("OPENROUTER_MODEL", "openai/gpt-4o"),
api_key=os.getenv("OPENROUTER_API_KEY"),
base_url=os.getenv("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1"),
temperature=0.7
)
return llm.bind_tools(self.tools)
def _should_continue(self, state: MessagesState) -> Literal["tools", END]:
"""判断是否需要调用工具"""
last_message = state["messages"][-1]
if last_message.tool_calls:
return "tools"
return END
def _call_model(self, state: MessagesState, model: Optional[str] = None):
"""调用模型"""
llm = self._get_llm(model)
system_prompt = """你是一个智能图片助手。你可以:
1. **分析图片**:识别图片内容、风格、文字等
2. **处理图片**:裁剪、提取文字、分析风格
3. **回答问题**:天气查询、网络搜索等
当用户上传图片并要求处理时,使用相应的工具。
如果不确定用户意图,先询问确认。"""
messages = [SystemMessage(content=system_prompt)] + state["messages"]
response = llm.invoke(messages)
return {"messages": [response]}
def build_graph(self, checkpointer, model: Optional[str] = None):
"""构建 Agent 工作流"""
tool_node = ToolNode(self.tools)
workflow = StateGraph(MessagesState)
workflow.add_node("agent", lambda state: self._call_model(state, model))
workflow.add_node("tools", tool_node)
workflow.add_edge(START, "agent")
workflow.add_conditional_edges(
"agent",
self._should_continue,
{"tools": "tools", END: END}
)
workflow.add_edge("tools", "agent")
return workflow.compile(checkpointer=checkpointer)四、用户交互流程
4.1 场景示例
4.2 前端集成示例
javascript
// static/js/tool-agent.js
class ToolAgentChat {
constructor() {
this.eventSource = null;
}
async sendMessage(prompt, images = [], options = {}) {
const response = await fetch('/api/chat/tool-agent/stream', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
prompt,
images,
model: options.model || 'openai/gpt-4o',
conversation_id: options.conversationId
})
});
// 处理 SSE 流
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = JSON.parse(line.slice(6));
if (data.tool_call) {
// 显示工具调用
this.showToolCall(data.tool_call);
} else if (data.tool_result) {
// 显示工具结果
this.showToolResult(data.tool_result);
} else if (data.content) {
// 流式内容
this.appendContent(data.content);
} else if (data.done) {
this.finishMessage();
}
}
}
}
}
showToolCall(toolCall) {
const toolElement = document.createElement('div');
toolElement.className = 'tool-call-indicator';
const toolIcons = {
'image_crop': '✂️',
'image_ocr': '📝',
'image_style_extract': '🎨',
'get_weather': '🌤️',
'search_web': '🔍'
};
toolElement.innerHTML = `
<div class="tool-header">
<span class="tool-icon">${toolIcons[toolCall.name] || '🔧'}</span>
<span class="tool-name">${this.getToolDisplayName(toolCall.name)}</span>
</div>
<div class="tool-args">${JSON.stringify(toolCall.args, null, 2)}</div>
`;
this.messageContainer.appendChild(toolElement);
}
showToolResult(result) {
const resultElement = document.createElement('div');
resultElement.className = 'tool-result';
if (result.success) {
resultElement.innerHTML = `
<div class="result-success">
✅ ${result.message || '工具执行成功'}
</div>
`;
} else {
resultElement.innerHTML = `
<div class="result-error">
❌ ${result.error || '工具执行失败'}
</div>
`;
}
this.messageContainer.appendChild(resultElement);
}
getToolDisplayName(name) {
const names = {
'image_crop': '图片裁剪',
'image_ocr': '文字识别',
'image_style_extract': '风格提取',
'get_weather': '天气查询',
'search_web': '网络搜索'
};
return names[name] || name;
}
}五、预期收益
5.1 功能增强
| 功能 | 改进前 | 改进后 |
|---|---|---|
| 图片处理 | 只能看图说话 | 可裁剪、识别文字、提取风格 |
| 实时信息 | 无法获取 | 可查询天气、搜索网络 |
| 用户体验 | 需要多轮对话 | 一次完成目标 |
5.2 技术收益
- 建立可扩展的工具体系
- 为后续更多工具集成打好基础
- 提升 Agent 智能化程度
六、实施计划
| 步骤 | 任务 | 预估时间 |
|---|---|---|
| 1 | 创建 services/tools/image_tools.py | 2h |
| 2 | 创建 services/tool_agent.py | 2h |
| 3 | 添加 API 端点 /api/chat/tool-agent/stream | 1h |
| 4 | 前端工具调用展示组件 | 2h |
| 5 | 测试和优化 | 1h |
| 总计 | 8h (1天) |