4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【AI】copilotをUIAを使用して疑似API化する!

4
Last updated at Posted at 2026-04-21

0. はじめに

このツールは、ローカル環境で動作する Copilot の操作を自動化するためのツール、auto_ai_cです。
Copilot の UI を自動操作し、プロンプト送信や応答取得を補助する仕組みを提供します。

API サーバー化やクラウド提供を目的としたものではありません。
すべての処理はユーザー自身の PC 内で完結します。

1. 注意事項

本ツールは、以下のCopilot利用規約に完全に従う形で設計されています。

ツールやコンピューター プログラム (ボットやスクレイパーなど) を使用して Copilot にアクセスしないでください。
Copilot は、個人的な使用のためにのみ使用できます。

本ツールは以下の点で規約に抵触しません:

  • 個人利用の範囲でのみ使用されること
  • クラウドに公開されず、外部サービスとして提供されないこと
  • Copilot の出力を第三者に提供しないこと
  • Copilot の制限解除(ジェイルブレイク)を目的としないこと

また、以下の規約にも準拠します:

お客様や他のお客様に Copilot を提供する Microsoft の能力を損なわないこと。
技術的な攻撃、過剰な使用、プロンプトベースの操作、「ジェイルブレイク」、その他の悪用に関与しないこと。

本ツールは Copilot の制限を解除したり、Microsoft の技術的制御を回避するものではありません。
あくまで ユーザー自身の PC 上での操作を自動化する補助ツールです。

2. 規約

利用規約(本ツール独自)

  • 本ツールを使用して Copilot を API サーバー化しないこと
  • 本ツールを使用して 他者の権利・プライバシーを侵害しないこと
  • 本ツールを使用して 違法行為を行わないこと
  • Copilot の複製・模倣 AI を作成・公開しないこと
  • 本ツールの使用によって発生したトラブル・損害について、開発者は一切責任を負いません

3. 許可された使い方

必ず Copilot の利用規約に従って使用してください。

許可される利用例

  • ローカルで完結する自動化ツールとして使用すること
    例)Copilot の応答をもとに、ファイル編集やターミナルコマンドを実行する

  • Flask を使って、自分のスマホから “自分の PC 上の Copilot” を遠隔操作すること
    ※LAN 内または VPN 内のみ
    ※外部公開は禁止

禁止される利用例

絶対に 個人利用の範囲を超えて使用しないでください。
他者に公開・提供することは禁止します。

  • Copilot の機能をクラウド経由で提供する
  • Copilot の出力を第三者に配布する
  • Copilot を API のように外部サービス化する
  • Copilot の制限解除(ジェイルブレイク)を目的とする

4.使用方法

使い方

※Cドライブ直下にファイルを置いたとします。

argument.exe

プロンプトをcopilotに送信します。
cmdなら

comand prompt
cd C:\
argumen.exe "プロンプト"

ubuntuなら

Ubuntu
cd mnt/c
argument.exe "プロンプト"

必ず、Egde内のcopilotタブをアクティブ(開いている状態)にしておいてください。
copilotからの返答は、.exeとおなじディレクトリに、myfile.txtとして配置されています。

clipboard.exe

クリップボードの中身をcopilotに送信します。
cmdなら

comand prompt
cd C:\
clipbpard.exe

ubuntuなら

Ubuntu
cd mnt/c
clipboard.exe

必ず、Egde内のcopilotタブをアクティブ(開いている状態)にしておいてください。
copilotからの返答は、.exeとおなじディレクトリに、myfile.txtとして配置されています。

5.clipboard.exeの仕組み

Program.cs
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Forms;

namespace clipboard
{
    class Program
    {
        // Copilot の入力欄にクリップボードの内容を送信
        static void SendToCopilot(string text)
        {
            var edge = AutomationElement.RootElement.FindFirst(
                TreeScope.Children,
                new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
            );

            if (edge == null) return;

            // Copilot の入力欄(AutomationId=userInput)
            var input = edge.FindFirst(
                TreeScope.Descendants,
                new PropertyCondition(AutomationElement.AutomationIdProperty, "userInput")
            );

            if (input == null) return;

            // ValuePattern で直接入力
            if (input.TryGetCurrentPattern(ValuePattern.Pattern, out object pattern))
            {
                var vp = (ValuePattern)pattern;
                vp.SetValue(text);
            }

            // Enter で送信
            SendKeys.SendWait("{ENTER}");
        }

