
AI 大模型面试如果只能准备一个工程化问题我建议优先准备这个怎么让 Agent 稳定输出结构化内容。原因很简单现在 Agent 已经不只是“聊天机器人”它要调工具、写文件、查数据库、跑工作流。模型输出只要是自然语言哪怕只有一个字段不符合预期下游代码就可能直接报错。面试官问这个问题表面在考 Prompt 怎么写实际在考你有没有处理过真实 Agent 的脏输出。单一手段很难拿到 100% 稳定我建议用四层约束Prompt 强制、正反示例、原生参数、代码校验。这篇文章会把这四条拆开讲清楚并给出一套可以直接落到代码里的 Agent 结构化输出实现。先说结论无论你用 OpenAI 系的接口还是本地部署的模型这四层约束都是通用的。第一层决定模型“原则上”按什么格式输出第二层决定模型“看到例子后”如何模仿输出第三层在接口层面强行约束数据结构第四层在代码层面做最终校验和重试。四层全部接上才能把结构化输出的失败率压到可控范围。文章后面会给出每一层的 Prompt 模板、调用参数和 Python 校验代码也会补充面试追问和排查清单。1. Agent 稳定输出结构化内容为什么成为面试必问到 2026 年AI 大模型相关岗位的面试已经不太会停留在“你了解 Agent 吗”这种概念层面更多会直接抛真实场景让 Agent 执行一个多步骤任务第一步把用户指令解析成结构化参数第二步调用工具第三步把工具结果整理成 JSON 返回。如果模型在第一步就输出了一段纯文本工具调用直接失败整个流程怎么兜底这个问题包含两个考察点。第一个是 Prompt Engineering 的基础功能不能把“输出格式要求”写清楚能不能用示例约束模型的输出风格。第二个是工程兜底能力Prompt 写得再好模型也有概率输出非法 JSON、漏字段、多字段、字段类型错误代码层需要一套校验和重试机制来兜住这些脏输出。很多候选人只答了第一点把系统提示词写得很复杂却忽略了模型输出天然具有概率性这就是被追问后暴露短板的地方。从实际开发角度看Agent 结构化输出的价值主要体现在四个方面工具调用参数解析比如让 Agent 生成 get_weather({city: 北京}) 的行为参数。数据抽取比如从长文本中抽取姓名、时间、金额。分类与决策比如判断用户请求是否需要转人工。多 Agent 协作上游 Agent 的输出直接作为下游 Agent 的输入。任何一个环节如果直接用 json.loads() 去解析模型输出一旦格式不合格整个链路就会断掉。面试官希望听到的答案不是“把 JSON 格式写在 Prompt 里”而是一套完整的约束体系。这也是本文用“四层约束”作为主线的原因。2. 四层约束方案速览先把整体方案列出来后面每一层单独展开。层级核心手段解决的问题是否能在代码层面兜底第一层Prompt 强制模型不知道格式要求是什么不能第二层正反示例模型知道格式要求但不一定模仿到位不能第三层原生参数接口层有 JSON Mode、Function Calling 等能力部分能第四层代码校验模型输出解析后可能缺少字段或类型不匹配能最终兜底这套体系的前三层尽量提高输出合规概率第四层负责最终兜底。实际项目中前两层通常用文本来解决第三层依赖具体模型接口能力第四层则完全由开发者控制。注意一个容易踩的误区不要认为某一层做到极致就够了。Prompt 写得再详细在低 temperature 下也有小概率输出多余内容Function Calling 在部分本地模型上并不完全稳定代码校验如果不接重试光报错也没有意义。四层必须协同工作而不是互相替代。3. 第一层Prompt 强制约束3.1 系统提示词里需要写清楚的三件事第一层约束的核心是让模型知道“你到底想要什么样的输出”。很多失败的 Prompt 都有一个共同问题只告诉模型“输出 JSON”但没有告诉模型 JSON 的结构、字段名、字段类型、取值规则和反例边界。Agent 开发时System Prompt 至少要包含三件事输出格式必须输出一个 JSON 对象禁止输出 Markdown、代码块或解释性文字。字段定义每个字段的键名、类型、含义、是否必填。边界条件某个字段缺失时用什么值填充比如默认值、空字符串还是 None。这三件事缺一不可。只写“请输出 JSON”模型大概率会输出带 json 代码块的文本写了字段名但不写类型字符串可能被输出成整数写了类型但不写缺失规则字段缺失时模型可能自行决定省略。3.2 一个可落地的 Prompt 模板下面是一个意图解析场景的 System Prompt 示例。实际使用时需要根据你的业务场景替换字段名和枚举值。你是一个智能助手 Agent 的意图解析器。 请从用户输入中提取关键信息并输出一个 JSON 对象。 输出要求 1. 只能输出一个 JSON 对象不要输出任何其他内容。 2. 不要使用 Markdown 代码块不要输出 json 标签。 3. JSON 必须包含以下字段 - intent: string取值范围为query_order、cancel_order、consult_human、unknown。 - order_id: string如果用户输入中没有订单号使用空字符串。 - reason: string描述用户的诉求如果没有明确诉求使用无。 4. 如果用户输入无法被识别intent 输出unknownreason 输出无法识别。这个 Prompt 的优点是字段名、类型、枚举值、缺失值规则全部明确。模型在绝大多数情况下会乖乖输出一个 JSON 对象但仍然不能保证百分百不出现 Markdown 代码块所以第一层只是“约束”不是“保证”。3.3 第一层的判断标准判断第一层是否生效不需要很复杂的指标。拿 30 条覆盖正常场景、边界场景、空输入场景的测试用例调用模型统计直接 json.loads() 的成功率。如果成功率已经达到 90% 以上说明 Prompt 基础是好的如果连这个标准都达不到不要急着调参数先改 Prompt重点检查字段定义和反例说明是否足够清晰。4. 第二层正反示例约束4.1 为什么示例比规则更有效对大多数模型来说规则定义是“抽象约束”示例是“具体模仿对象”。两者同时存在时模型更容易按照示例的风格输出。这也是 Few-shot 技术在结构化输出中一直有效的原因。面试时你可以这样回答Prompt 里的规则是限制性条件示例是生成样本模型通过少量示例学会“什么时候该输出什么格式”。正反示例结合比单纯堆砌规则更有说服力。反例尤其重要。模型经常出现的错误不是完全不知道格式而是会在某些边缘场景下输出多余内容。比如模型在输出 JSON 前加一句“好的根据您的要求查询结果如下”这句话在聊天场景没问题但在 Agent 工具调用场景就是致命错误。所以反例必须包含两种典型错误一种是非 JSON 格式比如带了解释性前缀或 Markdown 代码块另一种是字段错误。把这两种反例明确写到 messages 里模型会很大程度上收敛行为。4.2 正反示例的 messages 结构以 OpenAI 兼容接口为例正反示例通常放在 messages 里用 user 和 assistant 成对出现。下面是一个经过简化的示例。messages [ {role: system, content: 你是一个意图解析器必须输出 JSON 对象字段定义见系统提示词。}, {role: user, content: 我想查一下订单 20250101001 到哪了}, {role: assistant, content: {intent: query_order, order_id: 20250101001, reason: 查询订单状态}}, {role: user, content: 帮我取消订单 20250101002谢谢}, {role: assistant, content: {intent: cancel_order, order_id: 20250101002, reason: 用户要求取消订单}}, {role: user, content: 你之前那个回答能不能再说一遍}, {role: assistant, content: {intent: unknown, order_id: , reason: 无法识别}}, ]这里用了两个正例和一个反例。反例样本对应“无法识别”场景防止模型在无法理解用户意图时强行捏造订单号。真实项目中建议准备 5 到 10 组正反示例覆盖高频场景、边界场景和最容易出错的场景。正反示例不要和 System Prompt 字段定义冲突否则模型会被互相矛盾的信息搞乱。4.3 反例要覆盖哪些典型错误准备反例样本时可以优先覆盖这几类输出带前缀解释比如“好的生成结果如下”。输出带 Markdown 代码块比如 json 开头。输出缺少必填字段。输出字段类型错误比如把 order_id 输出成数组。输出超出枚举范围的值。每覆盖一种错误就补一组 user/assistant 对。模型看到此类输入对应的输出应该是什么样模仿的成本会远低于理解复杂规则的成本。这也是“正反示例”这一层被称为 Few-shot 约束的原因。5. 第三层模型原生参数与结构化输出5.1 JSON Mode 与 response_format当模型接口提供结构化输出能力时一定要用原生参数不要把希望全部押在 Prompt 上。OpenAI 系接口中常见做法是设置 response_format{type: json_object}并要求 Prompt 中出现“json”字样。在部分兼容接口中也提供类似 JSON Schema 的 response_format 声明。使用该参数后模型会优先输出合法 JSON虽然仍不能保证字段完全符合业务预期但能有效减少 Markdown 代码块前后的干扰文本。from openai import OpenAI client OpenAI( api_keyyour_api_key, base_urlyour_base_url ) response client.chat.completions.create( modelyour_model_name, messages[ {role: system, content: 你是意图解析器必须输出 JSON。}, {role: user, content: 我想查一下订单 20250101001 到哪了}, ], response_format{type: json_object}, temperature0, ) content response.choices[0].message.content print(content)上面代码中base_url 和 model_name 按你实际使用的服务商填写。如果使用的是本地部署模型也要确认服务是否兼容 OpenAI SDK。很多本地推理框架已经支持 response_format 参数但不支持时一般不会报错而是静默忽略需要单独验证。5.2 Function Calling 与 Tool CallingFunction Calling 是另一种更强的结构化输出约束。它的思路是先定义一个函数和参数 schema模型只能从 schema 中选字段生成调用参数接口返回的是结构化参数对象而不是需要二次解析的文本。这个方式非常适合 Agent 工具调用场景。tools [ { type: function, function: { name: query_order, description: 查询订单状态, parameters: { type: object, properties: { order_id: {type: string, description: 订单号}, }, required: [order_id] } } } ] response client.chat.completions.create( modelyour_model_name, messages[ {role: user, content: 我想查一下订单 20250101001 到哪了} ], toolstools, tool_choiceauto, temperature0, )返回结果中message.tool_calls[0].function.arguments 是一个 JSON 字符串再用 json.loads 解析即可。Function Calling 的好处是模型大概率会输出符合 schema 的参数坏处是并不是所有模型都对 Function Calling 支持得很好。如果实测某个模型在 Function Calling 下频繁返回空调用、重复调用或格式错误就需要回归到 Prompt JSON Mode 组合方案。5.3 重点控制的模型参数结构化输出场景下下面几个参数值得重点关注。参数建议策略temperature尽量设置为 0 或接近 0减少随机性top_p设置为 1 或按接口默认避免和 temperature 叠加扰动max_tokens给足 JSON 输出的空间防止输出被截断seed若接口支持固定 seed 便于问题复现response_format优先开启 JSON Object / JSON Schemastop必要时配置结束符避免模型输出多余文本这里特别提一个容易被忽略的点max_tokens 太小时模型输出可能被中途截断结果是一段不完整的 JSON代码校验阶段会频繁失败。面试时如果候选人能主动提到“max_tokens 截断导致 JSON 不完整”这个细节会比只说 temperature 要加分很多。6. 第四层代码校验与自动重试6.1 为什么代码校验是最终兜底前三层解决的是模型侧概率问题第四层解决的是代码侧确定性校验。无论模型输出多稳定代码里都应该有一个明确的结构化校验器。它能判断输出是否为合法 JSON、是否包含必填字段、字段类型是否匹配、枚举值是否合法。不是合法输出就直接抛出异常或触发重试。以 Pydantic 为例定义一个结构模型对模型输出做强校验。Pydantic 的好处是声明式定义字段和校验规则错误信息也足够清晰。下面的示例中IntentRequest 定义了 intent、order_id、reason 三个字段并限定 intent 的合法取值。from typing import Literal from pydantic import BaseModel, ValidationError, Field import json class IntentRequest(BaseModel): intent: Literal[query_order, cancel_order, consult_human, unknown] order_id: str reason: str 无 def parse_agent_response(content: str) - IntentRequest: data json.loads(content) return IntentRequest(**data)这里 json.loads 会把模型输出先解析成 Python 对象如果 content 是非法 JSON会抛 JSONDecodeError如果字段缺失或类型不对Pydantic 会抛 ValidationError。两层异常都适合在上层捕获并触发重试。6.2 自动重试把校验错误反馈给模型代码校验不只是报错还要把错误信息回传给模型让模型基于错误信息重新生成。这是 Agent 稳定输出结构化内容的最后一环。基本流程是模型第一次输出 - 代码校验 - 失败则拼接错误信息 - 重新发送请求。def generate_structured_json(messages: list[dict], max_retries: int 3): for attempt in range(max_retries): content call_model(messages) try: parsed parse_agent_response(content) return parsed except (json.JSONDecodeError, ValidationError) as e: messages.append({role: assistant, content: content}) messages.append({ role: user, content: f你刚才的输出不符合 JSON 格式要求错误信息{e}。请只输出一个合法 JSON 对象不要输出其他内容。 }) raise RuntimeError(多次重试后仍无法得到合法结构化输出)这种“失败后把错误信息拼回去再请求”的做法在 Agent 开发中非常实用。它相当于把代码校验的结果变成新的 Prompt 上下文让模型有机会自我纠正。实测中多数时候第二次就能纠正最多三次重试基本能拿到合法输出。如果三次仍失败最稳妥的做法是终止流程并进入人工兜底而不是无限重试避免浪费 token 和延迟。6.3 先校验再给下一次任务在多步 Agent 场景里还有一条原则上游 Agent 的结构化输出要先校验再传给下游。比如查询订单的 Agent 输出一个 order_id下游发送通知的 Agent 需要这个字段合法。如果不做校验就透传错误会扩散到整条链路。正确的做法是在每个 Agent 边界处都做一次解析和校验失败就重试或终止。这一步虽然增加了一点处理时间但能大幅提高整个 Agent 工作流的稳定性。7. 四层约束组合一个可运行的 Agent 示例把前面四层整合成一个完整流程可以这样理解第一步用 System Prompt 定义输出格式第二步在 messages 里追加正反示例第三步调用接口时开启 JSON Mode 并控制 temperature第四步用 Pydantic 校验结果校验失败则自动重试。完整代码示意如下。import json from typing import Literal from pydantic import BaseModel, ValidationError, Field from openai import OpenAI client OpenAI( api_keyyour_api_key, base_urlyour_base_url ) SYSTEM_PROMPT 你是一个智能助手 Agent 的意图解析器。 请从用户输入中提取关键信息并输出一个 JSON 对象。 输出要求 1. 只能输出一个 JSON 对象不要输出任何其他内容。 2. 不要使用 Markdown 代码块不要输出 json 标签。 3. JSON 必须包含以下字段 - intent: string取值范围为 query_order、cancel_order、consult_human、unknown。 - order_id: string如果用户输入中没有订单号使用空字符串。 - reason: string描述用户的诉求如果没有明确诉求使用无。 class IntentRequest(BaseModel): intent: Literal[query_order, cancel_order, consult_human, unknown] order_id: str reason: str 无 def build_messages(user_input: str): return [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: 我想查一下订单 20250101001 到哪了}, {role: assistant, content: {intent: query_order, order_id: 20250101001, reason: 查询订单状态}}, {role: user, content: user_input}, ] def call_model(messages): response client.chat.completions.create( modelyour_model_name, messagesmessages, response_format{type: json_object}, temperature0, ) return response.choices[0].message.content def parse_response(content: str) - IntentRequest: data json.loads(content) return IntentRequest(**data) def generate_intent(user_input: str, max_retries: int 3) - IntentRequest: messages build_messages(user_input) for attempt in range(max_retries): content call_model(messages) try: return parse_response(content) except (json.JSONDecodeError, ValidationError) as exc: messages.append({role: assistant, content: content}) messages.append({ role: user, content: f输出不符合要求错误信息{exc}。请只输出一个合法 JSON 对象不要输出 Markdown 或解释文字。 }) raise RuntimeError(无法得到合法结构化输出) if __name__ __main__: result generate_intent(帮我取消订单 20250101002谢谢) print(result.model_dump())这段代码可以直接改造成一个最小可运行的 Agent 意图解析模块。真实项目里你可以把 IntentRequest 换成业务自己的数据结构把 call_model 抽象成公共调用层把重试逻辑做成可配置项。整体代码结构不变。8. 常见问题与排查方法问题现象可能原因排查方式解决方案模型输出带 json 代码块Prompt 中格式要求不明确查看原始输出内容增加“不要使用 Markdown 代码块”的规则并配合正反示例模型输出缺少必填字段字段定义不清晰或模型上下文不足打印 messages 检查示例是否覆盖该场景补充字段缺失的正反示例开启 response_format 或 Function Callingjson.loads 报错模型输出被截断或包含解释文本查看 max_tokens 和输出尾部内容增大 max_tokens清理输出中的多余文本Pydantic 校验失败字段类型或枚举值超出预期查看 ValidationError 具体报错将错误信息回传给模型自动重试Function Calling 返回空调用模型对函数描述理解不足或本身能力有限检查函数 name 和 description 是否清晰简化函数 schema或改回 JSON Mode 方案重试后仍失败模型对该场景始终无法正确输出记录重试日志和错误样例终止流程进入人工兜底或模板兜底排查时建议做一件事把所有失败输出保存成日志包含原始 content、错误类型、messages 上下文。积累一段时间后这些失败样例就是你优化 Prompt 和训练数据的重要素材。9. 生产环境最佳实践第一永远不要把 Prompt 唯一化。系统里要预留一套固定的错误提示模板和重试策略任何模型输出都要经过校验器。第二控制调用成本。结构化输出的失败重试会消耗额外的 token建议设置重试上限并统计实际重试率。如果某个模型的重试率长期偏高果断换更合适的模型或调整 Prompt。第三把 schema 和 Prompt 分开维护。业务字段变更时只改 Pydantic 模型和 Prompt 中的字段描述不要把结构逻辑散落在代码各处。第四接口服务要加超时和限流。实际部署 Agent 服务时模型接口的延迟并不稳定超时时间要适当放宽同时限制单用户并发防止批量任务把服务打崩。第五要重视安全边界。Agent 上下文里如果涉及用户隐私、订单信息、金额数据模型输出日志需要脱敏不能直接把原始数据写进日志。涉及人脸、声音、版权素材等相关场景必须确认授权和合规要求。结构化输出本身是一种技术手段但最终产出内容是否合法合规仍然需要人工复核。第六批量任务要额外注意失败恢复。批量处理场景中单个任务失败不应该影响整批数据。建议每个任务独立捕获异常失败任务进入重试队列记录任务 ID 和错误日志整个批处理结束后统一查看失败明细。10. 总结与下一步四层约束这套方案最值得先动手验证的是代码校验层。先写一个 Pydantic 模型再写一段“解析失败就重试”的代码用一个本地模型跑 50 条测试输入记录失败率和重试次数。这个实验能帮你快速感知结构化输出的真实水平也会让你在面试中被问到“如果模型输出不合格怎么办”时能给出具体的代码方案而不是空谈思路。最容易踩的坑有两个。一是只用 Prompt 不加校验把稳定性寄托在模型的“自觉”上二是加了校验但不做重试报错后流程直接中断。记住一个原则Prompt 负责降低失败概率校验负责拦截失败结果重试负责挽回可修复的失败人工兜底负责处理最终的不可修复情况。下一步可以继续扩展的方向包括把校验 重试封装成通用装饰器或 Agent 框架组件在 Function Calling 和 JSON Mode 之间做一次效果对比把失败样例累积成微调数据集。四层约束不是面试标准答案而是一套可以持续迭代的工程方法论。建议收藏备用等真正写 Agent 时再回来对照实现。