限时福利领取


当LLM输出变成开发者的噩梦

上周对接某电商客服系统时,我们让LLM生成订单查询响应。理想中的数据结构应该是这样的:

{
  "order_id": "12345",
  "status": "shipped",
  "estimated_delivery": "2023-12-25"
}
但实际收到的是这种自由发挥版本:
{
  "您的订单": "#12345",
  "当前状态": "已发货",
  "预计到达时间": "圣诞节当天"
}

非结构化数据问题示例

为什么选择JSON Schema

| 方案 | 开发成本 | 可维护性 | 错误定位 | 扩展性 | |----------------|----------|----------|----------|--------| | 正则表达式 | 高 | 差 | 困难 | 弱 | | 自定义解析器 | 中 | 中 | 一般 | 中 | | JSON Schema | 低 | 优秀 | 精确 | 强 |

核心实战四步走

1. Schema语法三件套

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["order_id", "status"],
  "properties": {
    "order_id": {
      "type": "string",
      "pattern": "^\\d{5}$"
    },
    "status": {
      "enum": ["processing", "shipped", "delivered"]
    }
  }
}

2. 与LangChain集成示例

from langchain.output_parsers import StructuredOutputParser
from jsonschema import validate

schema = {
    # 上述schema定义
}

def validate_with_retry(llm_output, max_retries=3):
    for attempt in range(max_retries):
        try:
            validate(instance=llm_output, schema=schema)
            return llm_output
        except Exception as e:
            print(f"Attempt {attempt + 1} failed: {str(e)}")
            # 这里可以加入自动修正逻辑
    raise ValueError("Max retries exceeded")

代码验证流程

3. 动态Schema生成

def build_dynamic_schema(user_query):
    base_schema = {
        "type": "object",
        "properties": {}
    }

    if "日期" in user_query:
        base_schema["properties"]["date"] = {
            "type": "string",
            "format": "date"
        }

    return base_schema

4. 性能优化三板斧

  1. Schema缓存:使用@lru_cache装饰器缓存编译后的验证器
  2. 批量验证:采用jsonschema.Draft7Validator预编译实例
  3. 框架集成:FastAPI示例:
    @app.post("/validate")
    async def validate_data(data: dict):
        try:
            validate(instance=data, schema=settings.SCHEMA)
            return {"status": "valid"}
        except ValidationError as e:
            return JSONResponse(
                status_code=422,
                content={"detail": e.message}
            )

生产环境血泪教训

  • 递归深度:限制$ref嵌套不超过5层
  • 字符集陷阱:始终指定"contentEncoding": "UTF-8"
  • 版本管理:使用$schema字段声明版本

终极思考题

我们是否应该允许LLM在additionalProperties: false时依然能创造性地返回metadata字段?这个平衡点该如何把握?

架构思考

Logo

音视频技术社区,一个全球开发者共同探讨、分享、学习音视频技术的平台,加入我们,与全球开发者一起创造更加优秀的音视频产品!

更多推荐