当前位置: 首页 > news >正文

[MAF的Agent管道详解-04]如何让LLM按照要求的结构输出数据?

针对IChatClient的结构化输出可以通过调用如下这些重载的GetResponseAsync<T>扩展方法来完成。具体的实现很简单,这些方法最终会利用指定或者默认的JsonSerializerOptions针对泛型参数T生成一个ChatResponseFormatJson对象,并作为ChatOptionsResponseFormat属性。这个ResponseFormat承载的JSON Schema将提供给LLM指导它按照定义的格式生成输出内容。当IChatClient接收到LLM的响应结果时,利用匹配的JsonSerializerOptions对响应结果进行反序列化后,封装成一个ChatResponse<T>对象返回给调用方。

publicstaticclassChatClientStructuredOutputExtensions{publicstaticTask<ChatResponse<T>>GetResponseAsync<T>(thisIChatClientchatClient,IEnumerable<ChatMessage>messages,ChatOptions?options=null,bool?useJsonSchemaResponseFormat=null,CancellationTokencancellationToken=default(CancellationToken));publicstaticTask<ChatResponse<T>>GetResponseAsync<T>(thisIChatClientchatClient,stringchatMessage,ChatOptions?options=null,bool?useJsonSchemaResponseFormat=null,CancellationTokencancellationToken=default(CancellationToken));publicstaticTask<ChatResponse<T>>GetResponseAsync<T>(thisIChatClientchatClient,ChatMessagechatMessage,ChatOptions?options=null,bool?useJsonSchemaResponseFormat=null,CancellationTokencancellationToken=default(CancellationToken));publicstaticTask<ChatResponse<T>>GetResponseAsync<T>(thisIChatClientchatClient,stringchatMessage,JsonSerializerOptionsserializerOptions,ChatOptions?options=null,bool?useJsonSchemaResponseFormat=null,CancellationTokencancellationToken=default(CancellationToken));publicstaticTask<ChatResponse<T>>GetResponseAsync<T>(thisIChatClientchatClient,ChatMessagechatMessage,JsonSerializerOptionsserializerOptions,ChatOptions?options=null,bool?useJsonSchemaResponseFormat=null,CancellationTokencancellationToken=default(CancellationToken));publicstaticasyncTask<ChatResponse<T>>GetResponseAsync<T>(thisIChatClientchatClient,IEnumerable<ChatMessage>messages,JsonSerializerOptionsserializerOptions,ChatOptions?options=null,bool?useJsonSchemaResponseFormat=null,CancellationTokencancellationToken=default(CancellationToken));}

1. 调用GetResponseAsync方法获取结构化输出

在如下的演示程序中,我们定义了一个描述个人基本信息的Profile类。我们利用OpenAIClient创建了一个IChatClient对象,并调用了GetResponseAsync<Profile>方法来从指定的一段文本中提取个人信息。在得到作为响应的Response<Profile>对象后,利用Result属性提取反序列化响应内容生成的Profile对象,调试断言表明这个Profile对象与我们预设的Profile对象是相等的。

usingdotenv.net;usingMicrosoft.Extensions.AI;usingOpenAI;usingSystem.ClientModel;usingSystem.Diagnostics;usingSystem.Text.Json;DotEnv.Load();varmodel=Environment.GetEnvironmentVariable("MODEL")!;varapiKey=Environment.GetEnvironmentVariable("API_KEY")!;varopenAIUrl=Environment.GetEnvironmentVariable("OPENAI_URL")!;varopenAIClient=newOpenAIClient(credential:newApiKeyCredential(key:apiKey),options:newOpenAIClientOptions{Endpoint=newUri(openAIUrl)});varchatClient=openAIClient.GetResponsesClient().AsIChatClient(defaultModelId:model);varprofile=newProfile{Name="张三",Gender=Gender.Male,Age=26};varprompt="从下面内容中提取有效的个人信息:我叫张三,男,今年26岁";varresponse1=awaitchatClient.GetResponseAsync<Profile>(chatMessage:prompt);Debug.Assert(profile==response1.Result);publicenumGender{Male,Female,}classProfile:IEquatable<Profile>{publicstring?Name{get;set;}publicGenderGender{get;set;}publicintAge{get;set;}publicboolEquals(Profile?other){if(otherisnull)returnfalse;returnName==other.Name&&Gender==other.Gender&&Age==other.Age;}}

对于程序涉及的LLM调用,如下的两段JSON为发送的请求和接收的响应。可以看出针对Profile类型的JSON Schema被包含在发送给LLM的请求中,而LLM生成的响应内容则被成功地反序列化成了一个Profile对象。

{"model":"gpt-5.2-chat","text":{"format":{"type":"json_schema","name":"Profile","schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":["string","null"]},"gender":{"type":"string","enum":["Male","Female"]},"age":{"type":"integer"}},"additionalProperties":false,"required":["name","gender","age"]}}},"input":[{"type":"message","role":"user","content":[{"type":"input_text","text":"从下面内容中提取有效的个人信息:我叫张三,男,今年26岁"}]}]}
{"id":"resp_0962768f225127bc006a002eb6441881978b7b0150d57cdff9","object":"response","created_at":1778396854,"status":"completed","background":false,"completed_at":1778396858,"content_filters":[{"blocked":false,"source_type":"prompt","content_filter_raw":[],"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"}},"content_filter_offsets":{"start_offset":0,"end_offset":585,"check_offset":0}},{"blocked":false,"source_type":"completion","content_filter_raw":[],"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"}},"content_filter_offsets":{"start_offset":0,"end_offset":824,"check_offset":0}}],"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5.2-chat","output":[{"id":"rs_0962768f225127bc006a002eb6c400819798b1da517e1a1eda","type":"reasoning","summary":[]},{"id":"msg_0962768f225127bc006a002eba09b48197b34ee13d7b475432","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"{\"name\":\"张三\",\"gender\":\"Male\",\"age\":26}"}],"role":"assistant"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"json_schema","description":null,"name":"Profile","schema":{"type":"object","properties":{"name":{"type":["string","null"]},"gender":{"type":"string","enum":["Male","Female"]},"age":{"type":"integer"}},"additionalProperties":false,"required":["name","gender","age"]},"strict":true},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":0.85,"truncation":"disabled","usage":{"input_tokens":69,"input_tokens_details":{"cached_tokens":0},"output_tokens":217,"output_tokens_details":{"reasoning_tokens":192},"total_tokens":286},"user":null,"metadata":{}}

