{"record":{"id":"69bdc58b349b6a75","repo":"666ghj/MiroFish","slug":"ontology-result-must-be-an-object","errorCode":null,"errorMessage":"Ontology result must be an object","messagePattern":"Ontology result must be an object","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/app/services/ontology_generator.py","lineNumber":435,"sourceCode":"        \"\"\"长分块保留首尾，避免每个分块内部再次变成只看开头。\"\"\"\n\n        text = text.strip()\n        if len(text) <= char_limit:\n            return text\n\n        marker = \"\\n...(本分块中间内容省略)...\\n\"\n        if char_limit <= len(marker) + 20:\n            return text[:char_limit]\n\n        remaining = char_limit - len(marker)\n        head_len = remaining // 2\n        tail_len = remaining - head_len\n        return f\"{text[:head_len].rstrip()}{marker}{text[-tail_len:].lstrip()}\"\n    \n    def _validate_and_process(self, result: Dict[str, Any]) -> Dict[str, Any]:\n        \"\"\"验证和后处理结果\"\"\"\n        if not isinstance(result, dict):\n            raise ValueError(\"Ontology result must be an object\")\n\n        raw_entities = result.get(\"entity_types\")\n        raw_edges = result.get(\"edge_types\")\n        if not isinstance(raw_entities, list):\n            raw_entities = []\n        if not isinstance(raw_edges, list):\n            raw_edges = []\n        if not isinstance(result.get(\"analysis_summary\"), str):\n            result[\"analysis_summary\"] = \"\"\n\n        # Normalize entity entries before touching their fields. LLMs\n        # occasionally emit a bare string, null, or another scalar.\n        entity_name_map: Dict[str, str] = {}\n        processed_entities: List[Dict[str, Any]] = []\n        seen_entity_names = set()\n        for raw_entity in raw_entities:\n            if isinstance(raw_entity, str):\n                entity = {\"name\": raw_entity}","sourceCodeStart":417,"sourceCodeEnd":453,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/backend/app/services/ontology_generator.py#L417-L453","documentation":"Raised in OntologyGenerator._validate_and_process when the parsed LLM response for ontology generation is not a JSON object (dict). The generator asks the LLM for a JSON object with entity_types/edge_types/analysis_summary; if parsing yields a list, a bare string, or null (e.g. the model returned a JSON array, a markdown-fenced scalar, or empty content), this ValueError fires before any field extraction.","triggerScenarios":"LLM returns a JSON array of entities instead of an object; model outputs prose or a code block whose parsed JSON is a scalar; response truncated so the parser produced a non-dict fragment; weaker model ignoring the schema prompt.","commonSituations":"Switching to a smaller/cheaper model that ignores output-format instructions; prompts edited so the requested shape drifted; max_tokens set too low causing truncation and salvage-parsing; some providers returning top-level arrays by convention.","solutions":["Retry generation with the same prompt — non-dict output is often stochastic; add explicit 'respond with a single JSON object' instruction and an example in the prompt.","If the model consistently returns a list, wrap/normalize: accept a top-level list by mapping it to {\"entity_types\": result} if that matches intent.","Increase max_tokens to avoid truncation, and use JSON mode / response_format=json_object when the provider supports it.","Log the raw LLM output on failure to see exactly which shape the model emitted before patching."],"exampleFix":"# before\nresult = json.loads(raw)\nprocessed = gen._validate_and_process(result)\n# after\nresult = json.loads(raw)\nif isinstance(result, list):\n    result = {\"entity_types\": result}\nprocessed = gen._validate_and_process(result)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"def is_ontology_object(result: object) -> bool:\n    return isinstance(result, dict)","tryCatchPattern":"result = parse_llm_json(raw)\nif not isinstance(result, dict):\n    if isinstance(result, list):\n        result = {\"entity_types\": result}  # normalize observed LLM shape\n    else:\n        result = regenerate_with_stricter_prompt()  # one retry, then fail\nprocessed = gen._validate_and_process(result)","preventionTips":["Request JSON mode / response_format=json_object when the provider supports it.","Include a concrete JSON-object example in the prompt.","Log raw LLM output on validation failure to spot schema drift early."],"tags":["llm","json","validation","ontology","prompting"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}