AIをつかってフォームを自動入力させてみる


> ※この記事は、一部AI（ChatGPT）を活用して内容を整理・執筆しています。

---

## ☑ これは何？

登録フォームって、オートコンプリートがうまく働かないことありますよね？

* 郵便番号が2つの入力段に分かれている
* 電話番号が `tel1`, `tel2`, `tel3` に分割されている
* input の name 属性が自動入力に適してない

こんなときに、"AIにHTMLを読ませて、構造を理解させ、適切な入力値を自動表示"させるスクリプトをTampermonkeyで作ってみました。
なお、このスクリプト自体の設計・改善にもAI（ChatGPT）を活用しており、プロンプト設計や分岐条件の調整などはAIと対話しながら進めました。

---

## 👉 動作の概要

* `form`タグを検出したときに、「🧠 AI自動入力」ボタンを上部に追加
* ボタンを押すと、LLM (ChatGPT)がform構造を分析
* name/id/placeholder などを判断し、適切な値を各フィールドに自動入力

---

## 🎓 技術構成

* 実装言語: JavaScript (Tampermonkey)
* 外部AI: OpenAI GPT-4o
* DOM分析: `form.outerHTML`をLLMに送信
* レスポンスはJSONで返される\[{selector, value}, ...] の配列

---

## ☑ できること

* 任意のフォームに対応 (input nameが常に同じでなくてもセレクタ解釈で対応)
* 電話番号や郵便番号の分割にも自動対応
* フォームの内容を判定して、個人情報だけに限定表示

---

## コード一覧

<details>
<summary>クリックで表示</summary>

```javascript
// ==UserScript==
// @name         Smart Form AutoFill (LLM powered)
// @namespace    http://tampermonkey.net/
// @version      0.4
// @description  フォームにAI自動入力ボタンを追加し、ChatGPTを使って入力補完する
// @author       example
// @match        *://*/*
// @grant        none
// ==/UserScript==

(async function () {
  'use strict';

  const profile = {
    firstName: "Taro",
    lastName: "Yamada",
    fullName: "山田 太郎",
    email: "example@example.com",
    postalCode: "123-4567",
    postalCode1: "123",
    postalCode2: "4567",
    address: "東京都新宿区西新宿1-1-1",
    phone: "080-1234-5678",
    phone1: "080",
    phone2: "1234",
    phone3: "5678"
  };

  const apiKey = "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // ← ここにOpenAIのAPIキーを入力

  function isPersonalInfoForm(form) {
    const keywords = ['name', 'fullname', 'email', 'mail', 'address', 'tel', 'phone', 'postcode', 'zip'];
    const inputs = form.querySelectorAll('input, textarea, select');
    return [...inputs].some(input =>
      keywords.some(kw =>
        (input.name && input.name.toLowerCase().includes(kw)) ||
        (input.id && input.id.toLowerCase().includes(kw)) ||
        (input.placeholder && input.placeholder.toLowerCase().includes(kw))
      )
    );
  }

  function createButton(form) {
    const button = document.createElement('button');
    button.textContent = "🧠 AI自動入力";
    button.style.marginBottom = '10px';
    button.style.padding = '6px 12px';
    button.style.fontSize = '14px';
    button.style.cursor = 'pointer';
    button.style.backgroundColor = '#4CAF50';
    button.style.color = 'white';
    button.style.border = 'none';
    button.style.borderRadius = '4px';

    button.addEventListener('click', async (e) => {
      e.preventDefault();
      button.disabled = true;
      button.textContent = '🤖 入力中...';

      const html = form.outerHTML;

      const response = await fetch("https://api.openai.com/v1/chat/completions", {
        method: "POST",
        headers: {
          "Authorization": `Bearer ${apiKey}`,
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          model: "gpt-4o",
          messages: [
            {
              role: "system",
              content: `あなたはHTMLフォーム入力補助AIです。以下のフォームHTML構造とユーザープロファイルを元に、各入力フィールドへ値を適切にマッピングしてください。
・セレクタはCSSセレクタとして指定してください
・住所・電話番号・郵便番号が複数フィールドに分かれている場合、それぞれ分割して対応してください（例：080-1234-5678 → tel1, tel2, tel3）
・形式は絶対に JSON のみで、マークダウンや記号（\`\`\`）を含めないでください
・出力形式は：[ {"selector": ..., "value": ...}, ... ] のみを返してください`
            },
            {
              role: "user",
              content: `HTMLフォーム:\n${html}\n\nユーザープロファイル:\n${JSON.stringify(profile)}`
            }
          ],
          temperature: 0.2
        })
      });

      const result = await response.json();
      const content = result.choices?.[0]?.message?.content;
      console.log("LLM Response:", content);

      try {
        const cleaned = content.replace(/```[\s\S]*?\n/, '').replace(/```$/g, '').replace(/```/g, '').trim();
        const mappings = JSON.parse(cleaned);
        for (const { selector, value } of mappings) {
          const el = document.querySelector(selector);
          if (el) el.value = value;
          else console.warn(`セレクタ "${selector}" の要素が見つかりませんでした`);
        }
        button.textContent = '✅ 入力完了';
      } catch (err) {
        button.textContent = '⚠️ エラー';
        alert("LLMの出力のパースに失敗しました。コンソールを確認してください。");
        console.error(err);
      }

      setTimeout(() => {
        button.disabled = false;
        button.textContent = "🧠 AI自動入力";
      }, 3000);
    });

    form.parentNode.insertBefore(button, form);
  }

  const forms = document.querySelectorAll('form');
  forms.forEach(form => {
    if (isPersonalInfoForm(form)) {
      createButton(form);
    }
  });
})();

```

</details>

---

## 下記のような情報にはボタンは表示されません

* Qiitaのログインフォーム (user/passwordのみ)
* 検索フォーム

→ `input[name]` などに `name`, `mail`, `tel`, `address` などのキーワードが含まれる場合のみ表示

---

## 作りたい人へ

* Tampermonkey をChromeに追加
* 新規スクリプトを作成
* APIキーを設定
* フォームのあるページを開けばOK

---

## 今後の拡張案

* プロファイル切替 (自宅用/仕事用)
* Undo ボタン追加
* AIの出力をデバッグ表示

---

## まとめ

「言語や構造の振れのあるフォームに、文脈から適切に入力値を埋める」

これはLLMの特技を活かした、一般ユーザーでも使える実用的アプローチでした。

最初は自分用に作ったのですが、他の人も便利になる可能性を感じたので、よかったら試してみてください。

> ご意見や改善案も歓迎です！