项目拆解(二):模型输出是‘野‘的——三层兜底+重分析,把AI变成可靠零件
发布时间:2026/10/13 10:23:58 锦皓数字建站
:模型输出是‘野‘的——三层兜底+重分析,把AI变成可靠零件`)
项目拆解二模型输出是野的——三层兜底重分析把AI变成可靠零件研发效能智能助手 · 第 2 个项目第 2 篇 | 上篇拆架构这篇拆容错上一篇把 AI 代码归档系统的骨架拆完了FastAPI 四层结构、裸调 Ollama、Prompt 设计、SQLite 归档。评论区很多人在问同一个问题“你那个模型返回的 JSON 要是坏了怎么办模型崩了怎么办”问得好。这才是 AI 工程和调 API 玩具的分水岭。这篇就把系统里最见功夫的部分全拆开错误兜底的三层设计 重分析机制。所有代码都是项目里真实跑着的每个坑都是真机踩出来的。一、先说透大模型是系统里最不可靠的零件做传统后端的人有个习惯相信自己的代码。写json.loads就觉得一定会成功调接口就觉得一定会返回。做 AI 工程第一课就是把这种自信戒掉。大模型有三大野会挂Ollama 没启动、显存不够、机器卡死、请求超时会疯让它输出 JSON它给你json ...包起来或者直接输出一句好的我来分析一下会骗字段该是数组给你字符串该是数字给你五十该有风险给你空任何一个野没兜住用户看到的就是白屏、报错、或者更糟——假结果。所以这个系统从设计第一天就定了规矩大模型只负责提内容工程负责保可靠。围绕一次分析三层兜底层层设卡。二、错误全景图一次分析要过四道鬼门关用户点一下让 AI 分析并存档背后是这样的链路前端点击 → POST /api/analyze → 拼Prompt → 调Ollama → 解析JSON → 校验字段 → 入库 → 渲染上屏每一段都可能挂。我们把故障点全部列出来逐个设计对策环节会挂成什么样兜底层级调 Ollama连不上 / 超时 / HTTP 500第一层调用层JSON 解析多字了 / 用代码块包了 / 结构不对第二层解析层字段校验类型错 / 缺字段第二层解析层前端渲染空数组 / 带空格空串 / 空指针第三层展示层下面一层一层拆。三、第一层兜底调用层把技术错误翻译成人话直接看代码图1是 analyzer.py 的异常处理try:resprequests.post(f{OLLAMA_BASE_URL}/api/generate,jsonpayload,timeoutREQUEST_TIMEOUT,# 120秒7b首次加载要冷启动)exceptrequests.exceptions.ConnectionError:raiseOllamaError(连不上 Ollama请先在命令行执行: ollama serve)exceptrequests.exceptions.Timeout:raiseOllamaError(fOllama 超过{REQUEST_TIMEOUT}秒没响应模型可能在加载或机器卡顿)ifresp.status_code!200:raiseOllamaError(fOllama 返回 HTTP{resp.status_code},rawresp.text[:300])三个设计细节面试都能讲1. 超时给 120 秒不是随便拍的。7b 模型在 4GB 显存上首次加载要冷启动第一次请求慢得离谱。120 秒是实测出来的上限后面改成了真实倒计时给用户看见第五层用户就不焦虑了。2. 异常翻译成人话。ConnectionError对用户没有意义但连不上 Ollama请先在命令行执行 ollama serve是可执行的指令。AI 工程的产品化就是让错误提示可行动。3. 业务异常要带原始报文。OllamaError里存了rawresp.text[:300]——前端只看到一句中文但日志里能查到模型到底返回了什么。排查问题全靠这个尾巴。这里有个反直觉的设计底层异常一律翻译成业务异常抛出去不在调用层 try 到底。翻译是职责抛出是态度——路由层统一捕获转成 502前端统一渲染。错误处理最忌讳每层都 try 一下最后用户看到一脸懵。四、第二层兜底解析层JSON 坏了先别慌降噪重试一次第一层兜住了调不通但模型可能调通了却胡说八道。formatjson只是让 Ollama 用 JSON schema 解码不保证 7b 一定输出合法 JSON。实测它会干这些事在 JSON 外面包 json 代码块输出好的我分析如下再跟 JSON字段名改了language写成Language直接整个 JSON 语法错误解析层设计图2是完整重试机制# 第一次正常温度先试raw_call_ollama(prompt,temperature0.1)analysis_try_parse(raw)ifanalysisisnotNone:returnanalysis# 第二次温度降到 0再试一次logger.warning(第一次 JSON 解析失败用 temperature0.0 重试)raw_call_ollama(prompt,temperature0.0)analysis_try_parse(raw)ifanalysisisnotNone:returnanalysis# 两次都失败才认输raiseOllamaError(模型返回了无法解析的 JSON可能是模型能力不足或代码太奇怪,rawraw[:300])核心逻辑是_try_parse——解析失败返回 None 而不是抛异常让上层决定要不要重试def_try_parse(raw:str)-CodeAnalysis|None:rawraw.strip()# 防御有些模型还是会用 json ... 包一下剥掉ifraw.startswith():rawraw.strip()ifraw.lower().startswith(json):rawraw[4:].strip()try:datajson.loads(raw)returnCodeAnalysis(**data)except(json.JSONDecodeError,ValueError)ase:logger.warning(JSON 解析失败: %s; 原文前200字: %s,e,raw[:200])returnNone三个关键点1. 先剥代码块再解析。7b 实测经常用 json 包输出——这是它训练语料的习惯。strip() 去json 字样一次剥干净。这层防御让输出格式不完美从失败变成成功。2. 重试时温度降到 0.0。第一次用 0.1 是想让模型活一点别太机械但解析失败说明它已经疯了一次重试就必须把随机性压到最低——temperature0.0让输出退化成确定性通常第二次就老实了。实测重试成功率挺高用户几乎感觉不到这次重试。3. 两次失败才认输且带上原文。不无限重试浪费算力、拖死用户体验两次是实测出来的平衡点。认输时把raw[:300]塞进异常——开发者看到模型返回了无法解析的 JSON能立刻去日志翻原文而不是瞎猜。五、第三层兜底展示层模型输出的最后一道清洗前两层都过了数据入库了最后一步是上屏。但这里还有坑——模型在无风险时会返回什么真机实测它返回了[]甚至[ ]带空格的空串。前端直接渲染用户看到一个红色的空警告框吓一跳。修复代码index.html 里真实跑着的(a.risks||[]).map(ttt.trim()).filter(Boolean).length?a.risks.map(tspan classrisk${esc(t)}/span).join():span stylecolor:#34c759;无这段代码挺干净/span注意细节先t.trim()再filter(Boolean)。只filter(Boolean)挡不住[ ]带空格的字符串是真值必须先清空格再过滤。这是真机踩出来的第一次只写了filter(Boolean)第二天用户截图说为什么没有风险还弹红框一查是[ ]。同一行的另一个细节空的时候显示绿字无这段代码挺干净。空数组不展示空容器而是展示无风险的正面反馈——这种小细节决定了产品是能用还是好用。六、展示层的另一个细节真实倒计时不骗用户用户点了分析本地 7b 要跑 10-30 秒。进度条要不要不要——因为我们自己都不知道还要多久预估个假进度条用户一眼看穿更糟。方案是真实计时器index.htmlconststartTsDate.now();lettimersetInterval((){constsecMath.round((Date.now()-startTs)/1000);$(result).innerHTMLAI 分析中... 已用时${sec}秒本地 7b 模型稍慢请稍候;},1000);每秒更新已用时 X 秒——数字是真实流逝的时间不是拍脑袋的预估。用户体验知道系统没死知道大概要多久。同时分析期间按钮禁用$(btn).disabled true防止用户手滑重复提交把 Ollama 打爆。结果一到finally里停计时器、恢复按钮。小功能但等待是所有 AI 产品最容易被吐槽的环节值得较真。七、重分析机制一个不覆盖的设计取舍系统里有已归档的片段。用户经常想“这段代码当时分析漏了个风险能不能重新分析”这就是重分析需求。最初的设计方案是原地覆盖重分析 → 更新同一条记录。但细想有两个问题历史被抹掉了。如果新分析反而更差小模型不稳定旧结果找不回来无迹可循。看不到上次怎么判的、这次怎么判的没法对比模型进步所以最终做了**“回填式重分析”**点击列表里的片段 → 右侧展示结果 左侧自动回填代码→ 用户改不改都行 → 再点让 AI 分析并存档 →生成一条新记录。交互上它就像基于旧代码再分析一次但数据上每次都留痕。旧记录还在列表里新分析也入库了两个结果摆在一起天然可对比。等以后模型换大版本还能批量重跑一遍旧片段看模型进步了多少——这是覆盖式永远给不了的价值。顺手一提点击回填这个动作还顺带实现了查看详情点列表 → 右侧显示完整分析一个交互两种用途前端一行onclickshowDetail(id)就搞定。八、真机实测这些错误场景你迟早会遇到最后交个底这个系统在 4GB 显存 / 16GB 内存的机器上跑出来的真实错误场景场景现象兜底结果Ollama 没启动就点分析ConnectionError中文提示连不上 Ollama请先执行 ollama serve模型首次加载冷启动请求挂起很久120 秒超时兜底前端真实倒计时安抚模型输出被 json 包裹json.loads 失败剥代码块后解析成功用户无感知模型输出非法 JSON第一次解析失败temperature0.0 重试成功用户无感知连续两次都失败重试也失败抛无法解析的 JSON原文前300字进日志无风险时返回[ ]前端渲染空红框trimfilter(Boolean) 清洗显示无风险绿字你看真正的 AI 工程能力全在这些用户无感知的兜底里。用户只看到结果出来了或一句人话报错背后是三层设计在默默干活。九、总结兜底设计三条原则每个不可靠点都必须有失败路径。从调用到解析到渲染逐个列出故障点逐个设计兜底没有反正不会坏的侥幸错误必须可读、可行动。技术异常翻译成人话人话里带执行指令兜底要可观测。重试要打日志、异常要带原文、结果要留痕——出了问题能在日志里还原全程把这三条做到你就把调大模型的 Demo升级成了AI 工程。这套系统的完整源码含开发文档、全部注释、可直接运行在这里下载源码AI代码片段智能归档系统-FastAPIOllama本地大模型-完整源码资源地址https://download.csdn.net/download/weixin_54679968/93681154系列第三篇预告Prompt 工程实战——怎么让 7b 小模型稳定输出 60%-70% 准确率防照抄、防误报、边界认知全复盘。关注不迷路。关键词FastAPI、Ollama、Qwen2.5、本地大模型、错误兜底、JSON容错、重试机制、Prompt 工程、JavaPython
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。