        // Copilot の最新メッセージを取得
        static string GetLatestMessage()
        {
            var edge = AutomationElement.RootElement.FindFirst(
                TreeScope.Children,
                new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
            );

            if (edge == null) return "";

            while (true)
            {
                try
                {
                    // Copilot の返答ブロック(ai-message)だけを取得
                    var msgs = edge.FindAll(
                        TreeScope.Descendants,
                        new PropertyCondition(AutomationElement.ClassNameProperty, "group/ai-message")
                    );

                    if (msgs == null || msgs.Count == 0)
                        return "";

                    // 最新の返答ブロック
                    var latest = msgs[msgs.Count - 1];

                    // 返答ブロック内の Text を全部取得
                    var texts = latest.FindAll(
                        TreeScope.Descendants,
                        new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text)
                    );

                    if (texts == null || texts.Count == 0)
                        return "";

                    var sb = new StringBuilder();

                    // texts[0] はラベルなのでスキップ
                    for (int i = 1; i < texts.Count; i++)
                    {
                        string line = texts[i].Current.Name;

                        if (string.IsNullOrWhiteSpace(line))
                            continue;

                        // 正規化
                        line = line
                            .Replace('\u2010', '-')  // ハイフン
                            .Replace('\u2011', '-')  // ノンブレークハイフン
                            .Replace('\u2012', '-')  // フィギュアダッシュ
                            .Replace('\u2013', '-')  // エンダッシュ
                            .Replace('\u2014', '-')  // エムダッシュ
                            .Replace('\u2015', '-')  // ホリゾンタルバー
                            .Replace('\u2212', '-')  // マイナス記号
                            .Replace('/', '/')      // 全角スラッシュ
                            .Replace('\', '\\')     // 全角バックスラッシュ
                            .Replace('_', '_');     // 全角アンダーバー

                        sb.AppendLine(line);
                    }

                    return sb.ToString().TrimEnd('\r', '\n');
                }
                catch (ElementNotAvailableException)
                {
                    Thread.Sleep(50);
                    continue;
                }
            }
        }

        // Copilot の行数
        static int GetLatestMessageLineCount()
        {
            var edge = AutomationElement.RootElement.FindFirst(
                TreeScope.Children,
                new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
            );

            if (edge == null) return 0;

            // 最新の ai-message を探す
            var msgs = edge.FindAll(
                TreeScope.Descendants,
                new PropertyCondition(AutomationElement.ClassNameProperty, "group/ai-message")
            );

            if (msgs == null || msgs.Count == 0)
                return 0;

            var latest = msgs[msgs.Count - 1];

            // その中の Text ノードを全部数える
            var texts = latest.FindAll(
                TreeScope.Descendants,
                new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text)
            );

            return texts?.Count ?? 0;
        }

        // Copilot の生成完了待ち
        static string WaitUntilDone()
        {
            string last = "";
            int stable = 0;

            while (true)
            {
                string text = GetLatestMessage();

                if (string.IsNullOrWhiteSpace(text))
                {
                    Thread.Sleep(100);
                    continue;
                }

                // deepthink の2行は無視
                if (text == "考え中…" ||
                    text == "自分の考えを整理し、計画を作成する")
                {
                    Thread.Sleep(100);
                    continue;
                }

                // 3行以上になっていなければまだ生成途中
                int lineCount = text.Split('\n').Length;
                if (lineCount < 3)
                {
                    Thread.Sleep(100);
                    continue;
                }

                // 安定判定
                if (text != last)
                {
                    last = text;
                    stable = 0;
                }
                else
                {
                    stable++;
                }

                // 4回連続で同じ → 生成完了
                if (stable >= 4)
                {
                    // deepthink の2行を削除して返す
                    var cleaned = new StringBuilder();
                    foreach (var line in text.Split('\n'))
                    {
                        if (line.Contains("考え中…")) continue;
                        if (line.Contains("自分の考えを整理し、計画を作成する")) continue;
                        cleaned.AppendLine(line);
                    }
                    return cleaned.ToString().TrimEnd();
                }

                Console.WriteLine("待機中");
                Thread.Sleep(100);
            }
        }

        [STAThread]
        static void Main()
        {
            // テスト用のプロンプト
            // deepthinkを使って、世の中のめちゃくちゃ難しい問題に自分なりの答えを出して。

            // myfile.txt を空にする
            File.WriteAllText("myfile.txt", "", Encoding.UTF8);

            // クリップボードの内容を取得
            string prompt = Clipboard.GetText();

            if (string.IsNullOrWhiteSpace(prompt))
            {
                Console.WriteLine("クリップボードが空です。");

                return;
            }

            // Copilot に送信
            SendToCopilot(prompt);

            // 完了待ち
            string result = WaitUntilDone();

            // 実行ファイルと同じフォルダに myfile.txt を保存
            string exeDir = AppDomain.CurrentDomain.BaseDirectory;
            string outPath = Path.Combine(exeDir, "myfile.txt");

            File.WriteAllText(outPath, result, Encoding.UTF8);

            Console.WriteLine("=== Copilot 応答 ===");
            Console.WriteLine(result);
            Console.WriteLine("====================");
            Environment.Exit(0);
        }
    }
}

