黑马点评项目升级集成OFA-Image-Caption为商家菜品图片自动生成营销文案最近在跟一些做本地生活服务的朋友聊天他们都在头疼一件事每天要上架新菜品光是给每道菜写介绍文案就够呛。既要描述准确又要写得诱人还得带点营销味道这活儿费时费力还特别考验文笔。这让我想起了之前研究过的一个经典学习项目——黑马点评。它本身是一个功能完整的仿美团应用涵盖了用户、商户、订单、优惠券等核心模块。如果能在这样一个成熟的Java微服务架构里为商家增加一个“AI小助手”让系统自动看图写文案那该多实用今天我们就来聊聊怎么给“黑马点评”这个项目做个智能升级。核心思路很简单商家上传一张美味的菜品图片系统后台自动调用一个能“看懂”图片的AI模型我们选用OFA-Image-Caption让它生成一段基础的菜品描述。然后我们再把这个描述塞进预设好的营销文案模板里一份既准确又吸引人的菜品介绍就自动生成了。整个过程商家只需要点一下上传按钮。下面我就带你一步步看看怎么在现有的Spring Cloud微服务架构里平滑地集成这个AI能力。1. 为什么选择OFA模型与整体方案设计首先得解决“用什么”和“怎么接”的问题。市面上能描述图片的AI模型不少为什么选OFAOne-For-All主要是因为它“轻量”且“全能”。OFA是一个统一的多模态预训练模型一个模型就能干好多事其中就包括给图片生成文字说明Image Captioning。相比一些动辄几十G的庞然大物经过裁剪的OFA模型在精度和速度上取得了不错的平衡特别适合我们这种需要快速响应、并发处理的业务场景。接下来看整体方案。黑马点评原有的架构已经很清晰了比如有shop-service店铺服务、dish-service菜品服务。我们不想把AI代码硬塞进这些业务服务里那样会搞得代码很乱也不好维护。最好的办法是新增一个独立的AI微服务。具体流程是这样的商家在后台管理页面上传菜品图片。原有的dish-service接收到图片后并不自己处理而是通过Feign客户端调用我们新建的ai-service。ai-service收到调用加载OFA模型对图片进行分析生成一句如“一盘色泽红亮的红烧肉肥瘦相间配有翠绿葱花”这样的描述。接着ai-service内部再有一个简单的文案组装器把上面这句描述嵌入到像“今日招牌推荐{菜品描述}精选食材匠心烹制快来尝鲜吧”这样的模板里。最终生成的完整营销文案再返回给dish-service由它保存到菜品信息中。这样做的好处是解耦。AI部分独立成服务以后如果想换模型、加功能比如识别菜品食材、估算热量都在这个服务里折腾不会影响核心业务。2. 搭建AI微服务让OFA模型跑起来理论说完了开始动手。我们先在工程里新建一个模块叫hmdp-ai-service。2.1 服务基础框架搭建这一步和创建其他Spring Boot微服务没啥区别。在pom.xml里引入必要的依赖除了Spring Cloud那些常规的关键是深度学习框架。我们选用PyTorch因为OFA模型原生基于它。!-- 示例依赖版本需根据实际情况调整 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-openfeign/artifactId /dependency !-- 使用torch-java进行推理 -- dependency groupIdorg.bytedeco/groupId artifactIdpytorch-platform/artifactId version1.12.1-1.5.8/version /dependency然后配置application.yml注册到Nacos设置好端口。这里有个关键点OFA模型文件有点大我们需要在配置里指定一下模型下载或存放的路径。server: port: 8088 spring: application: name: ai-service cloud: nacos: server-addr: localhost:8848 ofa: model: # 指定OFA模型文件的本地路径或下载URL path: classpath:models/ofa_image_caption.pt2.2 核心功能图片描述生成接口服务搭好了现在写核心功能。我们创建一个ImageCaptionService它的任务就是加载模型并提供一个生成描述的方法。这里有个工程上的小细节直接在生产环境用Python加载模型再通过HTTP调用的方式在Java微服务体系里有点别扭。更优雅的方式是使用torch-java这类库在JVM内直接进行推理或者将模型封装为gRPC服务。为了简化演示我们假设已经将OFA模型转换为了TorchScript格式可以在Java中直接加载。Service Slf4j public class ImageCaptionService { private Module model; private Transform transform; PostConstruct public void init() throws Exception { log.info(开始加载OFA图像描述模型...); // 1. 加载TorchScript模型 model Module.load(ResourceUtils.getFile(classpath:models/ofa_caption.pt).getPath()); model.eval(); // 2. 初始化图像预处理变换需要与模型训练时一致 transform new Transform(); // 这里应包含Resize, CenterCrop, ToTensor, Normalize等操作 // 简化示例实际需按OFA要求实现 log.info(OFA模型加载完毕。); } public String generateCaption(MultipartFile imageFile) throws IOException { // 1. 将上传的图片文件转换为模型需要的Tensor格式 BufferedImage img ImageIO.read(imageFile.getInputStream()); // 使用transform对img进行预处理得到inputTensor // IValue inputTensor transform.apply(img); // 2. 执行模型推理此处为伪代码实际API调用取决于torch-java // ListIValue inputs Arrays.asList(inputTensor, IValue.from(图片里有什么)); // IValue output model.forward(inputs); // String rawCaption output.toStr(); // 为了演示我们模拟一个返回结果 String rawCaption simulateOFACaption(img); // 3. 对原始描述进行后处理比如修剪多余空格 return rawCaption.trim(); } // 模拟OFA生成描述的逻辑 private String simulateOFACaption(BufferedImage img) { // 在实际项目中这里就是模型的前向传播计算 // 此处返回模拟数据用于演示流程 return 一盘热气腾腾的红烧排骨色泽酱红油亮旁边点缀着少许白芝麻和葱花。; } }然后我们提供一个简单的REST接口给内部调用RestController RequestMapping(/ai) Slf4j public class AIController { Autowired private ImageCaptionService captionService; PostMapping(/generate-dish-desc) public ApiResultString generateDishDescription(RequestParam(image) MultipartFile imageFile) { try { log.info(收到图片描述生成请求文件大小{}, imageFile.getSize()); String baseDescription captionService.generateCaption(imageFile); // 接下来进行文案模板合成 String marketingCopy applyMarketingTemplate(baseDescription); return ApiResult.ok(marketingCopy); } catch (Exception e) { log.error(生成菜品描述失败, e); return ApiResult.fail(AI文案生成服务暂时不可用); } } private String applyMarketingTemplate(String baseDesc) { // 这里可以设计更复杂的模板逻辑比如随机选择模板或根据菜品类型选择 String[] templates { **匠心推荐** {description} 传统工艺慢火精炖每一口都是幸福的味道。, **吃货必点** 瞧这{dish}{description} 食材新鲜味道正宗错过后悔, **今日特供** {description} 由主厨亲自掌勺限量供应先到先得哦~ }; Random rand new Random(); String template templates[rand.nextInt(templates.length)]; // 简单替换实际可更智能如提取“红烧排骨”作为dish return template.replace({description}, baseDesc).replace({dish}, 这道菜); } }这样我们的AI微服务就有了一个核心接口/ai/generate-dish-desc。3. 业务服务改造让菜品服务学会“求助”AI服务准备就绪现在需要让原来的菜品服务知道怎么去调用它。我们在hmdp-dish-service里进行改造。首先声明一个Feign客户端指向我们刚创建的ai-service。FeignClient(name ai-service, path /ai) public interface AIServiceClient { PostMapping(value /generate-dish-desc, consumes MediaType.MULTIPART_FORM_DATA_VALUE) ApiResultString generateDishDescription(RequestPart(image) MultipartFile imageFile); }然后在商家上传菜品图片的业务逻辑里加入调用AI的步骤。通常上传图片的代码会在DishServiceImpl这样的类里。Service Slf4j public class DishServiceImpl implements IDishService { Autowired private AIServiceClient aiServiceClient; Autowired private StringRedisTemplate stringRedisTemplate; Override Transactional public DishDTO createDishWithImage(CreateDishRequest request, MultipartFile imageFile) { // 1. 保存图片到OSS或本地获取图片URL (原有逻辑) String imageUrl fileStorageService.upload(imageFile); // 2. 【新增】调用AI服务生成营销文案 String aiDescription null; try { ApiResultString result aiServiceClient.generateDishDescription(imageFile); if (result ! null result.isSuccess()) { aiDescription result.getData(); log.info(AI生成菜品描述成功{}, aiDescription); } } catch (Exception e) { log.warn(调用AI服务失败将使用默认描述, e); // 失败降级使用菜品名称作为基础描述 aiDescription 美味的 request.getName(); } // 3. 创建菜品实体保存图片URL和AI生成的描述 Dish dish new Dish(); dish.setName(request.getName()); dish.setPrice(request.getPrice()); dish.setImageUrl(imageUrl); dish.setDescription(aiDescription); // 存入AI生成的文案 dishMapper.insert(dish); // 4. 同步缓存等后续操作... return convertToDTO(dish); } }你看改造点非常清晰在保存图片之后、创建菜品实体之前插入一步Feign调用。并且我们做了一个简单的降级处理如果AI服务暂时不可用就回退到使用菜品名称生成一个最基础的描述保证主流程不被阻塞。4. 效果展示与优化思考功能集成完了实际用起来怎么样假设商家上传了一张水煮鱼的图片。原始图片一张拍得很有食欲的水煮鱼照片红油鲜亮鱼肉雪白上面铺着辣椒和花椒。OFA生成的基础描述“一碗铺满红色辣椒和花椒的水煮鱼片鱼肉白皙汤汁红亮看起来非常麻辣鲜香。”系统合成的最终营销文案“吃货必点瞧这水煮鱼一碗铺满红色辣椒和花椒的水煮鱼片鱼肉白皙汤汁红亮看起来非常麻辣鲜香。食材新鲜味道正宗错过后悔”这个文案比起“好吃的水煮鱼”这种干巴巴的描述是不是生动多了它自动抓住了图片里的核心元素辣椒、花椒、鱼肉、汤汁和感官关键词麻辣鲜香再套上营销话术一下子就能吸引顾客眼球。当然这只是第一个可用的版本。真正用到生产环境还有不少可以优化和深入的地方性能与稳定性模型推理是比较耗资源的。可以考虑引入简单的本地缓存对同一张图片通过MD5判断短时间内不再重复调用AI。或者使用消息队列将生成任务异步化避免同步调用阻塞上传接口。文案模板多样化现在的模板是随机选的可以做得更智能。比如识别出是“甜品”就用更温馨的文案模板是“烧烤”就用更豪爽的模板。甚至可以结合季节、节日动态更换模板。描述精细化OFA生成的是通用描述。我们可以尝试用大语言模型LLM对这段描述进行二次加工比如“请将这段菜品描述改写成更简短、更有网络传播感的文案并加入两个相关的热门话题标签。”这样生成的文案会更贴合社交媒体传播。成本控制AI API调用通常按次或按token收费。可以为每个商家设置每日或每月的免费生成额度超出部分需要购买这样既能推广功能又能控制成本。5. 总结给“黑马点评”加上自动生成菜品文案的功能整个过程就像给一个精密的机械手表增加了一个智能模块。我们通过新增一个独立的ai-service完美地将AI能力嵌入到现有的Spring Cloud微服务体系中没有对原有核心业务代码造成大的侵入。这个功能的价值是实实在在的。对于平台上的中小商家来说他们可能不擅长文案写作但这个AI小助手能帮他们快速生成专业、诱人的菜品介绍提升店铺的运营效率和形象。对于平台而言提供了增值服务增加了用户粘性。技术实现上从模型选型、服务设计、接口对接到业务集成每一步都是在平衡效果、性能和开发复杂度。现在这个方案是一个坚实的起点它已经能跑起来并产生价值。后续的优化比如异步处理、模板策略、结合LLM等都可以在这个架构上平滑地迭代。如果你也在做类似的应用不妨试试这个思路从一个小而实用的功能点切入让AI真正为你的业务赋能。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。
黑马点评项目升级:集成OFA-Image-Caption为商家菜品图片自动生成营销文案
黑马点评项目升级集成OFA-Image-Caption为商家菜品图片自动生成营销文案最近在跟一些做本地生活服务的朋友聊天他们都在头疼一件事每天要上架新菜品光是给每道菜写介绍文案就够呛。既要描述准确又要写得诱人还得带点营销味道这活儿费时费力还特别考验文笔。这让我想起了之前研究过的一个经典学习项目——黑马点评。它本身是一个功能完整的仿美团应用涵盖了用户、商户、订单、优惠券等核心模块。如果能在这样一个成熟的Java微服务架构里为商家增加一个“AI小助手”让系统自动看图写文案那该多实用今天我们就来聊聊怎么给“黑马点评”这个项目做个智能升级。核心思路很简单商家上传一张美味的菜品图片系统后台自动调用一个能“看懂”图片的AI模型我们选用OFA-Image-Caption让它生成一段基础的菜品描述。然后我们再把这个描述塞进预设好的营销文案模板里一份既准确又吸引人的菜品介绍就自动生成了。整个过程商家只需要点一下上传按钮。下面我就带你一步步看看怎么在现有的Spring Cloud微服务架构里平滑地集成这个AI能力。1. 为什么选择OFA模型与整体方案设计首先得解决“用什么”和“怎么接”的问题。市面上能描述图片的AI模型不少为什么选OFAOne-For-All主要是因为它“轻量”且“全能”。OFA是一个统一的多模态预训练模型一个模型就能干好多事其中就包括给图片生成文字说明Image Captioning。相比一些动辄几十G的庞然大物经过裁剪的OFA模型在精度和速度上取得了不错的平衡特别适合我们这种需要快速响应、并发处理的业务场景。接下来看整体方案。黑马点评原有的架构已经很清晰了比如有shop-service店铺服务、dish-service菜品服务。我们不想把AI代码硬塞进这些业务服务里那样会搞得代码很乱也不好维护。最好的办法是新增一个独立的AI微服务。具体流程是这样的商家在后台管理页面上传菜品图片。原有的dish-service接收到图片后并不自己处理而是通过Feign客户端调用我们新建的ai-service。ai-service收到调用加载OFA模型对图片进行分析生成一句如“一盘色泽红亮的红烧肉肥瘦相间配有翠绿葱花”这样的描述。接着ai-service内部再有一个简单的文案组装器把上面这句描述嵌入到像“今日招牌推荐{菜品描述}精选食材匠心烹制快来尝鲜吧”这样的模板里。最终生成的完整营销文案再返回给dish-service由它保存到菜品信息中。这样做的好处是解耦。AI部分独立成服务以后如果想换模型、加功能比如识别菜品食材、估算热量都在这个服务里折腾不会影响核心业务。2. 搭建AI微服务让OFA模型跑起来理论说完了开始动手。我们先在工程里新建一个模块叫hmdp-ai-service。2.1 服务基础框架搭建这一步和创建其他Spring Boot微服务没啥区别。在pom.xml里引入必要的依赖除了Spring Cloud那些常规的关键是深度学习框架。我们选用PyTorch因为OFA模型原生基于它。!-- 示例依赖版本需根据实际情况调整 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-openfeign/artifactId /dependency !-- 使用torch-java进行推理 -- dependency groupIdorg.bytedeco/groupId artifactIdpytorch-platform/artifactId version1.12.1-1.5.8/version /dependency然后配置application.yml注册到Nacos设置好端口。这里有个关键点OFA模型文件有点大我们需要在配置里指定一下模型下载或存放的路径。server: port: 8088 spring: application: name: ai-service cloud: nacos: server-addr: localhost:8848 ofa: model: # 指定OFA模型文件的本地路径或下载URL path: classpath:models/ofa_image_caption.pt2.2 核心功能图片描述生成接口服务搭好了现在写核心功能。我们创建一个ImageCaptionService它的任务就是加载模型并提供一个生成描述的方法。这里有个工程上的小细节直接在生产环境用Python加载模型再通过HTTP调用的方式在Java微服务体系里有点别扭。更优雅的方式是使用torch-java这类库在JVM内直接进行推理或者将模型封装为gRPC服务。为了简化演示我们假设已经将OFA模型转换为了TorchScript格式可以在Java中直接加载。Service Slf4j public class ImageCaptionService { private Module model; private Transform transform; PostConstruct public void init() throws Exception { log.info(开始加载OFA图像描述模型...); // 1. 加载TorchScript模型 model Module.load(ResourceUtils.getFile(classpath:models/ofa_caption.pt).getPath()); model.eval(); // 2. 初始化图像预处理变换需要与模型训练时一致 transform new Transform(); // 这里应包含Resize, CenterCrop, ToTensor, Normalize等操作 // 简化示例实际需按OFA要求实现 log.info(OFA模型加载完毕。); } public String generateCaption(MultipartFile imageFile) throws IOException { // 1. 将上传的图片文件转换为模型需要的Tensor格式 BufferedImage img ImageIO.read(imageFile.getInputStream()); // 使用transform对img进行预处理得到inputTensor // IValue inputTensor transform.apply(img); // 2. 执行模型推理此处为伪代码实际API调用取决于torch-java // ListIValue inputs Arrays.asList(inputTensor, IValue.from(图片里有什么)); // IValue output model.forward(inputs); // String rawCaption output.toStr(); // 为了演示我们模拟一个返回结果 String rawCaption simulateOFACaption(img); // 3. 对原始描述进行后处理比如修剪多余空格 return rawCaption.trim(); } // 模拟OFA生成描述的逻辑 private String simulateOFACaption(BufferedImage img) { // 在实际项目中这里就是模型的前向传播计算 // 此处返回模拟数据用于演示流程 return 一盘热气腾腾的红烧排骨色泽酱红油亮旁边点缀着少许白芝麻和葱花。; } }然后我们提供一个简单的REST接口给内部调用RestController RequestMapping(/ai) Slf4j public class AIController { Autowired private ImageCaptionService captionService; PostMapping(/generate-dish-desc) public ApiResultString generateDishDescription(RequestParam(image) MultipartFile imageFile) { try { log.info(收到图片描述生成请求文件大小{}, imageFile.getSize()); String baseDescription captionService.generateCaption(imageFile); // 接下来进行文案模板合成 String marketingCopy applyMarketingTemplate(baseDescription); return ApiResult.ok(marketingCopy); } catch (Exception e) { log.error(生成菜品描述失败, e); return ApiResult.fail(AI文案生成服务暂时不可用); } } private String applyMarketingTemplate(String baseDesc) { // 这里可以设计更复杂的模板逻辑比如随机选择模板或根据菜品类型选择 String[] templates { **匠心推荐** {description} 传统工艺慢火精炖每一口都是幸福的味道。, **吃货必点** 瞧这{dish}{description} 食材新鲜味道正宗错过后悔, **今日特供** {description} 由主厨亲自掌勺限量供应先到先得哦~ }; Random rand new Random(); String template templates[rand.nextInt(templates.length)]; // 简单替换实际可更智能如提取“红烧排骨”作为dish return template.replace({description}, baseDesc).replace({dish}, 这道菜); } }这样我们的AI微服务就有了一个核心接口/ai/generate-dish-desc。3. 业务服务改造让菜品服务学会“求助”AI服务准备就绪现在需要让原来的菜品服务知道怎么去调用它。我们在hmdp-dish-service里进行改造。首先声明一个Feign客户端指向我们刚创建的ai-service。FeignClient(name ai-service, path /ai) public interface AIServiceClient { PostMapping(value /generate-dish-desc, consumes MediaType.MULTIPART_FORM_DATA_VALUE) ApiResultString generateDishDescription(RequestPart(image) MultipartFile imageFile); }然后在商家上传菜品图片的业务逻辑里加入调用AI的步骤。通常上传图片的代码会在DishServiceImpl这样的类里。Service Slf4j public class DishServiceImpl implements IDishService { Autowired private AIServiceClient aiServiceClient; Autowired private StringRedisTemplate stringRedisTemplate; Override Transactional public DishDTO createDishWithImage(CreateDishRequest request, MultipartFile imageFile) { // 1. 保存图片到OSS或本地获取图片URL (原有逻辑) String imageUrl fileStorageService.upload(imageFile); // 2. 【新增】调用AI服务生成营销文案 String aiDescription null; try { ApiResultString result aiServiceClient.generateDishDescription(imageFile); if (result ! null result.isSuccess()) { aiDescription result.getData(); log.info(AI生成菜品描述成功{}, aiDescription); } } catch (Exception e) { log.warn(调用AI服务失败将使用默认描述, e); // 失败降级使用菜品名称作为基础描述 aiDescription 美味的 request.getName(); } // 3. 创建菜品实体保存图片URL和AI生成的描述 Dish dish new Dish(); dish.setName(request.getName()); dish.setPrice(request.getPrice()); dish.setImageUrl(imageUrl); dish.setDescription(aiDescription); // 存入AI生成的文案 dishMapper.insert(dish); // 4. 同步缓存等后续操作... return convertToDTO(dish); } }你看改造点非常清晰在保存图片之后、创建菜品实体之前插入一步Feign调用。并且我们做了一个简单的降级处理如果AI服务暂时不可用就回退到使用菜品名称生成一个最基础的描述保证主流程不被阻塞。4. 效果展示与优化思考功能集成完了实际用起来怎么样假设商家上传了一张水煮鱼的图片。原始图片一张拍得很有食欲的水煮鱼照片红油鲜亮鱼肉雪白上面铺着辣椒和花椒。OFA生成的基础描述“一碗铺满红色辣椒和花椒的水煮鱼片鱼肉白皙汤汁红亮看起来非常麻辣鲜香。”系统合成的最终营销文案“吃货必点瞧这水煮鱼一碗铺满红色辣椒和花椒的水煮鱼片鱼肉白皙汤汁红亮看起来非常麻辣鲜香。食材新鲜味道正宗错过后悔”这个文案比起“好吃的水煮鱼”这种干巴巴的描述是不是生动多了它自动抓住了图片里的核心元素辣椒、花椒、鱼肉、汤汁和感官关键词麻辣鲜香再套上营销话术一下子就能吸引顾客眼球。当然这只是第一个可用的版本。真正用到生产环境还有不少可以优化和深入的地方性能与稳定性模型推理是比较耗资源的。可以考虑引入简单的本地缓存对同一张图片通过MD5判断短时间内不再重复调用AI。或者使用消息队列将生成任务异步化避免同步调用阻塞上传接口。文案模板多样化现在的模板是随机选的可以做得更智能。比如识别出是“甜品”就用更温馨的文案模板是“烧烤”就用更豪爽的模板。甚至可以结合季节、节日动态更换模板。描述精细化OFA生成的是通用描述。我们可以尝试用大语言模型LLM对这段描述进行二次加工比如“请将这段菜品描述改写成更简短、更有网络传播感的文案并加入两个相关的热门话题标签。”这样生成的文案会更贴合社交媒体传播。成本控制AI API调用通常按次或按token收费。可以为每个商家设置每日或每月的免费生成额度超出部分需要购买这样既能推广功能又能控制成本。5. 总结给“黑马点评”加上自动生成菜品文案的功能整个过程就像给一个精密的机械手表增加了一个智能模块。我们通过新增一个独立的ai-service完美地将AI能力嵌入到现有的Spring Cloud微服务体系中没有对原有核心业务代码造成大的侵入。这个功能的价值是实实在在的。对于平台上的中小商家来说他们可能不擅长文案写作但这个AI小助手能帮他们快速生成专业、诱人的菜品介绍提升店铺的运营效率和形象。对于平台而言提供了增值服务增加了用户粘性。技术实现上从模型选型、服务设计、接口对接到业务集成每一步都是在平衡效果、性能和开发复杂度。现在这个方案是一个坚实的起点它已经能跑起来并产生价值。后续的优化比如异步处理、模板策略、结合LLM等都可以在这个架构上平滑地迭代。如果你也在做类似的应用不妨试试这个思路从一个小而实用的功能点切入让AI真正为你的业务赋能。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。