📚

TiDB 自動埋め込み機能と さくら AI Engine を使ったRAGシステム (2) HTML/WEBの取り込み

に公開

今日は前回に引き続きRAGシステムを改造します。
https://zenn.dev/sakura_internet/articles/50045f5ede9ee3

Jina.AI

HTMLをRAGに取り込む場合、そのまま取り込むと精度がかなり下がります。例えばこのような表記のHTMLの場合、

中身は以下となっており関係のない情報が大量に含まれます。

<div class="mw-heading mw-heading3"><h3 id="リアル志向"><span id=".E3.83.AA.E3.82.A2.E3.83.AB.E5.BF.97.E5.90.91"></span>リアル志向</h3></div>
<p>当時のロボットアニメでは、悪の組織を敵とする正義と悪の構図、あるいは宇宙からの侵略者を敵とした戦いであった。それに対して、国家間の紛争による人間どうしの戦争としている<sup id="cite&#95;ref-FOOTNOTE氷川藤津200032&#95;11-0" class="reference"><a href="#cite_note-FOOTNOTE氷川藤津200032-11"><span class="cite-bracket">&#91;</span>10<span class="cite-bracket">&#93;</span></a></sup>。
味方が善で敵を悪とする勧善懲悪の物語にしていない<sup id="cite&#95;ref-FOOTNOTE日経エンタテインメント!200840&#95;12-0" class="reference"><a href="#cite_note-FOOTNOTE日経エンタテインメント!200840-12"><span class="cite-bracket">&#91;</span>11<span class="cite-bracket">&#93;</span></a></sup></p>

このためまずマークダウンに変換する必要があります。
それを行ってくれるのがJIna.AIです。
https://r.jina.ai/<url>として指定を行うと以下の様なマークダウンがレスポンスとして出力されます。

### リアル志向
当時のロボットアニメでは、悪の組織を敵とする正義と悪の構図、あるいは宇宙からの侵略者を敵とした戦いであった。それに対して、国家間の紛争による人間どうしの戦争としている[[10]](https://ja.wikipedia.org/wiki/%E6%A9%9F%E5%8B%95%E6%88%A6%E5%A3%AB%E3%82%AC%E3%83%B3%E3%83%80%E3%83%A0#cite_note-FOOTNOTE%E6%B0%B7%E5%B7%9D%E8%97%A4%E6%B4%A5200032-11)。 味方が善で敵を悪とする勧善懲悪の物語にしていない[[11]](https://ja.wikipedia.org/wiki/%E6%A9%9F%E5%8B%95%E6%88%A6%E5%A3%AB%E3%82%AC%E3%83%B3%E3%83%80%E3%83%A0#cite_note-FOOTNOTE%E6%97%A5%E7%B5%8C%E3%82%A8%E3%83%B3%E3%82%BF%E3%83%86%E3%82%A4%E3%83%B3%E3%83%A1%E3%83%B3%E3%83%88%EF%BC%81200840-12)。

このレスポンスをチャンク分割してRAGに取り込みます。

上記に大量に含まれるハイパーリンクをRAGに取り込まないように--no-linkオプションも付与します。これにより、より精度の高いチャンクが生成されます。

さっそくやってみる

index.js の改造

index.js
// TiDB Cloud Starter PDFデータ挿入サンプル
// 必要なパッケージ: npm install mysql2 dotenv pdfjs-dist gpt-tokenizer

import mysql from 'mysql2/promise';
import dotenv from 'dotenv';
import fs from 'fs/promises';
import { encode } from 'gpt-tokenizer';
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.mjs';
import { insertWebsiteChunks } from './indexhtml.js';

dotenv.config();

// TiDB接続設定
const config = {
  host: process.env.TIDB_HOST,
  port: process.env.TIDB_PORT || 4000,
  user: process.env.TIDB_USER,
  password: process.env.TIDB_PASSWORD,
  database: process.env.TIDB_DATABASE,
  ssl: {
    rejectUnauthorized: true
  }
};

// テーブルが存在しない場合は作成、または再作成
async function createTableIfNotExists(connection, recreate = false) {
  try {
    console.log('テーブルの存在を確認中...');
    
    const [tables] = await connection.execute(
      "SHOW TABLES LIKE 'documents'"
    );
    
    if (tables.length > 0 && recreate) {
      console.log('既存のテーブルを削除します...');
      await connection.execute('DROP TABLE documents');
      console.log('✓ テーブルを削除しました');
    }
    
    if (tables.length === 0 || recreate) {
      console.log('テーブルを作成します...');
      
      const createTableSQL = `
        CREATE TABLE documents (
          id INT PRIMARY KEY AUTO_INCREMENT,
          content TEXT,
          source VARCHAR(255),
          chunk_index INT,
          content_vector VECTOR(1024) GENERATED ALWAYS AS (
            EMBED_TEXT("tidbcloud_free/amazon/titan-embed-text-v2", content)
          ) STORED
        )
      `;
      
      await connection.execute(createTableSQL);
      console.log('✓ テーブル "documents" を作成しました (source, chunk_indexカラム付き)');
    } else {
      console.log('✓ テーブル "documents" は既に存在します');
      
      // テーブル構造を確認
      const [columns] = await connection.execute(
        "SHOW COLUMNS FROM documents"
      );
      const hasSource = columns.some(col => col.Field === 'source');
      const hasChunkIndex = columns.some(col => col.Field === 'chunk_index');
      
      if (!hasSource || !hasChunkIndex) {
        console.log('\n⚠️  警告: 既存テーブルにはsourceまたはchunk_indexカラムがありません');
        console.log('テーブルを再作成する場合は、以下のコマンドを実行してください:');
        console.log('node index.js <PDFファイル> <トークン数> --recreate');
        throw new Error('テーブル構造が不一致です。--recreateオプションを使用してテーブルを再作成してください。');
      }
    }
  } catch (error) {
    if (error.message.includes('テーブル構造が不一致')) {
      throw error;
    }
    console.error('テーブル作成エラー:', error.message);
    throw error;
  }
}

// PDFファイルを読み込んでテキストを抽出
async function extractTextFromPDF(pdfPath) {
  try {
    console.log(`\nPDFファイルを読み込み中: ${pdfPath}`);
    const dataBuffer = await fs.readFile(pdfPath);
    
    // PDFドキュメントを読み込む
    const loadingTask = pdfjsLib.getDocument({
      data: new Uint8Array(dataBuffer),
      useSystemFonts: true,
    });
    
    const pdfDocument = await loadingTask.promise;
    const numPages = pdfDocument.numPages;
    
    console.log(`✓ PDF読み込み完了`);
    console.log(`  - ページ数: ${numPages}`);
    
    // 全ページからテキストを抽出
    let fullText = '';
    for (let pageNum = 1; pageNum <= numPages; pageNum++) {
      const page = await pdfDocument.getPage(pageNum);
      const textContent = await page.getTextContent();
      const pageText = textContent.items.map(item => item.str).join(' ');
      fullText += pageText + '\n';
    }
    
    console.log(`  - テキスト長: ${fullText.length}文字`);
    
    return fullText;
  } catch (error) {
    console.error('PDF読み込みエラー:', error.message);
    throw error;
  }
}

// テキストを指定トークン数で分割(強制分割文字列とオーバーラップに対応)
function splitTextByTokens(text, maxTokens = 512, forceSplitMarker = null, overlapChars = 0) {
  console.log(`\nテキストを${maxTokens}トークンごとに分割中...`);
  if (forceSplitMarker) {
    console.log(`強制分割マーカー: "${forceSplitMarker}"`);
  }
  if (overlapChars > 0) {
    console.log(`オーバーラップ: ${overlapChars}文字`);
  }
  
  // 強制分割マーカーが指定されている場合、まずそれで分割
  let segments = [text];
  if (forceSplitMarker) {
    segments = text.split(forceSplitMarker);
    console.log(`✓ 強制分割マーカーにより${segments.length}個のセグメントに分割`);
  }
  
  const chunks = [];
  
  // 各セグメントを処理
  for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex++) {
    const segment = segments[segmentIndex].trim();
    if (!segment) continue;
    
    // セグメントをトークン数で更に分割(オーバーラップ対応)
    const segmentChunks = splitSegmentByTokens(segment, maxTokens, overlapChars);
    chunks.push(...segmentChunks);
  }
  
  console.log(`✓ 最終的に${chunks.length}個のチャンクに分割しました`);
  
  // 各チャンクのトークン数を表示
  chunks.forEach((chunk, index) => {
    const tokens = encode(chunk).length;
    console.log(`  チャンク${index + 1}: ${tokens}トークン, ${chunk.length}文字`);
  });
  
  return chunks;
}

