← 精品代码功能 · 可复用实现库

精品功能 05:LLM 输出的容错 JSON 解析与降级链

\+ worry_debate_game/nodes.py(_clamp_int 第 305-309 行、evaluate_round 第 312-349 行)

返回字符串代替数组、给出越界数值。程序必须容错。

核心实现

第一层:容错解析(查资料,去掉围栏、抠出花括号):

def _loads_json_loose(content):
    """从模型输出中稳健地提取 JSON 对象(容忍代码块围栏/前后缀文字)。"""
    text = (content or "").strip()
    text = re.sub(r"^```[a-zA-Z]*\s*|```\s*$", "", text).strip()
    start, end = text.find("{"), text.rfind("}")
    if start != -1 and end != -1 and end > start:
        text = text[start : end + 1]
    return json.loads(text)

第二层:字段消毒(类型宽容 + 截断 + 空值兜底):

tags = data.get("tags") or []
if isinstance(tags, str):                       # 模型可能返回字符串而不是数组
    tags = re.split(r"[,,、\s]+", tags)
tags = [str(t).strip() for t in tags if str(t).strip()][:3]
return {
    "title": str(data.get("title") or "").strip()[:30],
    "body": str(data.get("body") or "").strip() or transcript,   # 正文空就用原文
    ...
}

第三层:数值夹逼(worry,模型可能给 100 分或 -3 分):

def _clamp_int(value, lo: int, hi: int, default: int) -> int:
    try:
        return max(lo, min(hi, int(round(float(value)))))
    except (TypeError, ValueError):
        return default

第四层:降级链(AI 失败/未配置 → 本地规则,且告知原因):

if AI_API_KEY:
    try:
        result = ai_summarize(transcript)
    except Exception as e:
        result = local_summarize(question, transcript)   # 降级
        result["warning"] = str(e)                        # 但如实告知
else:
    result = local_summarize(question, transcript)

设计亮点

  1. 四层防御:去围栏 → 抠 JSON → 字段消毒 → 数值夹逼 → 降级兜底,层层不漏
  2. 把模型当"能力强但不守规矩的实习生":prompt 是交办,代码是验收
  3. 降级不隐瞒:失败时附 warning 字段,用户知道发生了什么
  4. 永不崩溃:任何异常都被吞掉并给默认值——AI 功能挂了,主流程不能挂
  5. 正则 \{.*\} + 花括号截取双保险,处理"前言 + JSON + 后记"

可复用性评估

开源化建议

内置类型强制、范围夹逼、默认值表

你这个胜在零依赖、可直接嵌进任何项目

对照开源

方案特点
Prompt 约束 + 容错解析(本功能)零依赖、通用;要自己写容错
JSON Mode / 结构化输出 API更可靠;依赖模型支持
Function Calling最可靠;概念更多
pydantic + instructorPython 生态主流;较重

相关课程

第 10 课(prompt 与链)、第 15 课(结构化输出实战)