当前位置: 首页 > 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.cnnetsun.cn/news/2506358.html

相关文章:

  • Windows AirPods电量显示终极指南:解锁苹果耳机完整功能
  • 如何用茉莉花插件5分钟搞定Zotero中文文献管理:新手终极指南
  • 探索AI-Shoujo HF Patch:解锁游戏完整体验的终极方案
  • 调用外部服务却无监控?这可能是下一个雪崩的源头
  • 5个关键技巧优化抖音素材收集:开源下载器的进阶应用指南
  • 解锁AMD Ryzen处理器隐藏性能:SMUDebugTool深度调试实战指南
  • 家庭总吵架?跟易经学2招,比讲道理管用多了!
  • 低压电工-架空线路,室内线路
  • Android OTA提取终极指南:手机端Payload-Dumper-Android完整教程
  • N_m3u8DL-CLI-SimpleG:让M3U8视频下载变得如此简单的终极图形界面工具
  • 基于知识图谱InsightGraph — 让数据开口说话。
  • 基于零代码平台的学生考勤多维画像及高危群体专项分析实验
  • Sunshine游戏串流服务器:从零搭建你的专属云游戏平台
  • 三周、1.81倍、百亿:中国AI的压制性时刻
  • 真正的爱是接受对方本来的样子
  • SQLite Viewer:3分钟学会在线查看SQLite数据库的终极方案
  • 米哈游游戏字体完整指南:免费获取原神、星穹铁道、绝区零精美文字资源
  • ARMv8 TRCEVENTCTL1R寄存器解析与调试实践
  • Display Driver Uninstaller (DDU) 终极指南:显卡驱动彻底清理的完整解决方案
  • SpaceX与Anthropic达成合作:Anthropic年付150亿美元,SpaceX拟拓展AI计算服务
  • 找工厂用什么工具?为什么“收录企业更多“是个伪指标
  • 5分钟搞定百度网盘限速:baidu-wangpan-parse全功能指南
  • 瀚高企业版V9.1.1在pg_restore还原备份文件时提示extract函数语法问题
  • 线上故障排查与应急响应实战:从零开始建立你的SRE体系
  • 原神PC帧率解锁完整指南:轻松突破60FPS限制的终极方案
  • 使用TaotokenCLI工具一键配置开发环境与模型密钥
  • 茉莉花插件:Zotero中文文献管理的终极解决方案,5分钟打造高效科研工作流
  • GEO优化的时间窗口期:从流量分发到语义占位的技术范式转移
  • 东信身份证阅读器鸿蒙6.0开发实战:从零开始,手把手教你如何使用DevEco Studio开发app读取身份证信息
  • Spring-Ai-Alibaba [02] chatclient-demo