// セグメント内をトークン数で分割(オーバーラップ対応)
function splitSegmentByTokens(text, maxTokens, overlapChars = 0) {
  const sentences = text.split(/(?<=[.!?。!?])\s+|\n+/);
  const chunks = [];
  let currentChunk = '';
  let currentTokens = 0;
  let previousChunkEnd = ''; // 前のチャンクの末尾(オーバーラップ用)
  
  for (const sentence of sentences) {
    const sentenceTokens = encode(sentence).length;
    
    // 1文が最大トークン数を超える場合は強制分割
    if (sentenceTokens > maxTokens) {
      if (currentChunk) {
        // オーバーラップ用に末尾を保存
        previousChunkEnd = currentChunk.slice(-overlapChars);
        chunks.push(currentChunk.trim());
        currentChunk = previousChunkEnd; // 次のチャンクはオーバーラップで開始
        currentTokens = encode(currentChunk).length;
      }
      
      // 長い文を単語レベルで分割
      const words = sentence.split(/\s+/);
      let tempChunk = currentChunk;
      let tempTokens = currentTokens;
      
      for (const word of words) {
        const wordTokens = encode(word).length;
        if (tempTokens + wordTokens > maxTokens) {
          if (tempChunk.trim()) {
            // オーバーラップ用に末尾を保存
            previousChunkEnd = tempChunk.slice(-overlapChars);
            chunks.push(tempChunk.trim());
          }
          tempChunk = previousChunkEnd + word + ' '; // オーバーラップで開始
          tempTokens = encode(tempChunk).length;
        } else {
          tempChunk += word + ' ';
          tempTokens += wordTokens;
        }
      }
      
      if (tempChunk.trim()) {
        currentChunk = tempChunk;
        currentTokens = tempTokens;
      }
      continue;
    }
    
    // 現在のチャンクに追加できるか確認
    if (currentTokens + sentenceTokens <= maxTokens) {
      currentChunk += sentence + ' ';
      currentTokens += sentenceTokens;
    } else {
      // 現在のチャンクを保存
      if (currentChunk.trim()) {
        // オーバーラップ用に末尾を保存
        previousChunkEnd = currentChunk.slice(-overlapChars);
        chunks.push(currentChunk.trim());
      }
      // 新しいチャンクをオーバーラップで開始
      currentChunk = previousChunkEnd + sentence + ' ';
      currentTokens = encode(currentChunk).length;
    }
  }
  
  // 最後のチャンクを追加
  if (currentChunk.trim()) {
    chunks.push(currentChunk.trim());
  }
  
  return chunks;
}

// PDFから抽出したチャンクをデータベースに挿入
async function insertPDFChunks(pdfPath, maxTokens = 512, recreate = false, forceSplitMarker = null, overlapChars = 0) {
  let connection;
  
  try {
    // データベース接続
    console.log('\nTiDB Cloudに接続中...');
    connection = await mysql.createConnection(config);
    console.log('✓ 接続成功!');

    // テーブルが存在しない場合は作成
    await createTableIfNotExists(connection, recreate);

    // PDFからテキストを抽出
    const text = await extractTextFromPDF(pdfPath);
    
    // テキストをトークン数で分割(強制分割マーカーとオーバーラップ対応)
    const chunks = splitTextByTokens(text, maxTokens, forceSplitMarker, overlapChars);
    
    // チャンクをデータベースに挿入
    console.log('\nデータベースに挿入中...');
    const fileName = pdfPath.split(/[/\\]/).pop();
    
    for (let i = 0; i < chunks.length; i++) {
      const [result] = await connection.execute(
        'INSERT INTO documents (content, source, chunk_index) VALUES (?, ?, ?)',
        [chunks[i], fileName, i + 1]
      );
      console.log(`✓ チャンク${i + 1}/${chunks.length} 挿入完了 (ID: ${result.insertId})`);
    }

    // 挿入されたデータを確認
    console.log('\n挿入されたデータを確認:');
    const [rows] = await connection.execute(
      'SELECT id, source, chunk_index, LEFT(content, 100) as content_preview FROM documents WHERE source = ? ORDER BY chunk_index',
      [fileName]
    );
    console.table(rows);

    console.log(`\n✓ 処理完了: ${chunks.length}個のチャンクを挿入しました`);

  } catch (error) {
    console.error('エラーが発生しました:', error.message);
    throw error;
  } finally {
    if (connection) {
      await connection.end();
      console.log('\n接続を閉じました。');
    }
  }
}

// メイン処理
(async () => {
  console.log('=== TiDB Auto Embedding データ挿入サンプル ===');
  
  // コマンドライン引数を取得
  const args = process.argv.slice(2);
  const input = args[0];
  
  if (!input) {
    console.error('\nエラー: PDFファイルのパスまたはURLを指定してください');
    console.log('使用方法: node index.js <PDFファイル|URL> [最大トークン数] [オプション]');
    console.log('\nオプション:');
    console.log('  --recreate              テーブルを再作成');
    console.log('  --split=<文字列>        指定した文字列で強制分割');
    console.log('  --overlap=<文字数>      チャンク間のオーバーラップ文字数');
    console.log('  --no-links              マークダウンからリンクを削除 (Webサイトのみ)');
    console.log('  --show-content          取得した内容を表示 (Webサイトのみ、デバッグ用)');
    console.log('\n例 (PDF):');
    console.log('  node index.js ./sample.pdf 512');
    console.log('  node index.js ./sample.pdf 512 --split="\\f" --overlap=100');
    console.log('\n例 (Webサイト):');
    console.log('  node index.js https://example.com 512 --show-content  (内容確認)');
    console.log('  node index.js https://example.com 512 --no-links');
    console.log('  node index.js https://example.com 512 --split="##" --overlap=50 --no-links');
    process.exit(1);
  }
  
  // URLかPDFかを判定
  const isUrl = input.startsWith('http://') || input.startsWith('https://');
  
  // 最大トークン数(オプション、デフォルトは512)
  const maxTokens = parseInt(args[1]) || 512;
  
  // --recreateオプションの確認
  const recreate = args.includes('--recreate');
  
  // --splitオプションの確認
  let forceSplitMarker = null;
  const splitArg = args.find(arg => arg.startsWith('--split='));
  if (splitArg) {
    forceSplitMarker = splitArg.replace('--split=', '');
    forceSplitMarker = forceSplitMarker
      .replace(/\\n/g, '\n')
      .replace(/\\r/g, '\r')
      .replace(/\\t/g, '\t')
      .replace(/\\f/g, '\f');
  }
  
  // --overlapオプションの確認
  let overlapChars = 0;
  const overlapArg = args.find(arg => arg.startsWith('--overlap='));
  if (overlapArg) {
    overlapChars = parseInt(overlapArg.replace('--overlap=', '')) || 0;
    if (overlapChars < 0) overlapChars = 0;
  }
  
  // --no-linksオプションの確認
  const removeLinks = args.includes('--no-links');
  
  // --show-contentオプションの確認
  const showContent = args.includes('--show-content');
  
  console.log(`\n設定:`);
  console.log(`  入力: ${input}`);
  console.log(`  タイプ: ${isUrl ? 'Webサイト' : 'PDFファイル'}`);
  console.log(`  最大トークン数: ${maxTokens}`);
  if (recreate) {
    console.log(`  テーブル再作成: はい`);
  }
  if (forceSplitMarker) {
    console.log(`  強制分割マーカー: "${forceSplitMarker.replace(/\n/g, '\\n').replace(/\f/g, '\\f')}"`);
  }
  if (overlapChars > 0) {
    console.log(`  オーバーラップ: ${overlapChars}文字`);
  }
  if (removeLinks && isUrl) {
    console.log(`  リンク削除: はい`);
  }
  if (showContent && isUrl) {
    console.log(`  内容表示: はい`);
  }
  
  // URLの場合はindexhtml.jsの関数を呼び出し
  if (isUrl) {
    await insertWebsiteChunks(input, maxTokens, recreate, forceSplitMarker, overlapChars, removeLinks, showContent);
  } else {
    // PDFの場合は既存の処理
    await insertPDFChunks(input, maxTokens, recreate, forceSplitMarker, overlapChars);
  }
  
})().catch(error => {
  console.error('致命的なエラー:', error);
  process.exit(1);
});

以下2点の改造をしています。

  1. no-linkオプションの付与
  2. indexhtml.jsの呼び出し
    indexhtml.jsで実際にJina.AIを呼び出しHTMLをマークダウンに出力します。

indexhtml.js の作成

indexhtml.js
// TiDB Cloud Starter Webサイト取り込みサンプル (Jina AI使用)
// 必要なパッケージ: npm install mysql2 dotenv gpt-tokenizer

import mysql from 'mysql2/promise';
import dotenv from 'dotenv';
import { encode } from 'gpt-tokenizer';

dotenv.config();

// TiDB接続設定
const config = {
  host: process.env.TIDB_HOST,
  port: process.env.TIDB_PORT || 4000,
  user: process.env.TIDB_USER,
  password: process.env.TIDB_PASSWORD,
  database: process.env.TIDB_DATABASE,
  ssl: {
    rejectUnauthorized: true
  }
};

// テーブルが存在しない場合は作成、または再作成
async function createTableIfNotExists(connection, recreate = false) {
  try {
    console.log('テーブルの存在を確認中...');
    
    const [tables] = await connection.execute(
      "SHOW TABLES LIKE 'documents'"
    );
    
    if (tables.length > 0 && recreate) {
      console.log('既存のテーブルを削除します...');
      await connection.execute('DROP TABLE documents');
      console.log('✓ テーブルを削除しました');
    }
    
    if (tables.length === 0 || recreate) {
      console.log('テーブルを作成します...');
      
      const createTableSQL = `
        CREATE TABLE documents (
          id INT PRIMARY KEY AUTO_INCREMENT,
          content TEXT,
          source VARCHAR(255),
          chunk_index INT,
          content_vector VECTOR(1024) GENERATED ALWAYS AS (
            EMBED_TEXT("tidbcloud_free/amazon/titan-embed-text-v2", content)
          ) STORED
        )
      `;
      
      await connection.execute(createTableSQL);
      console.log('✓ テーブル "documents" を作成しました (source, chunk_indexカラム付き)');
    } else {
      console.log('✓ テーブル "documents" は既に存在します');
      
      // テーブル構造を確認
      const [columns] = await connection.execute(
        "SHOW COLUMNS FROM documents"
      );
      const hasSource = columns.some(col => col.Field === 'source');
      const hasChunkIndex = columns.some(col => col.Field === 'chunk_index');
      
      if (!hasSource || !hasChunkIndex) {
        console.log('\n⚠️  警告: 既存テーブルにはsourceまたはchunk_indexカラムがありません');
        console.log('テーブルを再作成する場合は、以下のコマンドを実行してください:');
        console.log('node indexhtml.js <URL> <トークン数> --recreate');
        throw new Error('テーブル構造が不一致です。--recreateオプションを使用してテーブルを再作成してください。');
      }
    }
  } catch (error) {
    if (error.message.includes('テーブル構造が不一致')) {
      throw error;
    }
    console.error('テーブル作成エラー:', error.message);
    throw error;
  }
}

