<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Case Study on AI2CORE - AI技術ブログ</title>
    <link>https://www.ai2core.com/categories/case-study/</link>
    <description>Recent content in Case Study on AI2CORE - AI技術ブログ</description>
    <generator>Hugo -- 0.146.4</generator>
    <language>ja</language>
    <lastBuildDate>Fri, 13 Feb 2026 08:00:00 +0900</lastBuildDate>
    <atom:link href="https://www.ai2core.com/categories/case-study/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>GitHub Copilot Enterprise導入で開発効率はどう変わる？ROIを徹底分析</title>
      <link>https://www.ai2core.com/posts/2026-02-13-copilot-enterprise/</link>
      <pubDate>Fri, 13 Feb 2026 08:00:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-02-13-copilot-enterprise/</guid>
      <description>企業向けCopilotの導入効果、コスト対効果、セキュリティ面のメリットを解説。</description>
      <content:encoded><![CDATA[<h1 id="github-copilot-enterprise導入で開発効率はどう変わるroiを徹底分析">GitHub Copilot Enterprise導入で開発効率はどう変わる？ROIを徹底分析</h1>
<h2 id="はじめに">はじめに</h2>
<p>「開発者の生産性を劇的に向上させる」という触れ込みで登場したGitHub Copilot。もはや多くの開発現場で欠かせないツールとなりつつあります。しかし、その一方で、特に企業での導入を検討する際には、次のような疑問や懸念が尽きないのではないでしょうか？</p>
<ul>
<li>「個人向けのCopilotと、高価なEnterprise版では一体何が違うのか？」</li>
<li>「月額$39/ユーザーというコストは、本当に投資に見合う価値（ROI）があるのか？」</li>
<li>「自社の機密情報であるソースコードが、AIの学習データとして使われてしまうのではないか？」</li>
<li>「具体的に、日々の開発フローがどのように変わり、チーム全体としてどのような恩恵を受けられるのか？」</li>
</ul>
<p>この記事は、まさにこうした疑問を持つ開発マネージャー、CTO、そして現場のエンジニアの方々に向けたものです。単なる機能紹介に留まらず、GitHub Copilot Enterpriseがもたらす真の価値、導入効果を測定するためのROI分析フレームワーク、そして多くの人が懸念するセキュリティ面について、プロの視点から徹底的に掘り下げて解説します。</p>
<p>この記事を読み終える頃には、あなたの組織がCopilot Enterpriseを導入すべきか否か、そして導入するならどのように活用し、その効果を最大化すべきか、明確な指針を得られるはずです。</p>
<h2 id="なぜ今github-copilot-enterpriseが重要なのか">なぜ今、GitHub Copilot Enterpriseが重要なのか？</h2>
<p>現代のソフトウェア開発は、その複雑性とスピードの要求がかつてないほど高まっています。マイクロサービス、クラウドネイティブ、多様なプログラミング言語とフレームワーク&hellip; 開発者がキャッチアップすべき技術領域は広がり続け、一方で市場投入までの時間は短縮を迫られています。</p>
<p>このような背景の中で、開発現場はいくつかの共通した課題に直面しています。</p>
<ol>
<li>
<p><strong>生産性の頭打ち</strong>: 開発者はコーディングだけに時間を使えるわけではありません。仕様の理解、既存コードの読解、テストコードの実装、Pull Requestのレビュー、ドキュメント作成など、付随するタスクに多くの時間が割かれています。特に、定型的なコード（ボイラープレートコード）の記述や、ライブラリのAPIを調べる時間は、開発のフローを中断させ、集中力を削ぐ大きな要因です。</p>
</li>
<li>
<p><strong>ナレッジのサイロ化と属人化</strong>: 経験豊富なエンジニアの頭の中にしかない設計思想や、社内共通ライブラリの「お作法」。これらはドキュメント化が追いつかず、新しくチームに参加したメンバーがキャッチアップするのに多大な時間を要します。結果として、オンボーディングコストが増大し、チーム全体の生産性向上を妨げます。</p>
</li>
<li>
<p><strong>セキュリティとコンプライアンスのリスク</strong>: 開発効率を上げるために、Stack Overflowやブログ記事からコードをコピー＆ペーストすることは日常的に行われます。しかし、そのコードに脆弱性が含まれていたり、意図せずライセンスに違反するコードを組み込んでしまったりするリスクは常に付きまといます。また、AI支援ツールを利用する際、自社の貴重なソースコードという知的財産が外部に漏洩したり、AIの学習に使われたりしないかという懸念は、企業にとって看過できない問題です。</p>
</li>
</ol>
<p>これらの根深い課題に対し、GitHub Copilot Enterpriseは、単なる「賢いコード補完ツール」を超えた、<strong>開発ライフサイクル全体を支援する統合プラットフォーム</strong>として、具体的な解決策を提示します。それは、個人の生産性を高めるだけでなく、チーム全体の知識共有を促進し、エンタープライズレベルのセキュリティを担保することで、開発組織全体のパフォーマンスを新たな次元へと引き上げる可能性を秘めているのです。</p>
<h2 id="github-copilot-enterpriseの核心機能何がすごいのか">GitHub Copilot Enterpriseの核心機能：何がすごいのか？</h2>
<p>Copilot Enterpriseは、個人向けのCopilot Businessプラン（$19/月）の全機能に加え、組織のナレッジを最大限に活用し、GitHubプラットフォームと深く統合された独自の機能を備えています。その核心となる機能を、具体的なコード例や利用シーンと共に見ていきましょう。</p>
<h3 id="1-githubcomとの完全統合リポジトリ全体がaiの脳になる">1. GitHub.comとの完全統合：リポジトリ全体がAIの「脳」になる</h3>
<p>Enterpriseプラン最大の目玉機能は、GitHub.com上でCopilot Chatが利用可能になり、<strong>リポジトリ全体をコンテキストとして対話できる</strong>点です。これは、開発者のゲームチェンジと言っても過言ではありません。</p>
<p>あなたの組織のプライベートリポジトリのコードは、セキュアな環境でインデックス化されます。これにより、Copilotはローカルで開いているファイルだけでなく、リポジトリ全体の構造、依存関係、コーディング規約を理解した上で、極めて精度の高い回答を生成します。</p>
<p><strong>【利用シーン：新メンバーのオンボーディング】</strong>
新しくプロジェクトに参加したメンバーが、巨大なコードベースを前に途方に暮れているとします。従来であれば、メンターが数時間をかけて説明したり、膨大なドキュメントを読んだりする必要がありました。しかし、Copilot Enterpriseがあれば、GitHubのリポジトリページでチャットを開き、こう質問するだけです。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Copilot Chatへのプロンプト例</span>
</span></span><span style="display:flex;"><span>@workspace このリポジトリの主な機能とアーキテクチャについて教えてください。
</span></span><span style="display:flex;"><span>ユーザー認証はどの部分で処理されていますか？
</span></span><span style="display:flex;"><span>新しいAPIエンドポイントを追加する場合、どのファイルにどのような変更を加えるのが一般的なパターンですか？
</span></span></code></pre></td></tr></table>
</div>
</div><p>Copilotは、リポジトリ内のコードを解析し、<code>README.md</code>や関連コードを引用しながら、的確な回答を返します。これにより、<strong>オンボーディングにかかる時間は劇的に短縮</strong>され、新メンバーは即座に価値を提供し始められます。</p>
<h3 id="2-copilot-chatの進化ideでも組織のナレッジを活用">2. Copilot Chatの進化：IDEでも組織のナレッジを活用</h3>
<p>IDE（VS Codeなど）内のCopilot Chatも、Enterprise版では組織のナレッジベースに接続されます。これにより、社内ライブラリや共通コンポーネントの利用が飛躍的に容易になります。</p>
<p><strong>【コード例：社内共通ライブラリの利用】</strong>
あなたの会社に、日付や通貨のフォーマットを行う <code>internal-utils</code> という共通ライブラリがあるとします。しかし、その使い方はドキュメントが古く、知っている人に聞かなければなりませんでした。</p>
<p>Copilot Enterprise環境では、以下のように曖昧なコメントを書くだけで、Copilotがリポジトリ内の利用例を学習し、適切なコードを提案してくれます。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">9
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// 商品価格を日本円形式でフォーマットする
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// ここからCopilotが提案を開始
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">formatCurrency</span> } <span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#39;@my-company/internal-utils&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">price</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">12800</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">formattedPrice</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">formatCurrency</span>(<span style="color:#a6e22e">price</span>, <span style="color:#e6db74">&#39;JPY&#39;</span>); <span style="color:#75715e">// &#39;￥12,800&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#a6e22e">formattedPrice</span>);
</span></span></code></pre></td></tr></table>
</div>
</div><p>このように、Copilotが「組織の暗黙知」を形式知へと変換し、開発者に提供してくれるのです。</p>
<h3 id="3-pull-requestサマリーの自動生成レビュー工数を劇的に削減">3. Pull Requestサマリーの自動生成：レビュー工数を劇的に削減</h3>
<p>コードレビューは品質を担保するために不可欠ですが、レビュワーにとっては大きな負担です。特に大規模な変更が含まれるPull Request（PR）では、変更の意図を理解するだけで多くの時間を消費します。</p>
<p>Copilot Enterpriseは、PRの差分を解析し、<strong>変更内容のサマリーを自動で生成</strong>する機能を提供します。</p>
<p><img alt="PR Summary Generation (conceptual image)" loading="lazy" src="https://i.imgur.com/example.png"><br>
<em>(これは概念図です。実際にはPRのDescription欄にMarkdown形式でサマリーが自動挿入されます)</em></p>
<p><strong>【生成されるサマリーの例】</strong></p>
<ul>
<li><strong>主な変更点</strong>:
<ul>
<li><code>src/services/AuthService.ts</code> にMFA（多要素認証）のロジックを追加しました。</li>
<li><code>src/controllers/UserController.ts</code> のログインAPIを更新し、MFAの検証ステップを組み込みました。</li>
</ul>
</li>
<li><strong>変更の意図</strong>:
<ul>
<li>セキュリティ強化のため、従来のパスワード認証に加えてTOTP（Time-based One-Time Password）による認証をサポートします。</li>
</ul>
</li>
<li><strong>ファイルごとの変更概要</strong>:
<ul>
<li><code>AuthService.ts</code>: <code>verifyTotp</code> メソッドを新規追加。</li>
<li><code>UserController.ts</code>: <code>/login</code> エンドポイントで <code>verifyTotp</code> を呼び出す処理を追加。</li>
</ul>
</li>
</ul>
<p>このサマリーがあるだけで、レビュワーは瞬時にPRの全体像を把握でき、より本質的なロジックや設計のレビューに集中できます。これにより、<strong>レビューのリードタイムが短縮され、開発サイクル全体が高速化</strong>します。</p>
<h3 id="4-copilot-for-docs公式ドキュメントを横断検索">4. Copilot for Docs：公式ドキュメントを横断検索</h3>
<p>開発中に、特定のライブラリやフレームワークの使い方を調べる時間は決して少なくありません。複数のブラウザタブを開き、公式サイトや技術ブログをさまよう経験は誰にでもあるでしょう。</p>
<p>Copilot Enterpriseに含まれる<code>Copilot for Docs</code>は、React, Azure, MDNといった主要な技術ドキュメントを学習しており、チャットインターフェースから自然言語で質問できます。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Copilot Chatへのプロンプト例</span>
</span></span><span style="display:flex;"><span>@docs ReactのuseEffectフックで、クリーンアップ関数が必要になるのはどのような場合ですか？具体的なコード例をMDNのドキュメントを基に示してください。
</span></span></code></pre></td></tr></table>
</div>
</div><p>Copilotは、信頼性の高い公式ドキュメントに基づいて、正確かつ簡潔な回答とコード例を提供します。これにより、コンテキストスイッチを最小限に抑え、開発者はコーディングに集中し続けることができます。</p>
<h2 id="メリットとroi投資対効果の徹底分析">メリットとROI（投資対効果）の徹底分析</h2>
<p>Copilot Enterpriseの導入には、月額$39/ユーザーという決して安くはないコストがかかります。この投資を正当化できるのか、ROIの観点から具体的に分析してみましょう。</p>
<h3 id="メリットの整理">メリットの整理</h3>
<p>まずは、これまで見てきた機能がもたらすメリットを整理します。</p>
<table>
  <thead>
      <tr>
          <th style="text-align: left">メリットカテゴリ</th>
          <th style="text-align: left">具体的な効果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><strong>開発者の生産性向上</strong></td>
          <td style="text-align: left">定型コードの自動生成、リファクタリング支援、テストコード生成によるコーディング時間の短縮。</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>コード品質の向上</strong></td>
          <td style="text-align: left">ユニットテストの網羅性向上、一貫性のあるコーディングスタイルの維持、ベストプラクティスに基づいたリファクタリング提案。</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>オンボーディングの高速化</strong></td>
          <td style="text-align: left">新メンバーが自力でコードベースを理解し、早期に戦力化。メンターの工数削減。</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>ナレッジ共有の促進</strong></td>
          <td style="text-align: left">属人化していた知識やノウハウがCopilotを通じてチーム全体に共有される。</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>レビュー効率の向上</strong></td>
          <td style="text-align: left">PRサマリー自動生成により、レビュワーの負担を軽減し、デプロイまでのリードタイムを短縮。</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>セキュリティとコンプライアンス</strong></td>
          <td style="text-align: left">IP補償による著作権リスクの低減。自社コードが学習に使われないことの保証。</td>
      </tr>
  </tbody>
</table>
<h3 id="roi試算フレームワーク">ROI試算フレームワーク</h3>
<p>ROIは以下の計算式で算出できます。</p>
<p><strong>ROI (%) = ( (リターン - コスト) / コスト ) * 100</strong></p>
<p>ここでは、具体的な数値を当てはめてシミュレーションしてみましょう。</p>
<p><strong>【前提条件】</strong></p>
<ul>
<li><strong>対象チーム</strong>: エンジニア 20名</li>
<li><strong>エンジニアの平均人件費（月額）</strong>: 80万円（時給換算: 約5,000円）</li>
<li><strong>GitHub Copilot Enterprise コスト</strong>: $39/月・人 (1ドル=155円換算で約6,045円/月・人)</li>
</ul>
<p><strong>1. コストの計算</strong>
年間コスト = 6,045円/人 × 20人 × 12ヶ月 = <strong>1,450,800円</strong></p>
<p><strong>2. リターンの計算（金銭的価値への換算）</strong>
リターンは「削減できた時間 × 時給」で計算します。GitHub社が実施した調査では、「開発タスクの完了時間が55%高速化された」という驚異的なデータも報告されていますが、ここではより控えめに見積もってみましょう。</p>
<ul>
<li><strong>A. コーディング時間削減:</strong>
<ul>
<li>1日のコーディング時間を4時間と仮定。</li>
<li>Copilot導入により、15%の効率化が実現できたとする。</li>
<li>削減時間/日・人 = 4時間 × 0.15 = 0.6時間 (36分)</li>
</ul>
</li>
<li><strong>B. レビュー・調査時間削減:</strong>
<ul>
<li>PRレビューやドキュメント調査など、1日あたり15分の時間削減が実現できたとする。</li>
</ul>
</li>
<li><strong>合計削減時間/日・人:</strong>
<ul>
<li>36分 + 15分 = 51分 (約0.85時間)</li>
</ul>
</li>
</ul>
<p>この削減時間を金額に換算します。</p>
<ul>
<li>月間リターン/人 = 0.85時間/日 × 5,000円/時間 × 20日/月 = 85,000円</li>
<li>年間リターン（チーム全体） = 85,000円/人 × 20人 × 12ヶ月 = <strong>20,400,000円</strong></li>
</ul>
<p><strong>3. ROIの算出</strong></p>
<ul>
<li>年間利益 = リターン(20,400,000円) - コスト(1,450,800円) = 18,949,200円</li>
<li><strong>ROI = (18,949,200円 / 1,450,800円) * 100 ≒ 1306%</strong></li>
</ul>
<p>この試算はあくまで一例ですが、<strong>1日にわずか数十分の時間短縮が実現できるだけで、投資額をはるかに上回るリターンが期待できる</strong>ことがわかります。実際には、オンボーディング期間の短縮による価値や、コード品質向上による将来的なバグ修正コストの削減といった、金銭換算しにくい「定性的」なメリットも上乗せされます。</p>
<h3 id="デメリットと注意点">デメリットと注意点</h3>
<p>もちろん、導入にあたってはデメリットや注意点も考慮すべきです。</p>
<ul>
<li><strong>コスト</strong>: 月額$39は、他のAIコーディングツールと比較して高価です。予算が限られる小規模なチームにとっては導入のハードルとなる可能性があります。</li>
<li><strong>AIへの過信</strong>: Copilotが生成するコードは完璧ではありません。時にはバグを含んだり、非効率的であったり、セキュリティ上の脆弱性を含んだりする可能性もあります。生成されたコードを鵜呑みにせず、最終的な品質担保は開発者自身が行うという意識が不可欠です。</li>
<li><strong>思考力の低下？</strong>: ボイラープレートコードの記述から解放される一方で、単純作業をAIに任せすぎることで、問題解決の基礎体力が低下するのではないかという懸念も一部で議論されています。Copilotを思考停止の道具ではなく、より創造的な作業に集中するための「思考の壁打ち相手」として活用するマインドセットが重要です。</li>
</ul>
<h2 id="現場で使える実践的なtips">現場で使える実践的なTips</h2>
<p>Copilot Enterpriseを導入しただけで、自動的に生産性が上がるわけではありません。その能力を最大限に引き出すための、いくつかの実践的なテクニックを紹介します。</p>
<h3 id="1-プロンプトエンジニアリングを極める">1. プロンプトエンジニアリングを極める</h3>
<p>Copilot Chatとの対話の質は、プロンプト（指示）の質に大きく左右されます。良いプロンプトには、以下の要素を含めることを意識しましょう。</p>
<ul>
<li><strong>役割（Role）</strong>: 「あなたは経験豊富なバックエンドエンジニアです」</li>
<li><strong>文脈（Context）</strong>: 「これからNode.jsとExpressを使って、ユーザー情報を返すAPIを作成します。データベースはPostgreSQLです」</li>
<li><strong>指示（Instruction）</strong>: 「<code>GET /api/users/:id</code> のエンドポイントの実装コードを生成してください」</li>
<li><strong>制約（Constraint）</strong>: 「エラーハンドリングを丁寧に行い、ユーザーが存在しない場合は404エラーを返すようにしてください。コードにはJSDoc形式でコメントを付けてください」</li>
</ul>
<p>また、IDEで利用できるスラッシュコマンドも強力です。</p>
<ul>
<li><code>/explain</code>: 選択したコードの動作を説明させる。複雑な正規表現や他人が書いたコードの解読に便利です。</li>
<li><code>/tests</code>: 選択したコードに対するユニットテストを生成させる。テストカバレッジの向上に直結します。</li>
<li><code>/fix</code>: コード中の問題を検出し、修正案を提案させる。</li>
<li><code>/doc</code>: 関数やクラスにドキュメントコメントを自動生成させる。</li>
</ul>
<h3 id="2-開発フロー全体に組み込む">2. 開発フロー全体に組み込む</h3>
<p>Copilotを単なるコーディング中の補完ツールとしてだけでなく、開発ライフサイクルの各フェーズで活用しましょう。</p>
<ul>
<li><strong>設計フェーズ</strong>: 「マイクロサービスの認証方式として、JWTとセッション認証のメリット・デメリットを比較して」といった壁打ち相手になってもらう。</li>
<li><strong>実装フェーズ</strong>: コード補完や定型処理の生成をフル活用。</li>
<li><strong>テストフェーズ</strong>: <code>/tests</code> コマンドでテストケースの雛形を素早く作成。</li>
<li><strong>レビューフェーズ</strong>: 自動生成されたPRサマリーでレビューを効率化。レビュワーもChatで「この変更によるパフォーマンスへの影響は？」などと質問する。</li>
<li><strong>ドキュメントフェーズ</strong>: <code>README.md</code> のセットアップ方法やAPI仕様書の草案を作成させる。</li>
</ul>
<h3 id="3-チームでベストプラクティスを共有する">3. チームでベストプラクティスを共有する</h3>
<ul>
<li><strong>ナレッジシェア</strong>: チーム内で見つけた便利なプロンプトや、効果的な使い方を共有するSlackチャンネルやWikiページを作成しましょう。</li>
<li><strong>ペアプロならぬトリオプロ</strong>: 開発者A、開発者B、そしてCopilotの3者でプログラミングを行う「トリオプログラミング」を試してみるのも面白いアプローチです。人間同士の議論に、AIの視点を加えることで、新たな発見があるかもしれません。</li>
</ul>
<h2 id="まとめ">まとめ</h2>
<p>GitHub Copilot Enterpriseは、単なるコード補完ツールではありません。それは、<strong>組織のコードという最大の資産をAIの力でナレッジへと昇華させ、開発ライフサイクル全体の生産性と品質を向上させるための戦略的投資</strong>です。</p>
<p>その核心は、自社のコードベースを理解し、GitHubプラットフォームと深く統合されることで、個人の生産性向上に留まらず、チーム全体のコラボレーションとナレッジ共有を加速させる点にあります。</p>
<p>もちろん、月額$39というコストは決して安くはありません。しかし、本記事で示したROIの試算フレームワークを用いて、あなたの組織における時間削減効果を具体的に見積もれば、その投資がもたらす価値はコストを遥かに上回る可能性が高いことがわかるはずです。</p>
<p>セキュリティに関しても、Enterprise版は「顧客のコードを学習データとして利用しない」「IP補償を提供する」といった企業が最も重視する点をクリアしており、安心して導入できる体制が整っています。</p>
<p>GitHub Copilot Enterpriseは、開発の未来を再定義するポテンシャルを秘めています。この記事が、あなたの組織がAI時代の開発へと舵を切るための一助となれば幸いです。まずは一部の先進的なチームでパイロット導入を開始し、その驚くべき効果を自ら体感してみてはいかがでしょうか。</p>
]]></content:encoded>
      <category>Case Study</category>
      <category>GitHub Copilot</category>
      <category>Enterprise</category>
      <category>ROI</category>
    </item>
    <item>
      <title>OpenClawで社内Wikiを自動巡回させてみた（実例紹介）</title>
      <link>https://www.ai2core.com/posts/2026-02-12-openclaw-usecase/</link>
      <pubDate>Thu, 12 Feb 2026 16:35:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-02-12-openclaw-usecase/</guid>
      <description>OpenClawを使って、散らばった社内情報を自動収集・要約するボットを作った事例。</description>
      <content:encoded><![CDATA[<h1 id="openclawで社内wikiを自動巡回させてみた実例紹介">OpenClawで社内Wikiを自動巡回させてみた（実例紹介）</h1>
<h2 id="はじめに">はじめに</h2>
<p>「あのプロジェクトの設計思想、どのドキュメントに書いてあったっけ？」
「新メンバー向けのオンボーディング手順、最新版はWiki？それともGoogle Docs？」
「先月の定例会の議事録、誰が持ってるんだっけ…」</p>
<p>エンジニアの皆さんなら、こんな経験に一度は心当たりがあるのではないでしょうか。</p>
<p>私たちのチームでも、情報がConfluence、Notion、Google Drive、GitHub Wikiなど、様々な場所に散在していました。いわゆる「情報のサイロ化」です。この結果、必要な情報を見つけるために多大な時間を費やし、時には見つけられずに同じ質問がSlackで繰り返される、という非効率が常態化していました。</p>
<p>この問題を解決すべく、私たちは社内に散らばった情報を自動で収集・要約し、一元的に検索できるボットの開発に取り組みました。そして、その中核技術として採用したのが、Webクローラーフレームワーク「Scrapy」とLLM（大規模言語モデル）を統合した「<strong>OpenClaw</strong>」です。</p>
<p>この記事では、OpenClawを使って実際に社内Wiki（今回はConfluenceを例にします）を自動巡回し、ページの情報を収集・要約するボットを構築した実例を、具体的なコードと共に詳しくご紹介します。社内のナレッジマネジメントに課題を感じているエンジニアの方々にとって、一つの実践的な解決策となれば幸いです。</p>
<h2 id="なぜこの技術話題が重要なのか背景と課題">なぜこの技術・話題が重要なのか（背景と課題）</h2>
<p>現代の開発現場では、スピードとコラボレーションが強く求められます。その基盤となるのが、円滑な情報共有です。しかし、組織が成長し、プロジェクトが複雑化するにつれて、情報は意図せず分散し、サイロ化していく傾向にあります。</p>
<h3 id="情報サイロ化が引き起こす深刻な問題">情報サイロ化が引き起こす深刻な問題</h3>
<p>情報のサイロ化は、単なる「探し物が面倒」というレベルの問題にとどまりません。</p>
<ol>
<li><strong>生産性の低下</strong>: 開発者は1日のうち、情報の検索に平均で20%以上の時間を費やしているという調査結果もあります。これは週に1日分を情報検索に浪費している計算になり、無視できないコストです。</li>
<li><strong>オンボーディングの困難化</strong>: 新しくチームに参加したメンバーが、必要な情報を自力でキャッチアップするのが非常に困難になります。教育担当者の負担が増え、新メンバーの立ち上がりも遅れてしまいます。</li>
<li><strong>ナレッジの陳腐化と属人化</strong>: ドキュメントが更新されずに古い情報が参照されたり、特定の人物しか知らない「暗黙知」が増えたりします。結果として、誤った意思決定や、担当者不在時の業務停滞リスクが高まります。</li>
<li><strong>機会損失</strong>: 過去のプロジェクトで得られた知見やノウハウが埋もれてしまい、再利用されません。車輪の再発明が繰り返され、組織全体の学習効率が低下します。</li>
</ol>
<h3 id="llm時代における新たなアプローチ">LLM時代における新たなアプローチ</h3>
<p>こうした課題に対し、これまでも全文検索エンジンの導入など、様々な対策が取られてきました。しかし、キーワード検索だけでは、大量の検索結果の中から本当に求めている情報（コンテキスト）を見つけ出すのは依然として困難でした。</p>
<p>ここで大きな変革をもたらしたのが、LLMの登場です。LLMは、自然言語で書かれた膨大なテキストを理解し、要約したり、質問に答えたりする能力を持っています。</p>
<p>このLLMの能力と、Web上の情報を体系的に収集するWebクローリング技術を組み合わせることで、「<strong>散在する情報を自動で収集し、文脈を理解した上で要約・整理し、自然言語で対話的に検索できる</strong>」という、次世代のナレッジマネジメントが実現可能になります。</p>
<p>OpenClawは、まさにこの「クローリング」と「LLMによるデータ処理」をシームレスに繋ぐために設計されたフレームワークであり、この課題に対する強力なソリューションとなり得るのです。</p>
<h2 id="具体的な解決策や詳細な解説">具体的な解決策や詳細な解説</h2>
<p>それでは、実際にOpenClawを使って社内Confluenceを巡回する情報収集ボットを構築するプロセスを、ステップ・バイ・ステップで解説していきます。</p>
<h3 id="システム全体のアーキテクチャ">システム全体のアーキテクチャ</h3>
<p>今回構築するシステムの全体像は以下のようになります。</p>
<pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">graph TD
    subgraph &#34;開発環境&#34;
        A[開発者] -- 1. 実行コマンド --&gt; B(OpenClaw実行環境);
    end

    subgraph &#34;OpenClawボット&#34;
        B -- 2. ログイン要求 --&gt; C(社内Wiki: Confluence);
        C -- 3. ログイン成功/Cookie発行 --&gt; B;
        B -- 4. ページ巡回要求 --&gt; C;
        C -- 5. HTMLを返す --&gt; B;
        B -- 6. HTMLから本文抽出 --&gt; D(LLM Pipeline);
        D -- 7. テキスト要約要求 --&gt; E[LLM API (例: GPT-4o)];
        E -- 8. 要約結果を返す --&gt; D;
        D -- 9. 処理済みデータを保存 --&gt; F[データストア (JSONファイル)];
    end

    A -- 10. 結果を確認 --&gt; F;
</code></pre><ol>
<li>開発者がローカル環境でOpenClawのクローラーを実行します。</li>
<li>クローラーはまずConfluenceにログインし、セッションを確立します。</li>
<li>指定されたスペースのページを順番に巡回し、各ページのHTMLコンテンツを取得します。</li>
<li>取得したHTMLから、ヘッダーやサイドバーなどの不要な部分を取り除き、本文テキストのみを抽出します。</li>
<li>抽出したテキストを、OpenClawのパイプライン機能を使ってLLM API（今回はOpenAIのGPT-4oを想定）に送信します。</li>
<li>LLMがテキストを要約し、その結果を返します。</li>
<li>ページのタイトル、URL、そしてLLMによる要約をセットにして、JSONファイルとして保存します。</li>
</ol>
<h3 id="step-1-環境構築">Step 1: 環境構築</h3>
<p>まず、開発環境を準備します。Python 3.8以上と、パッケージ管理ツールであるpipが必要です。仮想環境を作成することを強く推奨します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># 仮想環境の作成とアクティベート</span>
</span></span><span style="display:flex;"><span>python -m venv .venv
</span></span><span style="display:flex;"><span>source .venv/bin/activate
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># OpenClawと必要なライブラリのインストール</span>
</span></span><span style="display:flex;"><span>pip install openclaw
</span></span><span style="display:flex;"><span>pip install python-dotenv <span style="color:#75715e"># .envファイルでAPIキーを管理するため</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="step-2-プロジェクトの作成">Step 2: プロジェクトの作成</h3>
<p>OpenClawには、プロジェクトの雛形を生成する便利なコマンドが用意されています。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>claw new confluence_crawler
</span></span><span style="display:flex;"><span>cd confluence_crawler
</span></span></code></pre></td></tr></table>
</div>
</div><p>これを実行すると、以下のようなディレクトリ構造が生成されます。</p>
<pre tabindex="0"><code>confluence_crawler/
├── confluence_crawler/
│   ├── __init__.py
│   ├── items.py         # 収集するデータの構造を定義
│   ├── middlewares.py
│   ├── pipelines.py     # データ処理のパイプラインを定義
│   ├── settings.py      # プロジェクト全体の設定
│   └── spiders/         # クローラー本体を配置
│       └── __init__.py
└── claw.cfg
</code></pre><p>Scrapyを使ったことがある方なら、非常によく似た構造であることに気づくでしょう。OpenClawはScrapyをベースに構築されているため、Scrapyの知識を活かすことができます。</p>
<h3 id="step-3-itemの定義">Step 3: Itemの定義</h3>
<p>まず、収集したいデータの構造を<code>items.py</code>で定義します。今回はページのタイトル、URL、本文、そしてLLMによる要約を保存することにします。</p>
<p><strong><code>confluence_crawler/items.py</code></strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> scrapy
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ConfluencePageItem</span>(scrapy<span style="color:#f92672">.</span>Item):
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># 収集するデータのフィールドを定義</span>
</span></span><span style="display:flex;"><span>    url <span style="color:#f92672">=</span> scrapy<span style="color:#f92672">.</span>Field()
</span></span><span style="display:flex;"><span>    title <span style="color:#f92672">=</span> scrapy<span style="color:#f92672">.</span>Field()
</span></span><span style="display:flex;"><span>    content <span style="color:#f92672">=</span> scrapy<span style="color:#f92672">.</span>Field()
</span></span><span style="display:flex;"><span>    summary <span style="color:#f92672">=</span> scrapy<span style="color:#f92672">.</span>Field() <span style="color:#75715e"># LLMによる要約を格納するフィールド</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="step-4-クローラーspiderの実装">Step 4: クローラー（Spider）の実装</h3>
<p>次に、ボットの心臓部であるクローラーを<code>spiders</code>ディレクトリに作成します。今回は、Confluenceにログインし、特定のスペース内のページを巡回するクローラーを実装します。</p>
<p><strong><code>confluence_crawler/spiders/confluence_spider.py</code></strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">45
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">46
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">47
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">48
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">49
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">50
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">51
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">52
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">53
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">54
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">55
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">56
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">57
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">58
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">59
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">60
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">61
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">62
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">63
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">64
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">65
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">66
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">67
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">68
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">69
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">70
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">71
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">72
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">73
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">74
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">75
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">76
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> scrapy
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> confluence_crawler.items <span style="color:#f92672">import</span> ConfluencePageItem
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ConfluenceSpider</span>(scrapy<span style="color:#f92672">.</span>Spider):
</span></span><span style="display:flex;"><span>    name <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;confluence&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># .envファイルなどから設定を読み込むことを推奨</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># ここでは例として直接記述</span>
</span></span><span style="display:flex;"><span>    CONFLUENCE_BASE_URL <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;https://your-company.atlassian.net&#34;</span>
</span></span><span style="display:flex;"><span>    LOGIN_URL <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">{</span>CONFLUENCE_BASE_URL<span style="color:#e6db74">}</span><span style="color:#e6db74">/login&#34;</span>
</span></span><span style="display:flex;"><span>    START_URL <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">{</span>CONFLUENCE_BASE_URL<span style="color:#e6db74">}</span><span style="color:#e6db74">/wiki/spaces/YOUR_SPACE_KEY/pages&#34;</span>
</span></span><span style="display:flex;"><span>    USERNAME <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;your-email@example.com&#34;</span>
</span></span><span style="display:flex;"><span>    PASSWORD <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;your-password-or-api-token&#34;</span> <span style="color:#75715e"># APIトークンの使用を推奨</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">start_requests</span>(self):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># 最初にログインページにアクセス</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">yield</span> scrapy<span style="color:#f92672">.</span>Request(
</span></span><span style="display:flex;"><span>            url<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>LOGIN_URL,
</span></span><span style="display:flex;"><span>            callback<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>login,
</span></span><span style="display:flex;"><span>            dont_filter<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">login</span>(self, response):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># ログインフォームに認証情報をPOST</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># CSRFトークンなどが必要な場合は、responseから抽出して含める</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Confluenceのログイン仕様に合わせて調整が必要</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> scrapy<span style="color:#f92672">.</span>FormRequest<span style="color:#f92672">.</span>from_response(
</span></span><span style="display:flex;"><span>            response,
</span></span><span style="display:flex;"><span>            formdata<span style="color:#f92672">=</span>{
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;username&#34;</span>: self<span style="color:#f92672">.</span>USERNAME,
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;password&#34;</span>: self<span style="color:#f92672">.</span>PASSWORD
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>            callback<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>after_login
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">after_login</span>(self, response):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># ログインが成功したかを確認</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># ログイン後のリダイレクト先やページの内容で判断</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#e6db74">&#34;dashboard&#34;</span> <span style="color:#f92672">in</span> response<span style="color:#f92672">.</span>url:
</span></span><span style="display:flex;"><span>            self<span style="color:#f92672">.</span>logger<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Login successful!&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#75715e"># ログイン成功後、目的のページの巡回を開始</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">yield</span> scrapy<span style="color:#f92672">.</span>Request(url<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>START_URL, callback<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>parse_space)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">else</span>:
</span></span><span style="display:flex;"><span>            self<span style="color:#f92672">.</span>logger<span style="color:#f92672">.</span>error(<span style="color:#e6db74">&#34;Login failed!&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">parse_space</span>(self, response):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># スペース内のページ一覧から各ページへのリンクをたどる</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># CSSセレクタは実際のConfluenceのHTML構造に合わせて調整</span>
</span></span><span style="display:flex;"><span>        page_links <span style="color:#f92672">=</span> response<span style="color:#f92672">.</span>css(<span style="color:#e6db74">&#39;a.content-list-item-title::attr(href)&#39;</span>)<span style="color:#f92672">.</span>getall()
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> link <span style="color:#f92672">in</span> page_links:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">yield</span> response<span style="color:#f92672">.</span>follow(link, callback<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>parse_page)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># 「次へ」のリンクがあればたどる（ページネーション対応）</span>
</span></span><span style="display:flex;"><span>        next_page <span style="color:#f92672">=</span> response<span style="color:#f92672">.</span>css(<span style="color:#e6db74">&#39;a.pagination-next::attr(href)&#39;</span>)<span style="color:#f92672">.</span>get()
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> next_page:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">yield</span> response<span style="color:#f92672">.</span>follow(next_page, callback<span style="color:#f92672">=</span>self<span style="color:#f92672">.</span>parse_space)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">parse_page</span>(self, response):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># ページから情報を抽出</span>
</span></span><span style="display:flex;"><span>        item <span style="color:#f92672">=</span> ConfluencePageItem()
</span></span><span style="display:flex;"><span>        item[<span style="color:#e6db74">&#39;url&#39;</span>] <span style="color:#f92672">=</span> response<span style="color:#f92672">.</span>url
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># タイトルを抽出</span>
</span></span><span style="display:flex;"><span>        item[<span style="color:#e6db74">&#39;title&#39;</span>] <span style="color:#f92672">=</span> response<span style="color:#f92672">.</span>css(<span style="color:#e6db74">&#39;h1#title-text a::text&#39;</span>)<span style="color:#f92672">.</span>get() <span style="color:#f92672">or</span> \
</span></span><span style="display:flex;"><span>                        response<span style="color:#f92672">.</span>css(<span style="color:#e6db74">&#39;h1#title-text::text&#39;</span>)<span style="color:#f92672">.</span>get()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># 本文を抽出（メインコンテンツエリアを指定）</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># このセレクタが最も重要で、サイトの構造に合わせて調整が必要</span>
</span></span><span style="display:flex;"><span>        content_html <span style="color:#f92672">=</span> response<span style="color:#f92672">.</span>css(<span style="color:#e6db74">&#39;div#main-content&#39;</span>)<span style="color:#f92672">.</span>get()
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># HTMLタグを除去してプレーンテキストにする</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">from</span> w3lib.html <span style="color:#f92672">import</span> remove_tags
</span></span><span style="display:flex;"><span>        item[<span style="color:#e6db74">&#39;content&#39;</span>] <span style="color:#f92672">=</span> remove_tags(content_html)<span style="color:#f92672">.</span>strip()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Itemをパイプラインに渡す</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">yield</span> item
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>【注意点】</strong></p>
<ul>
<li><strong>認証</strong>: 上記は基本的なフォーム認証の例です。実際のConfluenceのバージョンや設定（SSOなど）によっては、より複雑な処理（APIトークンの利用、Cookieの手動設定など）が必要になります。</li>
<li><strong>CSSセレクタ</strong>: ConfluenceのHTML構造はバージョンによって変わる可能性があります。ブラウザの開発者ツールを使い、実際のページの構造を確認してセレクタを調整してください。これがクローリングの肝となります。</li>
</ul>
<h3 id="step-5-llm要約パイプラインの実装">Step 5: LLM要約パイプラインの実装</h3>
<p>ここがOpenClawの真骨頂です。Spiderから渡されたItemを受け取り、LLMを使って要約処理を行うパイプラインを<code>pipelines.py</code>に記述します。</p>
<p>まず、OpenAIのライブラリをインストールします。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pip install openai
</span></span></code></pre></td></tr></table>
</div>
</div><p>次に、プロジェクトのルートに<code>.env</code>ファイルを作成し、APIキーを記述します。</p>
<p><strong><code>.env</code></strong></p>
<pre tabindex="0"><code>OPENAI_API_KEY=&#34;sk-...&#34;
</code></pre><p>そして、パイプラインを実装します。</p>
<p><strong><code>confluence_crawler/pipelines.py</code></strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">45
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">46
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">47
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">48
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">49
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">50
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">51
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">52
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">53
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">54
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">55
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">56
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">57
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">58
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">59
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">60
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">61
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">62
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">63
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">64
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">65
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> os
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> dotenv <span style="color:#f92672">import</span> load_dotenv
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> openai <span style="color:#f92672">import</span> OpenAI
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> itemadapter <span style="color:#f92672">import</span> ItemAdapter
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># .envファイルから環境変数を読み込む</span>
</span></span><span style="display:flex;"><span>load_dotenv()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">LlmSummaryPipeline</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">open_spider</span>(self, spider):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Spider開始時に呼び出される</span>
</span></span><span style="display:flex;"><span>        api_key <span style="color:#f92672">=</span> os<span style="color:#f92672">.</span>getenv(<span style="color:#e6db74">&#34;OPENAI_API_KEY&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> api_key:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">raise</span> <span style="color:#a6e22e">ValueError</span>(<span style="color:#e6db74">&#34;OPENAI_API_KEY is not set in .env file&#34;</span>)
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>client <span style="color:#f92672">=</span> OpenAI(api_key<span style="color:#f92672">=</span>api_key)
</span></span><span style="display:flex;"><span>        spider<span style="color:#f92672">.</span>logger<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;LLM Summary Pipeline is enabled.&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">close_spider</span>(self, spider):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Spider終了時に呼び出される</span>
</span></span><span style="display:flex;"><span>        spider<span style="color:#f92672">.</span>logger<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;LLM Summary Pipeline is finished.&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">process_item</span>(self, item, spider):
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># 各Itemがパイプラインを通過するたびに呼び出される</span>
</span></span><span style="display:flex;"><span>        adapter <span style="color:#f92672">=</span> ItemAdapter(item)
</span></span><span style="display:flex;"><span>        content <span style="color:#f92672">=</span> adapter<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#39;content&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> content:
</span></span><span style="display:flex;"><span>            adapter[<span style="color:#e6db74">&#39;summary&#39;</span>] <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;Content is empty.&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> item
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># 長すぎるコンテンツはLLMに渡す前に切り詰める</span>
</span></span><span style="display:flex;"><span>        max_tokens_for_content <span style="color:#f92672">=</span> <span style="color:#ae81ff">8000</span>  <span style="color:#75715e"># モデルのコンテキスト長に応じて調整</span>
</span></span><span style="display:flex;"><span>        truncated_content <span style="color:#f92672">=</span> content[:max_tokens_for_content]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">try</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#75715e"># LLMに要約を依頼するプロンプト</span>
</span></span><span style="display:flex;"><span>            prompt <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            以下の社内Wikiのページ内容を、3つの箇条書きで簡潔に要約してください。
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            このページが「誰の」「どんな課題を解決する」ためのドキュメントなのかが明確にわかるようにしてください。
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            ---
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            </span><span style="color:#e6db74">{</span>truncated_content<span style="color:#e6db74">}</span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            ---
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            &#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            response <span style="color:#f92672">=</span> self<span style="color:#f92672">.</span>client<span style="color:#f92672">.</span>chat<span style="color:#f92672">.</span>completions<span style="color:#f92672">.</span>create(
</span></span><span style="display:flex;"><span>                model<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;gpt-4o&#34;</span>,  <span style="color:#75715e"># または &#34;gpt-3.5-turbo&#34; など</span>
</span></span><span style="display:flex;"><span>                messages<span style="color:#f92672">=</span>[
</span></span><span style="display:flex;"><span>                    {<span style="color:#e6db74">&#34;role&#34;</span>: <span style="color:#e6db74">&#34;system&#34;</span>, <span style="color:#e6db74">&#34;content&#34;</span>: <span style="color:#e6db74">&#34;あなたは優秀なアシスタントです。渡された文章を的確に要約します。&#34;</span>},
</span></span><span style="display:flex;"><span>                    {<span style="color:#e6db74">&#34;role&#34;</span>: <span style="color:#e6db74">&#34;user&#34;</span>, <span style="color:#e6db74">&#34;content&#34;</span>: prompt}
</span></span><span style="display:flex;"><span>                ],
</span></span><span style="display:flex;"><span>                temperature<span style="color:#f92672">=</span><span style="color:#ae81ff">0.3</span>,
</span></span><span style="display:flex;"><span>                max_tokens<span style="color:#f92672">=</span><span style="color:#ae81ff">500</span>
</span></span><span style="display:flex;"><span>            )
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            summary <span style="color:#f92672">=</span> response<span style="color:#f92672">.</span>choices[<span style="color:#ae81ff">0</span>]<span style="color:#f92672">.</span>message<span style="color:#f92672">.</span>content<span style="color:#f92672">.</span>strip()
</span></span><span style="display:flex;"><span>            adapter[<span style="color:#e6db74">&#39;summary&#39;</span>] <span style="color:#f92672">=</span> summary
</span></span><span style="display:flex;"><span>            spider<span style="color:#f92672">.</span>logger<span style="color:#f92672">.</span>info(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Successfully summarized: </span><span style="color:#e6db74">{</span>adapter<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#39;title&#39;</span>)<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">except</span> <span style="color:#a6e22e">Exception</span> <span style="color:#66d9ef">as</span> e:
</span></span><span style="display:flex;"><span>            adapter[<span style="color:#e6db74">&#39;summary&#39;</span>] <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Failed to generate summary: </span><span style="color:#e6db74">{</span>e<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>            spider<span style="color:#f92672">.</span>logger<span style="color:#f92672">.</span>error(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Error during summarization for </span><span style="color:#e6db74">{</span>adapter<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#39;url&#39;</span>)<span style="color:#e6db74">}</span><span style="color:#e6db74">: </span><span style="color:#e6db74">{</span>e<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># 次のパイプライン（または出力）にItemを渡す</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> item
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="step-6-設定の有効化">Step 6: 設定の有効化</h3>
<p>最後に、作成したパイプラインと、結果をJSONファイルに出力する設定を<code>settings.py</code>で有効にします。</p>
<p><strong><code>confluence_crawler/settings.py</code></strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e"># ... (他の設定) ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 作成したパイプラインを有効にする</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 数値が小さいほど先に実行される</span>
</span></span><span style="display:flex;"><span>ITEM_PIPELINES <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>   <span style="color:#e6db74">&#39;confluence_crawler.pipelines.LlmSummaryPipeline&#39;</span>: <span style="color:#ae81ff">300</span>,
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 結果をJSONファイルに出力する設定</span>
</span></span><span style="display:flex;"><span>FEEDS <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#39;output.json&#39;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;format&#39;</span>: <span style="color:#e6db74">&#39;json&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;encoding&#39;</span>: <span style="color:#e6db74">&#39;utf8&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;store_empty&#39;</span>: <span style="color:#66d9ef">False</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;fields&#39;</span>: [<span style="color:#e6db74">&#39;title&#39;</span>, <span style="color:#e6db74">&#39;url&#39;</span>, <span style="color:#e6db74">&#39;summary&#39;</span>], <span style="color:#75715e"># 出力するフィールドを指定</span>
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;indent&#39;</span>: <span style="color:#ae81ff">4</span>,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># クローリングの挙動に関する設定</span>
</span></span><span style="display:flex;"><span>ROBOTSTXT_OBEY <span style="color:#f92672">=</span> <span style="color:#66d9ef">False</span> <span style="color:#75715e"># 社内Wikiなのでrobots.txtは無視</span>
</span></span><span style="display:flex;"><span>DOWNLOAD_DELAY <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span> <span style="color:#75715e"># サーバーに負荷をかけないように1秒待つ</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># ... (他の設定) ...</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="step-7-実行と結果の確認">Step 7: 実行と結果の確認</h3>
<p>すべての準備が整いました。プロジェクトのルートディレクトリで以下のコマンドを実行します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>claw crawl confluence
</span></span></code></pre></td></tr></table>
</div>
</div><p>クローラーが動き出し、ログイン、ページ巡回、情報抽出、LLMによる要約、ファイルへの保存が順次実行されます。ログに進行状況が表示されるので、エラーが出ていないか確認しましょう。</p>
<p>処理が完了すると、<code>output.json</code>というファイルが生成されているはずです。中身を見てみましょう。</p>
<p><strong><code>output.json</code>（出力例）</strong></p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;title&#34;</span>: <span style="color:#e6db74">&#34;【設計書】〇〇機能のAPI仕様&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;url&#34;</span>: <span style="color:#e6db74">&#34;https://your-company.atlassian.net/wiki/spaces/DEV/pages/12345/API&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;summary&#34;</span>: <span style="color:#e6db74">&#34;・このドキュメントは、〇〇機能のバックエンドAPIを利用するクライアント開発者を対象としています。\n・APIのエンドポイント一覧、リクエスト/レスポンスのフォーマット、認証方法について詳細に解説しています。\n・クライアント実装時に発生しがちなエラーコードとその対処法も網羅しており、開発効率の向上を目的としています。&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;title&#34;</span>: <span style="color:#e6db74">&#34;新メンバー向けオンボーディングガイド&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;url&#34;</span>: <span style="color:#e6db74">&#34;https://your-company.atlassian.net/wiki/spaces/DEV/pages/67890/&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;summary&#34;</span>: <span style="color:#e6db74">&#34;・このガイドは、新しく開発チームに参加したメンバーがスムーズに業務を開始できるようにするためのものです。\n・開発環境のセットアップ手順、主要なリポジトリへのアクセス方法、チームのコミュニケーションルールについて説明しています。\n・最初の1週間で取り組むべきタスクリストが提示されており、早期の戦力化をサポートします。&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></td></tr></table>
</div>
</div><p>このように、社内Wikiの情報が構造化され、かつLLMによって要点がまとめられたデータセットを自動で作成することができました。</p>
<h2 id="メリットとデメリットあるいは他ツールとの比較">メリットとデメリット（あるいは他ツールとの比較）</h2>
<p>OpenClawを使ったこのアプローチには、いくつかのメリットとデメリットがあります。</p>
<h3 id="メリット">メリット</h3>
<ul>
<li><strong>柔軟性と拡張性の高さ</strong>: Scrapyベースであるため、複雑なログイン処理、JavaScriptで動的に生成されるコンテンツへの対応（Playwright連携など）、巡回ルールの細かなカスタマイズなど、非常に柔軟なクローリングが可能です。</li>
<li><strong>LLM連携の容易さ</strong>: パイプライン機構により、クローリング処理とLLMによるデータ処理をきれいに分離して実装できます。要約だけでなく、感情分析、エンティティ抽出、翻訳など、様々なLLMタスクを簡単に追加できます。</li>
<li><strong>非構造化データに強い</strong>: Webページという典型的な非構造化データをインプットとし、LLMの力で構造化データ（JSONなど）に変換できる点が最大の強みです。</li>
<li><strong>既存の資産を活用可能</strong>: 豊富なScrapyのドキュメントやコミュニティの知見をそのまま活用できます。</li>
</ul>
<h3 id="デメリット">デメリット</h3>
<ul>
<li><strong>学習コスト</strong>: ScrapyやWebクローリングの基本的な知識（HTTP、HTML、CSSセレクタなど）が必要です。全くの初心者には少しハードルが高いかもしれません。</li>
<li><strong>LLMのAPIコスト</strong>: 処理するページ数やテキスト量に比例して、LLMのAPI利用料が発生します。大規模なWikiを対象にする場合は、コストの見積もりが重要になります。</li>
<li><strong>メンテナンスコスト</strong>: クローリング対象のWebサイト（社内Wiki）のUIが変更されると、CSSセレクタなどを修正する必要があります。定期的なメンテナンスが不可欠です。</li>
</ul>
<h3 id="他ツールとの比較">他ツールとの比較</h3>
<table>
  <thead>
      <tr>
          <th style="text-align: left">ツール/アプローチ</th>
          <th style="text-align: left">メリット</th>
          <th style="text-align: left">デメリット</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: left"><strong>OpenClaw (本記事)</strong></td>
          <td style="text-align: left">柔軟なクローリングとLLM連携のバランスが良い。拡張性が高い。</td>
          <td style="text-align: left">Scrapyの学習コスト。APIコスト。メンテナンスコスト。</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>自前で実装 (Requests+BS4+LangChain)</strong></td>
          <td style="text-align: left">自由度が最も高い。</td>
          <td style="text-align: left">非同期処理、エラーハンドリング、パイプライン等をすべて自作する必要があり、開発・保守コストが高い。</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>RAG特化フレームワーク (LlamaIndex等)</strong></td>
          <td style="text-align: left">データソースコネクタが豊富。RAG構築に特化していて手軽。</td>
          <td style="text-align: left">クローリングのカスタマイズ性が限定的。複雑な認証やサイト構造に対応しきれない場合がある。</td>
      </tr>
      <tr>
          <td style="text-align: left"><strong>ノーコード/ローコードツール</strong></td>
          <td style="text-align: left">実装が非常に簡単で迅速。</td>
          <td style="text-align: left">細かい制御が難しい。対応しているデータソースや処理が限られる。ブラックボックス化しやすい。</td>
      </tr>
  </tbody>
</table>
<p>私たちのケースでは、社内WikiがSSO認証を導入しており、クローリングの際に特殊なヘッダー情報を付与する必要があったため、柔軟にリクエストをカスタマイズできるOpenClawが最適でした。</p>
<h2 id="現場で使える実践的なtips">現場で使える実践的なTips</h2>
<p>基本的な実装に加えて、より実用性を高めるためのTipsをいくつかご紹介します。</p>
<h3 id="1-差分クローリングで効率化とコスト削減">1. 差分クローリングで効率化とコスト削減</h3>
<p>毎回すべてのページをクロールするのは非効率で、LLMのコストもかさみます。ページの最終更新日時を取得し、前回クロールした日時以降に更新されたページのみを処理対象とすることで、大幅な効率化が可能です。</p>
<ul>
<li>Confluence APIなどを利用して最終更新日時を取得する。</li>
<li>Spiderの<code>parse_page</code>メソッド内で、更新日時をチェックし、古ければ<code>yield</code>しないロジックを追加する。</li>
<li>前回の実行日時をファイルやデータベースに保存しておく。</li>
</ul>
<h3 id="2-プロンプトエンジニアリングで要約の質を向上">2. プロンプトエンジニアリングで要約の質を向上</h3>
<p>LLMによる要約の質はプロンプトに大きく依存します。目的に合わせてプロンプトを工夫しましょう。</p>
<p><strong>悪い例:</strong>
<code>「以下の文章を要約して。」</code></p>
<p><strong>良い例:</strong></p>
<pre tabindex="0"><code>あなたは〇〇チームのテックリードです。
以下のドキュメントは、新しくチームに参加したメンバーが読むことを想定しています。
このドキュメントの【目的】、【主要な技術要素】、【注意すべき点】の3つの観点で、箇条書きで分かりやすく要約してください。
---
{本文テキスト}
---
</code></pre><p>役割（Role）、対象読者、出力形式などを具体的に指定することで、より意図に沿った質の高い要約が得られます。</p>
<h3 id="3-収集した情報の活用法slackボット連携">3. 収集した情報の活用法：Slackボット連携</h3>
<p>収集してJSONにまとめたデータは、そのままでは宝の持ち腐れです。これを活用する最も効果的な方法の一つが、Slackボットとの連携です。</p>
<ol>
<li><strong>データのインデックス化</strong>: 生成された<code>output.json</code>を、Elasticsearchやベクトルデータベース（Chroma, Pineconeなど）に登録します。</li>
<li><strong>Slackボットの作成</strong>: SlackのBoltフレームワークなどを使って、ユーザーからの質問を受け付けるボットを作成します。</li>
<li><strong>検索と回答</strong>: ユーザーがSlackで「〇〇機能の仕様について教えて」と質問すると、ボットはそのキーワードでデータベースを検索します。</li>
<li><strong>結果の提示</strong>: 検索にヒットしたページの要約とURLをSlackに投稿します。これにより、チームメンバーはSlackを離れることなく、必要な情報に迅速にアクセスできるようになります。</li>
</ol>
<h3 id="4-定期実行による情報の鮮度維持">4. 定期実行による情報の鮮度維持</h3>
<p>作成したクローラーは、GitHub ActionsやJenkins、あるいはサーバーのcronなどを使って定期的に（例えば毎晩）実行するようにしましょう。これにより、社内ナレッジベースを常に最新の状態に保つことができます。</p>
<h2 id="まとめ">まとめ</h2>
<p>本記事では、情報のサイロ化という多くの組織が抱える課題に対し、OpenClawを用いて社内Wikiを自動巡回し、LLMで情報を要約・構造化するボットを構築した実例をご紹介しました。</p>
<p>このアプローチにより、私たちは以下の成果を得ることができました。</p>
<ul>
<li><strong>情報検索時間の大幅な短縮</strong>: 複数のツールを横断して探す手間がなくなり、Slackボットを通じて数秒で関連情報にたどり着けるようになった。</li>
<li><strong>ナレッジの可視化と再利用</strong>: 埋もれていた過去のドキュメントが掘り起こされ、チーム全体の知識資産として活用されるようになった。</li>
<li><strong>新メンバーのオンボーディング効率化</strong>: 新メンバーが自律的に情報をキャッチアップしやすくなった。</li>
</ul>
<p>OpenClawは、柔軟なWebクローリング能力と強力なLLMによるデータ処理能力を兼ね備えた、非常にポテンシャルの高いフレームワークです。今回ご紹介した社内Wikiの巡回だけでなく、競合他社のWebサイト分析、業界ニュースの自動収集と要約、採用サイトからの情報抽出など、その応用範囲は無限大です。</p>
<p>情報過多の時代において、必要な情報を効率的に収集・整理・活用する能力は、個人にとっても組織にとっても重要な競争力となります。この記事が、皆さんの組織のナレッジマネジメントを一段階進化させるための一助となれば、これほど嬉しいことはありません。ぜひ、お使いの社内ツールで試してみてはいかがでしょうか。</p>
]]></content:encoded>
      <category>Case Study</category>
      <category>OpenClaw</category>
      <category>Automation</category>
      <category>Knowledge Management</category>
    </item>
  </channel>
</rss>
