From f4e742481d4f1f977e9791aedfe8cad5d6650700 Mon Sep 17 00:00:00 2001 From: ericwyuan Date: Thu, 20 Aug 2026 16:14:27 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20json=5Fparser=20=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E8=A6=81=E6=B1=82=E6=A8=A1=E5=9E=8B=E8=BE=93=E5=87=BA=20comput?= =?UTF-8?q?e=5Fprovider=20=E2=80=94=20Gemini=20=E6=9C=89=E6=95=88=E5=93=8D?= =?UTF-8?q?=E5=BA=94=E8=A2=AB=E8=AF=AF=E6=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因: validate_schema 把 compute_provider 列为必填并校验非空数组, 但提示词模板从不要求模型输出该字段(它是 orchestrator 在解析成功后 自行填充的内部记账字段)。Gemini 返回完整合法 JSON 时被误判解析失败, 100% 复现。NVIDIA 走单帧轻量解析绕过了该校验,故此前仅 Gemini 受影响。 修复: compute_provider 从必填清单移除,缺失/非法时归一为空数组, 由 format_cloud_result 用实际成功的 provider 覆盖填充。 验证: 单元测试模拟 Gemini 真实输出(无 compute_provider + markdown fence 包裹)解析通过。 --- .../fam_edge/ai_orchestrator/json_parser.py | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/fam-edge/src/fam_edge/ai_orchestrator/json_parser.py b/fam-edge/src/fam_edge/ai_orchestrator/json_parser.py index ae0e7a8..c0b96c6 100644 --- a/fam-edge/src/fam_edge/ai_orchestrator/json_parser.py +++ b/fam-edge/src/fam_edge/ai_orchestrator/json_parser.py @@ -46,8 +46,12 @@ def parse_vlm_json(raw: str) -> dict: def validate_schema(data: dict) -> dict: - """Schema 校验 + 脏数据清洗""" - required = ["global_summary", "entities_json", "frame_details", "compute_provider"] + """Schema 校验 + 脏数据清洗 + + 注意: compute_provider 是内部记账字段(orchestrator 在解析成功后填充), + 提示词从不要求模型输出它,故不做必填校验。 + """ + required = ["global_summary", "entities_json", "frame_details"] for k in required: if k not in data: raise VLMOutputInvalidError(f"缺失字段: {k}") @@ -94,11 +98,11 @@ def validate_schema(data: dict) -> dict: }) data["frame_details"] = cleaned_frames - # compute_provider 校验为数组 - if not isinstance(data["compute_provider"], list): - raise VLMOutputInvalidError("compute_provider 必须为数组") - if len(data["compute_provider"]) == 0: - raise VLMOutputInvalidError("compute_provider 不能为空数组") - data["compute_provider"] = [str(p) for p in data["compute_provider"]] + # compute_provider: 模型通常不输出(提示词不要求),缺失/非法时归一为空数组, + # 由 orchestrator.format_cloud_result 用实际成功的 provider 覆盖填充 + cp = data.get("compute_provider") + if not isinstance(cp, list): + cp = [] + data["compute_provider"] = [str(p) for p in cp] return data