// Jina AI ReaderでWebサイトをマークダウンに変換
async function fetchWebsiteAsMarkdown(url, showContent = false) {
  try {
    console.log(`\nJina AI ReaderでWebサイトを取得中: ${url}`);
    
    // Jina AI Readerのエンドポイント
    const jinaUrl = `https://r.jina.ai/${url}`;
    
    const response = await fetch(jinaUrl, {
      headers: {
        'Accept': 'text/markdown',
        'X-Return-Format': 'markdown'
      }
    });
    
    if (!response.ok) {
      throw new Error(`Jina AI Reader Error: ${response.status} - ${response.statusText}`);
    }
    
    const markdown = await response.text();
    
    console.log(`✓ Webサイト取得完了`);
    console.log(`  - マークダウン長: ${markdown.length}文字`);
    console.log(`  - 行数: ${markdown.split('\n').length}`);
    
    // 内容表示オプション
    if (showContent) {
      console.log('\n' + '='.repeat(80));
      console.log('取得した内容(最初の2000文字):');
      console.log('='.repeat(80));
      console.log(markdown.substring(0, 2000));
      if (markdown.length > 2000) {
        console.log('\n...(省略)...\n');
        console.log('='.repeat(80));
        console.log('最後の1000文字:');
        console.log('='.repeat(80));
        console.log(markdown.substring(markdown.length - 1000));
      }
      console.log('='.repeat(80) + '\n');
    }
    
    return markdown;
  } catch (error) {
    console.error('Webサイト取得エラー:', error.message);
    throw error;
  }
}

// マークダウンからリンクを削除する関数
function removeLinksFromMarkdown(markdown) {
  console.log('\nマークダウンからリンクを削除中...');
  
  let cleaned = markdown;
  
  // マークダウンリンク形式 [テキスト](URL) -> テキスト
  cleaned = cleaned.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
  
  // 単独のURL(http:// または https:// で始まる)を削除
  cleaned = cleaned.replace(/https?:\/\/[^\s)]+/g, '');
  
  // 連続する空白を1つに
  cleaned = cleaned.replace(/\s+/g, ' ');
  
  // 連続する改行を2つまでに
  cleaned = cleaned.replace(/\n{3,}/g, '\n\n');
  
  console.log(`✓ リンク削除完了`);
  console.log(`  - 削除後の長さ: ${cleaned.length}文字`);
  
  return cleaned.trim();
}

// テキストを指定トークン数で分割(強制分割文字列とオーバーラップに対応)
function splitTextByTokens(text, maxTokens = 512, forceSplitMarker = null, overlapChars = 0) {
  console.log(`\nテキストを${maxTokens}トークンごとに分割中...`);
  if (forceSplitMarker) {
    console.log(`強制分割マーカー: "${forceSplitMarker}"`);
  }
  if (overlapChars > 0) {
    console.log(`オーバーラップ: ${overlapChars}文字`);
  }
  
  // 強制分割マーカーが指定されている場合、まずそれで分割
  let segments = [text];
  if (forceSplitMarker) {
    segments = text.split(forceSplitMarker);
    console.log(`✓ 強制分割マーカーにより${segments.length}個のセグメントに分割`);
  }
  
  const chunks = [];
  
  // 各セグメントを処理
  for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex++) {
    const segment = segments[segmentIndex].trim();
    if (!segment) continue;
    
    // セグメントをトークン数で更に分割(オーバーラップ対応)
    const segmentChunks = splitSegmentByTokens(segment, maxTokens, overlapChars);
    chunks.push(...segmentChunks);
  }
  
  console.log(`✓ 最終的に${chunks.length}個のチャンクに分割しました`);
  
  // 各チャンクのトークン数を表示
  chunks.forEach((chunk, index) => {
    const tokens = encode(chunk).length;
    console.log(`  チャンク${index + 1}: ${tokens}トークン, ${chunk.length}文字`);
  });
  
  return chunks;
}

// セグメント内をトークン数で分割(オーバーラップ対応)
function splitSegmentByTokens(text, maxTokens, overlapChars = 0) {
  const sentences = text.split(/(?<=[.!?。!?])\s+|\n+/);
  const chunks = [];
  let currentChunk = '';
  let currentTokens = 0;
  
  for (const sentence of sentences) {
    const sentenceTokens = encode(sentence).length;
    
    // 1文が最大トークン数を超える場合は強制分割
    if (sentenceTokens > maxTokens) {
      if (currentChunk.trim()) {
        chunks.push(currentChunk.trim());
        currentChunk = '';
        currentTokens = 0;
      }
      
      const words = sentence.split(/\s+/);
      let tempChunk = '';
      let tempTokens = 0;
      
      for (const word of words) {
        const wordTokens = encode(word).length;
        if (tempTokens + wordTokens > maxTokens) {
          if (tempChunk.trim()) {
            chunks.push(tempChunk.trim());
          }
          tempChunk = word + ' ';
          tempTokens = wordTokens;
        } else {
          tempChunk += word + ' ';
          tempTokens += wordTokens;
        }
      }
      
      if (tempChunk.trim()) {
        currentChunk = tempChunk;
        currentTokens = tempTokens;
      }
      continue;
    }
    
    // 現在のチャンクに追加できるか確認
    if (currentTokens + sentenceTokens <= maxTokens) {
      currentChunk += sentence + ' ';
      currentTokens += sentenceTokens;
    } else {
      // 現在のチャンクを保存
      if (currentChunk.trim()) {
        chunks.push(currentChunk.trim());
      }
      // 新しいチャンクを開始(オーバーラップを追加)
      if (overlapChars > 0 && currentChunk.length > overlapChars) {
        const overlap = currentChunk.slice(-overlapChars);
        currentChunk = overlap + sentence + ' ';
        currentTokens = encode(currentChunk).length;
      } else {
        currentChunk = sentence + ' ';
        currentTokens = sentenceTokens;
      }
    }
  }
  
  // 最後のチャンクを追加
  if (currentChunk.trim()) {
    chunks.push(currentChunk.trim());
  }
  
  return chunks;
}

// Webサイトをマークダウンとしてチャンクをデータベースに挿入
export async function insertWebsiteChunks(url, maxTokens = 512, recreate = false, forceSplitMarker = null, overlapChars = 0, removeLinks = false, showContent = false) {
  let connection;
  
  try {
    // データベース接続
    console.log('\nTiDB Cloudに接続中...');
    connection = await mysql.createConnection(config);
    console.log('✓ 接続成功!');

    // テーブルが存在しない場合は作成
    await createTableIfNotExists(connection, recreate);

    // Jina AI ReaderでWebサイトを取得
    let markdown = await fetchWebsiteAsMarkdown(url, showContent);
    
    // リンク削除オプションが有効な場合
    if (removeLinks) {
      markdown = removeLinksFromMarkdown(markdown);
    }
    
    // マークダウンをトークン数で分割
    const chunks = splitTextByTokens(markdown, maxTokens, forceSplitMarker, overlapChars);
    
    // チャンクをデータベースに挿入
    console.log('\nデータベースに挿入中...');
    const sourceName = new URL(url).hostname;
    
    for (let i = 0; i < chunks.length; i++) {
      const [result] = await connection.execute(
        'INSERT INTO documents (content, source, chunk_index) VALUES (?, ?, ?)',
        [chunks[i], sourceName, i + 1]
      );
      console.log(`✓ チャンク${i + 1}/${chunks.length} 挿入完了 (ID: ${result.insertId})`);
    }

    // 挿入されたデータを確認
    console.log('\n挿入されたデータを確認:');
    const [rows] = await connection.execute(
      'SELECT id, source, chunk_index, LEFT(content, 100) as content_preview FROM documents WHERE source = ? ORDER BY chunk_index',
      [sourceName]
    );
    console.table(rows);

    console.log(`\n✓ 処理完了: ${chunks.length}個のチャンクを挿入しました`);

  } catch (error) {
    console.error('エラーが発生しました:', error.message);
    throw error;
  } finally {
    if (connection) {
      await connection.end();
      console.log('\n接続を閉じました。');
    }
  }
}