主に、関数(送信、解答生成まで待機、解答取得)とmainでできています。

送信

SendToCopilot
    static void SendToCopilot(string text)
        {
            var edge = AutomationElement.RootElement.FindFirst(
                TreeScope.Children,
                new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
            );

            if (edge == null) return;

            // Copilot の入力欄(AutomationId=userInput)
            var input = edge.FindFirst(
                TreeScope.Descendants,
                new PropertyCondition(AutomationElement.AutomationIdProperty, "userInput")
            );

            if (input == null) return;

            // ValuePattern で直接入力
            if (input.TryGetCurrentPattern(ValuePattern.Pattern, out object pattern))
            {
                var vp = (ValuePattern)pattern;
                vp.SetValue(text);
            }

            // Enter で送信
            SendKeys.SendWait("{ENTER}");
        }

まず、edge内のcopilotの入力欄のUIデータ(inspect.exeを使用)を取得し、そのデータに当てはまるところに直接、文字列を入れます。(マウスを使用しない)
その後、SendKeysを使用し、ctrl+v→enterを入力し、送信します。

WaitUntilDoneとGetLatestMessageLineCount

WaitUntilDone&GetLatestMessageLineCount
        // Copilot の行数
        static int GetLatestMessageLineCount()
        {
            var edge = AutomationElement.RootElement.FindFirst(
                TreeScope.Children,
                new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
            );

            if (edge == null) return 0;

            // 最新の ai-message を探す
            var msgs = edge.FindAll(
                TreeScope.Descendants,
                new PropertyCondition(AutomationElement.ClassNameProperty, "group/ai-message")
            );

            if (msgs == null || msgs.Count == 0)
                return 0;

            var latest = msgs[msgs.Count - 1];

            // その中の Text ノードを全部数える
            var texts = latest.FindAll(
                TreeScope.Descendants,
                new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text)
            );

            return texts?.Count ?? 0;
        }

        // Copilot の生成完了待ち
        static string WaitUntilDone()
        {
            string last = "";
            int stable = 0;

            while (true)
            {
                string text = GetLatestMessage();

                if (string.IsNullOrWhiteSpace(text))
                {
                    Thread.Sleep(100);
                    continue;
                }

                // deepthink の2行は無視
                if (text == "考え中…" ||
                    text == "自分の考えを整理し、計画を作成する")
                {
                    Thread.Sleep(100);
                    continue;
                }

                // 3行以上になっていなければまだ生成途中
                int lineCount = text.Split('\n').Length;
                if (lineCount < 3)
                {
                    Thread.Sleep(100);
                    continue;
                }

                // 安定判定
                if (text != last)
                {
                    last = text;
                    stable = 0;
                }
                else
                {
                    stable++;
                }

                // 4回連続で同じ → 生成完了
                if (stable >= 4)
                {
                    // deepthink の2行を削除して返す
                    var cleaned = new StringBuilder();
                    foreach (var line in text.Split('\n'))
                    {
                        if (line.Contains("考え中…")) continue;
                        if (line.Contains("自分の考えを整理し、計画を作成する")) continue;
                        cleaned.AppendLine(line);
                    }
                    return cleaned.ToString().TrimEnd();
                }

                Console.WriteLine("待機中");
                Thread.Sleep(100);
            }
        }

こちらも、edge内のcopilotのテキストのUIデータ(inspect.exeを使用)を取得し、そのデータに当てはまるところに直接、文字を取得します。(マウスを使用しない)
それを繰り返し、GetLatestMessageLineCount(copilotの行数)が三行以上(deepthinkでノイズが二行入ってしまう。)で、テキストの更新が4回行われていなかったら、テキストが全て生成されたと判断します。

GetLatestMessage

