目录
一句话简介
🎯 核心要点
📝 核心概念(超精炼)
💻 实现方式(最佳实践)
1) 强类型结构化输出(最常用)
2) 嵌套对象与数组(企业常见)
3) 流式结构化输出(可边收边用)
4) 国内模型(DeepSeek/Qwen)兼容策略
🏢 生产最佳实践
❓常见问题与解决
🎯 总结
上一篇
一句话简介
将大模型的自由文本输出,稳定地转为强类型 JSON 对象,用于可靠的业务集成与自动化处理。
🎯 核心要点
✅ 通过 JSON Schema 约束输出结构,减少解析异常
✅ 一行配置启用结构化输出:ChatOptions.ResponseFormat
✅ 自动从 C# 类型生成 Schema:AIJsonUtilities.CreateJsonSchema
✅ 支持嵌套对象、枚举、数组与流式输出
✅ 国内模型(如 DeepSeek)采用“提示词 + 纯 JSON”策略
📝 核心概念(超精炼)
ChatResponseFormat:定义模型返回格式(Text/Json/ForJsonSchema)
JSON Schema:描述 JSON 字段与类型的标准
AIJsonUtilities:从 C# 类型自动生成 JSON Schema
ChatOptions.ResponseFormat:向模型声明“必须按指定结构返回”
flowchart LR A[提示词] --> B[ResponseFormat<br/>JsonSchema] B --> C[LLM] C --> D[JSON] D --> E[反序列化为<br/>强类型对象]💻 实现方式(最佳实践)
1) 强类型结构化输出(最常用)
从 C# 类型生成 Schema,并要求模型按该结构返回。
using Microsoft.Extensions.AI; using System.Text.Json; using System.Text.Json.Serialization; publicclassPersonInfo { [JsonPropertyName("name")] publicstring? Name { get; set; } [JsonPropertyName("age")] publicint? Age { get; set; } [JsonPropertyName("occupation")] publicstring? Occupation { get; set; } [JsonPropertyName("location")] publicstring? Location { get; set; } } // 1) 生成 JSON Schema var schema = AIJsonUtilities.CreateJsonSchema(typeof(PersonInfo)); // 2) 配置结构化输出 var options = new ChatOptions { ResponseFormat = ChatResponseFormatJson.ForJsonSchema( schema: schema, schemaName: "PersonInfo", schemaDescription: "包含一个人的姓名、年龄、职业和地点") }; // 3) 请求并反序列化 var messages = new[] { new ChatMessage(ChatRole.System, "从文本中提取个人信息,严格按 JSON 返回。"), new ChatMessage(ChatRole.User, "张伟35岁,软件工程师,在北京工作。") }; var client = AIClientHelper.GetDefaultChatClient(); var result = await client.CompleteAsync(messages, options); var person = JsonSerializer.Deserialize<PersonInfo>(result.Message.Text!, JsonSerializerOptions.Web);为什么推荐:
模型端“强约束”,客户端“强类型”,连线最短、容错更高。
2) 嵌套对象与数组(企业常见)
适合评论分析、工单解析、多联系人提取等复杂结构。
public classSentimentAnalysis { [JsonPropertyName("sentiment")] publicstring? Sentiment { get; set; } // Positive/Neutral/Negative [JsonPropertyName("confidence")] publicdouble Confidence { get; set; } // 0.0-1.0 } publicclassProductReviewAnalysis { [JsonPropertyName("product_name")] publicstring? ProductName { get; set; } [JsonPropertyName("rating")] publicint Rating { get; set; } // 1-5 [JsonPropertyName("sentiment")] public SentimentAnalysis? Sentiment { get; set; } [JsonPropertyName("key_points")] public List<string>? KeyPoints { get; set; } [JsonPropertyName("recommendation")] publicbool Recommendation { get; set; } } var reviewSchema = AIJsonUtilities.CreateJsonSchema(typeof(ProductReviewAnalysis)); var reviewOptions = new ChatOptions { ResponseFormat = ChatResponseFormatJson.ForJsonSchema(reviewSchema, "ProductReviewAnalysis", "产品评论分析") }; var review = await client.CompleteAsync(new[] { new ChatMessage(ChatRole.System, "分析评论并按 JSON 返回:名称、评分、情感、要点、是否推荐。"), new ChatMessage(ChatRole.User, "iPhone 15 Pro 屏幕清晰、速度快、夜景强;价格稍高,但值得买。") }, reviewOptions); var analysis = JsonSerializer.Deserialize<ProductReviewAnalysis>(review.Message.Text!, JsonSerializerOptions.Web);要点:
嵌套结构、数组与约束在 Schema 中一次性声明,输出更稳定。
3) 流式结构化输出(可边收边用)
当内容较长时可流式接收,再整体反序列化。
var sb = new System.Text.StringBuilder(); await foreach (var chunk in client.CompleteStreamingAsync(messages, options)) { if (chunk.Text is not null) sb.Append(chunk.Text); } var streamed = JsonSerializer.Deserialize<PersonInfo>(sb.ToString(), JsonSerializerOptions.Web);4) 国内模型(DeepSeek/Qwen)兼容策略
部分模型暂不支持 ForJsonSchema,采用“纯 JSON 响应 + 严格提示”。
var ds = AIClientHelper.GetDeepSeekClient().GetChatClient("deepseek-chat").AsIChatClient(); var dsOptions = new ChatOptions { ResponseFormat = ChatResponseFormat.Json }; var system = @"严格按下列 JSON 返回,不要输出任何其他文本: { \"name\": \"字符串\", \"age\": 0, \"occupation\": \"字符串\", \"location\": \"字符串\" }"; var resp = await ds.GetResponseAsync(new[] { new ChatMessage(ChatRole.System, system), new ChatMessage(ChatRole.User, "刘洋42岁,数据科学家,深圳工作。") }, dsOptions); var person2 = JsonSerializer.Deserialize<PersonInfo>(resp.Text!, JsonSerializerOptions.Web);提示词关键:
明确“只返回 JSON、无需解释”,提供完整 JSON 模板与类型约束。
🏢 生产最佳实践
✅ 精简 Schema:仅保留业务必须字段,减少 Token 与偏差
✅ 使用 JsonStringEnumConverter 显式声明枚举,降低自由文本
✅ 统一 JsonSerializerOptions.Web,提升命名与容错一致性
✅ 失败兜底:捕获 JsonException 时尝试提取 JSON 片段或回退默认值
✅ 批量数据分批处理,降低上下文长度与失败率
❓常见问题与解决
问题 | 可能原因 | 解决方案 |
|---|---|---|
反序列化失败 | 返回不完全符合 Schema | 明确字段约束与示例;增加系统提示;兜底解析 |
字段缺失/命名不一致 | 命名风格不统一 | 统一使用 JsonPropertyName + JsonSerializerOptions.Web |
枚举值异常 | 模型自由发挥 | 提示中列出允许值,并使用字符串枚举转换器 |
国内模型格式漂移 | 不支持 ForJsonSchema | 使用 ChatResponseFormat.Json + 严格 JSON 模板 |
🎯 总结
✅ 结构化输出把“不可控的文本”变成“可控的对象”,便于对接业务系统
✅ ForJsonSchema + 强类型模型,是最稳且通用的生产方案
✅ 支持嵌套、数组、枚举与流式,覆盖主流企业场景
✅ 国内模型用“纯 JSON + 严格模板”同样可达标
https://github.com/mzhongl524/meai_dotnet_for_begenner
下一篇
引入地址