// メイン処理(直接実行された場合のみ)
if (import.meta.url === `file://${process.argv[1]}`) {
  (async () => {
    console.log('=== TiDB Auto Embedding Webサイト取り込みサンプル (Jina AI) ===');
    
    const args = process.argv.slice(2);
    const url = args[0];
    
    if (!url) {
      console.error('\nエラー: WebサイトのURLを指定してください');
      console.log('使用方法: node indexhtml.js <URL> [最大トークン数] [オプション]');
      console.log('\nオプション:');
      console.log('  --recreate              テーブルを再作成');
      console.log('  --split=<文字列>        指定した文字列で強制分割');
      console.log('  --overlap=<文字数>      チャンク間のオーバーラップ文字数');
      console.log('  --no-links              マークダウンからリンクを削除');
      console.log('  --show-content          取得した内容を表示(デバッグ用)');
      console.log('\n例:');
      console.log('  node indexhtml.js https://example.com 512');
      console.log('  node indexhtml.js https://example.com 512 --show-content  (内容確認)');
      console.log('  node indexhtml.js https://example.com 512 --recreate');
      console.log('  node indexhtml.js https://example.com 512 --split="##" --overlap=100');
      console.log('  node indexhtml.js https://example.com 512 --no-links');
      console.log('  node indexhtml.js https://example.com 512 --no-links --overlap=50');
      process.exit(1);
    }
    
    const maxTokens = parseInt(args[1]) || 512;
    const recreate = args.includes('--recreate');
    
    let forceSplitMarker = null;
    const splitArg = args.find(arg => arg.startsWith('--split='));
    if (splitArg) {
      forceSplitMarker = splitArg.replace('--split=', '');
      forceSplitMarker = forceSplitMarker
        .replace(/\\n/g, '\n')
        .replace(/\\r/g, '\r')
        .replace(/\\t/g, '\t');
    }
    
    let overlapChars = 0;
    const overlapArg = args.find(arg => arg.startsWith('--overlap='));
    if (overlapArg) {
      overlapChars = parseInt(overlapArg.replace('--overlap=', '')) || 0;
      if (overlapChars < 0) overlapChars = 0;
    }
    
    const removeLinks = args.includes('--no-links');
    const showContent = args.includes('--show-content');
    
    console.log(`\n設定:`);
    console.log(`  URL: ${url}`);
    console.log(`  最大トークン数: ${maxTokens}`);
    if (recreate) {
      console.log(`  テーブル再作成: はい`);
    }
    if (forceSplitMarker) {
      console.log(`  強制分割マーカー: "${forceSplitMarker}"`);
    }
    if (overlapChars > 0) {
      console.log(`  オーバーラップ: ${overlapChars}文字`);
    }
    if (removeLinks) {
      console.log(`  リンク削除: はい`);
    }
    if (showContent) {
      console.log(`  内容表示: はい`);
    }
    
    await insertWebsiteChunks(url, maxTokens, recreate, forceSplitMarker, overlapChars, removeLinks, showContent);
    
  })().catch(error => {
    console.error('致命的なエラー:', error);
    process.exit(1);
  });
}

テスト実行

ではやってみます。

node index.js https://ja.wikipedia.org/wiki/%E6%A9%9F%E5%8B%95%E6%88%A6%E5%A3%AB%E3%82%AC%E3%83%B3%E3%83%80%E3%83%A0 512 --split="##" --no-links
node index.js https://ja.wikipedia.org/wiki/%E6%A9%9F%E5%8B%95%E6%88%A6%E5%A3%AB%E3%82%AC%E3%83%B3%E3%83%80%E3%83%A0 512
 --split="##" --no-links
[dotenv@17.2.3] injecting env (5) from .env -- tip: 🗂️ backup and recover secrets: https://dotenvx.com/ops
[dotenv@17.2.3] injecting env (0) from .env -- tip: ✅ audit secrets and track compliance: https://dotenvx.com/ops
=== TiDB Auto Embedding データ挿入サンプル ===

設定:
  入力: https://ja.wikipedia.org/wiki/%E6%A9%9F%E5%8B%95%E6%88%A6%E5%A3%AB%E3%82%AC%E3%83%B3%E3%83%80%E3%83%A0
  タイプ: Webサイト
  最大トークン数: 512
  強制分割マーカー: "##"
  リンク削除: はい

TiDB Cloudに接続中...
✓ 接続成功!
テーブルの存在を確認中...
テーブルを作成します...
✓ テーブル "documents" を作成しました (source, chunk_indexカラム付き)

Jina AI ReaderでWebサイトを取得中: https://ja.wikipedia.org/wiki/%E6%A9%9F%E5%8B%95%E6%88%A6%E5%A3%AB%E3%82%AC%E3%83%B3%E3%83%80%E3%83%A0
✓ Webサイト取得完了
  - マークダウン長: 346190文字
  - 行数: 1048行

マークダウンからリンクを削除中...
✓ リンク削除完了
  - 削除後の長さ: 55627文字

テキストを512トークンごとに分割中...
強制分割マーカー: "##"
✓ 強制分割マーカーにより37個のセグメントに分割
✓ 最終的に101個のチャンクに分割しました
  チャンク1: 595トークン, 895文字
  チャンク2: 544トークン, 994文字
  チャンク3: 497トークン, 766文字
  チャンク4: 508トークン, 767文字
  チャンク5: 336トークン, 436文字
  チャンク6: 269トークン, 364文字
  チャンク7: 434トークン, 608文字
  チャンク8: 108トークン, 134文字
  チャンク9: 110トークン, 146文字
  チャンク10: 136トークン, 172文字
  チャンク11: 403トークン, 531文字
  チャンク12: 370トークン, 512文字
  チャンク13: 437トークン, 574文字
  チャンク14: 487トークン, 583文字
  チャンク15: 403トークン, 474文字
  チャンク16: 4トークン, 9文字
  チャンク17: 964トークン, 1139文字
  チャンク18: 449トークン, 613文字
  チャンク19: 406トークン, 528文字
  チャンク20: 554トークン, 900文字
  チャンク21: 407トークン, 659文字
  チャンク22: 261トークン, 323文字
  チャンク23: 198トークン, 260文字
  チャンク24: 505トークン, 736文字
  チャンク25: 462トークン, 568文字
  チャンク26: 502トークン, 619文字
  チャンク27: 509トークン, 643文字
  チャンク28: 497トークン, 632文字
  チャンク29: 481トークン, 618文字
  チャンク30: 513トークン, 673文字
  チャンク31: 359トークン, 450文字
  チャンク32: 259トークン, 354文字
  チャンク33: 394トークン, 525文字
  チャンク34: 204トークン, 254文字
  チャンク35: 378トークン, 453文字
  チャンク36: 359トークン, 472文字
  チャンク37: 428トークン, 584文字
  チャンク38: 509トークン, 706文字
  チャンク39: 331トークン, 449文字
  チャンク40: 457トークン, 634文字
  チャンク41: 423トークン, 615文字
  チャンク42: 151トークン, 222文字
  チャンク43: 26トークン, 37文字
  チャンク44: 3トークン, 4文字
  チャンク45: 479トークン, 609文字
  チャンク46: 234トークン, 281文字
  チャンク47: 198トークン, 258文字
  チャンク48: 81トークン, 115文字
  チャンク49: 474トークン, 626文字
  チャンク50: 461トークン, 597文字
  チャンク51: 116トークン, 201文字
  チャンク52: 98トークン, 132文字
  チャンク53: 229トークン, 324文字
  チャンク54: 513トークン, 663文字
  チャンク55: 11トークン, 15文字
  チャンク56: 403トークン, 520文字
  チャンク57: 236トークン, 282文字
  チャンク58: 153トークン, 242文字
  チャンク59: 503トークン, 662文字
  チャンク60: 38トークン, 49文字
  チャンク61: 530トークン, 783文字
  チャンク62: 491トークン, 702文字
  チャンク63: 497トークン, 689文字
  チャンク64: 507トークン, 648文字
  チャンク65: 504トークン, 714文字
  チャンク66: 493トークン, 736文字
  チャンク67: 457トークン, 645文字
  チャンク68: 514トークン, 681文字
  チャンク69: 127トークン, 155文字
  チャンク70: 50トークン, 70文字
  チャンク71: 455トークン, 631文字
  チャンク72: 556トークン, 891文字
  チャンク73: 517トークン, 807文字
  チャンク74: 542トークン, 892文字
  チャンク75: 76トークン, 126文字
  チャンク76: 527トークン, 867文字
  チャンク77: 438トークン, 807文字
  チャンク78: 530トークン, 850文字
  チャンク79: 531トークン, 857文字
  チャンク80: 528トークン, 858文字
  チャンク81: 525トークン, 853文字
  チャンク82: 534トークン, 846文字
  チャンク83: 267トークン, 486文字
  チャンク84: 561トークン, 763文字
  チャンク85: 128トークン, 200文字
  チャンク86: 544トークン, 815文字
  チャンク87: 530トークン, 815文字
  チャンク88: 137トークン, 258文字
  チャンク89: 527トークン, 770文字
  チャンク90: 505トークン, 726文字
  チャンク91: 332トークン, 456文字
  チャンク92: 544トークン, 841文字
  チャンク93: 535トークン, 762文字
  チャンク94: 534トークン, 865文字
  チャンク95: 102トークン, 166文字
  チャンク96: 525トークン, 842文字
  チャンク97: 377トークン, 573文字
  チャンク98: 536トークン, 939文字
  チャンク99: 532トークン, 830文字
  チャンク100: 509トークン, 792文字
  チャンク101: 130トークン, 232文字

