Kaynağa Gözat

对齐 ingest 接口:payload content 一律 string(what/why 结构化对象 json.dumps)

- build_payload/rebuild:what/why 的 content 对象 → json.dumps(ensure_ascii=False) 字符串(how 本就是字符串)
- 接口要求 content:str;之前 20/24 颗是 object 会 422,现 24/24 全 string
- lint:what/why 改为先 json.loads 解析再校验 name/kind/body / name/desc
- schema what/why:content 标为 string(JSON 字符串),内部结构移入 $comment
- frameworks.json 保留结构化不动;前端读 frameworks 不受影响
- 重生成 payloads.json,lint 24 颗 0 ERROR

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SamLee 3 hafta önce
ebeveyn
işleme
aba06201bb

+ 3 - 1
scripts/decompose.py

@@ -424,9 +424,11 @@ def build_payload(post, k: dict, how_titles: dict | None = None) -> dict:
         p = k["parent"]
         ext.append({"key": "出自", "type": "str",
                     "value": f"{how_titles.get(p.get('how_id'), p.get('how_id'))} 第{p.get('step')}步"})
+    c0 = build_content(k)  # how=字符串;what/why=结构化对象→json.dumps 成字符串(ingest 接口要求 content 为 string)
     return {"source": {"id": post.id, "source_type": "post", "title": post.title or "",
                        "author": post.author_name or "", "source_metadata": {"platform": post.platform, "url": post.url}},
-            "title": k.get("title"), "content": build_content(k),
+            "title": k.get("title"),
+            "content": c0 if isinstance(c0, str) else json.dumps(c0, ensure_ascii=False),
             "dim_creations": ["创作"], "dim_attributes": [TYPE2ATTR.get(t, "how")],
             "scopes": scopes, "custom_ext": ext}
 

+ 3 - 1
scripts/rebuild_payloads.py

@@ -105,7 +105,9 @@ def main() -> None:
             payloads.append({
                 "source": {"id": p.get("source_id") or f"xhs_{p['post_id']}", "source_type": "post", "title": p.get("title", ""),
                            "author": "", "source_metadata": {"platform": p.get("platform"), "url": p.get("url")}},
-                "title": k.get("title"), "content": content(k), "dim_creations": ["创作"],
+                "title": k.get("title"),
+                "content": (lambda c: c if isinstance(c, str) else json.dumps(c, ensure_ascii=False))(content(k)),
+                "dim_creations": ["创作"],
                 "dim_attributes": [TYPE2ATTR.get(t, "how")], "scopes": scopes, "custom_ext": ext})
     FW.write_text(json.dumps(d, ensure_ascii=False, indent=1), encoding="utf-8")
     PL.write_text(json.dumps(payloads, ensure_ascii=False, indent=2), encoding="utf-8")

+ 20 - 425
web/payloads.json

@@ -113,10 +113,7 @@
       }
     },
     "title": "爆款选题的“羞耻感”防伪定律",
-    "content": {
-      "name": "爆款选题的“羞耻感”防伪定律",
-      "desc": "真正能爆的选题往往由于其不仅通过了大脑审核,更通过了情绪审核。羞耻感是好选题的防伪标签,它本质上触碰到了社交礼仪包裹下、平时大家不敢说出口的真实内容。在内容过剩时代,观众需要的不是正确答案,而是有人帮他们说出“正确答案在特殊场景下不好使”的真相。没有情绪触发就没有转发,作者在发布前感到的安全感(无风险选题)通常等于观众的无感。冒犯感是对“正确共识”的挑战,这种撕裂感能产生更强的穿透力和爆发力。"
-    },
+    "content": "{\"name\": \"爆款选题的“羞耻感”防伪定律\", \"desc\": \"真正能爆的选题往往由于其不仅通过了大脑审核,更通过了情绪审核。羞耻感是好选题的防伪标签,它本质上触碰到了社交礼仪包裹下、平时大家不敢说出口的真实内容。在内容过剩时代,观众需要的不是正确答案,而是有人帮他们说出“正确答案在特殊场景下不好使”的真相。没有情绪触发就没有转发,作者在发布前感到的安全感(无风险选题)通常等于观众的无感。冒犯感是对“正确共识”的挑战,这种撕裂感能产生更强的穿透力和爆发力。\"}",
     "dim_creations": [
       "创作"
     ],
@@ -178,23 +175,7 @@
       }
     },
     "title": "赛道“圣经句”的特征",
