| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- """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)
|