データベースに挿入中...
✓ チャンク1/101 挿入完了 (ID: 1)
✓ チャンク2/101 挿入完了 (ID: 2)
✓ チャンク3/101 挿入完了 (ID: 3)
✓ チャンク4/101 挿入完了 (ID: 4)
✓ チャンク5/101 挿入完了 (ID: 5)
✓ チャンク6/101 挿入完了 (ID: 6)
✓ チャンク7/101 挿入完了 (ID: 7)
✓ チャンク8/101 挿入完了 (ID: 8)
✓ チャンク9/101 挿入完了 (ID: 9)
✓ チャンク10/101 挿入完了 (ID: 10)
✓ チャンク11/101 挿入完了 (ID: 11)
✓ チャンク12/101 挿入完了 (ID: 12)
✓ チャンク13/101 挿入完了 (ID: 13)
✓ チャンク14/101 挿入完了 (ID: 14)
✓ チャンク15/101 挿入完了 (ID: 15)
✓ チャンク16/101 挿入完了 (ID: 16)
✓ チャンク17/101 挿入完了 (ID: 17)
✓ チャンク18/101 挿入完了 (ID: 18)
✓ チャンク19/101 挿入完了 (ID: 19)
✓ チャンク20/101 挿入完了 (ID: 20)
✓ チャンク21/101 挿入完了 (ID: 21)
✓ チャンク22/101 挿入完了 (ID: 22)
✓ チャンク23/101 挿入完了 (ID: 23)
✓ チャンク24/101 挿入完了 (ID: 24)
✓ チャンク25/101 挿入完了 (ID: 25)
✓ チャンク26/101 挿入完了 (ID: 26)
✓ チャンク27/101 挿入完了 (ID: 27)
✓ チャンク28/101 挿入完了 (ID: 28)
✓ チャンク29/101 挿入完了 (ID: 29)
✓ チャンク30/101 挿入完了 (ID: 30)
✓ チャンク31/101 挿入完了 (ID: 31)
✓ チャンク32/101 挿入完了 (ID: 32)
✓ チャンク33/101 挿入完了 (ID: 33)
✓ チャンク34/101 挿入完了 (ID: 34)
✓ チャンク35/101 挿入完了 (ID: 35)
✓ チャンク36/101 挿入完了 (ID: 36)
✓ チャンク37/101 挿入完了 (ID: 37)
✓ チャンク38/101 挿入完了 (ID: 38)
✓ チャンク39/101 挿入完了 (ID: 39)
✓ チャンク40/101 挿入完了 (ID: 40)
✓ チャンク41/101 挿入完了 (ID: 41)
✓ チャンク42/101 挿入完了 (ID: 42)
✓ チャンク43/101 挿入完了 (ID: 43)
✓ チャンク44/101 挿入完了 (ID: 44)
✓ チャンク45/101 挿入完了 (ID: 45)
✓ チャンク46/101 挿入完了 (ID: 46)
✓ チャンク47/101 挿入完了 (ID: 47)
✓ チャンク48/101 挿入完了 (ID: 48)
✓ チャンク49/101 挿入完了 (ID: 49)
✓ チャンク50/101 挿入完了 (ID: 50)
✓ チャンク51/101 挿入完了 (ID: 51)
✓ チャンク52/101 挿入完了 (ID: 52)
✓ チャンク53/101 挿入完了 (ID: 53)
✓ チャンク54/101 挿入完了 (ID: 54)
✓ チャンク55/101 挿入完了 (ID: 55)
✓ チャンク56/101 挿入完了 (ID: 56)
✓ チャンク57/101 挿入完了 (ID: 57)
✓ チャンク58/101 挿入完了 (ID: 58)
✓ チャンク59/101 挿入完了 (ID: 59)
✓ チャンク60/101 挿入完了 (ID: 60)
✓ チャンク61/101 挿入完了 (ID: 61)
✓ チャンク62/101 挿入完了 (ID: 62)
✓ チャンク63/101 挿入完了 (ID: 63)
✓ チャンク64/101 挿入完了 (ID: 64)
✓ チャンク65/101 挿入完了 (ID: 65)
✓ チャンク66/101 挿入完了 (ID: 66)
✓ チャンク67/101 挿入完了 (ID: 67)
✓ チャンク68/101 挿入完了 (ID: 68)
✓ チャンク69/101 挿入完了 (ID: 69)
✓ チャンク70/101 挿入完了 (ID: 70)
✓ チャンク71/101 挿入完了 (ID: 71)
✓ チャンク72/101 挿入完了 (ID: 72)
✓ チャンク73/101 挿入完了 (ID: 73)
✓ チャンク74/101 挿入完了 (ID: 74)
✓ チャンク75/101 挿入完了 (ID: 75)
✓ チャンク76/101 挿入完了 (ID: 76)
✓ チャンク77/101 挿入完了 (ID: 77)
✓ チャンク78/101 挿入完了 (ID: 78)
✓ チャンク79/101 挿入完了 (ID: 79)
✓ チャンク80/101 挿入完了 (ID: 80)
✓ チャンク81/101 挿入完了 (ID: 81)
✓ チャンク82/101 挿入完了 (ID: 82)
✓ チャンク83/101 挿入完了 (ID: 83)
✓ チャンク84/101 挿入完了 (ID: 84)
✓ チャンク85/101 挿入完了 (ID: 85)
✓ チャンク86/101 挿入完了 (ID: 86)
✓ チャンク87/101 挿入完了 (ID: 87)
✓ チャンク88/101 挿入完了 (ID: 88)
✓ チャンク89/101 挿入完了 (ID: 89)
✓ チャンク90/101 挿入完了 (ID: 90)
✓ チャンク91/101 挿入完了 (ID: 91)
✓ チャンク92/101 挿入完了 (ID: 92)
✓ チャンク93/101 挿入完了 (ID: 93)
✓ チャンク94/101 挿入完了 (ID: 94)
✓ チャンク95/101 挿入完了 (ID: 95)
✓ チャンク96/101 挿入完了 (ID: 96)
✓ チャンク97/101 挿入完了 (ID: 97)
✓ チャンク98/101 挿入完了 (ID: 98)
✓ チャンク99/101 挿入完了 (ID: 99)
✓ チャンク100/101 挿入完了 (ID: 100)
✓ チャンク101/101 挿入完了 (ID: 101)

挿入されたデータを確認:
┌─────────┬─────┬────────────────────┬─────────────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ (index)idsource             │ chunk_index │ content_preview                                                                                            
                                                                                                │
├─────────┼─────┼────────────────────┼─────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 01'ja.wikipedia.org'1'Published Time: Tue, 25 Nov 2025 08:49:43 GMT 機動戦士ガンダム - Wikipedia =============== コンテンツにス キップ - [x] '                                                                                   │
│ 12'ja.wikipedia.org'2'関連作品関連作品サブセクションを切り替えます * 15.1 サウンドトラック * 15.2 小説 * 15.2.1 小説版『機動戦士ガンダム』 * 15.2.2『密会』 * 15.2.3 中根真明著 '                                               │
│ 23'ja.wikipedia.org'3'コモンズ * ウィキデータ項目 表示 サイドバーに移動 非表示 ![Image 4: 半保護されたページ]( "このページは半保護されています。") 出典: フリー百科事典『ウィキペディア(Wikip'                                 │
│ 34'ja.wikipedia.org'4'| | 映画:機動戦士ガンダム | | 監督 | 富野喜幸 藤原良二 | | 制作 | 日本サンライズ | | 配給 | 松竹 | | 封切日 | 1981年3月14日 | | 上映時間 | 137分'                                                        │
│ 45'ja.wikipedia.org'5'巨大人型ロボットを「モビルスーツ」という名の兵器と位置付け、戦争を舞台としている。ヒーローロボットが怪獣ロボットを必殺技で倒すことの繰り返しではないドラマ性を重視したストーリーであり、また、当時のロボ' │
│ 56'ja.wikipedia.org'6'企画の経緯 ----- サンライズ "サンライズ (アニメ制作ブランド)")は、当時ヒットしていた『宇宙戦艦ヤマト』のヒット理由を分析し、一部の熱狂的なファンを相手にした商売であるとわかり、「ハイター'               │
│ 67'ja.wikipedia.org'7'仮のタイトル「**フリーダム・ファイター**」をつけて検討を進めていた企画は、少年たちが戦争に巻き込まれ宇宙戦闘機で協力して戦う物語で、ロボットを登場させる予定はなかった。しかし、これだと少年たちが乗'     │
│ 78'ja.wikipedia.org'8'# リアル志向 当時のロボットアニメでは、悪の組織を敵とする正義と悪の構図、あるいは宇宙からの侵略者を敵とし た戦いであった。それに対して、国家間の紛争による人間どうしの戦争としている[[10]]()。'            │
│ 89'ja.wikipedia.org'9'# 現実感のある兵器 登場するロボットを「モビルスーツ」と呼ぶ兵器として扱い、また、ロボットに「量産機」が存 在する[[12]]()。電波障害を起こしレーダーを妨害するミノフスキー粒子という架空の粒子が'            │
│ 910'ja.wikipedia.org'10'# 人間ドラマ 戦争を舞台とした人間ドラマを展開している[[14]]()。主人公は、当時のロボットアニメにみられる熱 血でさわやかな少年などではなく、ひねくれ、恐れ、逃げだすなどをするような普通の人間で'            │
│ 1011'ja.wikipedia.org'11'# ニュータイプの誕生 新人類としての「ニュータイプ」の概念を扱っている。宇宙に住む時代になれば、その環境に 順応した新しい人類が誕生するものとして、そこで生まれる高次な人間ドラマを描いている[[16]'         │
│ 1112'ja.wikipedia.org'12'商品展開 ---- サンライズは中学生以上向けに作っていたが、各社は小学生以下向けに商品展開をしたため関連商品は不振に陥った。スポンサーのクローバーの要請により1979年9月にGアーマーが登場し、クロ'             │
│ 1213'ja.wikipedia.org'13'# プラモデル化 サンライズはクローバーに対して、玩具が不振ならばプラモデル化をするように提案したが、そんな にお金はかけられないと拒否された。そこでサンライズは他社にもプラモデルの商品化を呼びかけた['     │
│ 1314'ja.wikipedia.org'14'**赤い彗星**の異名を持つジオンの**シャア・アズナブル**少佐は、地球連邦軍の新造艦**ホワイトベース**を追尾してサイド7に辿り着く。サイド7に連邦軍の**V作戦**の基地がありモビルスーツの開'                    │
│ 1415'ja.wikipedia.org'15'ジオン軍でニュータイプとして育成されていた**ララァ・スン**にアムロは出会い、互いに心を通わせ引かれ合う。ニュータイプとして目覚めたアムロはシャアを追いつめるが、ララァはシャアを庇い命を落とす。ララ'     │
│ 1516'ja.wikipedia.org'16'登場人物 ----'                                                                                            
                                                                                                │
