
为了更好的理解Dify的agent、工作流等在现今或未来与我们的工作和生活方式有什么样的参与形式和程度,我们选择一个常用任务-图像处理中的背景去除,来搭建一个图像处理agent,不过,agent作为一个面向任务目标的实现方法,实际上需要和workflow来结合(循环/嵌入等),所以本文先从workflow开始搭建一个基本流程。
在动手之前,这里需要一个说明:实际上agent在现阶段普遍很大程度上基于LLM的应用,由于LLM可以展现出一些智能表现,所以它在理解模糊指令,自动化,经验性的替代方面做的比较好,当然这也才能让agent成为可能。所以综上所述,对于目前大多数任务,agent的程度并不高,比如我要求指定一个旅游计划,那么大模型可以天然的理解任务(编排好的计划表格式,语气等),搜索资料(天气,酒店,交通,历史信息等),这似乎还是在LLM的能力范围内,完全可以当做一个单一任务执行。所以讲了这么多, 本文认为agent的关键思想在于:
思考 – 行动 – 观察
一. 分析
所以我们构建agent需要体现如下几点,否则同一个单一任务或流程化/自动化任务相比没有任何区别,如何提现agent思想来更好的执行我们的背景去除这一任务?
任务拆分:也即执行什么背景去除?在图像分割领域,存在语义分割,前景分割,背景去除多种定义,所以我们首先能想到的是,利用结合LLM的形式,使得agent根据我们的模糊指令甚至无指令,来分析任务的指向和分配具体任务,具体包括:
1. 组成部分
随便一张图片,丢给这个背景去除agent来分析,首先他作为一个背景去除智能体,它要分析这个图片的内容:也即需要首先执行全景分割,全景分割的意思是,将一个图片的所有区域全部分割,并且全部指定标签,未分配的标签或者叫做未识别的区域指定为‘背景’(‘background’),由此作为agent的首要依据,也即我们人观察图片,如果不首先指定具体任务,人脑会尽量多的观察图片信息。另外还需要另一部分也即多模态模型进行的图像理解,目前针对图像理解的方式也有很多,比如图像预训练嵌入LLM,串联等等,可以为LLM提供进一步的信息。
2.全景分割
全景分割可以获得一个二维列表,描述了这一图像的类别和面积占比,基本上使得agent获得了一个简单的以区域为划分标准的图像信息,然后可以将这一步进行固定的提示词模板,和一些变量组合通过预设的编辑方式,制成自然语言形式的输入,来调用LLM进行分析,分析用户究竟需要什么样的任务?。
首先对于全景分割,可以直接的给出完整的分割结果,后续只需判断即可输出最终的结果来执行后续的任务,我们选择了一个较为稳定的Mask2Former-SwinL模型,在coco数据记上训练的,这个通过swin滑动窗口思想和mask注意力可以在大型数据集上取得最好的效果:

3.图像理解

另外,对于图像理解,可以基于很多开源模型的VL版本,比如Qwen2-VL-7B举例,在hugging face中预览测试https://huggingface.co/tasks/image-text-to-text:

如果继续对话,模型对于图像的理解会加深,可以给出强有力的内容判断:

可以看到,VL模型可以很好的给出理解,所以,可以将VL的结果结合全景分割出的图像信息表统一输入给目前较为先进的LLM(qwq-32B 2025-03),直接给出最终的结论:


通过以上的qwq的回复,其实可以得出一个很有趣的思路,我们可以让VL和LLM成为两个对话者,让LLM作为一个更智能但没看图的,让VL作为一个很会看图但不太智能的看图器,让LLM获取信息去向VL提问,类似现在有的大模型pk。这有点像中文房间问题,如果房间外的人可以很好的指导房间内的“人“翻译中文,那么房间外的人不需要学会中文,也不需要管房间内的是不是人,都可以完成翻译中文的任务。当然这一切只是一个建立在LLM的能力范围内的问题,它无法突破双方能力的界限,而且也不一定是本文任务的最佳解决方案,大概率不如多模态模型的一次性结论,但对于agent本身意义不在于单一任务,它的可拓展性才是核心价值,也即思考行动观察,所以还是值得尝试。
二. 部署
1.图像理解-Qwen2.5-VL
github主页:https://github.com/QwenLM/Qwen2.5-VL
huggingface项目主页:https://huggingface.co/Qwen/Qwen2-VL-7B-Instruct
modelscope项目主页:https://www.modelscope.cn/models/Qwen/Qwen2.5-VL-7B-Instruct
- 首先git拉取代码:
git clone https://github.com/QwenLM/Qwen2.5-VL.git
- docker环境准备:
依然是通过nvidia官方提供的pytorch镜像创建一个容器:
docker run --gpus all -it -v /opt/Qwen2.5-VL:/Qwen2.5-VL --label Qwen2.5-VL nvcr.io/nvidia/pytorch:23.05-py3
- pip安装所有依赖,切换到容器内被挂载的Qwen文件夹:
pip install -r requirements_web_demo.txt
- 安装flash-attention:
pip install -U flash-attn --no-build-isolation
- 参考modelscope的主页提供的quick start代码,python执行:
from modelscope import Qwen2_5_VLForConditionalGeneration, AutoTokenizer, AutoProcessor
from qwen_vl_utils import process_vision_info
# default: Load the model on the available device(s)
model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
"Qwen/Qwen2.5-VL-7B-Instruct", torch_dtype="auto", device_map="auto"
)
来实现拉取模型权重:


ok,依次执行modelscope中例子的剩余部分,可以实现成功调用:
例子:


结果:

本地测试:
使用自定义的图片和text:

+”请完整的说明这张图片中包含的信息,并且给出你认为图像中的主要部分是什么。然后你认为图像中的背景是什么,最应该分割出的主题是什么.”

output:
[‘这张图片展示了一只可爱的柯基犬幼崽,它正坐在草地上,旁边有一个黄色和蓝色相间的玩具球。柯基犬幼崽的毛色主要是浅棕色和白色,耳朵竖立,眼睛大而明亮,显得非常可爱。nn### 图像中的主要部分:n- 柯基犬幼崽:这是图像中最突出的部分,它的表情和姿态都非常吸引人。n- 玩具球:位于幼崽的右侧,是一个黄色和蓝色相间的玩具球,增加了画面的趣味性。nn### 背景:n背景是一片绿色的草地,上面点缀着许多白色的雏菊和其他小野花,给人一种自然、宁静的感觉。nn### 最应该分割出的主题:n- 柯基犬幼崽:因为它是图像的主要焦点,所以最应该被突出显示。通过适当的裁剪或调整,可以更好地突出它的可爱特征和表情。’]
不错!速度比较理想(2000*1000图像最大返回512token响应时间3s),flash_attn发挥稳定。
2.全景分割
同理从pytorch官方镜像开始:
根据facebook的官方页https://github.com/facebookresearch/Mask2Former/blob/main/INSTALL.md和hugging facehttps://huggingface.co/facebook/mask2former-swin-base-coco-panoptic的信息,查询模型mask2former-swin-base-coco-panoptic的环境:

选择nvidia的官方pytorch镜像21.06:
docker run -p 22344:22344 --gpus all -it -v /opt/mask2former-swin-base-coco-panoptic:/mask2former-swin-base-coco-panoptic --name mask2former-swin-base-coco-panoptic nvcr.io/nvidia/pytorch:23.01-py3

下载huggingface的模型文件:
git clone https://huggingface.co/facebook/mask2former-swin-base-coco-panoptic
完全参照huggingface项目主页的demo,写一个demo调用:
python demo_seg.py
取回result,解析文件,预览:
可以发现其中info结果中label_id即为全景分割的标签,segmentation为label的mask:


通过查看model文件夹可以在config找到所有配置参数,以及数据集labelid信息:

三. dify工作流
ok 首先参照Dify + deepseek搭建工作流进行dify的本地部署(本文不做云端的尝试),默认部署成功通过本地直接进入dify应用页面http://localhost/app,后直接创建空白模版:

1.开始/输入
直接创建一个必填的输入字段:

默认使用https的方式传入图像路径,此时如果你的后续服务部署在服务端,则需要传到web托管,如果部署在localhost,则直接路径传入即可,可以发现,在dify的工作日志中,我们定义的输入的img_url被正确传递:

2.http request方式调用服务
首先在服务端通过python flask部署好api服务:

进入docker容器,执行:
python app_dify


可以看到默认容器内的服务地址,以及映射的端口,22344是我的容器暴露的端口。
difyapp中,增加代码执行节点(不适用python在线编辑json可能会造成后续http无法解析的错误-dify的bug):

在代码节点后,直接编辑一个http节点,注意依次选择这几项,JSON选项直接使用dify的空间变量选刚才的代码节点~:

直接点击运行,我这里输入了托管的图片https://lsky.alosha.cn/4F4qyk.jpg,可以看到,返回的json中存储了在代码中设定好的response形式,其中存着分割的结果:

这里新增一个节点,可以发现前面执行的输出变量可以在变量中找到,方便后续使用:

至此一个简单的dify工作流通过简单代码结合http方式访问服务全流程就结束了
下一步:
接入之前部署的Qen2.5-VL,进行图像理解,结合LLM进行循环判断图像分割结果。
待完善!