[大模型应用].Net下接入VLM多模态模型分析

目录

一、目的

二、解决方案

[2.1 环境说明](#2.1 环境说明)

[2.2 功能实现流程](#2.2 功能实现流程)

[2.3 代码实现](#2.3 代码实现)

[2.3.1 提示词说明](#2.3.1 提示词说明)

[2.3.2 AI接口请求实体](#2.3.2 AI接口请求实体)

[2.3.3 AI接口返回实体](#2.3.3 AI接口返回实体)

三、文章总结


一、目的

目前在开发一个健康养生类产品时,考虑用户可以上传自身的病例报告,通过调用AI大模型给出病例报告解读。在实现的过程中碰到了一些问题,最后想着记录一下,避免大家以后踩坑。

首先理解一下什么是多模态模型,多模态模型是能够同时处理多种模态信息(文本、图像、音频、视频)的大语言模型。SiliconFlow 提供了多个支持不同模态组合的强大模型,能够:

  1. 视觉理解:理解图片内容、OCR、图像描述
  2. 视频分析:提取视频帧、理解视频内容、动作识别
  3. 音频处理:语音识别、音频内容分析
  4. 多模态融合:同时处理多种媒体类型的综合分析

在考虑使用VLM模型解决自己问题时,最开始第一个考虑是使用DeepSeek的开放接口,最开始通过网页版尝试分析图象,并根据提示词给出相关结果,发现是可行的且返回结果还不错。

但后续发现截止文章发布的时间点下,他们的API开放平台中的相关接口目前不支持多模态模型。DeepSeek本身使用VL2和OCR的视觉处理模型,但是在API开放平台中未开放,这个大家要注意。

最后对比硅基流动和百度千帆,选择了硅基流动。两者区别倒是不大,按时尝试了下几个视觉模型的返回结果,感觉硅基流动下处理时间和分析内容都还不错。当然主要是白嫖了14块钱。

对硅基流动感兴趣可以看看这个博文:【大模型应用】初学:硅基流动DeepSeekAI+Cherry-studio使用

二、解决方案

2.1 环境说明

语言:C#

技术框架:.Net6 Asp.Net WebApi

第三方接口:https://api.siliconflow.cn/v1/chat/completions

2.2 功能实现流程

这是一个医学报告图像分析服务,通过调用DeepSeek VL2多模态AI模型,对用户上传的医学报告图像进行智能分析和解读。

1、输入处理

  • 参数验证‌:确保imageBase64参数不为空
  • 格式兼容‌:支持原始base64字符串和完整data URI格式
  • 自动补全‌:对原始base64数据自动添加data URI前缀

2、分析流程设计

系统按照严格的医学报告分析步骤:

  1. 图像类型识别‌:判断是否为医学报告
  2. 关键信息提取‌:患者信息、检查项目、指标数值
  3. 专业解读‌:异常指标说明、健康风险评估

3、输出处理

标准化输出‌:统一的JSON响应格式

2.3 代码实现

以下是一个接口请示例中在进行Http请求时需要请求头信息,同时需要前往硅基流动平台获取ApiKey,接口地址:https://api.siliconflow.cn/v1/chat/completions

cs 复制代码
_httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
 _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {ApiKey}");
cs 复制代码
   /// <summary>
   /// 图像AI分析接口
   /// </summary>
   /// <param name="imageBase64"></param>
   /// <param name="userText"></param>
   /// <returns></returns>
   public async Task<string> SendChatWithImageAsync(string imageBase64, string userText = "请帮我描述这张图片的内容")
   {
       if (string.IsNullOrWhiteSpace(imageBase64))
       {
           return "Error: imageBase64 is required";
       }

       // 兼容传入 raw base64 或完整 data URI
       var trimmed = imageBase64.Trim();
       var dataUri = trimmed.StartsWith("data:image", StringComparison.OrdinalIgnoreCase)
           ? trimmed
           : $"data:image/jpeg;base64,{trimmed}";

       // 构造图文混合消息
       var Sys_Content = new List<ContentItem>
                   {
                       new ContentItem { Type = "text", Text = "你是一个专业的医学报告分析助手。" }
                   };
       var User_Content = new List<ContentItem>
                   {
                       new ContentItem { Type = "image_url", ImageUrl = new ImageUrl { Url = dataUri } },
                       new ContentItem { Type = "text", Text = "输入相关提示词内容" }
                   };
      
       var requestBody = new ChatRequestSModel
       {
           Model = "deepseek-ai/deepseek-vl2",
           Messages = new List<Message>
           {
               new Message
               {
                   Role = "system",
                   Content = Sys_Content
               },
               new Message
               {
                   Role = "user",
                   Content = User_Content
               }
           },
           MaxTokens = 4096,
           ResponseFormat = new ResponseFormat { Type = "json_object" },
           Stream = false
       };

       // 按 reqinfo 结构要求,序列化请求体
       var json = JsonSerializer.Serialize(requestBody, new JsonSerializerOptions { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
       var content = new StringContent(json, Encoding.UTF8, "application/json");

       try
       {
           var response = await _httpClient.PostAsync(ApiUrl, content);
           response.EnsureSuccessStatusCode();

           // 获取响应并反序列化为
           var responseBytes = await response.Content.ReadAsByteArrayAsync();
           var responseContent = Encoding.UTF8.GetString(responseBytes);

           // 提取 assistant 的 message.content
           ChatResponseSModel? resp = null;
           try
           {
               resp = JsonSerializer.Deserialize<ChatResponseSModel>(responseContent, new JsonSerializerOptions { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
           }
           catch
           {
               // 反序列化失败则直接返回原始字符串
               return responseContent;
           }

           if (resp?.Choices != null && resp.Choices.Count > 0 && resp.Choices[0]?.Message != null)
           {
               // content 本身就是一个 JSON 字符串(包含 success/message/content),直接返回该字符串
               return resp.Choices[0].Message.Content ?? string.Empty;
           }

           // 未取到有效内容时返回原始响应,避免信息丢失
           return responseContent;
       }
       catch (Exception ex)
       {
           return $"Error: {ex.Message}";
       }
   }

2.3.1 提示词说明

多模态信息在请求时用户可以输出相关提示词,让AI来指导如何对于上传文件进行处理,以下是一个参考的提示词内容。

提示词:

你是一个专业的医学报告分析助手。请按照以下步骤处理用户上传的图像:1. ‌图像类型识别‌:首先判断图像是否为医学报告(如CT报告、血液检验单、超声图像等)。若非医学报告,直接返回:{\"success\": false, \"message\": \"请传递医学报告\", \"content\": \"\"} 2. ‌内容分析‌:若确认为医学报告,则提取以下关键信息: - 患者基本信息(姓名、年龄、性别) - 检查项目(如MRI、X光、血液分析) - 主要指标与数值 - 医生结论或建议3. ‌解读方案‌:基于提取内容,生成通俗易懂的解读: - 异常指标说明及其临床意义 - 可能关联的健康风险 - 建议的后续行动(如复检、专科就诊)4. ‌输出格式‌:严格使用以下JSON结构: {\"success\": true, \"message\": \"分析成功\", \"content\": \"报告解读内容以Html格式返回\"} 注意事项‌:- 若图像模糊或信息不全,在content中注明"部分数据无法识别" - 避免提供未经证实的诊断,仅基于报告客观解读 - 所有医疗建议需标注"仅供参考,具体诊疗请遵医嘱"

2.3.2 AI接口请求实体

cs 复制代码
    public class ChatRequestSModel
    {
        /// <summary>
        /// 模型名称,例如:deepseek-chat
        /// </summary>
        public string Model { get; set; }

        /// <summary>
        /// 对话消息列表
        /// </summary>
        public List<Message> Messages { get; set; } = new List<Message>();

        /// <summary>
        /// 最大 tokens 限制
        /// </summary>
        public int MaxTokens { get; set; } = 1024;

        /// <summary>
        /// 响应格式
        /// </summary>
        public ResponseFormat ResponseFormat { get; set; }


        /// <summary>
        /// 是否流式输出
        /// </summary>
        public bool Stream { get; set; } = false;
    }

    /// <summary>
    /// 单条对话消息
    /// </summary>
    public class Message
    {
        /// <summary>
        /// 角色,如:user、assistant
        /// </summary>
        [JsonPropertyName("role")]
        public string Role { get; set; }

        /// <summary>
        /// 多段内容(文本、图片等)
        /// </summary>
        [JsonPropertyName("content")]
        public List<ContentItem> Content { get; set; } 
    }

    /// <summary>
    /// 图文内容项
    /// </summary>
    public class ContentItem
    {
        /// <summary>
        /// 内容类型:text 或 image_url
        /// </summary>
        [JsonPropertyName("type")]
        public string Type { get; set; }

        /// <summary>
        /// 文本内容(当 type = text 时使用)
        /// </summary>
        [JsonPropertyName("text")]
        public string Text { get; set; }

        /// <summary>
        /// 图片地址或数据(当 type = image 时使用)
        /// </summary>
        [JsonPropertyName("image_url")]
        public ImageUrl ImageUrl { get; set; }
    }

    /// <summary>
    /// 图片地址或 Base64 数据
    /// </summary>
    public class ImageUrl
    {
        /// <summary>
        /// 图片 URL(与 data 二选一)
        /// </summary>
        [JsonPropertyName("url")]
        public string Url { get; set; }

        [JsonPropertyName("detail")]
        public string Detail { get; set; }
    }

    /// <summary>
    /// 响应格式定义
    /// </summary>
    public class ResponseFormat
    {
        /// <summary>
        /// 类型,如:text
        /// </summary>
        [JsonPropertyName("type")]
        public string Type { get; set; }
    }

2.3.3 AI接口返回实体

cs 复制代码
  public class ChatResponseSModel
  {
      [JsonPropertyName("id")] public string Id { get; set; }
      [JsonPropertyName("object")] public string Object { get; set; }
      [JsonPropertyName("created")] public long Created { get; set; }
      [JsonPropertyName("model")] public string Model { get; set; }

      [JsonPropertyName("choices")] public List<Choice> Choices { get; set; } = new List<DeepSeekChoice>();

      [JsonPropertyName("usage")] public Usage Usage { get; set; }

      [JsonPropertyName("system_fingerprint")] public string SystemFingerprint { get; set; }
  }

  public class Choice
  {
      [JsonPropertyName("index")] public int Index { get; set; }
      [JsonPropertyName("message")] public DeepSeekMessageResp Message { get; set; }
      [JsonPropertyName("finish_reason")] public string FinishReason { get; set; }
  }

  public class MessageResp
  {
      [JsonPropertyName("role")] public string Role { get; set; }
      // 注意:在响应中 content 是一个字符串(JSON 字符串),与请求时的多段 content 结构不同
      [JsonPropertyName("content")] public string Content { get; set; }
  }

  public class Usage
  {
      [JsonPropertyName("prompt_tokens")] public int PromptTokens { get; set; }
      [JsonPropertyName("completion_tokens")] public int CompletionTokens { get; set; }
      [JsonPropertyName("total_tokens")] public int TotalTokens { get; set; }
  }

三、文章总结

以上多模态的使用是通过Http直接通过接口请求,没有使用通用的OpenApi标准方式调用,但是内部核心逻辑是一致。在使用过程也是踩坑不少。

相关推荐
胡八一3 小时前
Windows 7 运行 .NET 应用时报错“hostfxr.dll 加载失败 (HRESULT: 0x80070057)”的彻底解决指南
windows·.net
橙武低代码19 小时前
业务流低代码平台:从理念到实战
android·低代码·ai编程
webmote21 小时前
使用Claude Code进行编程——国内用户使用指南
ai编程·claude·代理·码农·claude code
却尘21 小时前
🚀 MCP基础完全上手指南:让Claude像开挂一样调用外部工具
aigc·ai编程·mcp
撒币使我快乐21 小时前
Generate Cursor Rules指令消失后的替代方案
ai编程·cursor
Violet_YSWY21 小时前
.net讲解
.net
有意义1 天前
Vibe Coding:人机共生时代的开发革命 —— 从概念到 Chrome 扩展实战
前端·ai编程·vibecoding
步步为营DotNet1 天前
.NET高性能内存管理
.net