公司动态
Prompt 应用第一版:先让输入、输出和失败路径可控
Prompt 应用第一版先让输入、输出和失败路径可控文中的事故链路和数值均为说明性场景不对应特定线上事件上线标准应按实际压测和业务约束确定。团队刚接触大模型应用时最容易走入一个陷阱希望写出一个能应对所有边缘场景的超级 Prompt。最初版本的 Prompt 只有两百字效果不理想。于是大家开始往里面加约束从异常格式处理到语气要求再到各种少样本示例。三周下来Prompt 被撑到了近两千 Token。结果不仅推理延迟飙升模型还经常顾此失彼——修改了 A 规则B 规则的遵循率立刻下降。第一版 LLM 应用的目标绝不是做出一份完美的 Prompt而是用最简的 Prompt 配合确定性的工程代码搭出一套能跑通、可观测、可快速迭代的交付流水线。flowchart TD A[用户原始请求] -- B[精简 Prompt 构建] B -- C[调用 LLM 模型] C -- D{输出结构校验} D -- 校验通过 -- E[解析数据并返回] D -- 格式畸形/缺失字段 -- F[正则/规则修补器] F -- 修补成功 -- E F -- 修补失败 -- G[单字段降级重试 Prompt] G -- C开局只留三条硬约束为什么不要把 Prompt 变成两千字的说明书把大模型当作一个拥有无限记忆且严格遵守指令的 C 编译器是绝大多数工程故障的根源。LLM 的本质是概率注意力分配指令越长注意力的注意力权重就会被稀释得越严重。第一版 Prompt 的设计原则只有一条只保留业务核心逻辑、输出格式定义和最关键的一条负向限制。其余的语气调整、修辞润色以及极罕见的边界情况全部交给后处理代码或第二阶段再去解决。在一次发票信息提取系统的重构中原先的 Prompt 详细规定了 18 种不同省份发票的格式差异导致 GPT-4o 经常在识别税号时混淆。后来我们将 Prompt 缩减到不足百字只定义了标准的 JSON 数据结构并限定“无法识别的字段统一返回 null”。至于不同省份的特殊发票逻辑全部拿到 Python 后处理阶段用正则表达式处理。修改后Prompt 的 Token 长度减少了 85%模型的整体字段提取准确率反而从 78% 提升到了 94%。结构化输出的底层逻辑用 JSON Schema 强约束代替自然语言祈祷在 Prompt 里写“请务必返回 JSON 格式不要包含任何 markdown 标记或解释性文字”这种做法在并发请求达到几万次时一定会崩溃。即使设置了temperature0模型在受到特定输入干扰时依然会在 JSON 前后加上json ...或者“好的这是为您生成的结果”。解决这个问题的根本方法是在 API 层面启用结构化输出Structured Outputs或使用 JSON Schema 进行强制约束。当开启 OpenAPI 规范的 Response Format JSON Schema 时底层推理框架会在采样解码阶段施加语法掩码Grammar Masking。这意味着模型在生成下一个 Token 时不符合 JSON 语法的 Token 对应的概率会被直接置为零。如果使用的是开源模型如 Qwen2.5 或 Llama3可以通过 vLLM 的guided_json或 Outlines 框架实现算子级别的 JSON 语法约束。这比在 Prompt 里面苦口婆心地劝说模型遵守格式要可靠得多。边界处理与容错闸门当 LLM 输出畸形 JSON 时发生的捕获与修复在生产环境中你永远无法彻底避免网络抖动或模型截断导致的输出损坏。工程代码必须具备容错闸门。典型的容错流水线应当分为三层字符串清洗层利用正则表达式截取最外层的{和}剥离多余的控制字符。结构体强校验层使用 Pydantic 或 JSON Schema 进行字段类型与必填项校验。定向修复与降级层如果解析失败不要直接把原始错误扔给上游而是拼接上一次错误的 Traceback 进行精准重试或者回退到确定性的规则兜底逻辑。import json import re from typing import Optional, Dict, Any from pydantic import BaseModel, Field, ValidationError class InvoiceData(BaseModel): invoice_code: str Field(description发票代码通常为10-12位数字) amount_tax: float Field(description含税金额) vendor_name: str Field(description开票方名称) tax_id: Optional[str] Field(defaultNone, description纳税人识别号) def extract_invoice_with_boundary_guard( llm_client, raw_text: str, max_retries: int 2 ) - Dict[str, Any]: 通过确定性工程校验治理 LLM 输出的不确定性 包含正则修补与降级策略避免无效的高成本 Token 消耗 system_prompt ( 你是一个严格的数据提取助手。请从文本中提取发票信息。 如果字段缺失请填充 null。 ) user_prompt f目标文本如下\n{raw_text} for attempt in range(max_retries 1): try: # 优先使用结构化 API 响应若模型不支持则退回到标准调用 response llm_client.chat.completions.create( modelgpt-4o-mini, messages[ {role: system, content: system_prompt}, {role: user, content: user_prompt} ], temperature0.0, response_format{type: json_object} ) raw_content response.choices[0].message.content # 第一道防线正则清洗潜在的 markdown 包裹符与前导后置废词 match re.search(r\{.*\}, raw_content, re.DOTALL) cleaned_content match.group(0) if match else raw_content # 第二道防线Pydantic 类型与边界严格校验 parsed_json json.loads(cleaned_content) validated_data InvoiceData(**parsed_json) return validated_data.model_dump() except (json.JSONDecodeError, ValidationError) as e: # 捕获结构解析失败记录上下文日志用于上线后的样本集回放 if attempt max_retries: # 最终降级返回保底兜底字典避免上游调用栈崩溃 return { invoice_code: , amount_tax: 0.0, vendor_name: UNKNOWN, tax_id: None, _parse_error: str(e) } # 针对重试请求动态追加错误信息告知模型上一次的非法输出 user_prompt f\n\n注意你上一次输出的格式非法错误原因{str(e)}。请严格返回标准 JSON。性能与成本的平衡点Token 消耗与延迟的线上实测数据在优化 Prompt 时必须建立量化的延迟与 Token 评估基线。我们对比了三种不同 Prompt 方案在 10,000 次生产请求中的表现方案提示词 Token 数平均响应延迟 (P95)格式校验通过率单次请求成本方案 A超长 Prompt 10-shot 示例1850 Token3.4 秒91.2%$0.0037方案 B精简 Prompt 纯自然语言约束120 Token0.8 秒76.5%$0.0002方案 C精简 Prompt JSON Schema 规则修补150 Token0.9 秒99.8%$0.0003数据非常直观方案 C 在保持极低延时和极低成本的同时通过工程代码兜底实现了接近 100% 的格式可靠性。第一版演进收尾不追求 100% 准确率建立自动评价集才是正道第一版上线后真正的核心任务不是继续蹲在电脑前人工调整 Prompt而是积累 Bad Case 并构建评测集。在上线的前两周我们利用日志中间件自动抽样 5% 的线上真实请求将其落盘为 JSONL 文件。人工标注出正确答案后使用 Pytest 编写一套端到端自动化测试回归脚本。每当团队尝试修改 Prompt 或更换底层小模型时只需要在终端运行一行pytest test_prompt_eval.py。5 分钟内评测脚本就会输出新 Prompt 在 500 个真实边界样本上的准确率、召回率和平均延时变动。靠数据说话而不是凭感觉调参这才是大模型应用从玩具走向面向生产环境的工程的分水岭。