Felo API PlatformFelo API Platform

研究

把一個搜尋詞變成 SEO 內容簡報

看目前哪些頁面在回答這個搜尋詞,把它們讀成 Markdown,梳理它們回答和遺漏的問題,再寫一份編輯可以直接拿去列大綱的簡報。

建置時間25 分鐘難度進階更新於2026-10-30
適合誰內容行銷SEO 專家編輯

複製 prompt

把方括號裡的欄位替換掉,然後貼到 Claude Code、Codex 或任何能存取終端機的 AI。它會安裝 Felo skill、在花額度前詢問,並執行這套工作流程。

為[產品或網站]寫一份針對查詢[目標查詢]的 SEO 內容簡報。

讀者:[讀者]。市場與語言:[地區]、[語言]。角度:[指南 / 比較 / 產品頁]。它支援的決策:[新文章 / 更新 / 補缺口]。

先讀目前有哪些頁面在回答這個查詢,再列大綱。輸出一份 Markdown 簡報,包含:
1. 目標查詢和你推斷的搜尋意圖,並提出這個判斷的依據。
2. 目前頁面涵蓋了什麼:標題、URL、開頭章節。
3. 目前頁面回答了哪些問題,漏掉了哪些問題。
4. 建議大綱,每個章節用一句話說明它必須證明什麼。
5. 來自來源的證據和例子,附連結。
6. 完整來源清單。

規則:
- 不要聲稱搜尋量、關鍵字難度、排名位置或流量。這個工作流程不測量這些;用「此處未測量」代替數字。
- 不要編造統計、引文或關於競品內容的說法。
- 把意圖描述為對頁面的解讀,而不是測量出的事實。
- 引文要短、註明出處並附連結。
- 把地區和語言落實為明確值。
- 消耗額度前,先展示預計執行的 API 呼叫並等待我批准。
- 不要發布、排程或傳送任何內容。
- 將結果寫入 ./seo-brief.md,並在對話中總結。

複製的內容也包含安裝步驟和安全規則。

你會得到什麼

一次執行的範例輸出,不是即時生成。

簡報範例(節錄)

SEO 簡報 — 「如何自動化支援分診」

目標查詢與意圖

查詢:「如何自動化支援分診」。回傳的頁面以實作步驟開頭,而不是產品頁,所以意圖讀作 how-to。這是對頁面的解讀,不是測量出的意圖標籤。

目前頁面涵蓋什麼

頁面URL開頭內容
自動化支援分診:實用指南https://example.com/guide路由規則,然後是標籤
分診自動化清單https://example.com/checklist清單本身

頁面回答的問題

  • 分診自動化改變了什麼
  • 從哪些路由規則開始

頁面漏掉的問題

  • 如何衡量分診是否真的改善
  • 分類器判斷錯誤時怎麼辦

建議大綱

  1. 分診自動化改變了什麼——證明它關乎佇列,而不是人力。
  2. 值得自動化的前三條路由規則——每條用實例證明。
  3. 第一個月如何衡量——用營運負責人拿得到的指標證明。
  4. 自動化出錯時——證明人工備援路徑。

證據

  • 路由規則範例,引自 https://example.com/guide(2026-10-30 核對)

此處未測量

搜尋量、關鍵字難度與排名位置。這個工作流程讀取公開頁面,不查詢搜尋索引或排名工具。

來源

  • https://example.com/guide
  • https://example.com/checklist

實際呼叫

給開發者:同一套工作流程的可複製程式碼,依執行順序排列。

