rebuild_payloads.py 4.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. """从 web/frameworks.json 重建 web/payloads.json(不调 LLM)——确定性。
  2. 顺带两个守卫修正:
  3. · 组件颗的 parent.how_id 必须指向同帖一个 how 颗,否则降级为 orphan 主颗(role=主, parent=null)。
  4. · 组件颗 payload 的「出自」用所属 how 的 title(而非裸 id)。
  5. 用法:python3 scripts/rebuild_payloads.py
  6. """
  7. from __future__ import annotations
  8. import json
  9. from pathlib import Path
  10. ROOT = Path(__file__).resolve().parent.parent
  11. FW = ROOT / "web/frameworks.json"
  12. PL = ROOT / "web/payloads.json"
  13. TYPE2ATTR = {"how": "how工序", "what": "what构成", "why": "why原理"}
  14. def content(k: dict) -> str:
  15. t = k["type"]
  16. if t == "how":
  17. L = [f"目标:{k.get('purpose','')}"]
  18. for i, s in enumerate(k.get("steps", []), 1):
  19. L += [f"步骤{i}(目的:{s.get('intent','')})",
  20. f" 指引:{s.get('directive','')}", f" 产出:{s.get('output','')}"]
  21. return "\n".join(L)
  22. if t == "what":
  23. return "\n".join([f"界定:{k.get('界定','')}", "构成:"]
  24. + [f"- {c.get('要素','')}:{c.get('说明','')}" for c in k.get("构成", [])])
  25. return f"主张:{k.get('主张','')}\n依据:{k.get('依据','')}\n对创作的影响:{k.get('对创作的影响','')}"
  26. def main() -> None:
  27. d = json.loads(FW.read_text(encoding="utf-8"))
  28. payloads = []
  29. demoted = 0
  30. for p in d["posts"]:
  31. ks = p["knowledges"]
  32. how_ids = {k["id"] for k in ks if k["type"] == "how"}
  33. how_titles = {k["id"]: k.get("title") for k in ks if k["type"] == "how"}
  34. for k in ks:
  35. k["业务阶段"] = [b for b in (k.get("业务阶段") or []) if b in ("灵感", "选题", "脚本")]
  36. if k.get("role") == "组件" and (k.get("parent") or {}).get("how_id") not in how_ids:
  37. k["role"] = "主"
  38. k["parent"] = None
  39. demoted += 1
  40. for k in ks:
  41. t = k["type"]
  42. scopes, seen = [], set()
  43. def add(lst):
  44. for sc in lst:
  45. key = (sc["scope_type"], sc["value"])
  46. if key not in seen:
  47. seen.add(key)
  48. scopes.append({"scope_type": sc["scope_type"], "value": sc["value"]})
  49. if t == "how":
  50. for s in k.get("steps", []):
  51. add(s.get("作用域", []))
  52. else:
  53. add(k.get("作用域", []))
  54. ext = [{"key": "业务阶段", "type": "str", "value": v} for v in (k.get("业务阶段") or [])]
  55. if t == "how":
  56. cs, cseen = [], set()
  57. for s in k.get("steps", []):
  58. c = s.get("创作阶段")
  59. if c and c not in cseen:
  60. cseen.add(c)
  61. cs.append(c)
  62. ext += [{"key": "创作阶段", "type": "str", "value": v} for v in cs]
  63. ext += [{"key": "动作", "type": "str", "value": s["动作"]} for s in k.get("steps", []) if s.get("动作")]
  64. if k.get("role") == "组件" and k.get("parent"):
  65. par = k["parent"]
  66. ext.append({"key": "出自", "type": "str",
  67. "value": f"{how_titles.get(par.get('how_id'), par.get('how_id'))} 第{par.get('step')}步"})
  68. payloads.append({
  69. "source": {"id": p.get("source_id") or f"xhs_{p['post_id']}", "source_type": "post", "title": p.get("title", ""),
  70. "author": "", "source_metadata": {"platform": p.get("platform"), "url": p.get("url")}},
  71. "title": k.get("title"), "content": content(k), "dim_creations": ["创作"],
  72. "dim_attributes": [TYPE2ATTR.get(t, "how工序")], "scopes": scopes, "custom_ext": ext})
  73. FW.write_text(json.dumps(d, ensure_ascii=False, indent=1), encoding="utf-8")
  74. PL.write_text(json.dumps(payloads, ensure_ascii=False, indent=2), encoding="utf-8")
  75. print(f"rebuilt {len(payloads)} payloads; 降级组件→orphan: {demoted}")
  76. if __name__ == "__main__":
  77. main()