"""Knowledge particle framing and type-shape cleanup.""" from __future__ import annotations import json from typing import Any from core.llm import chat_json as default_chat_json from core.models import Post from decode_content.contracts import BUSINESS_STAGES, CREATION_STAGES, WHAT_KINDS, SkillContract, load_contract from decode_content.gates import ChatJsonFn, how_gate, why_refute_gate CSTAGE = set(CREATION_STAGES) KINDS = set(WHAT_KINDS) KIND_FIX = {"多维度关系": "多维关系", "多维": "多维关系", "集合": "子集", "序列型": "序列"} def frame_knowledges( post: Post, read_text: str, *, contract: SkillContract | None = None, chat_json_fn: ChatJsonFn = default_chat_json, ) -> list[dict[str, Any]]: contract = contract or load_contract() user = ( f"原帖标题:{post.title or '(无)'}\n\n读懂后的完整内容:\n{read_text}\n\n" "按上面规则拆颗+判类型+成形+轻标签。作用域字段一律留空 []。" "只输出 JSON:{\"knowledges\":[ ... 见模板 ... ]}" ) return chat_json_fn(contract.phase1, user, timeout=120).get("knowledges") or [] def reshape_nonhow( knowledge: dict[str, Any], *, contract: SkillContract | None = None, chat_json_fn: ChatJsonFn = default_chat_json, ) -> list[dict[str, Any]]: contract = contract or load_contract() body = f"目标:{knowledge.get('purpose', '')}\n" + "\n".join( f"- 输入:{step.get('input', '')}|方法:{step.get('directive', '')}|产出:{step.get('output', '')}" for step in knowledge.get("steps", []) ) user = ( "【下面这块原被误判为 how 工序,实为「离散构成 / 原理」,请只拆成 What/Why 主颗——" "同一划分轴/组件库下的并列构成先合并成一颗 What 子集,body 里放各项;" "只有已展开成独立方法、判断框架或完整结构的构成项才单独拆 What;背后的原理/标准拆 Why;" "不要 how、不要组件颗,parent 一律 null。作用域字段留空 []。】\n\n" f"原标题:{knowledge.get('title', '')}\n{body}\n\n" "只输出 JSON:{\"knowledges\":[ ... 仅 what/why,见模板 ... ]}" ) try: out = chat_json_fn(contract.phase1, user, timeout=120).get("knowledges") or [] except Exception: out = [] reshaped: list[dict[str, Any]] = [] for idx, item in enumerate(out, 1): if item.get("type") == "how": continue item["id"] = f"{knowledge.get('id', 'k')}r{idx}" item["role"], item["parent"] = "主", None reshaped.append(item) return reshaped or [knowledge] def fix_fake_hows( knowledges: list[dict[str, Any]], *, contract: SkillContract | None = None, chat_json_fn: ChatJsonFn = default_chat_json, ) -> list[dict[str, Any]]: out: list[dict[str, Any]] = [] for knowledge in knowledges: if knowledge.get("type") == "how" and len(knowledge.get("steps", [])) >= 2: gate = how_gate(knowledge, chat_json_fn=chat_json_fn) if not gate.passed: reshaped = reshape_nonhow(knowledge, contract=contract, chat_json_fn=chat_json_fn) for item in reshaped: item.setdefault("metadata", {})["how_gate_reason"] = gate.reason out.extend(reshaped) continue out.append(knowledge) return out def drop_fake_whys( knowledges: list[dict[str, Any]], *, chat_json_fn: ChatJsonFn = default_chat_json, ) -> list[dict[str, Any]]: out: list[dict[str, Any]] = [] for knowledge in knowledges: if knowledge.get("type") == "why": gate = why_refute_gate(knowledge, chat_json_fn=chat_json_fn) if not gate.passed: verdict = gate.details.get("verdict") if verdict == "废话": continue if verdict in {"实为what", "实为how"}: knowledge["inferred"] = True knowledge["inferred_reason"] = f"why闸疑似{verdict}:{gate.reason}" out.append(knowledge) return out def normalize_knowledge_shapes(knowledges: list[dict[str, Any]]) -> list[dict[str, Any]]: how_ids = {k.get("id") for k in knowledges if k.get("type") == "how"} for knowledge in knowledges: knowledge["业务阶段"] = [ stage for stage in (knowledge.get("业务阶段") or []) if stage in BUSINESS_STAGES ] if knowledge.get("type") == "what" and knowledge.get("kind") not in KINDS: knowledge["kind"] = KIND_FIX.get((knowledge.get("kind") or "").strip(), knowledge.get("kind")) for step in knowledge.get("steps", []): if step.get("创作阶段") not in CSTAGE: step["创作阶段"] = None if knowledge.get("role") == "组件" and (knowledge.get("parent") or {}).get("how_id") not in how_ids: knowledge["role"] = "主" knowledge["parent"] = None return knowledges def frame_and_clean( post: Post, read_text: str, *, contract: SkillContract | None = None, chat_json_fn: ChatJsonFn = default_chat_json, ) -> list[dict[str, Any]]: contract = contract or load_contract() knowledges = frame_knowledges(post, read_text, contract=contract, chat_json_fn=chat_json_fn) knowledges = fix_fake_hows(knowledges, contract=contract, chat_json_fn=chat_json_fn) knowledges = drop_fake_whys(knowledges, chat_json_fn=chat_json_fn) return normalize_knowledge_shapes(knowledges) def knowledge_debug_json(knowledges: list[dict[str, Any]]) -> str: return json.dumps(knowledges, ensure_ascii=False)