│ 1617'ja.wikipedia.org'17'アムロ・レイ主人公。ガンダムのパイロット。サイド7に住んでいたときは機械いじりの好きな少年として有名であっ た。父は地球連邦軍のV作戦でガンダムの開発をしていたテム・レイ。軍人として振る舞うブライト・ノ'   │
│ 1718'ja.wikipedia.org'18'詳細については、下記のページを参照。 →「機動戦士ガンダムの登場人物 地球連邦軍」を参照 →「機動戦士ガンダム の登場人物 ジオン公国軍 (あ行-さ行) "機動戦士ガンダムの登場人物 ジオン公国軍 (あ'                │
│ 1819'ja.wikipedia.org'19'ガンダム」作詞 - 井荻麟 / 作曲 - 渡辺岳夫 / 編曲 - 松山祐士 / 唄 - 池田鴻、フィーリング・フリー、ミュージ ッククリエイション(キングレコード)エンディングテーマ「永遠にアムロ」作詞'                      │
│ 1920'ja.wikipedia.org'20'| 放送対象地域 | 放送局 | 放送日時 | 系列 | 備考 | | --- | --- | --- | --- | --- | | 中京広域圏 | 名古屋テレビ | 土曜 17:30 - 18:0'                                                                       │
│ 2021'ja.wikipedia.org'21'| | 石川県 | 石川テレビ | 金曜 16:00 - 16:30[[40]]() | 1980年4月25日まで放送[[41]]() | | 福井県 | 福井テレビ | 月曜 17:25 - 17:'                                                                          │
│ 2122'ja.wikipedia.org'22'# 公開試写会 本放送に先駆けて名古屋テレビ主催による公開試写会が春休みに旧:中区役所ホールで開催された。こ れは当時の主流となっていた往復ハガキで応募・抽選するもので、それまでは親子連れがほとんどだっ'    │
│ 2223'ja.wikipedia.org'23'# 再放送 * 本放送以後は名古屋テレビをはじめとするテレビ朝日系列の放送局や、テレビ埼玉などの独立放送局、CS 放送・BS放送放送局で繰り返し再放送されている。 * 2010年7月より、静岡県に1/1'                     │
│ 2324'ja.wikipedia.org'24'* BS11(アニメ+枠) 通常放送 初回:2007年12月2日 - 2008年9月21日、日曜日19時30分 - 20時00分 2回目:2014年3月23日 - 2015年1月4日、同上 セレクショ'                                                         │
│ 2425'ja.wikipedia.org'25'| | 第3話 | 4月21日 | 敵の補給艦を叩け! | 荒木芳久 | 小鹿英吉 | 1話完結エピソードのためカット。 ただしガデムの補給、シャアザクとガンダムの戦闘は反映。 | | 第4話 | 4'                                     │
│ 2526'ja.wikipedia.org'26'ガンダム | 星山博之 | 斧谷稔 | 小鹿英吉 | 安彦良和 | 1話完結エピソードだがマチルダ登場やアムロの心理描写が重要なため反映。 シャアの通信妨害はカット。 | | 第10話 | 6月9日 '                               │
│ 2627'ja.wikipedia.org'27'| | 第16話 | 7月21日 | セイラ出撃 | 山本優 | 斧谷稔 | 青鉢芳信 | 『哀・戦士編』の導入部。 セイラやコズンの投獄があるため反映。 | | 第17話 | 7月28日 | アムロ'                                             │
│ 2728'ja.wikipedia.org'28'ただしエルランがレビルへ異議を唱えるシーンのみ反映。 | | 第24話 | 9月15日 | 迫撃! トリプル・ドム | 山本優 | 斧谷稔 | 関田修 | 安彦良和 | マチルダが死ぬ他、名シーン多数'                                  │
│ 2829'ja.wikipedia.org'29'| | 第30話 | 10月27日 | 小さな防衛線 | 山本優 | 藤原良二 | カツ、レツ、キッカやセイラの伏線があるため反映 。 ただしアッガイの撃破シーンはほとんどカット。 | | 第31話 |'                                    │
│ 2930'ja.wikipedia.org'30'機動ビグ・ザム | 斧谷稔 | 関田修 | 一年戦争全体の伏線やドズル、スレッガー戦死のため反映。 | | 第37話 | 12 月15日 | テキサスの攻防 | 山本優 | 貞光紳也 | 中村一夫 | '                                       │
│ 3031'ja.wikipedia.org'31'* 第12話から第42話は、ガンダムとタイトルロゴが登場するもの。 映画化においては、各エピソードに対し主に以下 の変更点が加えられた。 * シリーズ全体で構成に影響のない、一話完結エピソードは基本的に'           │
│ 3132'ja.wikipedia.org'32'劇場版三部作 ------ 機動戦士ガンダム(劇場版)テレビ版第1話から第14話前半までを再編集した第1作で、1981年3 月14日に公開された。配給収入は9億3700万円[[46]]()。機動戦士ガン'                                 │
│ 3233'ja.wikipedia.org'33'# スタッフ(劇場版) * 製作 - 日本サンライズ "サンライズ (アニメ制作ブランド)")[[注 11]]() * 配給 - 松竹株式会社 * 製作 - 岸本吉功") * 企画 - 山浦栄二、伊藤'                                             │
│ 3334'ja.wikipedia.org'34'スタッフ(特別版) * 音響監督 - 鶴岡陽太(I)、百瀬慶一(III) * 録音監督 - 鶴岡陽太(II) * 音響演出 - 百瀬慶一(II) * サウンドデザイン - 笠松広司(I)、松長芳樹(I'                                   │
│ 3435'ja.wikipedia.org'35'# 主題歌・挿入歌(劇場版) 主題歌(第1作)「砂の十字架」作詞・作曲 - 谷村新司 / 編曲 - 青木望 / 唄 - やし きたかじん (キングレコード)オリコン週間シングルチャート - 最高21位 '                           │
│ 3536'ja.wikipedia.org'36'# 劇場版に関する反響 1980年10月2日発売の『日刊スポーツ』で、「**ポスト・ヤマトはガンダムだ**」との見出しで本作の映画化のスクープが報じられた[[49]]()[[50]]()。 1週間後の'                                 │
│ 3637'ja.wikipedia.org'37'第1作の公開に先立つ1981年2月22日、プロモーションの一環として2つのイベントがおこなわれた。午前中には新宿松 竹会館で入場者数を限定した「ガンダムフェスティバル」が開催され、大成功のうちに終了[['            │
│ 3738'ja.wikipedia.org'38'結果的に、劇場版第1作は大ヒットに終わった。特に前売り券は行列ができるほどの驚異的な売れ行きを見せ、続編の 実現の鍵になったとされる。続編においても行列はファンが参加できる「祭り」として重要な役割を担っ'  │
│ 3839'ja.wikipedia.org'39'# LD・ビデオ 本放送放映の後から劇場版が公開されたあたりに発売された映像媒体としては、ロブスター企画から1982年に発売されたテレビ版第42話「宇宙要塞ア・バオア・クー」と第43話「脱出」を収録し'              │
│ 3940'ja.wikipedia.org'40'# DVD DVD発売以降はすべてバンダイビジュアルから販売されている。 **劇場版 機動戦士ガンダム 特別版** 劇場版3部作のDVDは2000年12月21日に「特別版」として発売された。本作のDV'                                │
│ 4041'ja.wikipedia.org'41'# Blu-ray 「機動戦士ガンダム Blu-ray メモリアル ボックス」(BD-BOX)として2013年8月28日に発売。2017年9月27日には2013年版の封入特典を若干再編・簡素化したBD'                                               │
│ 4142'ja.wikipedia.org'42'# 4K ULTRA HD Blu-ray 「機動戦士ガンダム 劇場版三部作 4Kリマスターボックス」(UHD BD&BD-BOX)として2020年10月28日に発売[[68]]()。ドルビーアトモス'                                                        │
│ 4243'ja.wikipedia.org'43'# サウンドトラック →詳細は「機動戦士ガンダムのサウンドトラック」を参照'                                   
                                                                                                │
│ 4344'ja.wikipedia.org'44'# 小説'                                                                                                   
                                                                                                │