2. 通过设置ChatOptions的ResponseFormat属性来获取结构化输出

GetResponseAsync<T>方法最终会利用指定或者默认的JsonSerializerOptions针对泛型参数T生成一个ChatResponseFormatJson对象,并作为ChatOptionsResponseFormat属性。当我们直接调用GetResponseAsync方法时,ChatOptionsResponseFormat属性返回的JSON Schema将作为调用LLM提示词的一部分,用于指导LLM生成符合结构的输出。上面的演示程序与下面这段其实是完全等效的。

usingdotenv.net;usingMicrosoft.Extensions.AI;usingOpenAI;usingSystem.ClientModel;usingSystem.Diagnostics;usingSystem.Text.Json;DotEnv.Load();varmodel=Environment.GetEnvironmentVariable("MODEL")!;varapiKey=Environment.GetEnvironmentVariable("API_KEY")!;varopenAIUrl=Environment.GetEnvironmentVariable("OPENAI_URL")!;varopenAIClient=newOpenAIClient(credential:newApiKeyCredential(key:apiKey),options:newOpenAIClientOptions{Endpoint=newUri(openAIUrl)});varchatClient=openAIClient.GetResponsesClient().AsIChatClient(defaultModelId:model);varoptions=newChatOptions{ResponseFormat=ChatResponseFormat.ForJsonSchema<Profile>()};varresponse=awaitchatClient.GetResponseAsync(chatMessage:prompt,options:options);varprofile2=JsonSerializer.Deserialize<Profile>(response1.Messages.Last().Text,AIJsonUtilities.DefaultOptions);Debug.Assert(profile==newProfile{Name="张三",Gender=Gender.Male,Age=26});publicenumGender{Male,Female,}classProfile:IEquatable<Profile>{publicstring?Name{get;set;}publicGenderGender{get;set;}publicintAge{get;set;}publicboolEquals(Profile?other){if(otherisnull)returnfalse;returnName==other.Name&&Gender==other.Gender&&Age==other.Age;}}
http://www.jsqmd.com/news/863475/

相关文章:

  • 浏览器资源嗅探革命:猫抓扩展如何重新定义在线媒体捕获体验
  • 如何快速安装BetterNCM:终极网易云音乐插件管理指南
  • 深度解析Unity游戏实时翻译插件:XUnity.AutoTranslator的5大实战应用场景与架构设计
  • 大学买不到GPU怪我?黄仁勋斯坦福现场火力全开:是你们体制的错!
  • Sub2API + CCSwitch 实现 Codex 反向代理:多账号流量分发实战(解决codex手机号验证)可以润色吗
  • 【紧急更新】Midjourney v6.1金属纹理算法变更预警:3个必须重训的材质参数阈值,错过将导致PBR贴图链断裂
  • 武安市建龙废钢基地物资:峰峰矿专业的废钢回收公司推荐几家 - LYL仔仔
  • 2026年国内矿粉球团粘合剂头部厂家实力排行 - 奔跑123
  • 3步彻底解决Windows右键菜单卡顿:ContextMenuManager终极优化指南
  • 抖音无水印批量下载终极指南:douyin-downloader免费神器
  • 5分钟搭建个人游戏云:Sunshine跨平台串流服务器完全指南
  • 2026年全球优质筋膜枪选购指南:轻松找到你的专属按摩神器 - 博客万
  • G-Helper:华硕笔记本的轻量级性能管家,让你的电脑重获新生
  • N_m3u8DL-CLI-SimpleG:让M3U8视频下载变得如此简单的终极图形界面工具
  • 2026论文降AI率工具谁是性价比之王?早标网最低1.0元/千字性价比拉满 - 全维度降AI
  • MelonLoader终极指南:解锁Unity游戏无限可能的5个核心步骤
  • 2026年软考知识点—计算机等级考试—软件设计师考前备忘录—东方仙盟
  • 三步掌握AMD Ryzen处理器终极调优:SMUDebugTool完整使用指南
  • 2026港口码头监管低空平台建设指南:全域数字底座如何赋能安全高效运行 - 品牌2025
  • 【测试】一文读懂软件测试:新手真正需要的测试认知
  • Sunshine游戏串流终极方案:打造你的私人云游戏服务器
  • G-Helper:3步快速配置指南,释放华硕笔记本的真正潜能
  • 中兴光猫终极解锁指南:一键开启工厂模式与永久Telnet的完整解决方案
  • 2026年长春黄金回收避坑指南——福昌夏等六大机构实测对比 - 黄金上门回收
  • 高效掌握外语词汇:ToastFish桌面单词记忆工具完整指南
  • 鸣潮自动化助手:5步轻松实现后台智能战斗与资源收集
  • 2026年成都有哪些值得信赖的AI搜索优化公司? - 品牌推荐官方
  • 路由双雄对决:静态 vs 动态,从原理到实战的终极指南(万字长文)
  • 中兴光猫工厂模式解锁终极指南:zteOnu让你的网络管理更简单
  • 重庆顺坤机械科技:永川专业的CNC数控加工公司找哪家 - LYL仔仔