ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

DeepSeek-Agent-Harness-2026终极指南-第3章第15节-API协议内幕-结构化输出:让模型说机器话

DeepSeek-Agent-Harness-2026终极指南-第3章第15节-API协议内幕-结构化输出:让模型说机器话 结构化输出让模型说机器话你让模型输出 JSON它偏偏在前面加一句好的以下是您要的JSON“后面再包三层 markdown 代码块。这不是模型叛逆是你没跟它约法三章”。这节搭一套三层防御体系让模型输出变成机器可信赖的数据。本文导航问题的根源模型是话痨第一层防御JSON Output 模式第二层防御pydantic 校验第三层防御自动重试三层合体生产级输出管线防御性编程的哲学小结下节预告第 3 章的收官节。前面我们搞定了协议第 10 节、数据结构第 11 节、能力全景第 12 节、成本第 13 节、流式第 14 节——还剩最后一块拼图怎么让模型的输出被程序安全地消费。为什么这块拼图这么重要看一个 Agent 的典型工作流模型分析完任务输出一个行动计划——下一步调什么工具、参数是什么。这个计划要被你的代码解析执行。如果模型输出的是自然语言包裹的 JSON你的解析代码就永远活在今天它又换了个包装的恐惧里。Agent 的世界里模型输出 程序输入。这个接口不稳整个系统就是沙地上盖楼。问题的根源模型是话痨先直面问题的根源。模型的本质是什么一个被海量自然语言训练出来的、以说人话为本能的生成器。它的整个训练生涯都在学怎么把话说得像话——所以让它输出一个赤裸裸的 JSON它会本能地加开场白“好的以下是您需要的JSON”——礼貌但致命包 markdown json … ——它觉得这样更专业补注释JSON 里塞// 注释非法语法截尾输出到一半被 max_tokens 掐了还欠一个右括号自由发挥字段名多了少了、类型飘了age: 25字符串你写json.loads(model_output)撞上其中任何一条就是一场异常。别怪模型它只是做了它这辈子最擅长的事——说人话。是我们的问题没跟它约法三章。约法三章就是三层防御一层层看。第一层防御JSON Output 模式第一层是官方兜底JSON Output 模式。第 12 节露过面这次展开讲透。respclient.chat.completions.create(modeldeepseek-flash,messages[{role:system,content:你是 JSON 生成器只输出 JSON。},# 约束要写进 system{role:user,content:生成一个虚构的用户name, age, email},],response_format{type:json_object},# 官方开关强制 JSON)print(resp.choices[0].message.content)$ uv run python json_mode.py {name: 林小满, age: 28, email: linxiaomanexample.com}response_format{type: json_object}这个开关让服务端用受约束的解码保证输出是合法 JSON——技术上模型每生成一个 token 时都被限制在JSON 语法允许的下一个字符里选择语法上不可能跑出合法 JSON 的边界。这是三层里最硬的保证。但两个使用条件必须遵守否则直接报错system 或 user 消息里必须包含 “json” 这个词提示模型输出 JSON不然 API 会拒绝请求它只保证语法合法不保证结构符合——字段名对不对、类型对不对、有没有缺字段它不管。模型完全可能给你{username: 林小满}而你要的是name第二个条件就是第二层防御存在的原因。第二层防御pydantic 校验JSON Output 保证了话是机器话但没保证说的是你要的话。结构校验交给 pydantic——这也是五条铁律里JSON 处理一律 pydantic的主场。用 pydantic 定义你要的合同frompydanticimportBaseModel,EmailStr,FieldclassUser(BaseModel):name:strField(min_length1)# 不许空名字age:intField(ge0,le150)# 0~150防25岁字符串或-3email:EmailStr# 邮箱格式强校验然后一行验收userUser.model_validate_json(raw_json)# 解析类型转换约束一气呵成这行代码替你干了四件事解析 JSON、类型转换25→25如果模型给了字符串的话、范围校验age 不可能是 999、格式校验email 必须像邮箱。任何一关不过抛出带字段路径的详细错误——age: Input should be less than or equal to 150而不是json.loads那句没头没尾的Expecting value。更妙的是pydantic 还是双向的。校验模型输出只是半程你还可以用User.model_json_schema()反向生成 JSON Schema 塞进提示词让模型照着表格填——定义和校验共用同一份真相永远不会再提示词说一套、校验查一套importjson schemaUser.model_json_schema()promptf生成一个虚构用户严格遵守此 JSON Schema\n{json.dumps(schema)}这个pydantic 模型即合同的模式会成为 DeepPilot 全项目的通用范式——从工具参数校验第 21 节、到子 Agent 结果交接第 12 章、到 MCP 的 Schema 生成第 13 章全用它。第三层防御自动重试有前两层失败率已经很低但不是零。剩下的长尾怎么办——重试但不是无脑重试。结构化输出的重试有个黄金技巧把校验错误喂回去。模型第一次输出不合格不要默默重跑它可能再犯一模一样的错要把你哪里错了明确告诉它渲染错误:Mermaid 渲染失败: Parse error on line 4: ...| D[把错误信息追加进对话age 必须是 0-150 的整数你给... -----------------------^ Expecting SQE, DOUBLECIRCLEEND, PE, -), STADIUMEND, SUBROUTINEEND, PIPE, CYLINDEREND, DIAMOND_STOP, TAGEND, TRAPEND, INVTRAPEND, UNICODE_TEXT, TEXT, TAGSTART, got STR实践证明这一招的修复率极高——因为模型看得到自己错在哪改起来又快又准。统计上第二轮修复率通常在九成以上三轮内基本收敛。defask_user_json(client,max_retries3):msgs[{role:system,content:f生成用户严格遵守{User.model_json_schema()}},]foriinrange(max_retries):rawclient.chat.completions.create(modeldeepseek-flash,messagesmsgs,response_format{type:json_object},).choices[0].message.contenttry:returnUser.model_validate_json(raw)# 校验通过直接交付exceptExceptionase:msgs.append({role:assistant,content:raw})# 它上次的答案msgs.append({role:user,content:f输出不合规{e}\n请修正后重新输出 JSON。})raiseRuntimeError(结构化输出重试耗尽)# 降级交给上层注意重试上限3 次——重试不是无限次的耗尽就走降级记留痕、报警、走人工分支。这个有限重试 优雅降级的骨架第 19 节会扩展成整个 DeepPilot 的容错体系。三层合体生产级输出管线把三层叠起来就是一个可以直接搬进 DeepPilot 的完整函数。给它一个正经的定位这是模型输出与你的程序之间的海关所有越境数据必须过检structured.py —— 三层防御的结构化输出管线需 DEEPSEEK_API_KEY。importosfromopenaiimportOpenAIfrompydanticimportBaseModel,EmailStr,Field clientOpenAI(base_urlhttps://api.deepseek.com,api_keyos.environ[DEEPSEEK_API_KEY])classUser(BaseModel):合同字段即约束注释即文档。name:strField(min_length1)age:intField(ge0,le150)email:EmailStrdefask_structured(prompt:str,model_cls:type[BaseModel],max_retries:int3)-BaseModel:生产级管线JSON Output(语法) pydantic(结构) 错误回传重试(长尾)。schemamodel_cls.model_json_schema()msgs[{role:system,content:f只输出 JSON严格遵守此 Schema{schema}},{role:user,content:prompt}]forattemptinrange(1,max_retries1):rawclient.chat.completions.create(modeldeepseek-flash,messagesmsgs,response_format{type:json_object},# 第1层语法保证).choices[0].message.contenttry:returnmodel_cls.model_validate_json(raw)# 第2层结构校验exceptExceptionase:print(f[第{attempt}次校验失败]{e.__class__.__name__}:{str(e)[:60]})msgs[{role:assistant,content:raw},{role:user,content:f输出违规{e}。请修正后重新输出。}]# 第3层raiseRuntimeError(f{model_cls.__name__}结构化输出重试耗尽)if__name____main__:uask_structured(生成一个虚构的用户信息,User)print(f验收通过:{u.name}/{u.age}/{u.email})$ uv run python structured.py 验收通过: 林小满 / 28 / linxiaomanexample.com这段代码 40 行不到但每一层都在各自的防区干活语法层挡掉包装和截断结构层挡掉字段漂移重试层消化长尾。从此你的下游代码拿到的都是验过货的强类型对象解析这个词从你的字典里消失了。防御性编程的哲学这节背后其实是一个贯穿整个课程的哲学模型输出永远不可信但永远可以被校验。跟第 6 节的模型永远不可信但永远可以被约束对上暗号了第 6 节说的是能力边界模型不能直接执行必须过 Harness 的手工具系统、权限本节说的是数据边界模型输出不能直接用必须过校验的海关pydantic两句话合起来就是 Agent 工程的世界观模型是个聪明但不稳定的员工Harness 是一套让它发光、又让它不闯祸的制度。后面你写 Agent Loop、工具执行、子 Agent 交接会一次次遇到这里要不要信任模型的输出——默认答案是不要加校验。校验的成本永远低于出错后的排查成本。小结模型输出 程序输入这个接口必须稳否则系统是沙地盖楼模型话痨是本能不是 bug。三层防御JSON Output 保语法受约束解码、pydantic 保结构类型范围格式、错误回传重试保长尾把校验错误喂回模型修复率极高。pydantic 模型即合同model_json_schema()进提示词、model_validate_json()验输出定义与校验共享同一份真相。重试要有限度3 次封顶耗尽走降级留痕人工分支无限重试是事故放大器。世界观收口能力边界靠约束工具/权限数据边界靠校验pydantic——第 3 章理论筑基到此全线贯通。下节预告理论筑基区第 3~5 章的第 3 章完结。但真正的重头戏从下一节开始——第 4 章 Agent Loop智能体的心脏。第 16 节先上思想课ReAct 循环推理与行动的交替之舞。Thought-Action-Observation 三段式怎么运转、一个完整的 ReAct 对话长什么样我会给你一段带真实控制台输出的模拟、为什么这个 2022 年的论文思想至今统治所有 Agent 产品。从下一节开始你离亲手写出 DeepPilot只剩一步。如果觉得本文对你有帮助欢迎点赞、收藏、关注三连本系列持续更新中80篇硬核实战关注不迷路~
返回列表