│ 4445'ja.wikipedia.org'45'小説版『機動戦士ガンダム』 総監督 富野喜幸によって放映当時に執筆され、朝日ソノラマから出版された[[69]]()。表紙は第1巻はシャアの設定セル画、2巻と3巻は大河原邦男、本文挿絵は青鉢芳信。富野は'              │
│ 4546'ja.wikipedia.org'46'13]]()。また、セイラがアムロと肉体関係を持ち兄シャアの殺害を促すなど、各キャラの性格や関係性なども大幅に改変されている。物語は宇宙で展開され、地球は舞台とならないため、アニメ版の地球で登場したM'        │
│ 4647'ja.wikipedia.org'47'『密会』 1997年に角川mini文庫から上下巻が発刊され、2000年にそれらを統合・加筆して角川スニーカー文庫から『 密会〜アムロとララァ』として改めて発刊された。要点を絞ってアニメ版の物語の内面を描'              │
│ 4748'ja.wikipedia.org'48'中根真明著 『機動戦士ガンダム』 中根真明によって執筆された小説版も朝日ソノラマから発売されていたが、こちらは純粋なノベライズでテレビシリーズとほぼ同じ内容となっている[[77]]()[[78]]()'                   │
│ 4849'ja.wikipedia.org'49'# 漫画 機動戦士ガンダム(冒険王版) "機動戦士ガンダム (冒険王版)")テレビ放送と並行して、秋田書店発行の漫画雑誌『冒険王 "冒険王 (漫画雑誌)")』で岡崎優により連載された。多くの点でテレビ'                  │
│ 4950'ja.wikipedia.org'50'# フィルムコミック 講談社アニメコミックス版講談社より、劇場版3部作を漫画仕立てにしたフィルムコミックとして各5巻で全15巻が刊行された。各巻のサブタイトルはテレビシリーズのものが多く使用されている'        │
│ 5051'ja.wikipedia.org'51'MOBILE SUIT GUNDAM THE MOVIES I 劇場版第1作『ガンダム』のフィルムコミック。1996年8月刊行。MOBILE SUIT GUNDAM THE MOVIES II 劇場版'                                                                        │
│ 5152'ja.wikipedia.org'52'# TRPG * ジーク・ジオン")(1990年、ツクダホビー) * 機動戦士ガンダム:0079RPG")(1991年、大日本絵画")) *  機動戦士ガンダムRPG")(1997年、ホビージャパン) '                                                │
│ 5253'ja.wikipedia.org'53'# パチンコ・パチスロ パチンコ * CRフィーバー機動戦士ガンダム(2013年、SANKYO "三共 (パチンコ)")) * CRフィーバー機動戦士ガンダム -V作戦発動-(2015年、SANKYO'                                             │
│ 5354'ja.wikipedia.org'54'# 自動車 2013年10月1日にジオニックトヨタ社(トヨタ)よりシャア専用オーリスが発売された。これは元々コンセプトモデルとして2012年のイベントにトヨタからオーリスをシャア専用風に改装して出展し'               │
│ 5455'ja.wikipedia.org'55'- 2018年)の項目参照)。'                                                                                 
                                                                                                │
│ 5556'ja.wikipedia.org'56'# 郵便切手 2000年(平成12年)10月23日に郵政省(のちの日本郵便)は特殊切手「20世紀デザイン切手シリーズ」の 第15集として50円切手2枚、80円切手8枚からなる切手シートを発行した。70年'                          │
│ 5657'ja.wikipedia.org'57'# 落語 2016年2月28日に落語家の立川志らくが、創作落語『頑駄夢落語 らすとしゅーてぃんぐ』をサンライズ公認作 品として発表[[86]]()した。また同日ベルサール秋葉原で行われたガンダム関連イベ'                    │
│ 5758'ja.wikipedia.org'58'# クローバー製玩具 * ガンダム合体セット * ガンダムDX合体セット * 合体機構を外したガンダムDX * ガンダム(スタンダード版) * ガンタンク(スタンダード版) * コアファイター * D'                             │
│ 5859'ja.wikipedia.org'59'# 注釈 1. **^**1994年にサンライズがバンダイグループ(当時)の傘下に入り、2020年には創通がバンダイナムコHD の完全子会社となった事で、ガンダムはバンダイナムコの自社IPになった(サン'                        │
│ 5960'ja.wikipedia.org'60'**^**カトキハジメも『GUNDAM FIX』最終回でそのシーンを描き、当時の衝撃を語っている。'                      
                                                                                                │
