services.AddOpenAIChatCompletion(
        modelId: "qwen2.5-vl-32b-instruct",
        endpoint: new Uri("https://dashscope.aliyuncs.com/compatible-mode/v1"),
        apiKey: "sk-"
     );

使用SemanticKernel 

public classLlmAIResponse{

    [JsonPropertyName("gj")]
    public string 国家 { get; set; }
    [JsonPropertyName("zl")]
    public string 重量 { get; set; }
    [JsonPropertyName("lf")]
    public string 立方 { get; set; }    
    [JsonPropertyName("ckdm")]
    public string 仓库代码 { get; set; }
    [JsonPropertyName("yb")]
    public string 邮编 { get; set; }
    [JsonPropertyName("er")]
    public string? Error { get; set; }
}

服务部分
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;

    public async Task<IEnumerable<classLlmAIResponse>?> GetAnnotationAsync(string message)
    {
 

        var chatHistory = new ChatHistory();
        chatHistory.AddSystemMessage(
            """
            你是一个AI信息提取助手,需要从用户提供的文本中提取以下信息:
1. 国家(gj)
2. 重量(zl,单位kg)
3. 立方(lf)
4. 仓库代码(ckdm)
5. 邮编(yb)

要求:
1. 严格按照指定JSON格式返回结果,仅返回JSON数据,不要额外解释
2. isContent字段为布尔值:如果提取到至少一个有效信息则为true,否则为false
3. er字段仅在无有效信息时填写详细错误原因,有有效信息时不返回该字段
4. 提取的信息如果不存在,对应字段值为空字符串

返回格式示例:
[
  {
    "gj": "中国",
    "zl": "50kg",
    "lf": "2.5",
    "ckdm": "WH001",
    "yb": "100000",
    "isContent": true
  }
]

错误示例:
[
  {
    "gj": "",
    "zl": "",
    "lf": "",
    "ckdm": "",
    "yb": "",
    "isContent": false,
    "er": "未从消息中提取到任何有效信息(国家/重量/立方/仓库代码/邮编)"
  }
]
            """);
        chatHistory.AddUserMessage([
                new TextContent("提取消息")
            ]
        );
        var reply = await chatCompletionService.GetChatMessageContentAsync(chatHistory);
        if (reply.Content is null) return [];
        var jsonStr = Regex.Replace(reply.Content, @"^\s*```json|```\s*$", "", RegexOptions.Multiline).Trim();
       // Console.WriteLine(jsonStr);
        return reply.Content is null ? [] : JsonSerializer.Deserialize<IEnumerable<classLlmAIResponse>>(jsonStr);
    }

核心需求理解

你需要一个完整的ASP.NET Core Web API 项目,基于 SemanticKernel 调用阿里云通义千问(qwen2.5-vl-32b-instruct)模型,实现接收文本消息message,提取其中的国家、重量、立方、仓库代码、邮编等信息,并返回结构化的LlmAIResponse数据列表。

完整项目实现

