{
  "format": "mosael-workflow",
  "version": 1,
  "workflow_revision": 1,
  "name": "自媒体账号运营诊断",
  "description": "贴一个抖音、小红书或 B站账号的主页链接,取账号资料和最近 30 条作品(发布时间、文案、时长、播放 / 点赞 / 评论 / 收藏 / 转发),由代码算出发布频率、发布时段分布、互动率、头部作品和「最近一半对之前一半」的增长 / 衰退信号,再写一份运营诊断(现状、内容支柱、做得好的、问题、可执行建议)存成笔记,附关键数据表。数据来源在开始节点的下拉里二选一:内嵌浏览器(不用配置、不花钱,小红书 / 抖音要换成已登录的浏览器档案,其他平台也能用,数字是页面上的约数)或 TikHub(要装插件、填密钥,每次运行 2 次请求、每次约 0.001 美元起,以 TikHub 账单为准;字段全、更稳、不用登录)。另有 1–2 次 AI 对话(浏览器那一路多一次整理页面)。",
  "graph_hash": "be4e953843f761064f989ad1b3197dc2e05ae0b238b992b28dda2a50f342ef08",
  "graph": {
    "meta": {
      "template_id": "account_analysis",
      "template_version": 3,
      "source": "official"
    },
    "nodes": [
      {
        "id": "start",
        "type": "start",
        "name": "填账号与数据来源",
        "position": {
          "x": 40,
          "y": 535
        },
        "config": {
          "params": {
            "account_link": "",
            "data_source": "",
            "platform": "",
            "post_count": 30,
            "focus": "",
            "report_language": "中文"
          },
          "required_params": [
            "account_link",
            "data_source"
          ],
          "param_options": {
            "data_source": [
              {
                "value": "browser",
                "label": "内嵌浏览器",
                "description": "不用配置、不花钱;要登录才看得到的页面(小红书一定要、抖音多半要),在「用内嵌浏览器打开」里换成浏览器池里已登录的档案。B 站评论不登录只给前 3 条一级评论(每条下前 20 条回复),要取全也换成已登录 B 站的档案。页面上的数字是约数,发布时间常常缺"
              },
              {
                "value": "tikhub",
                "label": "TikHub",
                "description": "需安装 TikHub 插件、接好连接并填上 API 密钥(按次计费);数据更全更稳、不用登录",
                "requires": "tikhub_account"
              }
            ]
          }
        }
      },
      {
        "id": "link",
        "type": "social_link",
        "name": "认出平台和编号",
        "position": {
          "x": 360,
          "y": 535
        },
        "config": {
          "link": "{{start.account_link}}",
          "platform": "{{start.platform}}",
          "expect": "account"
        }
      },
      {
        "id": "use_tikhub",
        "type": "condition",
        "name": "用 TikHub 取数吗",
        "position": {
          "x": 1000,
          "y": 535
        },
        "config": {
          "left": "{{start.data_source}}",
          "op": "equals",
          "right": "tikhub"
        }
      },
      {
        "id": "tk_is_dy",
        "type": "condition",
        "name": "是抖音吗",
        "position": {
          "x": 1320,
          "y": 60
        },
        "config": {
          "left": "",
          "op": "equals",
          "right": "douyin"
        },
        "inputs": [
          "left"
        ]
      },
      {
        "id": "dy_profile",
        "type": "plugin_tool",
        "name": "TikHub 取抖音账号资料",
        "position": {
          "x": 1640,
          "y": 60
        },
        "config": {
          "plugin_id": "dev.mosael.tikhub",
          "tool_name": "douyin_web_handler_user_profile",
          "instance_id": "",
          "input": {
            "sec_user_id": "{{link.id}}"
          }
        }
      },
      {
        "id": "dy_posts",
        "type": "plugin_tool",
        "name": "TikHub 取抖音最近作品",
        "position": {
          "x": 1960,
          "y": 60
        },
        "config": {
          "plugin_id": "dev.mosael.tikhub",
          "tool_name": "douyin_web_fetch_user_post_videos",
          "instance_id": "",
          "input": {
            "sec_user_id": "{{link.id}}",
            "count": "{{start.post_count}}"
          }
        }
      },
      {
        "id": "dy_metrics",
        "type": "social_metrics",
        "name": "整理抖音作品并算指标",
        "position": {
          "x": 2280,
          "y": 60
        },
        "config": {
          "data": "",
          "kind": "posts",
          "profile": "",
          "limit": "{{start.post_count}}",
          "duration_unit": "milliseconds"
        },
        "inputs": [
          "data",
          "profile"
        ]
      },
      {
        "id": "tk_is_xhs",
        "type": "condition",
        "name": "是小红书吗",
        "position": {
          "x": 1320,
          "y": 250
        },
        "config": {
          "left": "",
          "op": "equals",
          "right": "xiaohongshu"
        },
        "inputs": [
          "left"
        ]
      },
      {
        "id": "xhs_profile",
        "type": "plugin_tool",
        "name": "TikHub 取小红书账号资料",
        "position": {
          "x": 1640,
          "y": 250
        },
        "config": {
          "plugin_id": "dev.mosael.tikhub",
          "tool_name": "xiaohongshu_app_v2_get_user_info",
          "instance_id": "",
          "input": {
            "user_id": "{{link.id}}",
            "share_text": "{{link.url}}"
          }
        }
      },
      {
        "id": "xhs_posts",
        "type": "plugin_tool",
        "name": "TikHub 取小红书最近作品",
        "position": {
          "x": 1960,
          "y": 250
        },
        "config": {
          "plugin_id": "dev.mosael.tikhub",
          "tool_name": "xiaohongshu_app_v2_get_user_posted_notes",
          "instance_id": "",
          "input": {
            "user_id": "{{link.id}}",
            "share_text": "{{link.url}}"
          }
        }
      },
      {
        "id": "xhs_metrics",
        "type": "social_metrics",
        "name": "整理小红书作品并算指标",
        "position": {
          "x": 2280,
          "y": 250
        },
        "config": {
          "data": "",
          "kind": "posts",
          "profile": "",
          "limit": "{{start.post_count}}",
          "duration_unit": "auto"
        },
        "inputs": [
          "data",
          "profile"
        ]
      },
      {
        "id": "tk_is_bili",
        "type": "condition",
        "name": "是B站吗",
        "position": {
          "x": 1320,
          "y": 440
        },
        "config": {
          "left": "",
          "op": "equals",
          "right": "bilibili"
        },
        "inputs": [
          "left"
        ]
      },
      {
        "id": "bili_profile",
        "type": "plugin_tool",
        "name": "TikHub 取B站账号资料",
        "position": {
          "x": 1640,
          "y": 440
        },
        "config": {
          "plugin_id": "dev.mosael.tikhub",
          "tool_name": "bilibili_web_fetch_user_profile",
          "instance_id": "",
          "input": {
            "uid": "{{link.id}}"
          }
        }
      },
      {
        "id": "bili_posts",
        "type": "plugin_tool",
        "name": "TikHub 取B站最近作品",
        "position": {
          "x": 1960,
          "y": 440
        },
        "config": {
          "plugin_id": "dev.mosael.tikhub",
          "tool_name": "bilibili_web_fetch_user_post_videos",
          "instance_id": "",
          "input": {
            "uid": "{{link.id}}",
            "ps": "{{start.post_count}}",
            "order": "pubdate"
          }
        }
      },
      {
        "id": "bili_metrics",
        "type": "social_metrics",
        "name": "整理B站作品并算指标",
        "position": {
          "x": 2280,
          "y": 440
        },
        "config": {
          "data": "",
          "kind": "posts",
          "profile": "",
          "limit": "{{start.post_count}}",
          "duration_unit": "auto"
        },
        "inputs": [
          "data",
          "profile"
        ]
      },
      {
        "id": "tk_unsupported",
        "type": "notify",
        "name": "TikHub 这一路不认这个平台",
        "position": {
          "x": 1640,
          "y": 630
        },
        "config": {
          "title": "没有开始分析:TikHub 这一路不支持这个平台",
          "body": "认出的平台是「{{link.platform}}」。TikHub 这一路接了抖音、小红书、B站;别的平台在开始节点的数据来源(data_source)里改选「内嵌浏览器」再运行。认错了的话,在开始节点的 platform 里写明平台。"
        }
      },
      {
        "id": "web_open",
        "type": "browser_open",
        "name": "用内嵌浏览器打开",
        "position": {
          "x": 1320,
          "y": 858
        },
        "config": {
          "url": "",
          "session_mode": "named",
          "session_name": "自媒体分析"
        },
        "inputs": [
          "url"
        ]
      },
      {
        "id": "web_read",
        "type": "browser_evaluate",
        "name": "往下滚几屏,读出页面文字",
        "position": {
          "x": 1640,
          "y": 858
        },
        "config": {
          "session": "",
          "expression": "(async () => {\n  const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n  const BILI = /bilibili\\.com\\/video\\/(BV[\\w]+)/;\n  const bili = location.href.match(BILI) || String(input.video_url || \"\").match(BILI);\n  if (bili) {\n    //: B 站:页面上下文直接调它自己的接口 —— 带登录态、结构化 JSON、分页器和楼中楼都在里面,\n    //: 评论区组件渲染失败(风控「玩命加载」)也不受影响。接口变了就落空,落回下面读 DOM 那条路。\n    try {\n      const getJson = async (url) => (await fetch(url, { credentials: \"include\" })).json();\n      const started = Date.now();\n      //: 时间预算要落在节点的脚本上限(timeout_ms)之内;页与页之间隔一下,不踩频控。\n      const budget = Number(input.budget_ms) || 15000;\n      const pause = input.page_pause_ms == null ? 400 : Number(input.page_pause_ms);\n      const outOfTime = () => Date.now() - started > budget;\n      const view = await getJson(\"https://api.bilibili.com/x/web-interface/view?bvid=\" + bili[1]);\n      const v = view && view.data;\n      if (v && v.aid) {\n        const total = v.stat ? Number(v.stat.reply) || 0 : 0;\n        const nav = await getJson(\"https://api.bilibili.com/x/web-interface/nav\").catch(() => null);\n        const loggedIn = Boolean(nav && nav.data && nav.data.isLogin);\n        //: 评论有多少抓多少,上限 comment_max(最多 2000 条)。\n        const wanted = Math.min(Number(input.comment_max) || 1000, 2000);\n        const seen = new Set();\n        const picked = [];\n        let stoppedBy = \"\";\n        //: 同一条评论只算一次(一级里自带的楼中楼预览、置顶,和翻出来的是同一条)。楼中楼拍平、带 ↳ 前缀。\n        const take = (c, isReply) => {\n          const id = c && (c.rpid_str || (c.rpid ? String(c.rpid) : \"\"));\n          if (!id || seen.has(id)) return;\n          seen.add(id);\n          picked.push({ isReply, comment: {\n            author: c.member ? c.member.uname : \"\",\n            //: 原文照搬:换行是原样的一部分;接口交回的 HTML 转义(&#39;)留给整理节点解一次(unescape_html)。\n            text: (isReply ? \"↳ \" : \"\") + ((c.content ? c.content.message : \"\") || \"\").trim(),\n            likes: typeof c.like === \"number\" ? c.like : \"\",\n            published_at: c.ctime ? new Date(c.ctime * 1000).toISOString().slice(0, 10) : \"\",\n          } });\n        };\n        const full = () => {\n          if (picked.length >= wanted) { stoppedBy = \"limit\"; return true; }\n          if (outOfTime()) { stoppedBy = \"time\"; return true; }\n          return false;\n        };\n        //: 一级评论逐页翻到平台不再给(第 1 页另带置顶)。未登录时 B 站只给 1 页 3 条 —— 照实停下,下面说清。\n        const threads = [];\n        for (let pn = 1; !(pn > 1 && full()); pn += 1) {\n          const r = await getJson(\"https://api.bilibili.com/x/v2/reply?type=1&oid=\" + v.aid + \"&sort=2&ps=20&pn=\" + pn);\n          const d = (r && r.data) || {};\n          const replies = d.replies || [];\n          const pinned = pn === 1 ? [...(d.top_replies || []), ...(d.upper && d.upper.top ? [d.upper.top] : [])] : [];\n          for (const one of [...pinned, ...replies]) {\n            take(one, false);\n            for (const sub of one.replies || []) take(sub, true);\n            threads.push(one);\n          }\n          if (replies.length < 20) break;\n          await wait(pause);\n        }\n        //: 楼中楼:一级里只自带前几条预览,回复更多的楼逐页翻 reply/reply,翻到平台不再给。\n        for (const one of threads) {\n          if (stoppedBy || full()) break;\n          if ((one.rcount || 0) <= (one.replies || []).length) continue;\n          for (let pn = 1; !full(); pn += 1) {\n            const r = await getJson(\"https://api.bilibili.com/x/v2/reply/reply?type=1&oid=\" + v.aid + \"&root=\" + (one.rpid_str || one.rpid) + \"&ps=20&pn=\" + pn);\n            const subs = (r && r.data && r.data.replies) || [];\n            for (const sub of subs) take(sub, true);\n            if (subs.length < 20) break;\n            await wait(pause);\n          }\n        }\n        const kept = picked.slice(0, wanted);\n        if (kept.length) {\n          const fetchedReplies = kept.filter((one) => one.isReply).length;\n          //: 取到的比平台说的少,说清是哪一种:没登录(平台只给前几条)、到了条数 / 时间上限、或者平台就是没给\n          //: (被删除、折叠、仅自己可见)。这几句话由模板按界面语言交进来(input.notes)。\n          const notes = input.notes || {};\n          const gap = kept.length >= total ? \"\"\n            : stoppedBy === \"limit\" ? notes.limit\n            : stoppedBy === \"time\" ? notes.time\n            : !loggedIn ? notes.login\n            : notes.platform;\n          //: 视频本身的数据也在 view 接口里(播放、点赞、时长、发布时间……)—— 一并交回,爆款拆解直接整理它。\n          //: 只带整理要用的那几格,原样的 view 返回很大,会白占运行记录。\n          const video = { bvid: v.bvid, aid: v.aid, title: v.title, pubdate: v.pubdate, duration: v.duration, desc: v.desc,\n                          owner: v.owner ? { name: v.owner.name, mid: v.owner.mid } : null, stat: v.stat || {} };\n          return { url: location.href, title: document.title, now: new Date().toISOString(), mode: \"api\", total,\n                   fetched: kept.length, fetched_roots: kept.length - fetchedReplies, fetched_replies: fetchedReplies,\n                   logged_in: loggedIn, gap_note: gap || \"\", expanded: -1, video,\n                   text: JSON.stringify({ total, comments: kept.map((one) => one.comment) }) };\n        }\n      }\n    } catch (e) { /* 接口这条路不通就落回读 DOM */ }\n  }\n  await wait(Number(input.settle_ms) || 2500);\n  const rounds = Math.min(Number(input.scrolls) || 0, 12);\n  let last = -1, stable = 0;\n  for (let i = 0; i < rounds; i += 1) {\n    window.scrollBy(0, Math.max(window.innerHeight, 800));\n    await wait(Number(input.pause_ms) || 1300);\n    const h = document.body.scrollHeight;\n    const atBottom = window.scrollY + window.innerHeight >= h - 4;\n    if (atBottom && h === last) { stable += 1; if (stable >= 2) break; } else { stable = 0; }\n    last = h;\n  }\n  const expandMax = Math.min(Number(input.expand_max) || 0, 20);\n  const expanders = [];\n  if (expandMax > 0) {\n    await wait(Number(input.expand_settle_ms) || 1200);\n    //: 目标站点说什么语言不归界面语言管 —— 两种都认。英文站点(YouTube 等)的折叠钮是\n    //: 「View 12 replies」「Show more replies」;认不出就这轮不展开,读到的仍是顶层评论。\n    const PATTERN = /^(共\\s*\\d+\\s*条回复|展开\\s*\\d*\\s*条回复|展开更多回复|查看回复|view\\s+\\d+\\s+(more\\s+)?repl|show\\s+more\\s+repl)/i;\n    //: 折叠钮的文字可能是「共<em>43</em>条回复」这种带子标签的 —— 不要求叶子,\n    //: 取**最深的**那个匹配(后代里没有再匹配的),点它才对得上点击处理器。\n    const collect = (root) => {\n      root.querySelectorAll(\"*\").forEach((el) => {\n        if (el.shadowRoot) collect(el.shadowRoot);\n        if (expanders.length >= expandMax || el.tagName === \"A\") return;\n        const text = (el.textContent || \"\").trim();\n        if (text.length <= 2 || text.length >= 40 || !PATTERN.test(text)) return;\n        const nested = Array.from(el.children).some((c) => PATTERN.test((c.textContent || \"\").trim()));\n        if (!nested) expanders.push(el);\n      });\n    };\n    collect(document);\n    //: 说明文字旁边另有一个「点击查看」时点那个:B 站的折叠钮是 <span>共5条回复，</span><bili-text-button>点击查看\n    //: </bili-text-button>,点 span 什么也不发生(实测展开报了 8 次、楼中楼一条没出来)。\n    const VIEW = /^(点击查看|查看|展开|view|show)/i;\n    const clickTarget = (el) => Array.from((el.parentElement && el.parentElement.children) || [])\n      .find((one) => one !== el && VIEW.test((one.textContent || \"\").trim())) || el;\n    for (const el of expanders) {\n      const target = clickTarget(el);\n      target.scrollIntoView({ block: \"center\" });\n      target.click();\n      await wait(Number(input.expand_wait_ms) || 600);\n    }\n  }\n  await wait(Number(input.final_ms) || 1500);\n  const SKIP = new Set([\"STYLE\", \"SCRIPT\", \"NOSCRIPT\", \"LINK\", \"META\"]);\n  const parts = [(document.body && document.body.innerText) || \"\"];\n  const visit = (root) => {\n    root.querySelectorAll(\"*\").forEach((el) => {\n      if (!el.shadowRoot) return;\n      const text = Array.from(el.shadowRoot.children)\n        .filter((one) => !SKIP.has(one.tagName))\n        .map((one) => one.innerText || \"\").join(\"\\n\").trim();\n      if (text) parts.push(text);\n      visit(el.shadowRoot);\n    });\n  };\n  visit(document);\n  const limit = Number(input.max_chars) || 30000;\n  return { url: location.href, title: document.title, now: new Date().toISOString(), expanded: expanders.length, text: parts.join(\"\\n\\n\").slice(0, limit) };\n})()",
          "input": {
            "settle_ms": 2500,
            "scrolls": 6,
            "pause_ms": 1200,
            "max_chars": 30000,
            "expand_max": 0
          }
        },
        "inputs": [
          "session"
        ]
      },
      {
        "id": "web_close",
        "type": "browser_close",
        "name": "关掉浏览器",
        "position": {
          "x": 1960,
          "y": 1010
        },
        "config": {
          "session": ""
        },
        "inputs": [
          "session"
        ]
      },
      {
        "id": "web_struct",
        "type": "llm",
        "name": "把页面文字抄成账号资料和作品清单",
        "position": {
          "x": 1960,
          "y": 858
        },
        "config": {
          "profile_id": "",
          "model": "",
          "preset": "precise",
          "system": "你会收到内嵌浏览器打开一个自媒体账号主页后读到的页面文字,里面可能夹着导航、推荐、登录提示。把这个账号的资料和页面上能看到的每一条作品整理出来。\n规则:\n- 只抄页面上真有的,不编、不补、不估;页面上没有的字段写空字符串。\n- 数字照页面原样抄(1.2万、3.4w、10万+ 都原样写),不要自己换算。\n- 发布时间:能确定就写成 YYYY-MM-DD(有钟点就 YYYY-MM-DD HH:MM);写的是相对时间(3天前、昨天、2小时前)就按\n  「读取时间」换算成日期;只写了月-日(09-21)就补上读取时间那一年;实在看不出就空字符串。\n- 页面主要是登录框、验证码、「登录后查看更多」时 login_wall = true,并在 notes 里说清看到了什么;\n  这时宁可少抄,不要把推荐位、广告、别人的作品当成这个账号的。\n只输出符合 JSON Schema 的对象。",
          "prompt": "平台(可能为空):{{link.platform}}\n主页:{{link.url}}\n读取时间:{{web_read.value.now}}\n页面标题:{{web_read.value.title}}\n\n页面文字:\n{{web_read.value.text}}",
          "response_format": "json_schema",
          "json_schema_name": "account_page",
          "json_schema": {
            "type": "object",
            "properties": {
              "account": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "handle": {
                    "type": "string",
                    "description": "平台上的账号 / 号码"
                  },
                  "bio": {
                    "type": "string"
                  },
                  "followers": {
                    "type": "string",
                    "description": "照页面原样抄(如 1.2万、3456、10万+);页面上没有就空字符串"
                  },
                  "following": {
                    "type": "string",
                    "description": "照页面原样抄(如 1.2万、3456、10万+);页面上没有就空字符串"
                  },
                  "likes_total": {
                    "type": "string",
                    "description": "照页面原样抄(如 1.2万、3456、10万+);页面上没有就空字符串"
                  },
                  "posts_total": {
                    "type": "string",
                    "description": "照页面原样抄(如 1.2万、3456、10万+);页面上没有就空字符串"
                  }
                },
                "required": [
                  "name",
                  "handle",
                  "bio",
                  "followers",
                  "following",
                  "likes_total",
                  "posts_total"
                ],
                "additionalProperties": false
              },
              "posts": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "title": {
                      "type": "string",
                      "description": "标题或文案的开头"
                    },
                    "published_at": {
                      "type": "string",
                      "description": "发布时间,写成 YYYY-MM-DD 或 YYYY-MM-DD HH:MM;看不出就空字符串"
                    },
                    "duration": {
                      "type": "string",
                      "description": "时长,写成 mm:ss 或秒数;没有就空字符串"
                    },
                    "views": {
                      "type": "string",
                      "description": "照页面原样抄(如 1.2万、3456、10万+);页面上没有就空字符串"
                    },
                    "likes": {
                      "type": "string",
                      "description": "照页面原样抄(如 1.2万、3456、10万+);页面上没有就空字符串"
                    },
                    "comments": {
                      "type": "string",
                      "description": "照页面原样抄(如 1.2万、3456、10万+);页面上没有就空字符串"
                    },
                    "collects": {
                      "type": "string",
                      "description": "照页面原样抄(如 1.2万、3456、10万+);页面上没有就空字符串"
                    },
                    "shares": {
                      "type": "string",
                      "description": "照页面原样抄(如 1.2万、3456、10万+);页面上没有就空字符串"
                    }
                  },
                  "required": [
                    "title",
                    "published_at",
                    "duration",
                    "views",
                    "likes",
                    "comments",
                    "collects",
                    "shares"
                  ],
                  "additionalProperties": false
                },
                "maxItems": 60
              },
              "login_wall": {
                "type": "boolean"
              },
              "notes": {
                "type": "string",
                "description": "页面上异常的地方:登录墙、只显示了一部分、看不出时间……没有就空字符串"
              }
            },
            "required": [
              "account",
              "posts",
              "login_wall",
              "notes"
            ],
            "additionalProperties": false
          },
          "json_schema_strict": "true",
          "temperature": 0.1,
          "max_tokens": 12000
        }
      },
      {
        "id": "web_metrics",
        "type": "social_metrics",
        "name": "整理页面上的作品并算指标",
        "position": {
          "x": 2280,
          "y": 858
        },
        "config": {
          "data": "{{web_struct.json.posts}}",
          "kind": "posts",
          "profile": "{{web_struct.json.account}}",
          "limit": "{{start.post_count}}"
        }
      },
      {
        "id": "data_block",
        "type": "template",
        "name": "汇合这一路的数据",
        "position": {
          "x": 2600,
          "y": 535
        },
        "config": {
          "template": "{{dy_metrics.summary}}{{xhs_metrics.summary}}{{bili_metrics.summary}}{{web_metrics.summary}}\n\n### 作品明细\n\n{{dy_metrics.table}}{{xhs_metrics.table}}{{bili_metrics.table}}{{web_metrics.table}}"
        }
      },
      {
        "id": "has_posts",
        "type": "condition",
        "name": "取到作品了吗",
        "position": {
          "x": 2920,
          "y": 535
        },
        "config": {
          "left": "{{dy_metrics.count}}{{xhs_metrics.count}}{{bili_metrics.count}}{{web_metrics.count}}",
          "op": "gt",
          "right": "0"
        }
      },
      {
        "id": "no_posts_notice",
        "type": "notify",
        "name": "没取到作品",
        "position": {
          "x": 3240,
          "y": 820
        },
        "config": {
          "title": "账号分析没有完成:一条作品都没取到",
          "body": "{{link.url}} 没取到作品。常见原因:要登录才看得到(小红书一定要、抖音多半要 —— 在「用内嵌浏览器打开」上改用浏览器池里已登录的档案);链接不是主页;账号没有公开作品;TikHub 的工具没勾选或额度用完。{{web_struct.json.notes}}"
        }
      },
      {
        "id": "report",
        "type": "llm",
        "name": "写运营诊断",
        "position": {
          "x": 3240,
          "y": 535
        },
        "config": {
          "profile_id": "",
          "model": "",
          "preset": "precise",
          "system": "你是资深的自媒体运营顾问。你会收到一个账号的资料、最近作品的统计指标和逐条作品数据。\n指标是代码算好的(发布频率、时段分布、互动率、头部作品、最近一半对之前一半的变化),直接引用,不要自己重算或改数。\n\n写一份运营诊断:\n- verdict:一句话结论(这个号现在处在什么状态、最该做的一件事)。\n- report_markdown 用二级标题分成这几节:\n  1. 现状概览:粉丝、发布频率与稳定性、发布时段、互动水平;\n  2. 内容支柱:把作品按题材聚成 3–6 类,每类写条数、代表作品、表现比平均好还是差;\n  3. 做得好的;\n  4. 问题(断更、时段、题材分散、钩子弱、互动结构失衡……有数据支撑才写);\n  5. 增长 / 衰退信号:结合「最近一半 vs 之前一半」,说清在涨还是在掉、可能为什么;\n  6. 可执行建议:5–8 条,每条写清做什么、多久做一次、用哪个指标看效果;\n  7. 数据局限:样本多少条、哪些字段取不到、浏览器那一路的数字是页面上的约数。\n- 只根据给到的数据下结论;没给的数(粉丝画像、完播率、收入)不要编,缺就说缺。\n- 用户写了关心的问题就单独一节回应。\n- 用{{start.report_language}}写。\n只输出符合 JSON Schema 的对象。",
          "prompt": "平台:{{link.platform}}\n账号主页:{{link.url}}\n数据来源:{{start.data_source}}\n用户关心的问题(可能为空):{{start.focus}}\n\n{{data_block.text}}",
          "response_format": "json_schema",
          "json_schema_name": "account_diagnosis",
          "json_schema": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string",
                "description": "笔记标题:带上账号名和「运营诊断」"
              },
              "verdict": {
                "type": "string",
                "description": "一句话结论"
              },
              "report_markdown": {
                "type": "string",
                "description": "完整报告,Markdown"
              }
            },
            "required": [
              "title",
              "verdict",
              "report_markdown"
            ],
            "additionalProperties": false
          },
          "json_schema_strict": "true",
          "temperature": 0.3,
          "max_tokens": 10000
        }
      },
      {
        "id": "quotes",
        "type": "quote_check",
        "name": "核对引用的原文",
        "position": {
          "x": 3560,
          "y": 535
        },
        "config": {
          "texts": {
            "verdict": "{{report.json.verdict}}",
            "report": "{{report.json.report_markdown}}"
          },
          "sources": {
            "posts": "{{dy_metrics.items}}{{xhs_metrics.items}}{{bili_metrics.items}}{{web_metrics.items}}",
            "account": "{{dy_metrics.account}}{{xhs_metrics.account}}{{bili_metrics.account}}{{web_metrics.account}}",
            "data": "{{data_block.text}}",
            "focus": "{{start.focus}}"
          }
        }
      },
      {
        "id": "save_note",
        "type": "note_create",
        "name": "存成笔记",
        "position": {
          "x": 3880,
          "y": 535
        },
        "config": {
          "title": "{{report.json.title}}",
          "markdown": "> {{quotes.texts.verdict}}\n\n{{quotes.texts.report}}\n\n---\n\n## 附:关键数据\n\n{{data_block.text}}\n\n{{quotes.summary}}",
          "tags": "账号分析"
        }
      },
      {
        "id": "done_notice",
        "type": "notify",
        "name": "诊断完成通知",
        "position": {
          "x": 4200,
          "y": 535
        },
        "config": {
          "title": "账号运营诊断已存成笔记",
          "body": "{{quotes.texts.verdict}}"
        }
      },
      {
        "id": "output",
        "type": "output",
        "name": "交付诊断与数据",
        "position": {
          "x": 4520,
          "y": 535
        },
        "config": {
          "values": {
            "note_id": "{{save_note.note_id}}",
            "verdict": "{{quotes.texts.verdict}}",
            "report": "{{quotes.texts.report}}",
            "platform": "{{link.platform}}",
            "data": "{{data_block.text}}",
            "quote_check": "{{quotes.summary}}"
          }
        }
      }
    ],
    "edges": [
      {
        "id": "start__link",
        "source": "start",
        "target": "link"
      },
      {
        "id": "link__use_tikhub",
        "source": "link",
        "target": "use_tikhub"
      },
      {
        "id": "use_tikhub__tk_is_dy__true",
        "source": "use_tikhub",
        "target": "tk_is_dy",
        "source_handle": "true"
      },
      {
        "id": "tk_is_dy__dy_profile__true",
        "source": "tk_is_dy",
        "target": "dy_profile",
        "source_handle": "true"
      },
      {
        "id": "dy_profile__dy_posts",
        "source": "dy_profile",
        "target": "dy_posts"
      },
      {
        "id": "dy_posts__dy_metrics",
        "source": "dy_posts",
        "target": "dy_metrics"
      },
      {
        "id": "tk_is_dy__tk_is_xhs__false",
        "source": "tk_is_dy",
        "target": "tk_is_xhs",
        "source_handle": "false"
      },
      {
        "id": "tk_is_xhs__xhs_profile__true",
        "source": "tk_is_xhs",
        "target": "xhs_profile",
        "source_handle": "true"
      },
      {
        "id": "xhs_profile__xhs_posts",
        "source": "xhs_profile",
        "target": "xhs_posts"
      },
      {
        "id": "xhs_posts__xhs_metrics",
        "source": "xhs_posts",
        "target": "xhs_metrics"
      },
      {
        "id": "tk_is_xhs__tk_is_bili__false",
        "source": "tk_is_xhs",
        "target": "tk_is_bili",
        "source_handle": "false"
      },
      {
        "id": "tk_is_bili__bili_profile__true",
        "source": "tk_is_bili",
        "target": "bili_profile",
        "source_handle": "true"
      },
      {
        "id": "bili_profile__bili_posts",
        "source": "bili_profile",
        "target": "bili_posts"
      },
      {
        "id": "bili_posts__bili_metrics",
        "source": "bili_posts",
        "target": "bili_metrics"
      },
      {
        "id": "tk_is_bili__tk_unsupported__false",
        "source": "tk_is_bili",
        "target": "tk_unsupported",
        "source_handle": "false"
      },
      {
        "id": "use_tikhub__web_open__false",
        "source": "use_tikhub",
        "target": "web_open",
        "source_handle": "false"
      },
      {
        "id": "web_open__web_read",
        "source": "web_open",
        "target": "web_read"
      },
      {
        "id": "web_read__web_close",
        "source": "web_read",
        "target": "web_close"
      },
      {
        "id": "web_read__web_struct",
        "source": "web_read",
        "target": "web_struct"
      },
      {
        "id": "web_struct__web_metrics",
        "source": "web_struct",
        "target": "web_metrics"
      },
      {
        "id": "dy_metrics__data_block",
        "source": "dy_metrics",
        "target": "data_block"
      },
      {
        "id": "xhs_metrics__data_block",
        "source": "xhs_metrics",
        "target": "data_block"
      },
      {
        "id": "bili_metrics__data_block",
        "source": "bili_metrics",
        "target": "data_block"
      },
      {
        "id": "web_metrics__data_block",
        "source": "web_metrics",
        "target": "data_block"
      },
      {
        "id": "data_block__has_posts",
        "source": "data_block",
        "target": "has_posts"
      },
      {
        "id": "has_posts__no_posts_notice__false",
        "source": "has_posts",
        "target": "no_posts_notice",
        "source_handle": "false"
      },
      {
        "id": "has_posts__report__true",
        "source": "has_posts",
        "target": "report",
        "source_handle": "true"
      },
      {
        "id": "report__quotes",
        "source": "report",
        "target": "quotes"
      },
      {
        "id": "quotes__save_note",
        "source": "quotes",
        "target": "save_note"
      },
      {
        "id": "save_note__done_notice",
        "source": "save_note",
        "target": "done_notice"
      },
      {
        "id": "done_notice__output",
        "source": "done_notice",
        "target": "output"
      },
      {
        "id": "d-link-platform-tk_is_dy-left",
        "source": "link",
        "target": "tk_is_dy",
        "kind": "data",
        "source_output": "platform",
        "target_input": "left"
      },
      {
        "id": "d-dy_posts-output-dy_metrics-data",
        "source": "dy_posts",
        "target": "dy_metrics",
        "kind": "data",
        "source_output": "output",
        "target_input": "data"
      },
      {
        "id": "d-dy_profile-output-dy_metrics-profile",
        "source": "dy_profile",
        "target": "dy_metrics",
        "kind": "data",
        "source_output": "output",
        "target_input": "profile"
      },
      {
        "id": "d-link-platform-tk_is_xhs-left",
        "source": "link",
        "target": "tk_is_xhs",
        "kind": "data",
        "source_output": "platform",
        "target_input": "left"
      },
      {
        "id": "d-xhs_posts-output-xhs_metrics-data",
        "source": "xhs_posts",
        "target": "xhs_metrics",
        "kind": "data",
        "source_output": "output",
        "target_input": "data"
      },
      {
        "id": "d-xhs_profile-output-xhs_metrics-profile",
        "source": "xhs_profile",
        "target": "xhs_metrics",
        "kind": "data",
        "source_output": "output",
        "target_input": "profile"
      },
      {
        "id": "d-link-platform-tk_is_bili-left",
        "source": "link",
        "target": "tk_is_bili",
        "kind": "data",
        "source_output": "platform",
        "target_input": "left"
      },
      {
        "id": "d-bili_posts-output-bili_metrics-data",
        "source": "bili_posts",
        "target": "bili_metrics",
        "kind": "data",
        "source_output": "output",
        "target_input": "data"
      },
      {
        "id": "d-bili_profile-output-bili_metrics-profile",
        "source": "bili_profile",
        "target": "bili_metrics",
        "kind": "data",
        "source_output": "output",
        "target_input": "profile"
      },
      {
        "id": "d-link-url-web_open-url",
        "source": "link",
        "target": "web_open",
        "kind": "data",
        "source_output": "url",
        "target_input": "url"
      },
      {
        "id": "d-web_open-session-web_read-session",
        "source": "web_open",
        "target": "web_read",
        "kind": "data",
        "source_output": "session",
        "target_input": "session"
      },
      {
        "id": "d-web_read-session-web_close-session",
        "source": "web_read",
        "target": "web_close",
        "kind": "data",
        "source_output": "session",
        "target_input": "session"
      }
    ]
  }
}