展開實際 API 呼叫
  1. 先看目前誰在回答

    用 Chat 問這個搜尋詞,拿到答案和它引用的頁面;這些頁面就是目前的競爭內容。

    seo-brief.ts
    const BASE = "https://openapi.felo.ai";
    const headers = {
      Authorization: `Bearer ${process.env.FELO_API_KEY}`,
      "Content-Type": "application/json",
    };
    
    async function felo<T>(path: string, body?: unknown): Promise<T> {
      const res = await fetch(`${BASE}${path}`, {
        method: body ? "POST" : "GET",
        headers,
        body: body ? JSON.stringify(body) : undefined,
      });
      if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
      return res.json() as Promise<T>;
    }
    
    type ChatData = {
      data: {
        answer: string;
        resources: { link: string; title: string; snippet: string }[];
      };
    };
    
    const query = "how to automate support triage";
    const ourSite = "example.com";
    
    const coverage = await felo<ChatData>("/v2/chat", {
      query: `What pages currently answer the search query "${query}"? List the pages and what each one covers.`,
    });
    
    const candidates = coverage.data.resources
      .filter((resource) => !resource.link.includes(ourSite))
      .slice(0, 5)
      .map((resource) => ({ url: resource.link, title: resource.title }));

    範例輸出

    {
      "answer": "Readers searching this usually want a step-by-step …",
      "sources": [
        { "link": "https://example.com/guide", "title": "…", "snippet": "…" }
      ]
    }
  2. 把頁面讀成 Markdown

    用 Web Fetch 把最強的三到五個頁面抓成 Markdown,保留標題和問題。

    seo-brief.ts
    type ExtractData = { data: { content: string } };
    
    const pages = await Promise.all(
      candidates.map(async (page) => {
        const res = await felo<ExtractData>("/v2/web/extract", {
          url: page.url,
          output_format: "markdown",
          with_readability: true,
          with_cache: true,
        });
        const checkedAt = new Date().toISOString().slice(0, 10);
        return { ...page, checkedAt, content: res.data.content };
      }),
    );

    範例輸出

    {
      "url": "https://example.com/guide",
      "checkedAt": "2026-10-30",
      "content": "# Automating support triage\n\n## Routing rules\n…"
    }
  3. 梳理覆蓋和遺漏

    把每頁的 H2/H3 和 FAQ 問題抽出來,做一張覆蓋表:哪些問題被回答,哪些沒有。

    seo-brief.ts
    type Heading = { page: string; heading: string };
    
    const headings: Heading[] = pages.flatMap((page) =>
      page.content
        .split("\n")
        .filter((line) => line.startsWith("## "))
        .map((line) => ({
          page: page.title,
          heading: line.replace(/^##\s+/, ""),
        })),
    );
    
    const questions = headings.filter((item) => item.heading.includes("?"));
    
    const byTopic = new Map<string, string[]>();
    for (const item of headings) {
      byTopic.set(item.heading, [...(byTopic.get(item.heading) ?? []), item.page]);
    }

    範例輸出

    // questions
    [
      { "page": "…", "heading": "What routes should you automate first?" }
    ]
  4. 寫簡報

    寫目標詞、意圖、覆蓋對比、遺漏問題、建議大綱和來源列表。

    seo-brief.ts
    const today = new Date().toISOString().slice(0, 10);
    
    const brief = [
      `# SEO brief — "${query}"`,
      `Checked ${today}. Site: ${ourSite}.`,
      "",
      "## Intent read",
      "The pages returned for this query lead with: " +
        headings.slice(0, 6).map((item) => item.heading).join("; ") +
        ". This is a reading of their structure, not a measured intent label.",
      "",
      "## What the pages cover",
      "| Page | URL | Checked |",
      "| --- | --- | --- |",
      ...pages.map((page) => `| ${page.title} | ${page.url} | ${page.checkedAt} |`),
      "",
      "## Questions the pages answer",
      ...questions.map((item) => `- ${item.heading} (${item.page})`),
      "",
      "## Topics more than one page covers",
      ...[byTopic.entries()]
        .filter(([, pageTitles]) => pageTitles.length > 1)
        .map(([heading, pageTitles]) => `- ${heading} (${pageTitles.length} pages)`),
      "",
      "## Proposed outline",
      ...questions.slice(0, 5).map((item, index) => `${index + 1}. ${item.heading}`),
      "",
      "## Questions they miss",
      "<!-- Only questions none of the fetched pages answer. Delete this comment when filled. -->",
      "",
      "## Not measured here",
      "Search volume, keyword difficulty, and ranking position. This workflow reads public pages; it does not query a search index or a rank tracker.",
      "",
      "## Sources",
      ...pages.map((page) => `- ${page.title}: ${page.url}`),
    ].join("\n");
    
    console.log(brief);

    範例輸出

    # SEO brief — "how to automate support triage"
  5. 存進 LiveDoc

    把簡報和頁面連結存進 LiveDoc,下次更新只抓變化的部分。

    seo-brief.ts
    type LiveDocData = { data: { short_id: string } };
    
    const briefDoc = await felo<LiveDocData>("/v2/livedocs", {
      name: `SEO briefs: ${ourSite}`,
      description: "One document resource per target query",
    });
    
    await felo(`/v2/livedocs/${briefDoc.data.short_id}/resources/urls`, {
      urls: pages.map((page) => ({ url: page.url, title: page.title })),
    });
    
    await felo(`/v2/livedocs/${briefDoc.data.short_id}/resources/doc`, {
      title: `SEO brief — ${query}`,
      content: brief,
    });
    
    console.log(briefDoc.data.short_id); // the next query adds to this archive

    範例輸出

    { "short_id": "PvyKouzJirXjFdst4uKRK3" }

包含什麼

  • 來自 Chat 的有來源頁面列表,以及答案背後的連結
  • 目前排名靠前的頁面,轉成 Markdown
  • 這些頁面覆蓋的標題和問題
  • 一份可直接列大綱的簡報
  • 保存來源的 LiveDoc,下次更新可以接著用

不做什麼

  • 不測排名、搜尋量、關鍵字難度或流量
  • 只讀公開頁面;登入牆和付費牆可能失敗
  • 不會替你發佈或改寫整篇文章
  • 一個搜尋詞對應一次執行;批量詞需要你自己迴圈
  • 不保證內容一定排名

常見問題

它測量搜尋量或排名嗎?

不測。它只讀公開頁面,判斷目前哪些內容在回答這個搜尋詞;排名和搜尋量請用你現有的 SEO 工具。

資料從哪裡來?

Chat 回傳有來源的頁面,Web Fetch 把頁面轉成 Markdown。簡報裡的每個判斷都指向具體頁面和日期。

能處理多個搜尋詞嗎?

可以,但每次只跑一個詞。批量處理時,為每個詞重複同樣的步驟,並各自保存一份簡報。

適合什麼類型的搜尋詞?

適合資訊型、比較型和「怎麼做」類查詢,因為公開頁面能提供足夠訊號。交易型詞還需要你自己的產品與轉換資料。

會直接寫文章嗎?

不會。它產出的是簡報和大綱,真正寫作仍由編輯或你的寫作 agent 完成。

一次要花多少?

Chat 和頁面擷取都會消耗按用量計費的額度;目前費率在價格頁面。prompt 會在花額度前請求確認。

相關工作流程

改成你的版本

同一套配方,依你實際的交付方式調整。

只做比較型搜尋詞

把範圍限定在「X vs Y」「最佳 X」這類詞,覆蓋表會更容易看出差異化角度。

加入你自己的產品頁

把產品頁作為額外來源加進去,讓大綱能落到你的功能和定價上。

每季更新

同一個 LiveDoc 重跑,觀察競爭頁面和大綱建議怎麼變化。

這套配方背後的參考頁面,以及下一步可以看什麼。