.net AI API应用 客户发的信息提取对接上下游系统报价
本文介绍了一个基于SemanticKernel和阿里云通义千问模型的ASP.NET Core WebAPI项目,用于从文本中提取结构化信息。项目通过配置OpenAI兼容接口连接阿里云服务,封装了AI信息提取功能,包括国家、重量、立方、仓库代码和邮编等字段的提取。系统提供GET/POST两种接口方式,包含完善的错误处理和日志记录。文章详细说明了项目配置、核心代码实现(模型类、服务接口、控制器)以及部

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数据列表。
完整项目实现
一、项目基础配置
- 创建项目:使用.NET 8 创建ASP.NET Core Web API 项目(命名为
AIExtractApi) - 安装依赖包:在 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();
三、项目使用说明
- 运行项目:直接启动项目(默认端口 5000/7000)
- 接口调用示例:
- 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" }
- GET 方式:
- 返回结果示例:
json
{ "code": 200, "data": [ { "gj": "美国", "zl": "100kg", "lf": "5.0", "ckdm": "US002", "yb": "900000", "isContent": true } ] }
总结
- 核心配置:通过
AddOpenAIChatCompletion配置 SemanticKernel 连接阿里云通义千问的 OpenAI 兼容接口,指定模型 ID、端点和 API Key。 - 服务封装:将 AI 调用逻辑封装到
AIService中,包含消息校验、对话构建、响应处理、JSON 解析和异常捕获,保证代码复用性和健壮性。 - 接口设计:提供 GET/POST 两种接口方式,支持不同场景调用,返回结构化 JSON 数据,包含状态码和提取结果。
- 关键优化:添加低温度参数保证 AI 返回结果稳定、配置 JSON 序列化规则处理中文、增加完善的异常处理和日志记录。
进阶建议
- 将 API Key 放到
appsettings.json配置文件中,避免硬编码:json
然后在 Program.cs 中读取:"AliyunDashScope": { "ModelId": "qwen2.5-vl-32b-instruct", "Endpoint": "https://dashscope.aliyuncs.com/compatible-mode/v1", "ApiKey": "sk-" }csharp
运行
var modelId = builder.Configuration["AliyunDashScope:ModelId"]!; var endpoint = new Uri(builder.Configuration["AliyunDashScope:Endpoint"]!); var apiKey = builder.Configuration["AliyunDashScope:ApiKey"]!; - 添加接口限流、请求验证等中间件,提升接口安全性。
- 扩展提示词优化提取精度,可根据实际业务场景调整系统提示词规则。
要将这个项目变成可直接下载、开箱即用的完整工程,我们需要补充项目目录结构、配置文件、依赖清单和部署说明。以下是完整的项目打包方案,你可以直接复制到本地运行。
一、 完整项目目录结构
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.cs、Services/IAIService.cs、Services/AIService.cs、Controllers/AIController.cs、Program.cs直接使用上一轮提供的完整代码即可,无需修改。
三、 打包与下载方式
方式 1:本地直接创建运行
- 在本地新建文件夹
AIExtractApi - 按照上面的目录结构,创建所有文件夹和文件,并粘贴对应代码
- 打开终端,进入项目根目录
- 执行命令
dotnet restore还原依赖 - 执行命令
dotnet run启动项目
方式 2:生成发布包(可部署到服务器)
- 本地完成项目编写后,执行发布命令:
bash
运行
dotnet publish -c Release -o ./publish - 项目根目录会生成
publish文件夹,里面包含所有运行所需文件 - 直接压缩
publish文件夹,即可作为下载包提供给他人 - 他人下载后,直接运行
AIExtractApi.exe即可启动服务(Windows),或通过dotnet AIExtractApi.dll启动(Linux/Mac)
四、 关键优化点
- 配置解耦:将 API Key、模型 ID 等配置移到
appsettings.json,避免硬编码 - 环境区分:开发环境和生产环境使用不同配置文件
- 一键部署:发布包包含所有依赖,无需额外安装组件
- 文档完善:提供 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
更多推荐


所有评论(0)