-    "content": {
-      "name": "赛道“圣经句”的特征",
-      "kind": "多维关系",
-      "维度拆分规则": null,
-      "body": [
-        {
-          "item_name": "普遍认同性",
-          "item_desc": "赛道里人人都会点头,表面上表现为“正确答案”。",
-          "作用域": []
-        },
-        {
-          "item_name": "实际体感缺失",
-          "item_desc": "在实际执行中往往没人真信,或者在特定场景下会失效、让人感到“不好使”。",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"赛道“圣经句”的特征\", \"kind\": \"多维关系\", \"维度拆分规则\": null, \"body\": [{\"item_name\": \"普遍认同性\", \"item_desc\": \"赛道里人人都会点头,表面上表现为“正确答案”。\", \"作用域\": []}, {\"item_name\": \"实际体感缺失\", \"item_desc\": \"在实际执行中往往没人真信,或者在特定场景下会失效、让人感到“不好使”。\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],
@@ -253,48 +234,7 @@
       }
     },
     "title": "7种常用叙事结构及其适用场景",
-    "content": {
-      "name": "7种常用叙事结构及其适用场景",
-      "kind": "子集",
-      "维度拆分规则": "类型枚举",
-      "body": [
-        {
-          "item_name": "三幕式结构",
-          "item_desc": "最基础的线性叙事,起承转合。分为:第一幕(开端)交代背景、人设和冲突触发;第二幕(发展)核心冲突升级,主角遭遇阻碍,是人物成长和情节铺垫关键;第三幕(高潮与结局)矛盾爆发并解决,情节收尾。",
-          "作用域": []
-        },
-        {
-          "item_name": "英雄之旅结构",
-          "item_desc": "核心是“平凡人到英雄的蜕变”,适合成长弧光设计。分为三个阶段:启程(离开舒适区)、考验(挑战与社交、对抗反派)、归来(战胜危机获成长)。",
-          "作用域": []
-        },
-        {
-          "item_name": "非线性结构",
-          "item_desc": "打破时间先后,通过时间拆解重组。常用手法包括闪回、插叙、倒叙、多视角切换。适用于制造悬念和多维度理解人物。",
-          "作用域": []
-        },
-        {
-          "item_name": "环形结构",
-          "item_desc": "首尾呼应形成闭环。包含非循环类(场景台词呼应)和循环类(重复经历某段事件,如无限循环或宿命感设定)。",
-          "作用域": []
-        },
-        {
-          "item_name": "多线叙事结构",
-          "item_desc": "多条独立故事线并行并在节点汇合。每条线有独立主角和冲突,通过切换提升剧情密度,适用于展现核心事件全貌或展现共同主题(如阶级、人性)。",
-          "作用域": []
-        },
-        {
-          "item_name": "嵌套式结构",
-          "item_desc": "层层递进的套层结构(如戏中戏、梦中梦)。外层引出内层,内外层互为影响呼应。",
-          "作用域": []
-        },
-        {
-          "item_name": "反结构",
-          "item_desc": "无结构或碎片化结构,抛弃线性逻辑和核心冲突。注重氛围营造、情绪表达和人性探讨,靠人物状态和细节描写。适配文艺片、实验性影片。",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"7种常用叙事结构及其适用场景\", \"kind\": \"子集\", \"维度拆分规则\": \"类型枚举\", \"body\": [{\"item_name\": \"三幕式结构\", \"item_desc\": \"最基础的线性叙事,起承转合。分为:第一幕(开端)交代背景、人设和冲突触发;第二幕(发展)核心冲突升级,主角遭遇阻碍,是人物成长和情节铺垫关键;第三幕(高潮与结局)矛盾爆发并解决,情节收尾。\", \"作用域\": []}, {\"item_name\": \"英雄之旅结构\", \"item_desc\": \"核心是“平凡人到英雄的蜕变”,适合成长弧光设计。分为三个阶段:启程(离开舒适区)、考验(挑战与社交、对抗反派)、归来(战胜危机获成长)。\", \"作用域\": []}, {\"item_name\": \"非线性结构\", \"item_desc\": \"打破时间先后,通过时间拆解重组。常用手法包括闪回、插叙、倒叙、多视角切换。适用于制造悬念和多维度理解人物。\", \"作用域\": []}, {\"item_name\": \"环形结构\", \"item_desc\": \"首尾呼应形成闭环。包含非循环类(场景台词呼应)和循环类(重复经历某段事件,如无限循环或宿命感设定)。\", \"作用域\": []}, {\"item_name\": \"多线叙事结构\", \"item_desc\": \"多条独立故事线并行并在节点汇合。每条线有独立主角和冲突,通过切换提升剧情密度,适用于展现核心事件全貌或展现共同主题(如阶级、人性)。\", \"作用域\": []}, {\"item_name\": \"嵌套式结构\", \"item_desc\": \"层层递进的套层结构(如戏中戏、梦中梦)。外层引出内层,内外层互为影响呼应。\", \"作用域\": []}, {\"item_name\": \"反结构\", \"item_desc\": \"无结构或碎片化结构,抛弃线性逻辑和核心冲突。注重氛围营造、情绪表达和人性探讨,靠人物状态和细节描写。适配文艺片、实验性影片。\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],
@@ -380,18 +320,7 @@
       }
     },
     "title": "剧本前提的构成公式",
-    "content": {
-      "name": "剧本前提的构成公式",
-      "kind": "多维关系",
-      "维度拆分规则": null,
-      "body": [
-        {
-          "item_name": "前提构成成分",
-          "item_desc": "由『作者立场+人物+冲突+结论』构成。其本质是指称主题、论题、基本思想等,指引剧本达到目标。可以通过结局倒推,且情感可以决定前提。",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"剧本前提的构成公式\", \"kind\": \"多维关系\", \"维度拆分规则\": null, \"body\": [{\"item_name\": \"前提构成成分\", \"item_desc\": \"由『作者立场+人物+冲突+结论』构成。其本质是指称主题、论题、基本思想等,指引剧本达到目标。可以通过结局倒推,且情感可以决定前提。\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],
@@ -445,28 +374,7 @@
       }
     },
     "title": "人物构成的三维度模型",
-    "content": {
-      "name": "人物构成的三维度模型",
-      "kind": "多维关系",
-      "维度拆分规则": null,
-      "body": [
-        {
-          "item_name": "生理维度",
-          "item_desc": "包含性别、年龄、外表以及遗传特征等。在重要性排序中位列第一。",
-          "作用域": []
-        },
-        {
-          "item_name": "社会维度",
-          "item_desc": "包含阶层、职业、教育、家庭、社交等。在重要性排序中位列第二。",
-          "作用域": []
-        },
-        {
-          "item_name": "心理维度",
-          "item_desc": "包含道德标准、志向、挫折、性格、态度等。是生理与社会的产物,重要性排序第三。",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"人物构成的三维度模型\", \"kind\": \"多维关系\", \"维度拆分规则\": null, \"body\": [{\"item_name\": \"生理维度\", \"item_desc\": \"包含性别、年龄、外表以及遗传特征等。在重要性排序中位列第一。\", \"作用域\": []}, {\"item_name\": \"社会维度\", \"item_desc\": \"包含阶层、职业、教育、家庭、社交等。在重要性排序中位列第二。\", \"作用域\": []}, {\"item_name\": \"心理维度\", \"item_desc\": \"包含道德标准、志向、挫折、性格、态度等。是生理与社会的产物,重要性排序第三。\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],
@@ -524,33 +432,7 @@
       }
     },
     "title": "剧本冲突的四种类型",
-    "content": {
-      "name": "剧本冲突的四种类型",
-      "kind": "子集",
-      "维度拆分规则": "冲突的演进特征",
-      "body": [
-        {
-          "item_name": "静态冲突",
-          "item_desc": "人物无可见发展,缺乏创造升级冲突的能力时会出现此类冲突。",
-          "作用域": []
-        },
-        {
-          "item_name": "跳跃冲突",
-          "item_desc": "当强迫人物作出与其本性不相符的行动时,冲突会产生不自然的跳跃。",
-          "作用域": []
-        },
-        {
-          "item_name": "缓慢升级冲突",
-          "item_desc": "编剧不干预自然产生,是对立统一的人物的共同结果,最为推荐。",
-          "作用域": []
-        },
-        {
-          "item_name": "预示冲突",
-          "item_desc": "即张力,是对冲突的允诺,用于维持观众吸引力。",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"剧本冲突的四种类型\", \"kind\": \"子集\", \"维度拆分规则\": \"冲突的演进特征\", \"body\": [{\"item_name\": \"静态冲突\", \"item_desc\": \"人物无可见发展,缺乏创造升级冲突的能力时会出现此类冲突。\", \"作用域\": []}, {\"item_name\": \"跳跃冲突\", \"item_desc\": \"当强迫人物作出与其本性不相符的行动时,冲突会产生不自然的跳跃。\", \"作用域\": []}, {\"item_name\": \"缓慢升级冲突\", \"item_desc\": \"编剧不干预自然产生,是对立统一的人物的共同结果,最为推荐。\", \"作用域\": []}, {\"item_name\": \"预示冲突\", \"item_desc\": \"即张力,是对冲突的允诺,用于维持观众吸引力。\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],
@@ -608,10 +490,7 @@
       }
     },
     "title": "冲突升级的原理与动力",
-    "content": {
-      "name": "冲突升级的原理与动力",
-      "desc": "冲突是行动的源泉,其本质由生活环境和社会状况构成。有效的冲突源于‘对立统一’:必须让具备强烈意志力(名誉、金钱、复仇等需求)的主使人物与同样强大且无情的对立人物相遇。冲突的推进模式是‘攻击—反击’,只有势均力敌的对手通过不断的决策(互为因果)产生反应,才能驱动冲突不断升级并自然走向高潮。"
-    },
+    "content": "{\"name\": \"冲突升级的原理与动力\", \"desc\": \"冲突是行动的源泉,其本质由生活环境和社会状况构成。有效的冲突源于‘对立统一’:必须让具备强烈意志力(名誉、金钱、复仇等需求)的主使人物与同样强大且无情的对立人物相遇。冲突的推进模式是‘攻击—反击’,只有势均力敌的对手通过不断的决策(互为因果)产生反应,才能驱动冲突不断升级并自然走向高潮。\"}",
     "dim_creations": [
       "创作"
     ],
@@ -906,38 +785,7 @@
       }
     },
     "title": "故事五部分构成序列",
-    "content": {
-      "name": "故事五部分构成序列",
-      "kind": "序列",
-      "维度拆分规则": null,
-      "body": [
-        {
-          "item_name": "阐述",
-          "item_desc": "故事的背景交代部分。",
-          "作用域": []
-        },
-        {
-          "item_name": "激励事件",
-          "item_desc": "打破平衡、激发欲望的诱发事件。分为随机(巧合)和有因(抉择)两种,通常在故事前1/4发生。",
-          "作用域": []
-        },
-        {
-          "item_name": "进展纠葛",
-          "item_desc": "由于抗力产生的冲突(上升动作)。",
-          "作用域": []
-        },
-        {
-          "item_name": "危机/高潮",
-          "item_desc": "危机是情节突转,高潮是问题的最终解决。",
-          "作用域": []
-        },
-        {
-          "item_name": "结局",
-          "item_desc": "故事的下降动作与收尾。",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"故事五部分构成序列\", \"kind\": \"序列\", \"维度拆分规则\": null, \"body\": [{\"item_name\": \"阐述\", \"item_desc\": \"故事的背景交代部分。\", \"作用域\": []}, {\"item_name\": \"激励事件\", \"item_desc\": \"打破平衡、激发欲望的诱发事件。分为随机(巧合)和有因(抉择)两种,通常在故事前1/4发生。\", \"作用域\": []}, {\"item_name\": \"进展纠葛\", \"item_desc\": \"由于抗力产生的冲突(上升动作)。\", \"作用域\": []}, {\"item_name\": \"危机/高潮\", \"item_desc\": \"危机是情节突转,高潮是问题的最终解决。\", \"作用域\": []}, {\"item_name\": \"结局\", \"item_desc\": \"故事的下降动作与收尾。\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],
@@ -999,28 +847,7 @@
       }
     },
     "title": "故事冲突的三种类型",
-    "content": {
-      "name": "故事冲突的三种类型",
-      "kind": "子集",
-      "维度拆分规则": "冲突范围",
-      "body": [
-        {
-          "item_name": "内心冲突",
-          "item_desc": "人物自身内部的挣扎。",
-          "作用域": []
-        },
-        {
-          "item_name": "个人冲突",
-          "item_desc": "人与人之间的直接对抗。",
-          "作用域": []
-        },
-        {
-          "item_name": "个人-外界冲突",
-          "item_desc": "人与社会、自然或未知环境的冲突。",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"故事冲突的三种类型\", \"kind\": \"子集\", \"维度拆分规则\": \"冲突范围\", \"body\": [{\"item_name\": \"内心冲突\", \"item_desc\": \"人物自身内部的挣扎。\", \"作用域\": []}, {\"item_name\": \"个人冲突\", \"item_desc\": \"人与人之间的直接对抗。\", \"作用域\": []}, {\"item_name\": \"个人-外界冲突\", \"item_desc\": \"人与社会、自然或未知环境的冲突。\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],
@@ -1079,33 +906,7 @@
       }
     },
     "title": "转折点的四重效果",
-    "content": {
-      "name": "转折点的四重效果",
-      "kind": "多维关系",
-      "维度拆分规则": null,
-      "body": [
-        {
-          "item_name": "惊奇",
-          "item_desc": "出乎意料的效果。",
-          "作用域": []
-        },
-        {
-          "item_name": "增强好奇心",
-          "item_desc": "引导观众对后续发展的探索欲。",
-          "作用域": []
-        },
-        {
-          "item_name": "见识",
-          "item_desc": "提供新的洞察。 ",
-          "作用域": []
-        },
-        {
-          "item_name": "新方向",
-          "item_desc": "改变故事的走向。",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"转折点的四重效果\", \"kind\": \"多维关系\", \"维度拆分规则\": null, \"body\": [{\"item_name\": \"惊奇\", \"item_desc\": \"出乎意料的效果。\", \"作用域\": []}, {\"item_name\": \"增强好奇心\", \"item_desc\": \"引导观众对后续发展的探索欲。\", \"作用域\": []}, {\"item_name\": \"见识\", \"item_desc\": \"提供新的洞察。 \", \"作用域\": []}, {\"item_name\": \"新方向\", \"item_desc\": \"改变故事的走向。\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],
@@ -1159,10 +960,7 @@
       }
     },
     "title": "真正选择的“两难”原理",
-    "content": {
-      "name": "真正选择的“两难”原理",
-      "desc": "真正的选择必须是两难之择,即在不可调和的两善中取其一,或在两恶中取其轻。这种危机时刻是故事中最高压、最紧张的时刻,不仅能制造强烈的冲突,还能揭示人物的深层性格。"
-    },
+    "content": "{\"name\": \"真正选择的“两难”原理\", \"desc\": \"真正的选择必须是两难之择,即在不可调和的两善中取其一,或在两恶中取其轻。这种危机时刻是故事中最高压、最紧张的时刻,不仅能制造强烈的冲突,还能揭示人物的深层性格。\"}",
     "dim_creations": [
       "创作"
     ],
@@ -1211,10 +1009,7 @@
       }
     },
     "title": "情感回报递减定理",
-    "content": {
-      "name": "情感回报递减定理",
-      "desc": "如果情感连续重复出现,其效果会随着时间减少。因此在创作中需要通过价值的正面与负面动态交替,来引导核心情感(快感与痛感)的转变,从而确保持续吸引观众。"
-    },
+    "content": "{\"name\": \"情感回报递减定理\", \"desc\": \"如果情感连续重复出现,其效果会随着时间减少。因此在创作中需要通过价值的正面与负面动态交替,来引导核心情感(快感与痛感)的转变,从而确保持续吸引观众。\"}",
     "dim_creations": [
       "创作"
     ],
@@ -1370,38 +1165,7 @@
       }
     },
     "title": "视频分镜头要素表",
-    "content": {
-      "name": "视频分镜头要素表",
-      "kind": "多维关系",
-      "维度拆分规则": null,
-      "body": [
-        {
-          "item_name": "镜号",
-          "item_desc": "标注镜头的先后序号",
-          "作用域": []
-        },
-        {
-          "item_name": "画面",
-          "item_desc": "描述视频的具体视觉画面内容",
-          "作用域": []
-        },
-        {
-          "item_name": "景别/角度",
-          "item_desc": "规定摄影机的拍摄距离与构图角度",
-          "作用域": []
-        },
-        {
-          "item_name": "时长",
-          "item_desc": "单个镜头的持续时间",
-          "作用域": []
-        },
-        {
-          "item_name": "视频文案",
-          "item_desc": "对应镜头的旁白、对白或台词",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"视频分镜头要素表\", \"kind\": \"多维关系\", \"维度拆分规则\": null, \"body\": [{\"item_name\": \"镜号\", \"item_desc\": \"标注镜头的先后序号\", \"作用域\": []}, {\"item_name\": \"画面\", \"item_desc\": \"描述视频的具体视觉画面内容\", \"作用域\": []}, {\"item_name\": \"景别/角度\", \"item_desc\": \"规定摄影机的拍摄距离与构图角度\", \"作用域\": []}, {\"item_name\": \"时长\", \"item_desc\": \"单个镜头的持续时间\", \"作用域\": []}, {\"item_name\": \"视频文案\", \"item_desc\": \"对应镜头的旁白、对白或台词\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],
@@ -1456,43 +1220,7 @@
       }
     },
     "title": "常用的六种摄影构图法",
-    "content": {
-      "name": "常用的六种摄影构图法",
-      "kind": "子集",
-      "维度拆分规则": "布局逻辑",
-      "body": [
-        {
-          "item_name": "水平线构图",
-          "item_desc": "利用地平线等水平元素,使画面稳定宽广",
-          "作用域": []
-        },
-        {
-          "item_name": "对角线构图",
-          "item_desc": "利用倾斜线条引导视线,增加动感,吸引注意力",
-          "作用域": []
-        },
-        {
-          "item_name": "中心构图",
-          "item_desc": "将主体放于正中心,使主体非常突出",
-          "作用域": []
-        },
-        {
-          "item_name": "框式构图",
-          "item_desc": "利用周边的元素形成框架,产生穿透感或神秘感",
-          "作用域": []
-        },
-        {
-          "item_name": "九宫格构图",
-          "item_desc": "按九宫格交叉点排列主体,使其更醒目",
-          "作用域": []
-        },
-        {
-          "item_name": "三分法构图",
-          "item_desc": "将重点分布在画面的1/3处,用于简单化复杂画面",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"常用的六种摄影构图法\", \"kind\": \"子集\", \"维度拆分规则\": \"布局逻辑\", \"body\": [{\"item_name\": \"水平线构图\", \"item_desc\": \"利用地平线等水平元素,使画面稳定宽广\", \"作用域\": []}, {\"item_name\": \"对角线构图\", \"item_desc\": \"利用倾斜线条引导视线,增加动感,吸引注意力\", \"作用域\": []}, {\"item_name\": \"中心构图\", \"item_desc\": \"将主体放于正中心,使主体非常突出\", \"作用域\": []}, {\"item_name\": \"框式构图\", \"item_desc\": \"利用周边的元素形成框架,产生穿透感或神秘感\", \"作用域\": []}, {\"item_name\": \"九宫格构图\", \"item_desc\": \"按九宫格交叉点排列主体,使其更醒目\", \"作用域\": []}, {\"item_name\": \"三分法构图\", \"item_desc\": \"将重点分布在画面的1/3处,用于简单化复杂画面\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],
@@ -1546,28 +1274,7 @@
       }
     },
     "title": "短视频脚本黄金结构",
-    "content": {
-      "name": "短视频脚本黄金结构",
-      "kind": "序列",
-      "维度拆分规则": null,
-      "body": [
-        {
-          "item_name": "引子",
-          "item_desc": "通过视觉冲击或创意起手,利用开头噱头抓住用户",
-          "作用域": []
-        },
-        {
-          "item_name": "内容主体",
-          "item_desc": "进行中间论述,把握故事线与节奏",
-          "作用域": []
-        },
-        {
-          "item_name": "结尾",
-          "item_desc": "总结全文,留下印象点、情感寄托或行动引导",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"短视频脚本黄金结构\", \"kind\": \"序列\", \"维度拆分规则\": null, \"body\": [{\"item_name\": \"引子\", \"item_desc\": \"通过视觉冲击或创意起手,利用开头噱头抓住用户\", \"作用域\": []}, {\"item_name\": \"内容主体\", \"item_desc\": \"进行中间论述,把握故事线与节奏\", \"作用域\": []}, {\"item_name\": \"结尾\", \"item_desc\": \"总结全文,留下印象点、情感寄托或行动引导\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],
@@ -1621,23 +1328,7 @@
       }
     },
     "title": "Vlog脚本创作公式清单",
-    "content": {
-      "name": "Vlog脚本创作公式清单",
-      "kind": "子集",
-      "维度拆分规则": "想引发的观众反应",
-      "body": [
-        {
-          "item_name": "共鸣公式",
-          "item_desc": "共鸣 = 人物 + 状态 + 情感",
-          "作用域": []
-        },
-        {
-          "item_name": "好奇公式",
-          "item_desc": "好奇 = 时间/观点 + 悬念",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"Vlog脚本创作公式清单\", \"kind\": \"子集\", \"维度拆分规则\": \"想引发的观众反应\", \"body\": [{\"item_name\": \"共鸣公式\", \"item_desc\": \"共鸣 = 人物 + 状态 + 情感\", \"作用域\": []}, {\"item_name\": \"好奇公式\", \"item_desc\": \"好奇 = 时间/观点 + 悬念\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],
@@ -1695,10 +1386,7 @@
       }
     },
     "title": "视频节奏的时长分配原理",
-    "content": {
-      "name": "视频节奏的时长分配原理",
-      "desc": "长视频需要通过周期性的刺激来维持用户注意力,通常每15秒需设置一个重点内容;而短视频留给创作者的时间极短,必须在3-5秒内迅速呈现出核心主题,否则用户极易流失。"
-    },
+    "content": "{\"name\": \"视频节奏的时长分配原理\", \"desc\": \"长视频需要通过周期性的刺激来维持用户注意力,通常每15秒需设置一个重点内容;而短视频留给创作者的时间极短,必须在3-5秒内迅速呈现出核心主题,否则用户极易流失。\"}",
     "dim_creations": [
       "创作"
     ],
@@ -1747,28 +1435,7 @@
       }
     },
     "title": "小红书笔记标题3个万能公式",
-    "content": {
-      "name": "小红书笔记标题3个万能公式",
-      "kind": "子集",
-      "维度拆分规则": "标题构成的侧重点",
-      "body": [
-        {
-          "item_name": "时间+结果+经验",
-          "item_desc": "构成要素为具体的执行时间、达成的结果及总结的经验。适用于有具体成果背书的内容。例:『半年时间涨粉11w,全靠这个万能公式』,用具象数字和结果钩住用户。",
-          "作用域": []
-        },
-        {
-          "item_name": "痛点+解决方法+保证",
-          "item_desc": "构成要素为点出用户当下的困扰、给出对策并提供效果承诺。适用于攻略类或教程类内容。例:『小红书不涨粉,3大技巧照做就对了』,精准戳中需求并下承诺。",
-          "作用域": []
-        },
-        {
-          "item_name": "方法+保证",
-          "item_desc": "直接展示操作方法并给予效果保障。旨在传达一种易操作感,降低用户的决策/学习门槛。例:『傻瓜式化妆手法,有手就行』。",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"小红书笔记标题3个万能公式\", \"kind\": \"子集\", \"维度拆分规则\": \"标题构成的侧重点\", \"body\": [{\"item_name\": \"时间+结果+经验\", \"item_desc\": \"构成要素为具体的执行时间、达成的结果及总结的经验。适用于有具体成果背书的内容。例:『半年时间涨粉11w,全靠这个万能公式』,用具象数字和结果钩住用户。\", \"作用域\": []}, {\"item_name\": \"痛点+解决方法+保证\", \"item_desc\": \"构成要素为点出用户当下的困扰、给出对策并提供效果承诺。适用于攻略类或教程类内容。例:『小红书不涨粉,3大技巧照做就对了』,精准戳中需求并下承诺。\", \"作用域\": []}, {\"item_name\": \"方法+保证\", \"item_desc\": \"直接展示操作方法并给予效果保障。旨在传达一种易操作感,降低用户的决策/学习门槛。例:『傻瓜式化妆手法,有手就行』。\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],
@@ -1826,58 +1493,7 @@
       }
     },
     "title": "标题创作的9种实用技巧",
-    "content": {
-      "name": "标题创作的9种实用技巧",
-      "kind": "子集",
-      "维度拆分规则": "类型枚举",
-      "body": [
-        {
-          "item_name": "提问式",
-          "item_desc": "用困惑开头直击需求,直接命中用户的疑问或痛点。",
-          "作用域": []
-        },
-        {
-          "item_name": "设问式",
-          "item_desc": "先抛问题再给方案,通过自问自答勾起好奇心并预告内容价值。",
-          "作用域": []
-        },
-        {
-          "item_name": "数字式",
-          "item_desc": "使用具体数字(如价格、数量、天数等),增强干货感并降低用户的决策成本。",
-          "作用域": []
-        },
-        {
-          "item_name": "利益式",
-          "item_desc": "直接展现价值,把用户能通过笔记得到的好处摆在台面上。",
-          "作用域": []
-        },
-        {
-          "item_name": "悬念式",
-          "item_desc": "隐藏关键细节或结论,只说一半内容,激发点击进去寻找答案的好奇心。",
-          "作用域": []
-        },
-        {
-          "item_name": "对比式",
-          "item_desc": "通过前后或不同对象之间的状态对比,制造差异感和冲突感。",
-          "作用域": []
-        },
-        {
-          "item_name": "劝诫式",
-          "item_desc": "利用避坑和规避损失的心理,抓住用户害怕失去或犯错的重视感。",
-          "作用域": []
-        },
-        {
-          "item_name": "共情式",
-          "item_desc": "替人发声,说出用户想说但没说出的话,以此引发强烈的情绪共鸣。",
-          "作用域": []
-        },
-        {
-          "item_name": "热点式",
-          "item_desc": "绑定节日、热门人物或社会热点事件进行借势营销。",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"标题创作的9种实用技巧\", \"kind\": \"子集\", \"维度拆分规则\": \"类型枚举\", \"body\": [{\"item_name\": \"提问式\", \"item_desc\": \"用困惑开头直击需求,直接命中用户的疑问或痛点。\", \"作用域\": []}, {\"item_name\": \"设问式\", \"item_desc\": \"先抛问题再给方案,通过自问自答勾起好奇心并预告内容价值。\", \"作用域\": []}, {\"item_name\": \"数字式\", \"item_desc\": \"使用具体数字(如价格、数量、天数等),增强干货感并降低用户的决策成本。\", \"作用域\": []}, {\"item_name\": \"利益式\", \"item_desc\": \"直接展现价值,把用户能通过笔记得到的好处摆在台面上。\", \"作用域\": []}, {\"item_name\": \"悬念式\", \"item_desc\": \"隐藏关键细节或结论,只说一半内容,激发点击进去寻找答案的好奇心。\", \"作用域\": []}, {\"item_name\": \"对比式\", \"item_desc\": \"通过前后或不同对象之间的状态对比,制造差异感和冲突感。\", \"作用域\": []}, {\"item_name\": \"劝诫式\", \"item_desc\": \"利用避坑和规避损失的心理,抓住用户害怕失去或犯错的重视感。\", \"作用域\": []}, {\"item_name\": \"共情式\", \"item_desc\": \"替人发声,说出用户想说但没说出的话,以此引发强烈的情绪共鸣。\", \"作用域\": []}, {\"item_name\": \"热点式\", \"item_desc\": \"绑定节日、热门人物或社会热点事件进行借势营销。\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],
@@ -1947,28 +1563,7 @@
       }
     },
     "title": "海外短视频爆款创意策略清单",
-    "content": {
-      "name": "海外短视频爆款创意策略清单",
-      "kind": "子集",
-      "维度拆分规则": "类型枚举",
-      "body": [
-        {
-          "item_name": "超现实主义(SURREALISM)",
-          "item_desc": "将日常生活物品巨大化。通过体积对比产生视觉冲击力,创造新鲜感。适合原材料/纯净成分品牌进行视觉吸引。例:@the_ordinary。",
-          "作用域": []
-        },
-        {
-          "item_name": "错觉式转场(ILLUSION TRANSTION)",
-          "item_desc": "利用看似背景的物体作为遮挡或引导进行转场,利用背景与前景的巧妙一致性。适合低预算追求高质量质感的创作场景。",
-          "作用域": []
-        },
-        {
-          "item_name": "反差式展示(THE PROUD FOUNDER)",
-          "item_desc": "创始人先展示凌乱、未修边幅的状态(Rotting),接着切入作为成功品牌创始人的自信状态。利用巨大的心理与视觉落差产生张力,适合用于与潜在客户建立情感联结。",
-          "作用域": []
-        }
-      ]
-    },
+    "content": "{\"name\": \"海外短视频爆款创意策略清单\", \"kind\": \"子集\", \"维度拆分规则\": \"类型枚举\", \"body\": [{\"item_name\": \"超现实主义(SURREALISM)\", \"item_desc\": \"将日常生活物品巨大化。通过体积对比产生视觉冲击力,创造新鲜感。适合原材料/纯净成分品牌进行视觉吸引。例:@the_ordinary。\", \"作用域\": []}, {\"item_name\": \"错觉式转场(ILLUSION TRANSTION)\", \"item_desc\": \"利用看似背景的物体作为遮挡或引导进行转场,利用背景与前景的巧妙一致性。适合低预算追求高质量质感的创作场景。\", \"作用域\": []}, {\"item_name\": \"反差式展示(THE PROUD FOUNDER)\", \"item_desc\": \"创始人先展示凌乱、未修边幅的状态(Rotting),接着切入作为成功品牌创始人的自信状态。利用巨大的心理与视觉落差产生张力,适合用于与潜在客户建立情感联结。\", \"作用域\": []}]}",
     "dim_creations": [
       "创作"
     ],

+ 4 - 40
创作知识提取-skill/format/ingest-payload-what.schema.json

@@ -2,7 +2,7 @@
   "$schema": "http://json-schema.org/draft-07/schema#",
   "$id": "ingest-payload-what.schema.json",
   "title": "创作知识 Ingest Payload · What(构成)",
-  "$comment": "What 颗 = 界定/列举/分类某创作对象。content 为【结构化实体】对象(name + kind + 维度拆分规则 + body[]),不是字符串。概要放进 custom_ext。一颗知识 = 一个 payload;同帖多颗共享 source.id。",
+  "$comment": "What 颗 = 界定/列举/分类某创作对象。content 是【结构化实体的 JSON 字符串】(接口要求 content 为 string,故对象 json.dumps 后传);解析后结构 = name + kind + 维度拆分规则 + body[]。概要放进 custom_ext。一颗知识 = 一个 payload;同帖多颗共享 source.id。",
   "type": "object",
   "required": ["source", "title", "content", "dim_creations", "dim_attributes", "scopes"],
   "additionalProperties": false,
@@ -29,45 +29,9 @@
     },
     "title": { "type": "string", "minLength": 1, "$comment": "知识名,如 剧本冲突的四种类型" },
     "content": {
-      "type": "object",
-      "$comment": "结构化实体(对应前端「知识实体 JSON」)",
-      "required": ["name", "kind", "body"],
-      "additionalProperties": false,
-      "properties": {
-        "name": { "type": "string", "minLength": 1, "$comment": "知识名/框架名,通常同 title" },
-        "kind": { "type": "string", "enum": ["子集", "多维关系", "序列"] },
-        "维度拆分规则": {
-          "type": ["string", "null"],
-          "$comment": "仅 kind=子集 时填:此子集沿哪个维度切分(如 冲突形态);序列/多维关系留空或 null"
-        },
-        "body": {
-          "type": "array",
-          "minItems": 1,
-          "$comment": "扁平条目列表(替代旧的「主体」自由小节)",
-          "items": {
-            "type": "object",
-            "required": ["item_name", "item_desc", "作用域"],
-            "additionalProperties": false,
-            "properties": {
-              "item_name": { "type": "string", "minLength": 1 },
-              "item_desc": { "type": "string" },
-              "作用域": {
-                "type": "array",
-                "$comment": "item 级作用域,当前留空占位 [],仅用颗级 scopes(后续再启用)",
-                "items": {
-                  "type": "object",
-                  "required": ["scope_type", "value"],
-                  "additionalProperties": false,
-                  "properties": {
-                    "scope_type": { "type": "string", "enum": ["substance", "form", "feeling", "effect", "intent"] },
-                    "value": { "type": "string", "minLength": 1 }
-                  }
-                }
-              }
-            }
-          }
-        }
-      }
+      "type": "string",
+      "minLength": 1,
+      "$comment": "结构化实体的 JSON 字符串(json.dumps(ensure_ascii=False))。解析后对象(对应前端「知识实体 JSON」)= { name(知识名,通常同title) + kind(子集|多维关系|序列) + 维度拆分规则(仅子集填,沿哪个维度切分;否则null) + body[](扁平条目: item_name + item_desc + 作用域[](item级,当前留空占位[],仅用颗级 scopes)) }。结构校验见 lint-payload.py。"
     },
     "dim_creations": {
       "type": "array",

+ 4 - 9
创作知识提取-skill/format/ingest-payload-why.schema.json

@@ -2,7 +2,7 @@
   "$schema": "http://json-schema.org/draft-07/schema#",
   "$id": "ingest-payload-why.schema.json",
   "title": "创作知识 Ingest Payload · Why(原理)",
-  "$comment": "Why 颗 = 解释某做法/判断背后的道理。content 为结构化对象 {name,desc}:name=原理名,desc=阐述(是什么+为什么,忠实整合原帖;不含'怎么用')。一颗知识 = 一个 payload;同帖多颗共享 source.id。",
+  "$comment": "Why 颗 = 解释某做法/判断背后的道理。content 是结构化对象 {name,desc} 的 JSON 字符串(接口要求 content 为 string,故 json.dumps 后传):name=原理名,desc=阐述(是什么+为什么,忠实整合原帖;不含'怎么用')。一颗知识 = 一个 payload;同帖多颗共享 source.id。",
   "type": "object",
   "required": ["source", "title", "content", "dim_creations", "dim_attributes", "scopes"],
   "additionalProperties": false,
@@ -29,14 +29,9 @@
     },
     "title": { "type": "string", "minLength": 1, "$comment": "原理名,如 爆款选题的情绪触发原理" },
     "content": {
-      "type": "object",
-      "required": ["name", "desc"],
-      "additionalProperties": false,
-      "properties": {
-        "name": { "type": "string", "$comment": "原理名,通常同 title" },
-        "desc": { "type": "string", "minLength": 1, "$comment": "阐述(是什么+为什么,忠实整合原帖,不含'怎么用')" }
-      },
-      "$comment": "结构化对象 {name,desc}:name=原理名,desc=阐述(是什么+为什么,忠实整合)"
+      "type": "string",
+      "minLength": 1,
+      "$comment": "结构化对象 {name,desc} 的 JSON 字符串(json.dumps(ensure_ascii=False))。解析后:name=原理名(通常同title) + desc=阐述(是什么+为什么,忠实整合原帖,不含'怎么用')。结构校验见 lint-payload.py。"
     },
     "dim_creations": {
       "type": "array",

+ 19 - 8
创作知识提取-skill/tools/lint-payload.py

@@ -70,23 +70,34 @@ def lint_one(p: dict, idx: int) -> tuple[list[str], list[str]]:
         elif k == "创作阶段" and v not in 创作阶段:
             err.append(f"{tag} 创作阶段 非法值:{v}(应 ∈ {sorted(创作阶段)})")
 
+    # content 一律是 string(ingest 接口要求);what/why 是结构化对象的 json.dumps,解析回 dict 再校验
+    raw = p.get("content")
+    if not isinstance(raw, str):
+        err.append(f"{tag} content 须为 string(接口要求),实为 {type(raw).__name__}")
+    parsed = None
+    if isinstance(raw, str) and kind in ("what", "why"):
+        try:
+            parsed = json.loads(raw)
+        except (ValueError, TypeError):
+            err.append(f"{tag} {kind} 的 content 应是结构化对象的 JSON 字符串,解析失败")
+
     # How vs What/Why 形态一致性
     if kind == "how":
         if "创作阶段" not in keys_present:
             warn.append(f"{tag} how 颗通常每步带『创作阶段』,custom_ext 里没有")
         if "动作" not in keys_present:
             warn.append(f"{tag} how 颗通常带『动作』(从内容提炼),custom_ext 里没有")
-        c = p.get("content", "")
-        if not isinstance(c, str) or ("步骤" not in c and "目标" not in c):
+        c = raw if isinstance(raw, str) else ""
+        if "步骤" not in c and "目标" not in c:
             warn.append(f"{tag} how 的 content 建议含『目标…』+『步骤N…』结构")
     elif kind in ("what", "why"):
         bad = keys_present & {"创作阶段", "动作"}
         if bad:
             err.append(f"{tag} {kind} 简化版不应有 {bad}(只保留业务阶段)")
-        if kind == "what":  # What 的 content 是结构化实体对象
-            c = p.get("content")
+        if kind == "what":  # What 的 content 解析后是结构化实体对象
+            c = parsed
             if not isinstance(c, dict):
-                err.append(f"{tag} what 的 content 须为结构化对象 {{name,kind,body}},实为 {type(c).__name__}")
+                err.append(f"{tag} what 的 content 解析后须为对象 {{name,kind,body}},实为 {type(c).__name__}")
             else:
                 if not c.get("name"):
                     err.append(f"{tag} what.content 缺 name")
@@ -101,10 +112,10 @@ def lint_one(p: dict, idx: int) -> tuple[list[str], list[str]]:
                     warn.append(f"{tag} 子集型建议填 维度拆分规则")
             if "概要" not in keys_present:
                 warn.append(f"{tag} what 颗建议 custom_ext 带『概要』")
-        if kind == "why":  # Why 的 content 是结构化对象 {name,desc}
-            c = p.get("content")
+        if kind == "why":  # Why 的 content 解析后是结构化对象 {name,desc}
+            c = parsed
             if not isinstance(c, dict):
-                err.append(f"{tag} why 的 content 须为结构化对象 {{name,desc}},实为 {type(c).__name__}")
+                err.append(f"{tag} why 的 content 解析后须为对象 {{name,desc}},实为 {type(c).__name__}")
             else:
                 if not c.get("name"):
                     err.append(f"{tag} why.content 缺 name")