Conversation
Clean up LLM responses by removing Markdown code fences (```json and ```), trimming whitespace, and then parsing the cleaned string as JSON. If json.loads fails, raise a ValueError with the invalid payload. This prevents crashes when the model wraps JSON in code blocks instead of returning raw JSON (previously returned json.loads(response) directly).
There was a problem hiding this comment.
Pull request overview
该 PR 旨在解决部分大模型在 response_format={"type":"json_object"} 下仍返回被 Markdown 代码块包裹的 JSON,导致后端 json.loads() 解析失败并触发 500 的问题。
Changes:
- 在
chat_json中增加对 Markdown 代码块围栏(/json)的清理逻辑 - 为 JSON 解析增加
try/except,将解析失败转换为更明确的异常
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| except json.JSONDecodeError: | ||
| raise ValueError(f"LLM返回的JSON格式无效: {cleaned_response}") No newline at end of file |
There was a problem hiding this comment.
这里的异常信息把 cleaned_response 原文完整拼进了 ValueError。由于上层 API(例如图谱相关接口)会把 str(e)/traceback 返回给前端并记录日志,这会把模型返回内容(可能包含用户上传文档片段/敏感信息)直接泄露且可能导致响应体过大。建议只返回截断后的预览(例如前 N 字符)并把完整内容仅写入服务端日志或附加到内部调试字段。
| return json.loads(cleaned_response) | ||
| except json.JSONDecodeError: | ||
| raise ValueError(f"LLM返回的JSON格式无效: {cleaned_response}") No newline at end of file |
There was a problem hiding this comment.
chat_json 的返回类型标注为 Dict[str, Any],但 json.loads 可能解析出 list/str/number 等非对象类型;而现有调用方普遍使用 .get(...),这会在运行时崩溃。建议在 loads 后校验解析结果必须是 dict,否则抛出更明确的异常。
| return json.loads(cleaned_response) | |
| except json.JSONDecodeError: | |
| raise ValueError(f"LLM返回的JSON格式无效: {cleaned_response}") | |
| data = json.loads(cleaned_response) | |
| except json.JSONDecodeError: | |
| raise ValueError(f"LLM返回的JSON格式无效: {cleaned_response}") | |
| if not isinstance(data, dict): | |
| raise ValueError(f"LLM返回的JSON必须是对象类型,但实际为: {cleaned_response}") | |
| return data |
| except json.JSONDecodeError: | ||
| raise ValueError(f"LLM返回的JSON格式无效: {cleaned_response}") No newline at end of file |
There was a problem hiding this comment.
当前捕获 json.JSONDecodeError 后丢弃了原始异常的位置信息/原因,排查格式问题会更困难。建议 except json.JSONDecodeError as e,并在新的异常里包含 e.msg/e.lineno/e.colno 等关键信息,同时使用异常链(raise ... from e)保留堆栈。
| except json.JSONDecodeError: | |
| raise ValueError(f"LLM返回的JSON格式无效: {cleaned_response}") | |
| except json.JSONDecodeError as e: | |
| raise ValueError( | |
| f"LLM返回的JSON格式无效: {cleaned_response} " | |
| f"(错误: {e.msg}, 行: {e.lineno}, 列: {e.colno})" | |
| ) from e |
|
经过测试好像这样依旧会500,你调试的时候是可以解决问题的吗? |
|
我找到原因了, MiniMax M2.5 是推理模型,即使通过 OpenAI 兼容 API 调用,其 content 字段会包含 ... 思维链内容。实际返回大致如下: 而 OpenAI 的推理模型(如 o1、o3)把思考内容和最终回复分开存放在不同字段:
而 MiniMax M2.5 通过 OpenAI 兼容 API 调用时,默认把思考内容直接塞进 content 字段,用 标签包裹和正文混在一起。 MiniMax 文档里也提到了这一点:原生的 OpenAI API 的 MiniMax-M2.5 模型 content 字段会包含 标签内容 最新的commit代码已经修复 |
Some models (MiniMax M2.5, GLM-4.7, GLM-5) don't respect json_object format and return markdown-wrapped JSON, causing json.loads() to fail. Changes: - Add json_utils.py with clean_llm_json_response(), parse_llm_json() - Auto-strip markdown code blocks (```json ... ```) - Provide safe_parse_llm_json() for non-throwing usage - Update llm_client.py to use new helper Related: 666ghj#72, 666ghj#64, 666ghj#58, 666ghj#48
|
最新的代码,今天使用minimax2.5还是存在500错误 [backend] [02:52:07] INFO: 调用 LLM 生成本体定义... |
生成本体结构时部分模型(已测试minimax-m2.1 minimax-m2.5 glm-4.7 glm-5都有相同情况)似乎不遵守json_object的格式,会返回markdown包裹的json代码块 导致json.loads()解析错误
对md代码块标记进行了清理并额外增加了try except防止出错导致500
#64
#58
#48
不确定是否都是这个原因导致的