│ 6061'ja.wikipedia.org'61'# 出典 1. **^**五十嵐 1999, pp.34. 2. **^**氷川 2023, pp.92–94. 3. **^**ガンダム関連の年間売上高が初の1000億円超え、バンダイナムコHD -'                                                                   │
│ 6162'ja.wikipedia.org'62'**^**「’80年上半期アニメGP総決算」『アニメージュ』第3巻第10号、徳間書店、1980年10月、13-36頁。 21. ^ _**a**__**b**__**c**__**d**_「シリーズ・'                                                            │
│ 6263'ja.wikipedia.org'63'^ _**a**__**b**__**c**__**d**__**e**__**f**__**g**_「全国放映リスト」『アニメージュ』1980年7月号、徳間書店、124 - 125頁。 33. **'                                                                         │
│ 6364'ja.wikipedia.org'64'^ _**a**__**b**_「1981年邦画四社<封切配収ベスト作品>」『キネマ旬報』1982年2月下旬号、キネマ旬報社、1982 年、124頁。 47. **^**「1982年邦画四社<封切配収ベ'                                               │
│ 6465'ja.wikipedia.org'65'^ _**a**__**b**_『機動戦士ガンダム 宇宙世紀vol.1 歴史編』ラポート、1998年8月、38-39頁。 56. **^**『ファン ロード』1980年8月号、ラポート、38-39頁。 '                                                       │
│ 6566'ja.wikipedia.org'66'**^**“「ガンダムチャンネル」登録者79万人突破 劇場版三部作をYouTubeで無料配信 23日から”. _くらテク_. 2020年1月22日閲覧。アーカイブ 2021年1月22日 - ウェイバッ'                                             │
│ 6667'ja.wikipedia.org'67'**^**富野喜幸『機動戦士ガンダム』(初版)朝日ソノラマ〈ソノラマ文庫〉、1979年11月30日。全国書誌番号:80008486。 70. ^ _**a**__**b**__**c**_富野 200'                                                       │
│ 6768'ja.wikipedia.org'68'**^**富野喜幸(原作), 矢立肇(原作), 中根真明(文)『機動戦士ガンダム』 2巻、朝日ソノラマ〈ソノラマ文庫 〉、1980年4月。ASIN "ASIN (識別子)")B000J88P2I。全国書'                                          │
│ 6869'ja.wikipedia.org'69'**^**江戸の街でぶつかる「アムロ」と「シャア」――古典落語とガンダムがコラボ 立川志らくさん“頑駄夢(ガンダム )落語”動画公開アーカイブ 2023年4月25日 - ウェイバックマシン - 「機動戦'                         │
│ 6970'ja.wikipedia.org'70'# 小説 富野由悠季『密会〜アムロとララァ』株式会社KADOKAWA〈角川文庫〉、2000年10月1日。ISBN4-04-410153-1。'                                                                                                │
│ 7071'ja.wikipedia.org'71'# その他 明智惠子 編『富野由悠季の世界』株式会社キネマ旬報社、2019年6月22日。ISBN978-4-87376-468-9。 五十 嵐浩司 編『ガンプラ・ジェネレーション』株式会社講談社、199'                                      │
│ 7172'ja.wikipedia.org'72'関連項目 ---- * クローバー "クローバー (玩具メーカー)") * バンダイ * ガンプラ * サンライズ "サンライズ (アニメ制作ブランド)") * MSV(モビルスーツバリエーション) '                                       │
│ 7273'ja.wikipedia.org'73'無敵鋼人ダイターン3 | 機動戦士ガンダム (1979年4月7日 ‐ 1980年1月26日) | 無敵ロボ トライダーG7 (1980年2 月2日 ‐ 1981年1月24日) | | テレビ朝日土曜17'                                                    │
│ 7374'ja.wikipedia.org'74'* MS BOYS * MSV戦記 ジョニー・ライデン * パイロット列伝 * サンダーボルト * 鉄の駻馬 * 黒衣の狩人 * 虹霓の シン・マツナガ * ジオン公国幼年学校") * FAR EAST'                                                │
│ 7475'ja.wikipedia.org'75'* オンライン | | その他 | * ガンダムセンチュリー * 戦場写真集 * GUNDAM OFFICIALS * 戦略戦術大図鑑 * MSV * THE RIDE * クライシス * ハーモニー'                                                             │
│ 7576'ja.wikipedia.org'76'ガンダム * 砂の十字架 * 嵐の中で輝いて * 永遠の扉 * 未来の二人に * 機動戦士ガンダム CD-BOX | | | --- | | U.C.0084 - 0107 | | アニメ | * Ζ'                                                                │
│ 7677'ja.wikipedia.org'77'(メビウスの宇宙を越えて) "BEYOND THE TIME (メビウスの宇宙を越えて)") * 流星のナミダ * Everlasting "Everlasting (Kyleeの曲)") * mer'                                                                       │
│ 7778'ja.wikipedia.org'78'ENGAGE | | 楽曲 | * 0079-0088 | | | | | | 登場人物 | | --- | | | 地球連邦軍 | * アムロ * カイ * カツ * ス レッガー * セイラ * テ'                                                                          │
│ 7879'ja.wikipedia.org'79'(ガンダムシリーズ)") * ゼファー * GPシリーズ * TR-1 * TR-6 * ケストレル * サイコ * Ζ * デルタ "百式 (ガン ダムシリーズ)") * Ζプラス * S * ΖΖ '                                                             │
│ 7980'ja.wikipedia.org'80'リック・ドム | | ティターンズ | * ギャプラン * ジ・O * バーザム * バウンド・ドック * プロジェクト・セイレ ーネ * マラサイ | | エゥーゴ | * ネモ "ネモ (ガンダムシ'                                         │
│ 8081'ja.wikipedia.org'81'| | ジオン公国 | * ガウ "ガウ (ガンダムシリーズ)") * ギャロップ "ギャロップ (ガンダムシリーズ)") * グワジ ン * ザンジバル "ザンジバル (ガンダムシリーズ)") * ド'                                           │
│ 8182'ja.wikipedia.org'82'作戦・計画 | * V作戦 * ビンソン計画 * 統合整備計画 * SE計画 * Ζ計画 * 星一号作戦 | | | 施設・地名 | | 地球連邦軍 | * ジャブロー * ニュータイプ研究所 | |'                                                 │
│ 8283'ja.wikipedia.org'83'* 編 * 歴 ガンダムシリーズ | | --- | | シリーズ | * ![Image 11: 一覧記事]( "一覧記事")シリーズ一覧 * ![Image 12: 一覧記事]( "一覧記事")宇'                                                                │
│ 8384'ja.wikipedia.org'84'* アド・ステラ * 恒暦 | | 映像作品 | | テレビ | * 機動戦士ガンダム(第15話) * 機動戦士Ζガンダム * 機動戦 士ガンダム ΖΖ(ダブルゼータ) * ガンバレ!SDガンダム大行進'                                       │
│ 8485'ja.wikipedia.org'85'特別篇、機動戦士ガンダム 第08MS小隊 ミラーズ・リポート) * GUNDAM Mission to the Rise * ガンダム新体験 グ リーンダイバーズ * G-SAVIOUR * ∀ガンダム'                                                        │
│ 8586'ja.wikipedia.org'86'* 機動戦士ガンダム MS IGLOO -1年戦争秘録- * リング・オブ・ガンダム * 超電影版 SDガンダム三国伝 * 機動戦士 ガンダム 00(ダブルオー)-A wakening of the T'                                                   │
│ 8687'ja.wikipedia.org'87'模型戦士ガンプラビルダーズ ビギニングG * 機動戦士ガンダム THE ORIGIN | | Web配信 | * 機動戦士ガンダム サンダーボルト * 機動戦士ガンダム Twilight AXIS *'                                                  │
│ 8788'ja.wikipedia.org'88'毎日放送(土6、アニメイズム、日5) * TBS * テレビ東京 * NHK(NHK総合) * 日本テレビ(火曜プラチナイトアニ メ) | | 玩具展開 | * ガンプラ * ハイグレード・ユニバーサル'                                     │
│ 8889'ja.wikipedia.org'89'* ROBOT 魂 * ハイコンプリートモデル * ハイコンプリートモデルプログレッシブ * ジャンボグレード * ガンダムコレクション * ガシャポン戦士 * 元祖SDガンダム * ガンダムクロス '                                 │
│ 8990'ja.wikipedia.org'90'機動戦士Ζガンダム(1985年) * 機動戦士ガンダムΖΖ(1986年) * 機動戦士Vガンダム(1993年) * ブレンパワード (1998年) * ∀ガンダム(1999年) * OVERMANキング'                                                │
│ 9091'ja.wikipedia.org'91'破嵐万丈シリーズ(薔薇戦争、憂鬱ミュージアム、ヒット・カップル、愛はシベリアから) * 機動戦士ガンダム 逆襲のシャア(ベルトーチカ・チルドレン、ハイ・ストリーマー) * ガイア・ギア * 機動戦士ガ'           │
│ 9192'ja.wikipedia.org'92'ガンダム * 疾風ザブングル * STAND UP TO THE VICTORY 〜トゥ・ザ・ヴィクトリー〜 * ターンAターン | | --- | | 原案 | * 銀河漂流バイファム(1983年)'                                                         │
│ 9293'ja.wikipedia.org'93'聖戦士ダンバイン * 装甲騎兵ボトムズ * 銀河漂流バイファムシリーズ * 重戦機エルガイム * 巨神ゴーグ * 機甲界 ガリアン * 超力ロボ ガラット * ダーティペア "ダーティペア (アニメ)"'                             │
│ 9394'ja.wikipedia.org'94'"星界の戦旗 (アニメ)")シリーズ * BRIGADOON まりんとメラン * DINOZAURS: THE SERIES共 * GEAR戦士電童 * アル ジェントソーマ * 犬夜叉 "犬夜叉 (ア'                                                             │
│ 9495'ja.wikipedia.org'95'ダグ&キリル ラブライブ!シリーズ* ラブライブ! "ラブライブ! (テレビアニメ)") * ラブライブ!サンシャイン!! "ラブライブ!サンシャイン!! (テレビアニメ)") * ラブライブ!虹ヶ咲'                                   │
│ 9596'ja.wikipedia.org'96'* にじよん あにめーしょん * 幻日のヨハネ -SUNSHINE in the MIRROR- ガンダムビルドシリーズ* ガンダムビルドファイターズ * ガンダムビルドファイターズトライシリーズ *'                                        │
│ 9697'ja.wikipedia.org'97'SDガンダムシリーズ* Mk-〇、嵐を呼ぶ学園祭 * 戦国伝 暴終空城の章 * 緊急出撃 * スーパーGアームズ * 戦国伝 天下泰平編 * 聖機兵物語 * 三国伝 | ![Image 22]() |'                                               │
│ 9798'ja.wikipedia.org'98'(アニメ)") * 百日紅 〜Miss HOKUSAI〜 "百日紅 (漫画)") * アクセル・ワールド INFINITE∞BURST * ゼーガペインADP * コードギアス 反逆のルルーシュシリー'                                                        │
│ 9899'ja.wikipedia.org'99'閃光の旗の下に * 闇夜の時代劇") * 沈黙の艦隊 * 勇者指令ダグオン 水晶の瞳の少年 * ダイノゾーン * 思春期美少女合体ロボ ジーマイン | | 2000年代 | * 勇者王ガオガイガーFI'                                    │
│ 99100'ja.wikipedia.org'100'移:放送期間中にバンダイナムコピクチャーズへ制作移管 | | 典拠管理データベース![Image 25: ウィキデータを編 集]( "ウィキデータを編集") | | --- | | 全般 | * VIA'                                             │
│ 100101'ja.wikipedia.org'101'* プライバシー・ポリシー * ウィキペディアについて * 免責事項 * 行動規範 * 開発者 * 統計 * Cookieに関する声明 * モバイルビュー * ![Image 27: Wikimedi'                                                     │
└─────────┴─────┴────────────────────┴─────────────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

✓ 処理完了: 101個のチャンクを挿入しました

接続を閉じました。

では最後に検索を行います。

node search.js ガンダムとは?
[dotenv@17.2.3] injecting env (5) from .env -- tip: 🛠️  run anywhere with `dotenvx run -- yourcommand`
=== TiDB + さくらのAI Engine RAGシステム ===

TiDB Cloudに接続中...
✓ 接続成功!

検索クエリ: "ガンダムとは?"
取得件数: 上位3件

=== ベクトル検索結果 ===

[1] ID: 19 | Source: ja.wikipedia.org | Chunk: 19
    ガンダム」作詞 - 井荻麟 / 作曲 - 渡辺岳夫 / 編曲 - 松山祐士 / 唄 - 池田鴻、フィーリング・フリー、ミュージッククリエイション(キングレコード)エンディングテ ーマ「永遠にアムロ」作詞 - 井荻麟 / 作曲 - 渡辺岳夫 / 編曲 - 松山祐士 / 唄 - 池田鴻、フィーリング・フリ...

[2] ID: 5 | Source: ja.wikipedia.org | Chunk: 5
    巨大人型ロボットを「モビルスーツ」という名の兵器と位置付け、戦争を舞台としている。ヒーローロボットが怪獣ロボットを必殺技で倒すことの繰り返しではないドラマ性を重視したストーリーであり、また、当時のロボットアニメに定型の人物構成とは異なる多様な人物たちによる群像劇となっている[[2]]()。 初回放送...

[3] ID: 1 | Source: ja.wikipedia.org | Chunk: 1
    Published Time: Tue, 25 Nov 2025 08:49:43 GMT 機動戦士ガンダム - Wikipedia =============== コンテンツにスキップ - [x] メインメニュー メインメニュー サイドバーに移動 非表示 案内 * メインページ * コミュニティ・...

────────────────────────────────────────────────────────────

=== さくらのAI Engineで回答生成中... ===

=== 生成された回答 ===

ガンダムとは、**「モビルスーツ」という名前の巨大人型ロボットを兵器として用い、戦争を舞台にした作品群**です(文書2)。  
- ただのロボットアクションにとどまらず、**ドラマ性を重視したストーリー**で、当時のロボットアニメに見られる定型的な人物構成とは異なる多様なキャラクターが登場する**群像劇**となっています(文書2)。
- 初回放送時の視聴率は低かったものの、熱心なファンの声に応えて再放送されると高視聴率を獲得し、後に発売されたプラモデル(通称「ガンプラ」)が大ヒットしたことから、**アニメ・ゲーム・グッズ・イベントなど多様なビジネスへと拡大**した大規模IPへと成長しました(文書2)。

要するに、ガンダムは「モビルスーツ」と呼ばれるロボット兵器を中心に、戦争と人間ドラマを描く日本の代表的なロボットアニメシリーズ・フランチャイズです。


--- トークン使用量 ---
入力トークン: 1499
出力トークン: 383
合計トークン: 1882
さくらインターネット株式会社

Discussion