AIWear 智能衣橱项目实战总览我把衣柜接入了 AISpring Boot × LangChain × CLIP多模态项目从开发到云端部署如果一个衣物管理系统只能上传图片、显示图片那么它和普通网盘并没有本质区别。我希望 AIWear 能真正理解图片知道图片里有什么允许用户用文字或另一张图片找到它还能根据自然语言指令编辑或合并图片。这个想法最终变成了一套由 Vue、Spring Boot 和 Python AI 服务共同组成的应用。它既包含用户、文件、数据库、鉴权等传统软件系统能力也包含图片理解、向量检索和 Agent 工具调用等 AI 能力。更重要的是这些模块已经被连接成了从浏览器到云服务器的完整业务流程。AIWear 能做什么目前项目包含以下核心功能。1. 两种方式完成登录或注册用户可以使用邮箱验证码也可以使用用户名和密码。邮箱验证码暂存在 Redis 中并设置五分钟有效期用户名密码使用 BCrypt 保存 Hash认证成功后由后端生成 JWT前端后续通过Authorization: Bearer token携带身份。对应接口POST /api/user/send-code POST /api/user/auth POST /api/user/logout2. 上传并管理自己的图片图片上传并不是直接存入服务器目录。Java 后端先检查文件类型和大小再调用 Python 服务判断图片是否属于服装、穿搭或人物范围。验证通过后图片被上传到阿里云 OSS文件名、大小、用户 ID 和 OSS 地址写入 MySQL。对应接口POST /api/file/upload/image GET /api/file/my-images3. 使用文字或图片搜索每张上传成功的图片还会进入 AI 索引流程。Python 从 OSS 下载图片调用视觉模型生成描述并用本地 CLIP 模型生成归一化的 512 维图片向量随后将描述、向量、用户 ID 和 OSS 地址保存到 Redis。当前代码支持两条检索路径文搜图在视觉模型生成的图片描述中进行文本匹配。图搜图将查询图片转换为 CLIP 向量与已有图片向量计算余弦相似度。需要特别说明当前版本没有使用 CLIP 文本编码器做跨模态文本向量检索。文搜图使用描述匹配图搜图使用 CLIP 向量。这是当前实现的真实边界也是后续可以继续优化的方向。对应接口POST /api/file/search4. 使用自然语言编辑或合并图片蓝色”或“让人物穿上第一张图中的衣服”。Java 校验图片归属后将用户选择自己上传的图片并输入指令例如“把衣服改成图片和指令发送给 Python。Python 中的 Agent 根据输入参数判断应该调用哪个工具只有image_path时调用edit_image_tool。同时存在image_path1和image_path2时调用merge_image_tool。工具调用 DashScope 图像编辑模型并返回结果 URL。为了避免业务长期依赖模型提供的临时地址Java 会再次下载结果并上传到项目自己的 OSS最后保存操作记录。对应接口POST /api/file/edit POST /api/file/merge GET /api/record/my整体架构数据与文件Python AI 服务Java 业务服务访问入口客户端推理并选择工具页面请求与 /api 请求/api 反向代理业务数据验证码与 JWT 状态上传原图和生成结果HTTP 调用图片描述与向量索引通过 OSS URL 下载图片浏览器Vue 3 Pinia AxiosNginx :80静态资源 /api 反向代理Controller接收参数与统一响应Service鉴权与业务编排MyBatis-Plus MapperPythonAiService跨服务适配层Flask API :5000Qwen-VL图片描述Qwen Plus内容审核与 Agent 决策CLIP ViT-B/16图片向量Deep AgentSkill ToolQwen Image EditMySQL用户 / 文件 / 记录Redis验证码 / JWT / AI 索引阿里云 OSS原图与处理结果这个架构可以分成三条主线。Java 业务系统线Spring Boot 是系统的业务中心负责接收前端请求并完成参数转换。解析 JWT确定当前用户。约束用户只能操作自己的图片。读写 MySQL、Redis 和 OSS。调用 Python 服务并处理返回结果。使用ResultT统一前端响应。使用 AOP 记录带有ApiLog的接口调用。传统业务能力留在 Java可以保持数据一致性、权限边界和接口结构清晰。Python AI 能力线Python 服务集中负责更适合 Python 生态的工作使用 LangChain 调用通义千问模型。使用 Transformers 和 PyTorch 加载本地 CLIP。生成图片描述和图片向量。计算搜索相似度。创建 Deep Agent并注册图片编辑和合并工具。Java 不需要理解模型加载和推理细节只依赖稳定的 HTTP 接口。部署与基础设施线Docker Compose 管理 MySQL、Redis 和 Nginx。Java JAR 与 Python Flask 服务运行在 Linux 宿主机上Nginx 容器通过host.docker.internal将/api请求转发到宿主机的8081端口。前端执行npm run build后生成dist再挂载进 Nginx 容器。这样用户只需要访问 80 端口不需要感知 Java 和 Python 的内部端口。一张图片上传后经历了什么下面这条流程连接了项目中的大部分组件。RedisMySQLOSSPython AISpring BootNginxVue用户RedisMySQLOSSPython AISpring BootNginxVue用户multipart/form-data Bearer TokenJSONuserId ossUrl当前实现记录警告基础上传仍成功alt[索引成功][索引失败]alt[图片不符合要求][图片符合要求]选择图片并点击上传POST /api/file/upload/image反向代理到 8081解析 JWT、检查类型和大小POST /api/validate-imageQwen-VL 生成描述Qwen Plus 判断业务范围allowtrue/false返回业务错误上传失败上传原图返回 OSS URL写入 files 表POST /api/upload-image通过 URL 下载图片生成描述与 CLIP 向量保存图片 AI 索引返回 imageId、描述和向量维度返回错误或调用异常ResultFileUploadResponse返回上传结果显示上传结果这条链路体现了 AIWear 的核心设计Java 控制业务顺序和数据归属Python提供模型能力MySQL保存可查询的业务事实Redis保存短期状态和 AI 索引OSS承担文件存储。Agent、Skill 和 Tool 在项目里分别做什么这三个概念容易混在一起在 AIWear 中可以这样理解Tool 是可执行函数。edit_image_tool和merge_image_tool真正读取图片并调用图像模型。Skill 是写给 Agent 的操作说明。SKILL.md描述何时使用工具及输入输出要求。Agent 是决策和编排者。它阅读当前输入、工具参数和 Skill 说明选择工具并组织调用参数。Java 传给 Python 的不是一句模糊聊天内容而是图片文件和明确的操作指令。Python 再将临时文件路径组织成 Promptinstruction: 把衣服改成蓝色 image_path: /tmp/aiwear_xxx.binAgent 根据存在的字段选择edit_image_tool。两张图片时字段变为image_path1和image_path2Agent 则选择merge_image_tool。数据分别保存在哪里数据保存位置原因用户、文件、操作记录MySQL结构固定、需要持久化查询邮箱验证码Redis有过期时间验证后立即删除JWT 缓存Redis支持复用、登出和服务端失效图片描述与向量Redis当前版本便于快速读取和相似度计算原图与处理结果OSS文件体积大不适合直接放入数据库前端静态资源Nginx 挂载目录直接通过 80 端口访问CLIP 模型Python 服务本地目录避免每次启动从网络下载我从项目中真正学到了什么项目最有价值的部分不是记住某个注解或某条 Docker 命令而是理解不同组件如何共同完成一条业务链路前端请求格式必须和 Java Controller 的参数来源一致。Controller 只负责接口边界Service 负责业务编排。Java 与 Python 的连接本质上是一组明确的 HTTP 契约。模型生成的结果仍然需要权限校验、持久化和错误处理。Redis、MySQL 和 OSS 解决的是不同类型的数据问题。本地可运行不等于服务器可部署环境变量、网络边界和进程管理同样重要。当前实现边界为了准确描述项目当前版本仍有以下可改进点文搜图目前使用图片描述的关键词匹配可升级为 CLIP 文本向量或独立 Embedding 服务。Python 使用 Flask 开发服务器正式部署可改为 Gunicorn 等 WSGI 服务。Java 跨服务调用使用直接创建的RestTemplate可增加统一超时、重试和错误映射。自动化测试覆盖较少需要补充 Service、Controller 和 Python API 测试。Java 与 Python 目前使用后台命令运行可改为 systemd 或容器化服务。代码中部分早期中文文本存在编码显示问题需要统一为 UTF-8。