一、项目基础配置
  1. 创建项目:使用.NET 8 创建ASP.NET Core Web API 项目(命名为AIExtractApi
  2. 安装依赖包:在 NuGet 包管理器中安装以下包:

    bash

    运行

    Install-Package Microsoft.SemanticKernel -Version 1.15.0
    Install-Package Microsoft.SemanticKernel.Connectors.OpenAI -Version 1.15.0
    Install-Package System.Text.Json -Version 8.0.3
    Install-Package Microsoft.Extensions.Http -Version 8.0.0
    
二、核心代码实现
1. 模型类(LlmAIResponse.cs)

修正类名格式并补充缺失的isContent字段(你示例中要求但原类未定义):

csharp

运行

using System.Text.Json.Serialization;

namespace AIExtractApi.Models;

/// <summary>
/// AI提取结果响应模型
/// </summary>
public class LlmAIResponse
{
    /// <summary>
    /// 国家
    /// </summary>
    [JsonPropertyName("gj")]
    public string 国家 { get; set; } = string.Empty;

    /// <summary>
    /// 重量(kg)
    /// </summary>
    [JsonPropertyName("zl")]
    public string 重量 { get; set; } = string.Empty;

    /// <summary>
    /// 立方
    /// </summary>
    [JsonPropertyName("lf")]
    public string 立方 { get; set; } = string.Empty;

    /// <summary>
    /// 仓库代码
    /// </summary>
    [JsonPropertyName("ckdm")]
    public string 仓库代码 { get; set; } = string.Empty;

    /// <summary>
    /// 邮编
    /// </summary>
    [JsonPropertyName("yb")]
    public string 邮编 { get; set; } = string.Empty;

    /// <summary>
    /// 是否有有效提取数据
    /// </summary>
    [JsonPropertyName("isContent")]
    public bool 是否有内容 { get; set; }

    /// <summary>
    /// 错误信息(可选)
    /// </summary>
    [JsonPropertyName("er")]
    public string? 错误信息 { get; set; }
}
2. AI 服务接口与实现

IAIService.cs(接口):

csharp

运行

using AIExtractApi.Models;

namespace AIExtractApi.Services;

/// <summary>
/// AI信息提取服务接口
/// </summary>
public interface IAIService
{
    /// <summary>
    /// 提取消息中的指定信息
    /// </summary>
    /// <param name="message">待提取的文本消息</param>
    /// <returns>提取结果列表</returns>
    Task<IEnumerable<LlmAIResponse>?> ExtractMessageInfoAsync(string message);
}

AIService.cs(实现类):

csharp

运行

using AIExtractApi.Models;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using System.Text.Json;
using System.Text.RegularExpressions;

namespace AIExtractApi.Services;

/// <summary>
/// AI信息提取服务实现
/// </summary>
public class AIService : IAIService
{
    private readonly IChatCompletionService _chatCompletionService;
    private readonly ILogger<AIService> _logger;

    // JSON序列化配置(处理中文和空值)
    private static readonly JsonSerializerOptions _jsonOptions = new()
    {
        Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
        IgnoreNullValues = true,
        PropertyNameCaseInsensitive = true
    };

    public AIService(IChatCompletionService chatCompletionService, ILogger<AIService> logger)
    {
        _chatCompletionService = chatCompletionService;
        _logger = logger;
    }

    public async Task<IEnumerable<LlmAIResponse>?> ExtractMessageInfoAsync(string message)
    {
        // 空值校验
        if (string.IsNullOrWhiteSpace(message))
        {
            return new List<LlmAIResponse>
            {
                new()
                {
                    是否有内容 = false,
                    错误信息 = "输入消息不能为空"
                }
            };
        }

        try
        {
            // 构建对话历史
            var chatHistory = new ChatHistory();
            
            // 系统提示词(明确提取规则和格式)
            chatHistory.AddSystemMessage("""
                你是一个AI信息提取助手,需要从用户提供的文本中提取以下信息:
                1. 国家(gj)
                2. 重量(zl,单位kg)
                3. 立方(lf)
                4. 仓库代码(ckdm)
                5. 邮编(yb)
                
                要求:
                1. 严格按照指定JSON格式返回结果,仅返回JSON数据,不要额外解释
                2. isContent字段为布尔值:如果提取到至少一个有效信息则为true,否则为false
                3. er字段仅在无有效信息时填写详细错误原因,有有效信息时不返回该字段
                4. 提取的信息如果不存在,对应字段值为空字符串
                
                返回格式示例:
                [
                  {
                    "gj": "中国",
                    "zl": "50kg",
                    "lf": "2.5",
                    "ckdm": "WH001",
                    "yb": "100000",
                    "isContent": true
                  }
                ]
                
                错误示例:
                [
                  {
                    "gj": "",
                    "zl": "",
                    "lf": "",
                    "ckdm": "",
                    "yb": "",
                    "isContent": false,
                    "er": "未从消息中提取到任何有效信息(国家/重量/立方/仓库代码/邮编)"
                  }
                ]
                """);

            // 添加用户消息
            chatHistory.AddUserMessage(message);

            // 调用AI模型获取响应
            var reply = await _chatCompletionService.GetChatMessageContentAsync(
                chatHistory,
                new ChatRequestSettings { Temperature = 0.1 } // 低温度保证结果稳定
            );

            // 空响应处理
            if (string.IsNullOrWhiteSpace(reply.Content))
            {
                return new List<LlmAIResponse>
                {
                    new()
                    {
                        是否有内容 = false,
                        错误信息 = "AI模型返回空响应"
                    }
                };
            }

            // 清理AI返回的JSON(移除```json包裹符)
            var jsonStr = Regex.Replace(
                reply.Content.Trim(), 
                @"^\s*```json|```\s*$", 
                "", 
                RegexOptions.Multiline
            ).Trim();

            _logger.LogInformation("AI返回的JSON内容:{JsonStr}", jsonStr);

            // JSON反序列化
            var result = JsonSerializer.Deserialize<IEnumerable<LlmAIResponse>>(jsonStr, _jsonOptions);
            
            return result ?? new List<LlmAIResponse>
            {
                new()
                {
                    是否有内容 = false,
                    错误信息 = "JSON反序列化失败,返回空结果"
                }
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "提取消息信息时发生异常");
            return new List<LlmAIResponse>
            {
                new()
                {
                    是否有内容 = false,
                    错误信息 = $"处理失败:{ex.Message}"
                }
            };
        }
    }
}
3. API 控制器(AIController.cs)

csharp

运行

using AIExtractApi.Models;
using AIExtractApi.Services;
using Microsoft.AspNetCore.Mvc;

namespace AIExtractApi.Controllers;

/// <summary>
/// AI信息提取接口
/// </summary>
[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class AIController : ControllerBase
{
    private readonly IAIService _aiService;

    public AIController(IAIService aiService)
    {
        _aiService = aiService;
    }

    /// <summary>
    /// 提取消息中的国家、重量、立方、仓库代码、邮编信息
    /// </summary>
    /// <param name="message">待提取的文本消息</param>
    /// <returns>结构化的提取结果</returns>
    /// <response code="200">提取成功,返回结果列表</response>
    /// <response code="400">输入参数为空</response>
    [HttpGet("extract")]
    [ProducesResponseType(typeof(IEnumerable<LlmAIResponse>), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> ExtractInfo([FromQuery] string message)
    {
        if (string.IsNullOrWhiteSpace(message))
        {
            return BadRequest(new { code = 400, message = "输入消息不能为空" });
        }

        var result = await _aiService.ExtractMessageInfoAsync(message);
        return Ok(new { code = 200, data = result });
    }

    /// <summary>
    /// (POST版本)提取消息中的指定信息
    /// </summary>
    /// <param name="request">包含消息的请求体</param>
    /// <returns>结构化的提取结果</returns>
    [HttpPost("extract")]
    public async Task<IActionResult> ExtractInfoPost([FromBody] ExtractRequest request)
    {
        if (request == null || string.IsNullOrWhiteSpace(request.Message))
        {
            return BadRequest(new { code = 400, message = "输入消息不能为空" });
        }

        var result = await _aiService.ExtractMessageInfoAsync(request.Message);
        return Ok(new { code = 200, data = result });
    }
}

/// <summary>
/// POST请求体模型
/// </summary>
public class ExtractRequest
{
    /// <summary>
    /// 待提取的文本消息
    /// </summary>
    public string Message { get; set; } = string.Empty;
}
4. 项目入口配置(Program.cs)

csharp

运行

using AIExtractApi.Services;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;

var builder = WebApplication.CreateBuilder(args);

// 1. 添加控制器
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// 2. 配置SemanticKernel连接阿里云通义千问(兼容OpenAI接口)
builder.Services.AddOpenAIChatCompletion(
    modelId: "qwen2.5-vl-32b-instruct",
    endpoint: new Uri("https://dashscope.aliyuncs.com/compatible-mode/v1"),
    apiKey: "sk-" // 建议放到配置文件中,此处仅演示
);

// 3. 注册AI服务
builder.Services.AddScoped<IAIService, AIService>();

var app = builder.Build();

// 开发环境启用Swagger
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();
三、项目使用说明
  1. 运行项目:直接启动项目(默认端口 5000/7000)
  2. 接口调用示例
    • GET 方式:http://localhost:5000/api/ai/extract?message=中国 重量50kg 立方2.5 仓库代码WH001 邮编100000
    • POST 方式:请求地址http://localhost:5000/api/ai/extract,请求体:

      json

      {
        "Message": "美国 重量100kg 立方5.0 仓库代码US002 邮编900000"
      }
      
  3. 返回结果示例

    json

    {
      "code": 200,
      "data": [
        {
          "gj": "美国",
          "zl": "100kg",
          "lf": "5.0",
          "ckdm": "US002",
          "yb": "900000",
          "isContent": true
        }
      ]
    }
    

总结

  1. 核心配置:通过AddOpenAIChatCompletion配置 SemanticKernel 连接阿里云通义千问的 OpenAI 兼容接口,指定模型 ID、端点和 API Key。
  2. 服务封装:将 AI 调用逻辑封装到AIService中,包含消息校验、对话构建、响应处理、JSON 解析和异常捕获,保证代码复用性和健壮性。
  3. 接口设计:提供 GET/POST 两种接口方式,支持不同场景调用,返回结构化 JSON 数据,包含状态码和提取结果。
  4. 关键优化:添加低温度参数保证 AI 返回结果稳定、配置 JSON 序列化规则处理中文、增加完善的异常处理和日志记录。

进阶建议

  1. 将 API Key 放到appsettings.json配置文件中,避免硬编码:

    json

    "AliyunDashScope": {
      "ModelId": "qwen2.5-vl-32b-instruct",
      "Endpoint": "https://dashscope.aliyuncs.com/compatible-mode/v1",
      "ApiKey": "sk-"
    }
    
    然后在 Program.cs 中读取:

    csharp

    运行

    var modelId = builder.Configuration["AliyunDashScope:ModelId"]!;
    var endpoint = new Uri(builder.Configuration["AliyunDashScope:Endpoint"]!);
    var apiKey = builder.Configuration["AliyunDashScope:ApiKey"]!;
    
  2. 添加接口限流、请求验证等中间件,提升接口安全性。
  3. 扩展提示词优化提取精度,可根据实际业务场景调整系统提示词规则。

要将这个项目变成可直接下载、开箱即用的完整工程,我们需要补充项目目录结构、配置文件、依赖清单和部署说明。以下是完整的项目打包方案,你可以直接复制到本地运行。

一、 完整项目目录结构

plaintext

AIExtractApi/
├─ AIExtractApi.csproj          // 项目文件
├─ Program.cs                   // 入口文件
├─ appsettings.json             // 配置文件
├─ appsettings.Development.json // 开发环境配置
├─ .gitignore                   // Git忽略文件
├─ README.md                    // 项目说明文档
├─ Controllers/
│  └─ AIController.cs           // API控制器
├─ Models/
│  └─ LlmAIResponse.cs          // 数据模型
└─ Services/
   ├─ IAIService.cs             // 服务接口
   └─ AIService.cs              // 服务实现

二、 所有文件完整代码

1. AIExtractApi.csproj(项目依赖)

xml

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <RootNamespace>AIExtractApi</RootNamespace>
  </PropertyGroup>

  <ItemGroup>
    <!-- SemanticKernel 核心包 -->
    <PackageReference Include="Microsoft.SemanticKernel" Version="1.15.0" />
    <PackageReference Include="Microsoft.SemanticKernel.Connectors.OpenAI" Version="1.15.0" />
    <!-- 日志和JSON相关包 -->
    <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
    <PackageReference Include="System.Text.Json" Version="8.0.3" />
  </ItemGroup>

</Project>
2. appsettings.json(生产环境配置)

json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "AliyunDashScope": {
    "ModelId": "qwen2.5-vl-32b-instruct",
    "Endpoint": "https://dashscope.aliyuncs.com/compatible-mode/v1",
    "ApiKey": "sk-"
  }
}
3. appsettings.Development.json(开发环境配置)

json

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "Microsoft.AspNetCore": "Information"
    }
  },
  "AliyunDashScope": {
    "ModelId": "qwen2.5-vl-32b-instruct",
    "Endpoint": "https://dashscope.aliyuncs.com/compatible-mode/v1",
    "ApiKey": "sk-"
  }
}
4. .gitignore(Git 忽略文件)

