how_decode_v8_new_structure.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. """
  2. HOW 解构 V8 - 适配新的输入输出结构
  3. 新的输入结构:
  4. - 帖子信息:examples_new/阿里多多酱/作者历史帖子/{帖子ID}.json
  5. - what解构结果:examples_new/阿里多多酱/output/{帖子ID}_{运行日期}_{运行时间}.json
  6. 新的输出结构:
  7. - examples_new/阿里多多酱/how_output/{帖子ID}_{运行日期}_{运行时间}.json
  8. 核心方法:
  9. - 拆步骤分析:
  10. - Step 1: 来源类型初筛 - 找出可能的来源
  11. - Step 2: 解构每个来源 - 明确标识【输入→处理→输出】
  12. - Step 3: 路径验证 - 评分和证据
  13. - Step 4: 综合结论 - 最可能的路径
  14. - 多模态方式传递历史帖子
  15. - 只分析灵感点,且只使用名称(避免what中间过程的干扰)
  16. """
  17. import asyncio
  18. import json
  19. import os
  20. from typing import Dict, List
  21. from datetime import datetime
  22. from agents import Agent, Runner
  23. from lib.my_trace import set_trace
  24. from lib.utils import read_json
  25. from lib.client import get_model
  26. MODEL_NAME = "google/gemini-2.5-flash"
  27. # ============================================================================
  28. # 多模态消息构建
  29. # ============================================================================
  30. def build_post_multimodal_content(post_data: Dict) -> List[Dict]:
  31. """构建单个帖子的多模态内容"""
  32. images = post_data.get('images', [])
  33. image_count = len(images)
  34. content = []
  35. if images:
  36. content.append({
  37. "type": "input_text",
  38. "text": f"[帖子图集:{image_count}张图片,第一张是封面]"
  39. })
  40. for img_url in images:
  41. content.append({
  42. "type": "input_image",
  43. "detail": "auto",
  44. "image_url": img_url
  45. })
  46. post_info = f"""
  47. <标题>
  48. {post_data.get('title', '')}
  49. </标题>
  50. <正文>
  51. {post_data.get('body_text', '')}
  52. </正文>
  53. <发布时间>
  54. {post_data.get('publish_time', '')}
  55. </发布时间>
  56. <互动数据>
  57. 点赞: {post_data.get('like_count', 0)}, 收藏: {post_data.get('collect_count', 0)}
  58. </互动数据>
  59. """
  60. content.append({
  61. "type": "input_text",
  62. "text": post_info.strip()
  63. })
  64. return content
  65. # ============================================================================
  66. # Step 1: 来源类型初筛 Agent
  67. # ============================================================================
  68. STEP1_PROMPT = """
  69. 你是一个创作溯源分析专家。
  70. 你的任务:对给定的灵感点,**广召回**所有可能的来源,并给出初步推测路径。
  71. ## 关键要求
  72. 1. **广召回**:对每个来源类型(A/B/C/D)都要分析,即使可能性很低也要列出
  73. 2. **可能性评估**:给出高/中/低的评级
  74. 3. **初步推测路径**:用3-5步描述从原始点到灵感点的推导过程
  75. ## 推测路径的要求
  76. ### 原始点
  77. - **B类来源**:原始点是博主历史帖子数据
  78. - **C类来源**:原始点是外部平台信息(小红书/微博/知乎等)
  79. - **A类来源**:原始点是当前帖子的其他灵感点
  80. - **D类来源**:原始点是混合的
  81. ### 可用操作类型
  82. 只能使用这三种操作:
  83. 1. **从内搜**:搜索/浏览博主历史帖子、回忆过往经验
  84. 2. **从外搜**:搜索外部平台、浏览热点话题、查询知识
  85. 3. **信息处理**:观察、对比、提取、归纳、联想、组合、类比
  86. ### 步骤格式
  87. 每步必须明确:操作类型 + 具体做什么 + 输出什么
  88. 示例:
  89. - `步骤1 [从内搜]: 浏览历史帖子 → 发现经常发"胖猫穿衣"内容`
  90. - `步骤2 [信息处理]: 观察这些帖子的共同特征 → 体型圆滚滚`
  91. - `步骤3 [信息处理]: 联想类比体型特征 → 像煤气罐`
  92. ### 注意事项
  93. - **不能跳步骤**:关键词、概念的来源必须说清楚
  94. - **不能有黑盒**:不能用"突然想到"、"产生灵感"等说法
  95. - **数字世界操作**:只能操作数字化的数据,不能有物理世界交互
  96. - **合适的粒度**:3-5步说清楚,不要太细(不说底层实现),不要太粗(不能黑盒)
  97. ## 来源类型分类
  98. **A. 从其他点推导**
  99. - 从当前帖子的其他灵感点推导得出
  100. **B. 从博主账号历史**
  101. - 从历史帖子中的内容、风格、经验推导
  102. **C. 从外部信息**
  103. - 从平台热点、流行梗、社会现象推导
  104. **D. 混合输入**
  105. - 由多个来源融合创新
  106. ## 输出格式
  107. ```json
  108. {
  109. "可能的来源": {
  110. "A_其他点推导": {
  111. "可能性": "高/中/低",
  112. "理由": "为什么这个来源是可能的(1-2句话)",
  113. "初步推测路径": [
  114. "步骤1 [操作类型]: 具体操作 → 输出结果",
  115. "步骤2 [操作类型]: 具体操作 → 输出结果",
  116. "步骤3 [操作类型]: 具体操作 → 输出结果"
  117. ]
  118. },
  119. "B_博主历史": {
  120. "可能性": "高/中/低",
  121. "理由": "为什么这个来源是可能的(1-2句话)",
  122. "初步推测路径": [
  123. "原始点: 博主历史帖子数据",
  124. "步骤1 [从内搜]: 具体操作 → 输出结果",
  125. "步骤2 [信息处理]: 具体操作 → 输出结果",
  126. "步骤3 [信息处理]: 具体操作 → 输出结果"
  127. ]
  128. },
  129. "C_外部信息": {
  130. "可能性": "高/中/低",
  131. "理由": "为什么这个来源是可能的(1-2句话)",
  132. "初步推测路径": [
  133. "原始点: 外部平台信息",
  134. "步骤1 [从外搜]: 具体操作 → 输出结果",
  135. "步骤2 [信息处理]: 具体操作 → 输出结果",
  136. "步骤3 [信息处理]: 具体操作 → 输出结果"
  137. ]
  138. },
  139. "D_混合输入": {
  140. "可能性": "高/中/低",
  141. "理由": "可能混合了哪些来源",
  142. "初步推测路径": [
  143. "原始点: 混合(历史数据+外部信息)",
  144. "步骤1 [操作类型]: 具体操作 → 输出结果",
  145. "步骤2 [操作类型]: 具体操作 → 输出结果",
  146. "步骤3 [操作类型]: 具体操作 → 输出结果"
  147. ]
  148. }
  149. }
  150. }
  151. ```
  152. """
  153. step1_agent = Agent(
  154. name="Source Type Filter",
  155. instructions=STEP1_PROMPT,
  156. model=get_model(MODEL_NAME),
  157. tools=[],
  158. )
  159. # ============================================================================
  160. # Step 2: 深入分析 Agent
  161. # ============================================================================
  162. STEP2_B_PROMPT = """
  163. 你是一个创作溯源分析专家。
  164. 你的任务:解构从博主历史如何一步步得到这个灵感点。
  165. ## 核心要求:明确标识 输入 → 处理 → 输出
  166. ### 输入
  167. - 具体是博主历史中的哪个/哪些帖子?
  168. - 这些帖子里有什么内容?(图片/文字/主题)
  169. ### 处理过程(一步步推导)
  170. - 步骤1:创作者观察/接收到什么信息?
  171. - 步骤2:产生了什么联想/思考?
  172. - 步骤3:如何转化为具体的灵感?
  173. - (可以有更多步骤)
  174. ### 输出
  175. - 最终得到的灵感点
  176. ## 输出要求
  177. 输出JSON格式:
  178. ```json
  179. {
  180. "输入_博主历史帖子": {
  181. "相关帖子": [
  182. {
  183. "帖子序号": "历史帖子X/总数",
  184. "标题": "...",
  185. "关键内容": "具体是图片中什么/文字里什么"
  186. }
  187. ]
  188. },
  189. "处理_从输入到灵感的推导": {
  190. "步骤1": {
  191. "动作": "观察/接收",
  192. "内容": "创作者看到/注意到了什么"
  193. },
  194. "步骤2": {
  195. "动作": "联想/思考",
  196. "内容": "产生了什么想法/联系"
  197. },
  198. "步骤3": {
  199. "动作": "转化/形成",
  200. "内容": "如何变成具体的灵感"
  201. }
  202. },
  203. "输出_最终灵感": "灵感点名称"
  204. }
  205. ```
  206. """
  207. STEP2_C_PROMPT = """
  208. 你是一个创作溯源分析专家。
  209. 你的任务:解构从外部信息如何一步步得到这个灵感点。
  210. ## 核心要求:明确标识 输入 → 处理 → 输出
  211. ### 输入
  212. - 具体是什么外部信息?(热点话题/流行梗/社会现象)
  213. - 这些信息的具体内容是什么?
  214. ### 处理过程(一步步推导)
  215. - 步骤1:创作者接触到什么外部信息?
  216. - 步骤2:如何理解/解读这个信息?
  217. - 步骤3:如何与自己的内容结合?
  218. - 步骤4:如何转化为具体的灵感?
  219. - (可以有更多步骤)
  220. ### 输出
  221. - 最终得到的灵感点
  222. ## 输出要求
  223. 输出JSON格式:
  224. ```json
  225. {
  226. "输入_外部信息": {
  227. "信息类型": "平台热点/流行梗/社会现象",
  228. "具体内容": "是什么话题/梗/现象",
  229. "信息来源": "在哪里看到/了解到"
  230. },
  231. "处理_从输入到灵感的推导": {
  232. "步骤1": {
  233. "动作": "接触/了解",
  234. "内容": "创作者看到/听到了什么"
  235. },
  236. "步骤2": {
  237. "动作": "理解/解读",
  238. "内容": "如何理解这个信息"
  239. },
  240. "步骤3": {
  241. "动作": "结合/融合",
  242. "内容": "如何与自己的内容结合"
  243. },
  244. "步骤4": {
  245. "动作": "转化/形成",
  246. "内容": "如何变成具体的灵感"
  247. }
  248. },
  249. "输出_最终灵感": "灵感点名称"
  250. }
  251. ```
  252. """
  253. step2_b_agent = Agent(
  254. name="Blogger History Analyzer",
  255. instructions=STEP2_B_PROMPT,
  256. model=get_model(MODEL_NAME),
  257. tools=[],
  258. )
  259. step2_c_agent = Agent(
  260. name="External Info Analyzer",
  261. instructions=STEP2_C_PROMPT,
  262. model=get_model(MODEL_NAME),
  263. tools=[],
  264. )
  265. # ============================================================================
  266. # Step 3: 路径验证 Agent
  267. # ============================================================================
  268. STEP3_PROMPT = """
  269. 你是一个创作溯源分析专家。
  270. 你的任务:对每个来源路径进行验证和评分。
  271. ## 验证维度
  272. 1. **支持证据**(3-5条具体证据)
  273. 2. **反驳点**(如果有不支持的因素)
  274. 3. **可能性评分**(1-10分,基于证据强度)
  275. ## 输出要求
  276. 输出JSON格式:
  277. ```json
  278. {
  279. "来源类型": "B",
  280. "支持证据": [
  281. "证据1: ...",
  282. "证据2: ...",
  283. "证据3: ..."
  284. ],
  285. "反驳点": [
  286. "反驳1: ..."
  287. ],
  288. "可能性评分": 8,
  289. "评分说明": "为什么给这个分数"
  290. }
  291. ```
  292. """
  293. step3_agent = Agent(
  294. name="Path Validator",
  295. instructions=STEP3_PROMPT,
  296. model=get_model(MODEL_NAME),
  297. tools=[],
  298. )
  299. # ============================================================================
  300. # Step 4: 综合结论 Agent
  301. # ============================================================================
  302. STEP4_PROMPT = """
  303. 你是一个创作溯源分析专家。
  304. 你的任务:基于前面的分析,给出综合结论。
  305. ## 输出要求
  306. 输出JSON格式:
  307. ```json
  308. {
  309. "最可能的来源路径": "...",
  310. "各来源的占比": {
  311. "B_博主历史": "60%",
  312. "C_外部信息": "40%"
  313. },
  314. "完整推导路径": "从...到...最终形成...",
  315. "关键转折点": "...",
  316. "整体置信度": 85
  317. }
  318. ```
  319. """
  320. step4_agent = Agent(
  321. name="Conclusion Synthesizer",
  322. instructions=STEP4_PROMPT,
  323. model=get_model(MODEL_NAME),
  324. tools=[],
  325. )
  326. # ============================================================================
  327. # 从新格式的what结果中提取所有点
  328. # ============================================================================
  329. def extract_all_points_v8(what_result: Dict) -> List[Dict]:
  330. """
  331. 从新格式的 what 解构结果中提取所有的点
  332. 重要:只提取灵感点,且只保留名称字段
  333. - 描述、在帖子中的体现等都是what的中间过程,会干扰how的解构
  334. """
  335. points = []
  336. # 只提取灵感点,且只保留名称
  337. inspiration_points = what_result.get('三点解构', {}).get('灵感点', {}).get('points', [])
  338. for idx, point in enumerate(inspiration_points, 1):
  339. points.append({
  340. 'type': '灵感点',
  341. 'id': f'灵感点{idx}',
  342. 'name': point.get('灵感点', '') # 只要名称,不要其他字段
  343. })
  344. return points
  345. # ============================================================================
  346. # 加载博主历史数据
  347. # ============================================================================
  348. def load_blogger_history_v8(history_dir: str, target_post_id: str) -> Dict:
  349. """加载博主历史数据 - V8版本"""
  350. history_posts = []
  351. for filename in os.listdir(history_dir):
  352. if filename.endswith('.json'):
  353. post_id = filename.replace('.json', '')
  354. # 只过滤掉当前帖子本身(按ID)
  355. if post_id != target_post_id:
  356. filepath = os.path.join(history_dir, filename)
  357. with open(filepath, 'r', encoding='utf-8') as f:
  358. data = json.load(f)
  359. history_posts.append(data)
  360. # 按时间排序
  361. history_posts.sort(key=lambda x: x.get('publish_timestamp', 0))
  362. return {
  363. "历史帖子数": len(history_posts),
  364. "历史帖子列表": history_posts
  365. }
  366. # ============================================================================
  367. # 拆步骤分析(复用之前的逻辑)
  368. # ============================================================================
  369. async def analyze_point_step_by_step(
  370. point: Dict,
  371. all_points: List[Dict],
  372. blogger_history: Dict,
  373. account_name: str
  374. ):
  375. """拆步骤分析单个点"""
  376. print(f"\n{'='*80}")
  377. print(f"拆步骤溯源分析: {point['id']} - {point['name']}")
  378. print(f"{'='*80}")
  379. # ========== 准备基础上下文 ==========
  380. content = []
  381. # 待溯源的点信息(只有名称,避免what中间过程的干扰)
  382. content.append({
  383. "type": "input_text",
  384. "text": f"""
  385. # 待溯源的灵感点
  386. **名称**: {point['name']}
  387. **说明**: 这是从what解构中提取的灵感点名称,请分析这个灵感点是如何产生的。
  388. """
  389. })
  390. # 其他点信息
  391. other_points_info = []
  392. for p in all_points:
  393. if p['id'] != point['id']:
  394. other_points_info.append(f"- {p['id']}: {p['name']}")
  395. content.append({
  396. "type": "input_text",
  397. "text": f"""
  398. ---
  399. # 其他点(可能的输入来源A)
  400. {chr(10).join(other_points_info)}
  401. """
  402. })
  403. # 博主历史信息(多模态)
  404. history_posts = blogger_history.get('历史帖子列表', [])
  405. content.append({
  406. "type": "input_text",
  407. "text": f"""
  408. ---
  409. # 博主历史信息(可能的输入来源B)
  410. **账号名称**: {account_name}
  411. **历史帖子数量**: {len(history_posts)} 个
  412. 以下是博主的所有历史帖子(按发布时间排序):
  413. """
  414. })
  415. # 为每个历史帖子构建多模态内容
  416. for idx, hist_post in enumerate(history_posts, 1):
  417. content.append({
  418. "type": "input_text",
  419. "text": f"\n## 历史帖子 {idx}/{len(history_posts)}\n"
  420. })
  421. hist_post_content = build_post_multimodal_content(hist_post)
  422. content.extend(hist_post_content)
  423. # ========== Step 1: 来源类型初筛 ==========
  424. print(f"\n{'='*60}")
  425. print("Step 1: 来源类型初筛")
  426. print(f"{'='*60}")
  427. step1_messages = [{
  428. "role": "user",
  429. "content": content + [{
  430. "type": "input_text",
  431. "text": "\n---\n\n请根据以上信息,判断这个点最可能来自哪些来源类型(只选1-3个最可能的)。"
  432. }]
  433. }]
  434. result1 = await Runner.run(step1_agent, input=step1_messages)
  435. print(f"\n✅ Step 1 结果:\n{result1.final_output[:300]}...\n")
  436. step1_result = extract_json(result1.final_output)
  437. # 暂时只返回 Step 1 结果,不继续后面的步骤
  438. return {
  439. "灵感点": point['name'],
  440. "step1_来源可能性分析": step1_result
  441. }
  442. # TODO: 后续步骤暂时注释掉,先把 Step 1 做扎实
  443. # selected_types = step1_result.get('选择的来源类型', [])
  444. #
  445. # # ========== Step 2: 解构每个来源(输入→处理→输出) ==========
  446. # print(f"\n{'='*60}")
  447. # print(f"Step 2: 解构每个来源 - 输入→处理→输出 (针对类型: {', '.join(selected_types)})")
  448. # print(f"{'='*60}")
  449. #
  450. # step2_results = {}
  451. #
  452. # for source_type in selected_types:
  453. # if source_type == "B":
  454. # print(f"\n▶ Step 2.B: 解构从博主历史如何得到灵感")
  455. # step2_messages = [{
  456. # "role": "user",
  457. # "content": content + [{
  458. # "type": "input_text",
  459. # "text": f"""
  460. # ---
  461. #
  462. # Step 1 已确定:这个灵感点可能来自"博主账号历史"
  463. #
  464. # 请解构:从博主历史如何一步步得到这个灵感点?
  465. #
  466. # 要求明确标识:
  467. # - 输入:具体是哪个历史帖子?里面有什么内容?
  468. # - 处理:如何从输入一步步推导到灵感?(步骤1、2、3...)
  469. # - 输出:最终得到的灵感点
  470. # """
  471. # }]
  472. # }]
  473. # result2b = await Runner.run(step2_b_agent, input=step2_messages)
  474. # print(f"\n✅ Step 2.B 结果:\n{result2b.final_output[:300]}...\n")
  475. # step2_results['B'] = extract_json(result2b.final_output)
  476. #
  477. # elif source_type == "C":
  478. # print(f"\n▶ Step 2.C: 解构从外部信息如何得到灵感")
  479. # step2_messages = [{
  480. # "role": "user",
  481. # "content": content + [{
  482. # "type": "input_text",
  483. # "text": f"""
  484. # ---
  485. #
  486. # Step 1 已确定:这个灵感点可能来自"外部信息"
  487. #
  488. # 请解构:从外部信息如何一步步得到这个灵感点?
  489. #
  490. # 要求明确标识:
  491. # - 输入:具体是什么外部信息?(热点/梗/现象)
  492. # - 处理:如何从输入一步步推导到灵感?(步骤1、2、3、4...)
  493. # - 输出:最终得到的灵感点
  494. # """
  495. # }]
  496. # }]
  497. # result2c = await Runner.run(step2_c_agent, input=step2_messages)
  498. # print(f"\n✅ Step 2.C 结果:\n{result2c.final_output[:300]}...\n")
  499. # step2_results['C'] = extract_json(result2c.final_output)
  500. #
  501. # # ========== Step 3: 路径验证 ==========
  502. # print(f"\n{'='*60}")
  503. # print("Step 3: 路径验证")
  504. # print(f"{'='*60}")
  505. #
  506. # step3_results = []
  507. #
  508. # for source_type in selected_types:
  509. # print(f"\n▶ Step 3.{source_type}: 验证来源路径")
  510. #
  511. # step2_analysis = json.dumps(step2_results.get(source_type, {}), ensure_ascii=False, indent=2)
  512. #
  513. # step3_messages = [{
  514. # "role": "user",
  515. # "content": [{
  516. # "type": "input_text",
  517. # "text": f"""
  518. # 基于前面的分析:
  519. #
  520. # Step 1: 初筛选择了来源类型 {source_type}
  521. # Step 2: 深入分析结果:
  522. # {step2_analysis}
  523. #
  524. # 请对这个来源路径进行验证:列出支持证据、反驳点、给出评分。
  525. # """
  526. # }]
  527. # }]
  528. #
  529. # result3 = await Runner.run(step3_agent, input=step3_messages)
  530. # print(f"\n✅ Step 3.{source_type} 结果:\n{result3.final_output[:300]}...\n")
  531. # step3_results.append(extract_json(result3.final_output))
  532. #
  533. # # ========== Step 4: 综合结论 ==========
  534. # print(f"\n{'='*60}")
  535. # print("Step 4: 综合结论")
  536. # print(f"{'='*60}")
  537. #
  538. # all_analysis = {
  539. # "step1": step1_result,
  540. # "step2": step2_results,
  541. # "step3": step3_results
  542. # }
  543. #
  544. # step4_messages = [{
  545. # "role": "user",
  546. # "content": [{
  547. # "type": "input_text",
  548. # "text": f"""
  549. # 基于前面所有步骤的分析:
  550. #
  551. # {json.dumps(all_analysis, ensure_ascii=False, indent=2)}
  552. #
  553. # 请给出综合结论:最可能的来源路径、各来源占比、完整推导过程。
  554. # """
  555. # }]
  556. # }]
  557. #
  558. # result4 = await Runner.run(step4_agent, input=step4_messages)
  559. # print(f"\n✅ Step 4 结果:\n{result4.final_output[:300]}...\n")
  560. #
  561. # final_result = {
  562. # "灵感点": point['name'],
  563. # "step1_来源类型初筛": step1_result,
  564. # "step2_深入分析": step2_results,
  565. # "step3_路径验证": step3_results,
  566. # "step4_综合结论": extract_json(result4.final_output)
  567. # }
  568. #
  569. # return final_result
  570. def extract_json(text: str) -> Dict:
  571. """从文本中提取JSON"""
  572. try:
  573. if "```json" in text:
  574. json_start = text.index("```json") + 7
  575. json_end = text.index("```", json_start)
  576. json_text = text[json_start:json_end].strip()
  577. elif "```" in text:
  578. json_start = text.index("```") + 3
  579. json_end = text.index("```", json_start)
  580. json_text = text[json_start:json_end].strip()
  581. else:
  582. json_text = text
  583. return json.loads(json_text)
  584. except:
  585. return {"原始输出": text}
  586. # ============================================================================
  587. # Main
  588. # ============================================================================
  589. async def main(current_time, log_url):
  590. import sys
  591. # 参数解析
  592. if len(sys.argv) < 2:
  593. print("用法: python how_decode_v8_new_structure.py <what_result_file>")
  594. print("示例: python how_decode_v8_new_structure.py examples_new/阿里多多酱/output/685b593800000000120141d3_20251104_111017.json")
  595. sys.exit(1)
  596. what_result_file = sys.argv[1]
  597. # 解析文件名:{帖子ID}_{运行日期}_{运行时间}.json
  598. filename = os.path.basename(what_result_file)
  599. filename_without_ext = filename.replace('.json', '')
  600. parts = filename_without_ext.split('_')
  601. if len(parts) < 3:
  602. print(f"❌ 文件名格式不正确: {filename}")
  603. print("期望格式: {帖子ID}_{运行日期}_{运行时间}.json")
  604. sys.exit(1)
  605. post_id = parts[0]
  606. run_date = parts[1]
  607. run_time = parts[2]
  608. print("="*80)
  609. print("HOW 解构 V8 - 拆步骤溯源分析")
  610. print("="*80)
  611. print(f"\n目标帖子ID: {post_id}")
  612. print(f"运行日期: {run_date}")
  613. print(f"运行时间: {run_time}")
  614. # 读取 what 解构结果
  615. what_result = read_json(what_result_file)
  616. if not what_result:
  617. print(f"❌ 无法读取文件: {what_result_file}")
  618. sys.exit(1)
  619. # 从输入路径中提取账号名称
  620. # 路径格式: examples_new/{账号名}/output/{帖子ID}_{运行日期}_{运行时间}.json
  621. path_parts = what_result_file.split('/')
  622. if len(path_parts) >= 3 and path_parts[0] == 'examples_new':
  623. author_name = path_parts[1]
  624. else:
  625. print(f"❌ 无法从路径中提取账号名称: {what_result_file}")
  626. sys.exit(1)
  627. # 构建路径
  628. base_dir = f"examples_new/{author_name}"
  629. history_dir = f"{base_dir}/作者历史帖子"
  630. # 读取目标帖子信息(用于获取账号名称)
  631. target_post_file = f"{history_dir}/{post_id}.json"
  632. target_post = read_json(target_post_file)
  633. account_name = target_post.get('channel_account_name', author_name) if target_post else author_name
  634. # 加载博主历史数据
  635. print(f"\n加载博主历史数据...")
  636. blogger_history = load_blogger_history_v8(history_dir, post_id)
  637. print(f"✓ 已加载 {blogger_history['历史帖子数']} 个历史帖子")
  638. # 提取所有灵感点(只提取灵感点,且只保留名称)
  639. all_points = extract_all_points_v8(what_result)
  640. print(f"\n从 WHAT 解构中提取了 {len(all_points)} 个灵感点:")
  641. for p in all_points:
  642. print(f" - {p['id']}: {p['name']}")
  643. # 对每个灵感点进行溯源分析
  644. source_analysis_results = []
  645. for point in all_points:
  646. result = await analyze_point_step_by_step(
  647. point, all_points, blogger_history, account_name
  648. )
  649. source_analysis_results.append(result)
  650. # 添加延迟避免API限流
  651. await asyncio.sleep(2)
  652. # 保存结果
  653. now = datetime.now()
  654. output_filename = f"{post_id}_{now.strftime('%Y%m%d')}_{now.strftime('%H%M%S')}.json"
  655. output_dir = f"{base_dir}/how_output"
  656. os.makedirs(output_dir, exist_ok=True)
  657. output_file = f"{output_dir}/{output_filename}"
  658. final_result = {
  659. "how_解构_V8": {
  660. "版本说明": "V8 - 拆步骤溯源分析,适配新的输入输出结构",
  661. "目标帖子ID": post_id,
  662. "运行时间": now.strftime('%Y-%m-%d %H:%M:%S'),
  663. "log_url": log_url,
  664. "历史数据统计": {
  665. "历史帖子数": blogger_history['历史帖子数'],
  666. "数据格式": "多模态(图片 + 结构化文本)"
  667. },
  668. "分析范围": "只分析灵感点",
  669. "灵感点数量": len(all_points),
  670. "分析方法": "拆步骤(Step1初筛 -> Step2深入 -> Step3验证 -> Step4结论),只使用灵感点名称,避免what中间过程的干扰",
  671. "灵感点溯源分析": source_analysis_results
  672. }
  673. }
  674. with open(output_file, 'w', encoding='utf-8') as f:
  675. json.dump(final_result, f, ensure_ascii=False, indent=2)
  676. print("\n" + "="*80)
  677. print(f"✓ V8 溯源分析完成!结果已保存到:")
  678. print(f" {output_file}")
  679. print("="*80)
  680. if __name__ == "__main__":
  681. from agents import trace
  682. current_time, log_url = set_trace()
  683. with trace("how decode v8"):
  684. asyncio.run(main(current_time, log_url))