今年面试大模型相关岗位时,“如何让 Agent 稳定输出结构化内容”几乎是绕不开的一道题。很多候选人能把 Agent 的原理讲得头头是道,但一被问到工程落地的细节就卡住了——模型偶尔多输出一个字段、少闭合一个括号、文字解释混入 JSON 中间,下游程序直接崩溃。这些问题的本质,并不是模型“不够聪明”,而是我们默认了大模型会乖乖按格式输出。
在真实项目中,让 Agent 稳定输出结构化内容需要一套组合拳。本文把这套方案拆成四层约束:Prompt 强制、正反示例、原生参数、代码校验。每一层解决一类问题,四层叠加之后,才能做到“绝大多数时候稳定,偶尔不稳定也能自动修复”。这篇文章会结合面试回答思路和可运行的代码示例展开,希望对你准备面试和落地 Agent 项目都有帮助。
1. 为什么结构化输出是 Agent 开发的核心问题
1.1 Agent 场景与普通对话的差异
普通对话场景中,模型输出的是给人看的内容,多一个语气词、少一截解释都不影响交流。但 Agent 场景完全不同,模型的输出往往要交给下游系统处理,比如:
- 让 Agent 判断用户意图,返回一个操作指令。
- 让 Agent 抽取一段文本中的结构化信息,写入数据库。
- 让 Agent 决定调用哪个工具、传入什么参数。
- 让 Agent 完成任务后,返回一段可被前端渲染的结果。
在这些场景里,模型的输出必须符合约定好的格式,才能被代码解析。否则,哪怕模型回答的内容再正确,只要 JSON 格式损坏,整个 Agent 链路就走不下去。
1.2 为什么大模型输出不稳定
大模型本身是一个概率模型。它生成每一个 token 时,都根据前文上下文计算一个概率分布,然后从这个分布中采样。这意味着:
- 同样的 prompt,两次输出可能不同。
- 模型可能生成看似合法但语义跑偏的内容。
- 模型可能误以为用户要求它“解释一下”,于是把回答写成了描述性文字。
即使把 temperature 调成 0,也不代表输出是确定性的,只是降低了随机采样的随机性。因此,做 Agent 开发时,不能把“模型会老老实实按格式输出”当作假设,而应该把它当作一条需要不断加固的链路。
1.3 面试官到底在考察什么
面试官问结构化输出,表面上看是在问技术方案,实际上考察的是候选人对大模型落地的理解深度。通常会关联这几个层面:
- 是否了解大模型生成机制的不确定性。
- 是否知道 Prompt 层面的优化手段及其边界。
- 是否熟悉不同厂商 API 的参数含义。
- 是否具备工程防御思维,比如解析异常、重试、兜底。
- 是否能从一个完整链路的角度设计解决方案。
所以,回答这个问题时,最忌讳只回答一句“我在 Prompt 里让它输出 JSON”。更好的回答方式是给出分层的约束方案,并说明每层的优缺点和适用场景。这正是本文要展开的内容。
2. 四层约束的整体设计思路
在给出代码之前,先站在面试官的视角把四层约束的整体设计讲清楚。
| 层级 | 核心手段 | 解决什么问题 | 局限性 |
|---|---|---|---|
| Prompt 强制 | 系统提示词、格式模板、字段约束 | 让模型知道要按什么格式输出 | 软约束,模型可能不遵守 |
| 正反示例 | 少样本示例、错误示例、边界示例 | 让模型理解格式的细节和边界 | 占 token,对复杂格式帮助有限 |
| 原生参数 | response_format、temperature、seed 等 | 从 API 层面约束输出格式 | 依赖平台能力,不同平台差异大 |
| 代码校验 | JSON Schema 校验、解析防御、重试 | 保证最终进入业务逻辑的数据合法 | 无法保证内容语义正确,只是格式合法 |
这四层约束可以理解成一道流水线:
Prompt 约束格式方向 + 示例约束格式细节 + 原生参数对齐 API 能力 + 代码校验守住最后一道关实际项目中,四层不是互斥的,而是叠加使用。我见过很多团队只做了第一层,结果模型一换版本就出问题;也有团队只做第四层,不做 Prompt 优化,导致重试率高、体验差。合理的做法是每一层都做一点,把模型输出拉到可控的区间,同时让代码具备自愈能力。
3. 第一层约束:Prompt 强制
3.1 系统提示词的基本写法
Prompt 强制是最直接的手段,核心思路是在系统提示词里明确告诉模型:你的输出必须是什么格式、包含哪些字段、遵循什么规则。
一个常见但比较粗糙的写法是:
请以 JSON 格式输出。这种写法的问题在于,没有告诉模型 JSON 的结构长什么样。模型可能输出一个数组,可能多一层嵌套,可能把某个字段改成别的名字。下面这个写法会好很多:
请以 JSON 格式输出,结构如下: { "intent": "字符串,表示用户意图", "params": { "city": "字符串,城市名称", "date": "字符串,日期,格式为 YYYY-MM-DD" } }不过,只看这个描述,模型仍然可能自由发挥。更稳的做法是给出一个“模板”,同时把字段规则写清楚。
3.2 完整 Prompt 示例
一个面向 Agent 任务的系统提示词,至少应该包含以下部分:
- 角色定义:让模型知道自己的职责。
- 输出格式:具体到 JSON 结构。
- 字段说明:每个字段的含义和取值约束。
- 输出规则:什么情况下输出什么,不允许输出什么。
你是一个智能客服 Agent 的意图识别模块。 用户输入一段自然语言,你需要判断用户意图,并输出结构化 JSON 结果。 输出格式必须严格遵循以下结构: { "intent": "buy / refund / consult / unknown", "params": { "product": "用户询问的商品名称,未提及则为空字符串", "order_id": "用户提供的订单号,未提及则为空字符串" }, "confidence": 0.0 到 1.0 之间的数字,表示置信度 } 输出规则: 1. 只输出 JSON,不要输出任何解释性文字。 2. 不要使用 Markdown 代码块包裹 JSON。 3. intent 只能取 "buy"、"refund"、"consult"、"unknown" 四个值。 4. 如果无法识别意图,intent 输出 "unknown"。这个 Prompt 从角色、格式、字段、规则四个维度约束输出。相比单纯写“输出 JSON”,这种写法能让模型少犯很多格式错误。
3.3 Prompt 强制层的局限
需要清醒地认识到,Prompt 强制属于软约束。模型不一定会严格跟随,原因包括:
- 模型本身对格式的遵循能力有限。
- 用户输入中如果包含特殊要求,可能干扰模型的输出行为。
- 模型版本的更新可能导致行为漂移。
- 复杂格式下,模型容易出现字段缺失或类型错误。
所以,Prompt 强制只是第一步,不能作为唯一防线。面试中如果能说出这个观点,会让面试官觉得你真正踩过坑。
4. 第二层约束:正反示例
4.1 示例的三种类型
在 Prompt 中加入示例,属于少样本学习(Few-shot Learning)的范畴。模型会对示例产生较强的模仿倾向。示例可以分为三类:
- 正例:告诉模型“符合要求的输出长什么样”。
- 反例:告诉模型“这种输出是错的,不要这样”。
- 边界示例:让模型理解模糊情况下的处理方式。
反例的价值往往被低估。很多时候模型不是不会输出 JSON,而是它以为“加一段解释也没关系”。一个反例可以直接说明这一点:
错误输出示例(绝对禁止): 好的,我来帮您分析用户意图。根据您的输入,我判断用户想购买商品,以下是结果: {"intent": "buy", "params": {"product": "手机", "order_id": ""}}通过这个反例,模型能明确知道“不要解释,直接输出 JSON”的含义。
4.2 正反示例在 Prompt 中的组织方式
在实际应用中,一般把示例放在格式说明之后:
输入示例: 用户说:我想买一部华为手机 正确输出示例: {"intent": "buy", "params": {"product": "华为手机", "order_id": ""}} 输入示例: 用户说:帮我查一下昨天的订单 正确输出示例: {"intent": "consult", "params": {"product": "", "order_id": ""}} 错误输出示例(禁止模仿): 用户意图是购买,商品是华为手机。 {"intent": "buy", "params": {"product": "华为手机", "order_id": ""}} 边界输出示例: 用户说:随便看看 {"intent": "unknown", "params": {"product": "", "order_id": ""}}这里注意一点:示例的输入部分要和真实用户输入风格接近。如果示例过于“规范”,模型在遇到口语化输入时可能泛化得不好。
4.3 示例数量与 token 的平衡
示例不是越多越好。每个示例都会消耗上下文 token,影响响应速度和成本。在大多数场景下,正例 2 到 3 个、反例 1 个、边界示例 1 个就够了。如果任务本身格式固定、规则简单,可能不需要太多示例,Prompt 强制那层已经把问题解决大半了。
面试中可以补充一个工程经验:如果模型频繁在同一个格式点上出错,优先针对这个点增加一个反例,比新增一个正例更有效。
5. 第三层约束:原生参数
5.1 为什么需要原生参数
有些大模型服务商提供了专门约束输出格式的 API 参数。相比在 Prompt 里“请求”模型输出 JSON,原生参数是从 API 层面保证模型输出符合格式。这类能力通常叫 JSON Mode、Structured Output 或受控解码。
不同平台的叫法和用法不一样。以常见的 OpenAI 兼容接口为例,大致支持以下几类参数:
| 参数 | 作用 | 使用建议 |
|---|---|---|
| response_format | 指定输出为 JSON 对象 | 适合需要机器解析的场景 |
| temperature | 控制随机性 | 结构化输出场景建议调低 |
| top_p | 控制采样范围 | 通常配合 temperature 使用 |
| max_tokens | 限制输出长度 | 防止截断导致 JSON 不完整 |
| seed | 指定随机种子 | 提高可复现性,但不保证完全一致 |
如果你使用的是国内大模型平台,通常也会提供类似能力,但参数名可能不同。面试中可以说“具体参数名因平台而异,但思路是一致的”。
5.2 一个基于 OpenAI 兼容接口的调用示例
下面是一个 Python 调用示例,演示如何通过原生参数约束模型输出 JSON。为了演示方便,这里使用 OpenAI 兼容接口的通用写法,实际平台需要按照你的 SDK 文档调整:
# 文件路径:examples/structured_call.py from openai import OpenAI client = OpenAI( api_key="your-api-key", base_url="your-base-url" ) response = client.chat.completions.create( model="your-model-name", response_format={"type": "json_object"}, temperature=0.0, max_tokens=500, messages=[ { "role": "system", "content": ( "你是一个订单查询助手。用户输入查询请求后," "输出 JSON,包含 order_id 和 status 两个字段。" ) }, { "role": "user", "content": "帮我查一下订单 20240101123456 的状态" } ] ) content = response.choices[0].message.content print(content)如果平台支持response_format且模型本身具备 JSON 输出能力,返回的content会是一个合法的 JSON 对象字符串,不会夹杂解释性文字。
5.3 参数调整的边界
这里必须说清楚一个容易被误解的点:response_format这类参数只是约束了输出的结构“是 JSON”,并不保证 JSON 里的内容一定符合业务预期。
什么意思?
- 模型可能输出合法的 JSON,但字段值是错误的。
- 模型可能输出合法的 JSON,但缺少某个业务必填字段。
- 模型可能把字符串类型写成数字类型。
所以,原生参数解决的是“格式合法”问题,内容正确性还需要代码校验来兜底。
另外,temperature=0不意味着输出 100% 确定。部分推理模型内部会做多次采样,模型版本、上下文长度、并行计算都可能引入变化。如果要确保可复现,可以设置seed,但对生产场景来说,代码校验比“追求完全可复现”更有价值。
6. 第四层约束:代码校验
6.1 为什么代码校验必不可少
做完前三层约束后,模型输出大概率已经很规范,但仍可能偶发以下几种问题:
- JSON 解析失败,比如多了一个逗号。
- JSON 合法,但缺少必填字段。
- JSON 合法,但字段类型不符。
- JSON 合法,但字段值不在允许范围内。
- 输出被 max_tokens 截断,导致 JSON 不完整。
代码校验的作用,就是把“模型输出”和“业务逻辑”之间加一道闸门。无论模型说什么,只要过不了校验,就不能进入下一步。
6.2 使用 JSON Schema 校验
JSON Schema 是一种描述 JSON 数据结构的规范,能定义字段类型、必填项、取值范围等约束。Python 中可以用jsonschema库来做校验。
安装依赖:
pip install jsonschema下面是一个校验函数示例:
# 文件路径:validator.py import json import jsonschema from jsonschema import ValidationError AGENT_SCHEMA = { "type": "object", "properties": { "intent": { "type": "string", "enum": ["buy", "refund", "consult", "unknown"] }, "params": { "type": "object", "properties": { "product": {"type": "string"}, "order_id": {"type": "string"} }, "required": ["product", "order_id"] }, "confidence": { "type": "number", "minimum": 0.0, "maximum": 1.0 } }, "required": ["intent", "params", "confidence"] } def validate_agent_output(raw_content: str): """ 校验模型输出,返回解析后的 dict。 校验失败时抛出 ValueError。 """ try: data = json.loads(raw_content) except json.JSONDecodeError as e: raise ValueError(f"JSON 解析失败: {e}") from e try: jsonschema.validate(instance=data, schema=AGENT_SCHEMA) except ValidationError as e: raise ValueError(f"JSON Schema 校验失败: {e.message}") from e return data# 文件路径:demo_validate.py from validator import validate_agent_output # 模拟模型输出 model_content = '{"intent": "buy", "params": {"product": "手机", "order_id": ""}, "confidence": 0.95}' try: result = validate_agent_output(model_content) print("校验通过:", result) except ValueError as e: print("校验失败:", e)如果模型输出多了一个字段remark,使用 JSON Schema 校验默认不会报错,因为 JSON Schema 默认允许额外字段。如果你希望严格禁止,可以加上"additionalProperties": false。实际项目中要不要加,取决于你的业务场景。如果下游序列化对多余字段不敏感,可以放宽这个限制,减少因模型输出额外字段而导致的失败。
6.3 重试与兜底机制
代码校验还有一个重要角色:配合重试机制。当校验失败时,不要直接把错误抛给用户,而是可以选择让模型重新生成一次。这一步在 Agent 链路里非常常见。
一个简单的重试流程:
# 文件路径:agent_with_retry.py import time from openai import OpenAI from validator import validate_agent_output client = OpenAI( api_key="your-api-key", base_url="your-base-url" ) SYSTEM_PROMPT = """ 你是一个订单查询助手。用户输入查询请求后,输出 JSON,包含 order_id 和 status 两个字段。 只输出 JSON,不要输出任何其他文字。 """ def call_model(user_input: str, max_retries: int = 2): retries = 0 while retries <= max_retries: try: response = client.chat.completions.create( model="your-model-name", response_format={"type": "json_object"}, temperature=0.0, messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_input} ] ) content = response.choices[0].message.content result = validate_agent_output(content) return result except ValueError as e: print(f"第 {retries + 1} 次校验失败: {e}") retries += 1 if retries <= max_retries: time.sleep(0.5) raise RuntimeError("模型多次输出无法通过校验") if __name__ == "__main__": result = call_model("帮我查一下订单 20240101123456 的状态") print(result)这里的关键点是:校验失败后,可以简单重试,也可以在重试时把错误信息回传给模型,让模型“意识到”自己的输出有问题。后者效果更好,但实现复杂度高一些。面试中如果能聊到这一层,会显得很有实战经验。
6.4 代码校验的边界
代码校验能保证“格式合法”,但无法保证“语义正确”。模型可能输出一个格式完全合法、但内容其实错误的 JSON。所以,代码校验是兜底方案,不是质量保证方案。
如果要进一步提升质量,可以增加:
- 字段级别的规则校验,比如日期格式、金额范围。
- 与外部系统的交叉验证。
- 模型自检,让模型重新审视自己的输出。
- 人工介入渠道,用于低置信度场景。
7. 完整实战:四层约束封装成一个 Agent 输出模块
把上面四层约束串起来,做一个完整的 Agent 结构化输出模块。这个模块可以复用到实际项目中,主要包含四个文件:
agent_output_module/ ├── agent.py ├── prompt.py ├── schema.py └── validator.py7.1 prompt.py:Prompt 与示例
# 文件路径:agent_output_module/prompt.py SYSTEM_PROMPT = """ 你是智能客服 Agent 的意图识别模块。 用户输入一段自然语言,你需要判断用户意图,并输出结构化 JSON 结果。 输出格式必须严格遵循以下结构: { "intent": "buy / refund / consult / unknown", "params": { "product": "用户询问的商品名称,未提及则为空字符串", "order_id": "用户提供的订单号,未提及则为空字符串" }, "confidence": 0.0 到 1.0 之间的数字,表示置信度 } 输出规则: 1. 只输出 JSON,不要输出任何解释性文字。 2. 不要使用 Markdown 代码块包裹 JSON。 3. intent 只能取 "buy"、"refund"、"consult"、"unknown" 四个值。 4. 如果无法识别意图,intent 输出 "unknown"。 正确输出示例: {"intent": "buy", "params": {"product": "华为手机", "order_id": ""}, "confidence": 0.93} 错误输出示例(禁止模仿): 用户想购买华为手机。结果如下: {"intent": "buy", "params": {"product": "华为手机", "order_id": ""}, "confidence": 0.93} 边界输出示例: 用户说:随便看看 {"intent": "unknown", "params": {"product": "", "order_id": ""}, "confidence": 0.55} """7.2 schema.py:JSON Schema 定义
# 文件路径:agent_output_module/schema.py AGENT_SCHEMA = { "type": "object", "properties": { "intent": { "type": "string", "enum": ["buy", "refund", "consult", "unknown"] }, "params": { "type": "object", "properties": { "product": {"type": "string"}, "order_id": {"type": "string"} }, "required": ["product", "order_id"] }, "confidence": { "type": "number", "minimum": 0.0, "maximum": 1.0 } }, "required": ["intent", "params", "confidence"] }7.3 validator.py:校验与解析
# 文件路径:agent_output_module/validator.py import json import jsonschema from jsonschema import ValidationError from schema import AGENT_SCHEMA def parse_and_validate(raw_content: str): """ 解析模型输出并校验格式。 参数: raw_content: 模型返回的原始字符串 返回: 校验通过的 dict 异常: ValueError: 解析或校验失败 """ if not raw_content: raise ValueError("模型输出为空") try: data = json.loads(raw_content) except json.JSONDecodeError as e: raise ValueError(f"JSON 解析失败: {e}") from e try: jsonschema.validate(instance=data, schema=AGENT_SCHEMA) except ValidationError as e: raise ValueError(f"JSON Schema 校验失败: {e.message}") from e # 额外业务规则:confidence 必须大于阈值 if data.get("confidence", 0) < 0.3: raise ValueError("置信度过低") return data这里加了一个简单的业务规则:置信度低于 0.3 时即使格式合法也视为不可信。实际项目中,这种规则可以根据场景灵活设计。
7.4 agent.py:Agent 调用链封装
# 文件路径:agent_output_module/agent.py import time from openai import OpenAI from prompt import SYSTEM_PROMPT from validator import parse_and_validate class AgentOutputClient: def __init__(self, api_key: str, base_url: str, model: str): self.client = OpenAI( api_key=api_key, base_url=base_url ) self.model = model def run(self, user_input: str, max_retries: int = 2): """ 调用模型并返回结构化结果。 参数: user_input: 用户输入 max_retries: 最大重试次数 返回: 结构化 dict """ retries = 0 while retries <= max_retries: try: response = self.client.chat.completions.create( model=self.model, response_format={"type": "json_object"}, temperature=0.0, max_tokens=500, messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_input} ] ) raw_content = response.choices[0].message.content result = parse_and_validate(raw_content) return result except ValueError as e: print(f"第 {retries + 1} 次校验失败,原因: {e}") retries += 1 if retries <= max_retries: time.sleep(0.5) raise RuntimeError("模型多次输出无法通过校验")7.5 运行测试
# 文件路径:run_demo.py from agent_output_module.agent import AgentOutputClient client = AgentOutputClient( api_key="your-api-key", base_url="your-base-url", model="your-model-name" ) inputs = [ "我想买一部华为手机", "帮我查一下订单 20240101123456 的状态", "今天天气怎么样", ] for text in inputs: print(f"用户输入: {text}") try: result = client.run(text) print("结构化输出:", result) except RuntimeError as e: print("任务失败:", e) print("-" * 40)预期输出大致如下:
用户输入: 我想买一部华为手机 结构化输出: {'intent': 'buy', 'params': {'product': '华为手机', 'order_id': ''}, 'confidence': 0.93} ---------------------------------------- 用户输入: 帮我查一下订单 20240101123456 的状态 结构化输出: {'intent': 'consult', 'params': {'product': '', 'order_id': '20240101123456'}, 'confidence': 0.89} ---------------------------------------- 用户输入: 今天天气怎么样 结构化输出: {'intent': 'unknown', 'params': {'product': '', 'order_id': ''}, 'confidence': 0.51} ----------------------------------------实际输出内容与你使用的模型、Prompt 有关,但整体格式应该是健壮的。如果模型偶发输出不合格,重试机制会自动介入。
这个实战案例已经把四层约束完整串起来了。面试中讲到这个模块,你可以在白板上画出链路图,然后按层解释每一层的职责。逻辑清晰、层次分明,会是一个比较加分的回答。
8. 常见问题与排查思路
真正做 Agent 结构化输出时,会遇到不少细碎问题。下面整理了一份高频问题排查表。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 模型偶尔输出解释性文字 | Prompt 约束力度不够,或用户输入干扰模型 | 增加反例,强调“只输出 JSON”,必要时使用 response_format 参数 |
| JSON 合法但缺少字段 | Prompt 没说明必填字段,或模型偷懒 | 在 JSON Schema 里配置 required,并在 Prompt 中强调字段必填 |
| JSON 解析偶发失败 | 输出被 max_tokens 截断,或模型输出了多余字符 | 调大 max_tokens,清理输出内容,增加重试机制 |
| response_format 参数不生效 | 平台不支持该参数,或模型版本不支持 | 查看 API 文档,确认该模型是否支持 JSON Mode |
| 校验通过但内容错误 | 模型语义理解错了 | 优化 Prompt、增加 few-shot 示例、考虑增加模型自检环节 |
| 重试次数过多导致接口超时 | 每次重试都走完整调用流程 | 设置超时时间,重试时带上错误信息让模型修正输出 |
| 输出稳定但偶尔字段值枚举越界 | 枚举值约束只写在 Prompt,没写进 Schema | 在 Schema 中配置 enum,代码校验阶段拦截非法值 |
8.1 排查结构化输出问题的方法论
遇到结构化输出不稳定时,建议按下面的顺序排查:
- 先看原始输出。把模型返回的原始字符串打印出来,观察是格式问题、内容问题还是截断问题。
- 区分格式错误与语义错误。格式错误是第一优先级,先通过 Prompt、原生参数、代码校验解决;语义错误需要深入分析 Prompt 和示例。
- 检查具体出错字段。记录哪个字段频繁出错,针对该字段增加说明或示例。
- 检查是否触发了平台安全机制。有些平台会对提示词做合规检测,如果命中违规提示,模型会直接拒绝输出。需要检查提示词内容是否符合平台规范。
- 观察模型版本变化。同一个 Prompt 在不同模型版本之间可能表现出明显差异,版本升级后需要回归测试。
8.2 一个具体的截断案例
假设模型输出如下内容:
{"intent": "buy", "params": {"product": "华为手机", "order_id": ""}, "confidence": 0.9明显缺少闭合括号,这是max_tokens截断导致的问题。排查思路是:
- 打印原始输出,确认末尾是否被截断。
- 查看 token 使用量,判断是否达到上限。
- 适当调大
max_tokens。 - 在代码解析时,可以尝试用容错解析库,比如
json_repair,但生产环境更推荐直接重试。
json_repair是一个开源库,能自动修复部分 JSON 语法错误。用法如下:
from json_repair import repair_json broken_json = '{"intent": "buy", "params": {"product": "华为手机", "order_id": ""}, "confidence": 0.9' repaired = repair_json(broken_json) print(repaired)不过要注意,json_repair是容错手段,不能替代重试机制和 Schema 校验。它能修复语法层面的小问题,但对语义错误无能为力。
9. 最佳实践与工程建议
9.1 Prompt 与代码分离管理
实际项目中,Prompt 往往需要频繁调整。如果每次改 Prompt 都要改代码、发版本,效率就太低了。建议把 Prompt 模板和示例放到配置中心或独立的文本文件中,做到“不改代码就能调 Prompt”。
另外,Prompt 建议用模板引擎统一管理,比如 Python 的string.Template或Jinja2。好处是可以动态插入变量,比如把当前日期、业务白名单等信息拼进系统提示词。
9.2 建立回归测试集
结构化输出最怕“改一处坏一片”。建议准备一组回归测试用例,覆盖正常输入、边界输入、非法输入、易混淆输入。每次修改 Prompt 或更换模型后,跑一遍回归测试,对比输出是否符合预期。
回归测试集不需要很大,20 到 50 条就足够发现问题。
9.3 监控与日志
Agent 调用链中,模型输出是最不可控的环节。建议记录以下日志:
- 原始输出内容。
- 解析是否成功。
- 校验失败的原因。
- 重试次数。
- 最终是否成功。
有了这些日志,才能快速定位是 Prompt 问题、参数问题还是模型问题。
9.4 给模型留退路
不是所有输入都应该被强行结构化。当模型置信度低时,可以返回unknown意图,并让 Agent 转入人工处理或澄清对话。强行让模型给一个低置信度答案,可能引入严重业务风险,尤其在涉及订单、支付、医疗等场景时。
9.5 安全的变更流程
如果你负责的 Agent 服务已经线上运行,涉及 Prompt 修改、模型版本切换、结构化约束调整时,建议遵循以下流程:
- 先在测试环境跑回归用例。
- 小流量灰度发布。
- 对比新旧版本的输出质量。
- 确认无问题后再全量发布。
- 保留回滚方案。
这个流程不仅能减少线上事故,在面试中讲出来,也能体现工程化的思考方式。
10. 面试回答模板与进阶建议
10.1 一个可参考的面试回答结构
面试中被问到“如何让 Agent 稳定输出结构化内容”时,可以这样组织回答:
第一步,说明问题本质: 大模型是基于概率采样的生成模型,输出天然具有不确定性。Agent 场景下,模型输出要被下游代码消费,所以必须做约束和兜底。 第二步,给出分层的方案: 我一般会从四个层面来约束。 Prompt 层面,明确输出格式、字段规则和禁止项。 示例层面,通过正例、反例和边界示例,让模型模仿正确格式。 参数层面,使用 response_format、temperature 等原生参数,从 API 层面限制格式。 代码层面,用 JSON Schema 校验、重试和兜底逻辑,保证进入业务系统的数据合法。 第三步,讲清每层的边界: Prompt 和示例是软约束,模型可能不遵守。 原生参数能保证格式合法,但不保证内容正确。 代码校验是底线,但只能拦截格式问题,不能解决语义错误。 第四步,补充实战经验: 比如线上遇到过 max_tokens 截断导致 JSON 解析失败,通过调大 max_tokens 和增加重试解决。 也遇到过模型升级后输出行为变化,所以建立了回归测试集。这个回答有概念、有方案、有分层、有实战案例,整体会显得比较完整。
10.2 进阶学习方向
如果还想继续深挖,可以从以下几个方向扩展:
- 不同平台的 Structured Output 实现原理,比如 OpenAI、Anthropic 以及国内主流大模型平台的差异。
- 更复杂的 Agent 编排,比如多工具调用时如何约束参数。
- 模型自检与反思机制,让模型校验自己的输出。
- 大模型输出评测体系,用自动化指标评估结构化输出的质量。
这些点无论对面试还是实际项目,都很有价值。AI 大模型相关职位面试中,考察的往往不是你是否知道某个 API,而是你是否具备系统性解决问题的能力。四层约束不是一个标准答案,但它是从工程实践中沉淀下来的通用思路。理解每一层的边界和配合方式,你就能在面试中答出深度,也能在真实项目中让 Agent 稳定落地。