split.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. """拆分:把帖子拆成一个或多个 What/Why/How 知识片段。"""
  2. from __future__ import annotations
  3. from typing import Optional
  4. from creation_knowledge.integrations.llm import ChatFn, default_chat
  5. from creation_knowledge.models import Evidence, ExtractedContent, KnowledgeItem, Post
  6. from creation_knowledge.prompts import load_prompt
  7. from creation_knowledge.stages._common import content_for_llm, norm_text, norm_types
  8. SYSTEM = "你是创作知识拆分助手,原文有什么提什么,不编造。"
  9. def _to_int(value) -> Optional[int]:
  10. try:
  11. return int(value)
  12. except (TypeError, ValueError):
  13. return None
  14. def _to_evidence(raw) -> Optional[Evidence]:
  15. """证据可能是字符串(旧)或 {text, card}(新)。"""
  16. if isinstance(raw, dict):
  17. text = norm_text(raw.get("text"))
  18. return Evidence(text=text, card=_to_int(raw.get("card"))) if text else None
  19. text = norm_text(raw)
  20. return Evidence(text=str(raw).strip()) if text else None
  21. def _to_item(raw: dict) -> Optional[KnowledgeItem]:
  22. what = norm_text(raw.get("what"))
  23. why = norm_text(raw.get("why"))
  24. how = norm_text(raw.get("how"))
  25. # knowledge_types 以非空字段为准(模型给的若不一致,用实际有内容的对齐)
  26. types = norm_types(raw.get("knowledge_types"))
  27. actual = [t for t, v in (("what", what), ("why", why), ("how", how)) if v]
  28. types = [t for t in types if t in actual] or actual
  29. if not types:
  30. return None # 三个都空,丢弃
  31. title = norm_text(raw.get("title")) or (what or why or how or "")[:20]
  32. evidence = [e for e in (_to_evidence(x) for x in (raw.get("evidence") or [])) if e]
  33. source_cards = [n for n in (_to_int(x) for x in (raw.get("source_cards") or [])) if n is not None]
  34. return KnowledgeItem(
  35. title=title, knowledge_types=types, what=what, why=why, how=how,
  36. source_cards=source_cards, evidence=evidence,
  37. )
  38. def split_post(
  39. post: Post,
  40. content: ExtractedContent,
  41. *,
  42. chat: Optional[ChatFn] = None,
  43. env_file: str = ".env",
  44. ) -> list[KnowledgeItem]:
  45. chat = chat or default_chat(env_file)
  46. user = load_prompt("split").format(
  47. title=post.title or "(无)",
  48. topics="、".join(post.topic_list) or "(无)",
  49. content=content_for_llm(post, content) or "(空)",
  50. )
  51. data = chat(SYSTEM, user)
  52. items = [_to_item(raw) for raw in (data.get("items") or [])]
  53. return [it for it in items if it is not None]