GetLatestMessage
        // Copilot の最新メッセージを取得
        static string GetLatestMessage()
        {
            var edge = AutomationElement.RootElement.FindFirst(
                TreeScope.Children,
                new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
            );

            if (edge == null) return "";

            while (true)
            {
                try
                {
                    // Copilot の返答ブロック(ai-message)だけを取得
                    var msgs = edge.FindAll(
                        TreeScope.Descendants,
                        new PropertyCondition(AutomationElement.ClassNameProperty, "group/ai-message")
                    );

                    if (msgs == null || msgs.Count == 0)
                        return "";

                    // 最新の返答ブロック
                    var latest = msgs[msgs.Count - 1];

                    // 返答ブロック内の Text を全部取得
                    var texts = latest.FindAll(
                        TreeScope.Descendants,
                        new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text)
                    );

                    if (texts == null || texts.Count == 0)
                        return "";

                    var sb = new StringBuilder();

                    // texts[0] はラベルなのでスキップ
                    for (int i = 1; i < texts.Count; i++)
                    {
                        string line = texts[i].Current.Name;

                        if (string.IsNullOrWhiteSpace(line))
                            continue;

                        // 正規化
                        line = line
                            .Replace('\u2010', '-')  // ハイフン
                            .Replace('\u2011', '-')  // ノンブレークハイフン
                            .Replace('\u2012', '-')  // フィギュアダッシュ
                            .Replace('\u2013', '-')  // エンダッシュ
                            .Replace('\u2014', '-')  // エムダッシュ
                            .Replace('\u2015', '-')  // ホリゾンタルバー
                            .Replace('\u2212', '-')  // マイナス記号
                            .Replace('/', '/')      // 全角スラッシュ
                            .Replace('\', '\\')     // 全角バックスラッシュ
                            .Replace('_', '_');     // 全角アンダーバー

                        sb.AppendLine(line);
                    }

                    return sb.ToString().TrimEnd('\r', '\n');
                }
                catch (ElementNotAvailableException)
                {
                    Thread.Sleep(50);
                    continue;
                }
            }
        }

こちらは、copilotの応答を取得する関数です。
1番下にある最新メッセージだけを取得します。
改行の読み取りができないので、空白を改行とみなします。
なので、プロンプトに「半角のスペースを入れないでください」という旨の内容を入れてください。

main

main
        [STAThread]
        static void Main()
        {
            // テスト用のプロンプト
            // deepthinkを使って、世の中のめちゃくちゃ難しい問題に自分なりの答えを出して。

            // myfile.txt を空にする
            File.WriteAllText("myfile.txt", "", Encoding.UTF8);

            // クリップボードの内容を取得
            string prompt = Clipboard.GetText();

            if (string.IsNullOrWhiteSpace(prompt))
            {
                Console.WriteLine("クリップボードが空です。");

                return;
            }

            // Copilot に送信
            SendToCopilot(prompt);

            // 完了待ち
            string result = WaitUntilDone();

            // 実行ファイルと同じフォルダに myfile.txt を保存
            string exeDir = AppDomain.CurrentDomain.BaseDirectory;
            string outPath = Path.Combine(exeDir, "myfile.txt");

            File.WriteAllText(outPath, result, Encoding.UTF8);

            Console.WriteLine("=== Copilot 応答 ===");
            Console.WriteLine(result);
            Console.WriteLine("====================");
            Environment.Exit(0);
        }

先ほどの関数を元に、
入力(SendToCopilot)
⬇︎
出力完成まで待つ(WaitUntilDone)
⬇︎
出力を取得(GetLatestMessage)
に、出力をmyfile.txtに保存をmainで追加しました。

6.argument.exeの仕組み

Project.cs
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Forms;

namespace argument
{
    class Program
    {
        // Copilot に送信
        static void SendToCopilot(string text)
        {
            Clipboard.SetText(text);

            var edge = AutomationElement.RootElement.FindFirst(
                TreeScope.Children,
                new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
            );

            if (edge == null) return;

            var input = edge.FindFirst(
                TreeScope.Descendants,
                new PropertyCondition(AutomationElement.AutomationIdProperty, "userInput")
            );

            if (input == null)
            {
                Console.WriteLine("入力欄が見つかりません");
                return;
            }

            input.SetFocus();
            SendKeys.SendWait("^v");
            SendKeys.SendWait("{ENTER}");
        }

        // 最新の返答本文を取得
        static string GetLatestMessage()
        {
            var edge = AutomationElement.RootElement.FindFirst(
                TreeScope.Children,
                new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
            );

            if (edge == null) return "";

            var msgs = edge.FindAll(
                TreeScope.Descendants,
                new PropertyCondition(AutomationElement.ClassNameProperty, "group/ai-message")
            );

            if (msgs == null || msgs.Count == 0)
                return "";

            var latest = msgs[msgs.Count - 1];

            var texts = latest.FindAll(
                TreeScope.Descendants,
                new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text)
            );