plaintext

# .NET Core
bin/
obj/
*.dll
*.pdb
*.user
*.suo
*.cache
*.config
.DS_Store
*.log
.vscode/
.vs/
5. README.md(项目说明)

markdown

# AIExtractApi
基于SemanticKernel + 阿里云通义千问的信息提取API,支持提取文本中的国家、重量、立方、仓库代码、邮编信息。

## 环境要求
- .NET 8.0 SDK
- 阿里云DashScope API Key(已内置测试Key,建议替换为自己的)

## 快速启动
1. 克隆或下载项目到本地
2. 打开终端,进入项目根目录
3. 执行命令:`dotnet run`
4. 访问 Swagger 文档:`http://localhost:5000/swagger`

## 接口调用
### GET 方式

GET http://localhost:5000/api/ai/extract?message=中国 重量 50kg 立方 2.5 仓库代码 WH001 邮编 100000

plaintext


### POST 方式

POST http://localhost:5000/api/ai/extractContent-Type: application/json

{"Message": "美国 重量 100kg 立方 5.0 仓库代码 US002 邮编 900000"}

plaintext


## 配置修改
如需替换API Key或模型,修改 `appsettings.json` 中的 `AliyunDashScope` 节点。
6. 其余文件代码
  • Models/LlmAIResponse.csServices/IAIService.csServices/AIService.csControllers/AIController.csProgram.cs直接使用上一轮提供的完整代码即可,无需修改。