            if (texts == null || texts.Count == 0)
                return "";

            var sb = new StringBuilder();

            for (int i = 1; i < texts.Count; i++)
            {
                string line = texts[i].Current.Name;

                if (string.IsNullOrWhiteSpace(line))
                    continue;

                line = line
                    .Replace('\u2010', '-')
                    .Replace('\u2011', '-')
                    .Replace('\u2012', '-')
                    .Replace('\u2013', '-')
                    .Replace('\u2014', '-')
                    .Replace('\u2015', '-')
                    .Replace('\u2212', '-')
                    .Replace('/', '/')
                    .Replace('\', '\\')
                    .Replace('_', '_');

                sb.AppendLine(line);
            }

            return sb.ToString().Trim();
        }

        // 最新 ai-message の Text ノード数
        static int GetLatestMessageLineCount()
        {
            var edge = AutomationElement.RootElement.FindFirst(
                TreeScope.Children,
                new PropertyCondition(AutomationElement.ClassNameProperty, "Chrome_WidgetWin_1")
            );

            if (edge == null) return 0;

            var msgs = edge.FindAll(
                TreeScope.Descendants,
                new PropertyCondition(AutomationElement.ClassNameProperty, "group/ai-message")
            );

            if (msgs == null || msgs.Count == 0)
                return 0;

            var latest = msgs[msgs.Count - 1];

            var texts = latest.FindAll(
                TreeScope.Descendants,
                new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text)
            );

            return texts?.Count ?? 0;
        }

        // Copilot の生成完了待ち
        static string WaitUntilDone()
        {
            string last = "";
            int stable = 0;

            while (true)
            {
                string text = GetLatestMessage();

                if (string.IsNullOrWhiteSpace(text))
                {
                    Thread.Sleep(100);
                    continue;
                }

                // deepthink の2行は無視
                if (text == "考え中…" ||
                    text == "自分の考えを整理し、計画を作成する")
                {
                    Thread.Sleep(100);
                    continue;
                }

                // 3行以上になっていなければまだ生成途中
                int lineCount = text.Split('\n').Length;
                if (lineCount < 3)
                {
                    Thread.Sleep(100);
                    continue;
                }

                // 安定判定
                if (text != last)
                {
                    last = text;
                    stable = 0;
                }
                else
                {
                    stable++;
                }

                // 4回連続で同じ → 生成完了
                if (stable >= 4)
                {
                    // deepthink の2行を削除して返す
                    var cleaned = new StringBuilder();
                    foreach (var line in text.Split('\n'))
                    {
                        if (line.Contains("考え中…")) continue;
                        if (line.Contains("自分の考えを整理し、計画を作成する")) continue;
                        cleaned.AppendLine(line);
                    }
                    return cleaned.ToString().TrimEnd();
                }

                Console.WriteLine("待機中");
                Thread.Sleep(100);
            }
        }

        [STAThread]
        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("引数がありません。");
                return;
            }

            string prompt = args[0];

            // Copilot に送信
            SendToCopilot(prompt);

            // 完了待ち
            string result = WaitUntilDone();

            // 保存処理
            string exeDir = AppDomain.CurrentDomain.BaseDirectory;
            string outPath = Path.Combine(exeDir, "myfile.txt");
            File.WriteAllText(outPath, result, Encoding.UTF8);

            Console.WriteLine("=== Copilot 応答 ===");
            Console.WriteLine(result);
            Console.WriteLine("====================");
            Environment.Exit(0);
        }
    }
}

これは、clipboard版からmainを差し替えて、因数版にしたものです。

main
        [STAThread]
        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("引数がありません。");
                return;
            }

            string prompt = args[0];

            // Copilot に送信
            SendToCopilot(prompt);

            // 完了待ち
            string result = WaitUntilDone();

            // 保存処理
            string exeDir = AppDomain.CurrentDomain.BaseDirectory;
            string outPath = Path.Combine(exeDir, "myfile.txt");
            File.WriteAllText(outPath, result, Encoding.UTF8);

            Console.WriteLine("=== Copilot 応答 ===");
            Console.WriteLine(result);
            Console.WriteLine("====================");
            Environment.Exit(0);
        }
    }

args[0]を使用して、因数に対応しています。
基本的なコードは同じです。

7.終わりに

いかがだったでしょうか。
ぜひauto_ai_cを使って、AI使用の効率化をしてみてください!
copilotの解答を、そのまま使用することも出来ますので、AIツールなどにも向いてるかと思います。
copilot利用規約と、本ツールの利用規約をしっかり守って、正しい使い方をしてくださいね!

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?