三、 打包与下载方式

方式 1:本地直接创建运行
  1. 在本地新建文件夹 AIExtractApi
  2. 按照上面的目录结构,创建所有文件夹和文件,并粘贴对应代码
  3. 打开终端,进入项目根目录
  4. 执行命令 dotnet restore 还原依赖
  5. 执行命令 dotnet run 启动项目
方式 2:生成发布包(可部署到服务器)
  1. 本地完成项目编写后,执行发布命令:

    bash

    运行

    dotnet publish -c Release -o ./publish
    
  2. 项目根目录会生成 publish 文件夹,里面包含所有运行所需文件
  3. 直接压缩 publish 文件夹,即可作为下载包提供给他人
  4. 他人下载后,直接运行 AIExtractApi.exe 即可启动服务(Windows),或通过 dotnet AIExtractApi.dll 启动(Linux/Mac)

四、 关键优化点

  1. 配置解耦:将 API Key、模型 ID 等配置移到 appsettings.json,避免硬编码
  2. 环境区分:开发环境和生产环境使用不同配置文件
  3. 一键部署:发布包包含所有依赖,无需额外安装组件
  4. 文档完善:提供 README 说明,降低使用门槛

最终

你是一个AI信息提取助手,需要从用户提供的文本中提取以下信息:
1. 意图类型(yttype):
- 只要文本中包含「单号」且是「查询物流/查位置/查进度」场景,yttype=单号;
- 只要文本中包含「国家/重量/立方/仓库代码/邮编」任意一个,yttype=报价参数;
- 无有效信息则yttype为空字符串。
2. 单号(dh):文本中用于「查询物流/查位置/查进度」的数字/字母组合串(如1234566789),是单号核心特征。
3. 国家(gj,有邮编的从邮编里取)
4. 重量(zl,单位kg,斤要转为kg,**强制要求:转换后仅填写结果值,绝不允许添加任何注释、说明、来源标注(包括但不限于 // 斤已转化为千克 等内容)**)
5. 立方(lf)
6. 仓库代码(ckdm)
7. 邮编(yb)


### 核心硬性要求 ###
1.  仅返回标准JSON数组数据,绝对禁止添加任何额外内容:包括但不限于注释(//、/* */)、备注、说明文字、括号补充、换行解释等,JSON内只能包含键和字符串值。
2. isContent字段为布尔值:提取到至少一个有效信息则为true,否则为false;
- 只要识别出单号(无论是否有其他信息),yttype必须为「单号」;
- 无单号但有国家/重量等任意一个,yttype为「报价参数」。
3.  er字段仅在无任何有效信息时填写错误原因,有有效信息时**必须省略该字段**。
4.  提取的信息不存在时,对应字段值为空字符串;**重量转换场景是重点约束对象,任何情况下都不能在zl字段值后加注释**。

### 场景示例(必须严格参考此逻辑)###
示例1(单号查询场景,正确结果):
用户文本:帮我查下1234566789 到哪里了
返回JSON:
[
  {
    "yttype": "单号",
    "dh": "1234566789",
    "gj": "",
    "zl": "",
    "lf": "",
    "ckdm": "",
    "yb": "",
    "isContent": true
  }
]

示例2(报价参数+重量转换场景,正确结果):
用户文本:美国 13斤 邮编12345
返回JSON:
[
  {
    "yttype": "报价参数",
    "dh": "",
    "gj": "美国",
    "zl": "6.5kg",
    "lf": "",
    "ckdm": "",
    "yb": "12345",
    "isContent": true
  }
]

示例3(无有效信息场景,错误结果):
[
  {
    "yttype": "",
    "dh": "",
    "gj": "",
    "zl": "",
    "lf": "",
    "ckdm": "",
    "yb": "",
    "isContent": false,
    "er": "未从消息中提取到任何有效信息(单号/国家/重量/立方/仓库代码/邮编)"
  }
]

 其他专题

.net AI开发02 1后端框架: ASP.NET Core2.AI框架: Semantic Kernerl (SK)、Agent Framework3.知识库:向量数据库(Qdrant)+关系型数

https://blog.csdn.net/cao919/article/details/155896184

AI开发 1后端框架: ASP.NET Core2.AI框架: Semantic Kernerl (SK)、Agent Framework3.知识库:向量数据库(Qdrant)+关系型数据库(Post

https://blog.csdn.net/cao919/article/details/155895060

.net AI MCP 入门 适用于模型上下文协议的 C# SDK 简介(MCP)
https://blog.csdn.net/cao919/article/details/147915384

C# .net ai Agent AI视觉应用 写代码 改作业 识别屏幕 简历处理 标注等
https://blog.csdn.net/cao919/article/details/146504537

C# net deepseek RAG AI开发 全流程 介绍
https://blog.csdn.net/cao919/article/details/147915384

WPF halcon 机器视觉
https://blog.csdn.net/cao919/article/details/134790240 

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