AIアシスタントを使用してスクリプトツールを作成する

この進行中の Docker Labs GenAI シリーズ AI開発者ツールのエキサイティングな空間を探ります。 Dockerでは、誇大広告なしでオープンに探求できる広大な範囲があると信じています。 私たちは探索を共有し、開発者コミュニティとリアルタイムで協力します。 開発者はGitHub Copilotのようなオートコンプリートツールを採用し、チャットを使用していますが、AIツールがソフトウェアのライフサイクル全体を通じて、より具体的なタスクやインターフェースを支援する可能性は大いにあります。 したがって、私たちの探求は広範囲に及びます。 ソフトウェアをオープンソースとしてリリースするので、私たちと一緒にプレイしたり、探索したり、ハックしたりできます。

LLM は、データの変換に非常に優れています。 たとえば、最近、 Pylint ツールによって生成された一部のデータを使用して作業していました。 このツールは、コード違反の大きな配列を生成します。 

2400x1260 Docker Labs Genai

返されるデータの種類を示す例を次に示します。

[
    {
        "type": "convention",
        "module": "app",
        "line": 1,
        "column": 0,
        "endLine": 1,
        "endColumn": 13,
        "path": "src/app.py",
        "symbol": "missing-module-docstring",
        "message": "Missing module docstring",
        "message-id": "C0114"
    },
    {
       ...
    },
    ...
]

AIアシスタントとのこのセッションでは、データベースを作成してデータを挿入することで、AIが分析しやすくなると判断しました(LLMはSQLを書くのが得意です)。 今ではいつものように、アシスタントがSQLを生成できるかどうかを確認するための簡単なプロンプトを作成しました。

1. Read the json data from /thread/violations.json
2. For each element in the array, transform each element into two SQL INSERT statements.

* The first should insert columns PATH, START_LINE, END_LINE, START_COLUMN, END_COLUMN into a table named RANGES.
* The second should insert columns MESSAGE, TYPE, RANGE, and VIOLATION_ID into a table named VIOLATIONS.

LLMは明らかにこの種のタスクに長けており、これも例外ではありませんでした。 私たちのプロンプトエンジンは、ローカルファイルを読み取る機能で拡張されていましたが、それ以外は、これは非常に単純なプロンプトでした(GPT-3.5を使用しました)。 LLM は正しい INSERT ステートメントで応答しました。

INSERT INTO RANGES (PATH, START_LINE, END_LINE, START_COLUMN, END_COLUMN)
VALUES ('src/app.py', 1, 1, 0, 13);
INSERT INTO VIOLATIONS (MESSAGE, TYPE, RANGE, VIOLATION_ID)
VALUES ('Missing module docstring', 'convention', (SELECT ID FROM RANGES WHERE PATH='src/app.py' AND START_LINE=1 AND START_COLUMN=0), 'C0114');
...
...

私たちはこの種の機能に慣れ始めているので、結果はそれほど驚くことではありませんでした。 しかし、ここでのコンテキストウィンドウはどうでしょうか? このデータをすべてLLMに渡すことは、特にこれが継続的に行う必要があるタスクである場合は、あまり意味がありません。 さらに、これは実際にはプログラマーが問題を解決した方法ではありません。

プログラマーはプログラムを書くでしょう。

ですから、LLMに何かをするように頼むのではなく、同じことをするためのプログラムを書くように頼むべきです。 同じプロンプトから始めて、「JavaScript プログラムを書いてください...」というプレフィックスを付けましょう。言い換えれば、LLM に JavaScript でタスクをどのように達成するかを説明してもらい、うまくいけばループから抜け出すように自動化してもらいましょう。 調整されたプロンプトを次に示します。

Write a javascript function that reads json from a file named `/thread/violations.json`.

For each element in the array, transform each element into two SQL INSERT statements.

* The first should insert columns PATH, START_LINE, END_LINE, START_COLUMN, END_COLUMN into a table named RANGES.
* The second should insert columns MESSAGE, TYPE, RANGE, and VIOLATION_ID into a table named VIOLATIONS.

and write them to the file /thread/insert.sql

Now execute the javascript code in a container.

ツールコード

現在のLLMは、この種のタスクを実行できます。 GPT-3によって生成された出力のサンプルを次に示します。5。 ざっとスキャンすると、多くの人がこれがおそらくうまくいくと納得させるでしょう。 ただし、プロンプトエンジンがすでにDockerで実行されている場合は、それを実行することもできます。

const fs = require('fs');
const crypto = require('crypto');

function getRandomId() {
    return crypto.randomBytes(16).toString('hex');
}

function escapeString(str) {
    return str.replace(/'/g, \\\"''\\\");
}

let data = JSON.parse(fs.readFileSync('/thread/violations.json', 'utf8'));
let sqlStatements = '';

data.forEach((violation) => {
    let id = getRandomId();
    sqlStatements += `INSERT INTO RANGES(ID, PATH, START_LINE, END_LINE, START_COLUMN, END_COLUMN) VALUES('${id}', '${escapeString(violation.path)}', ${violation.line}, ${violation.endLine}, ${violation.column}, ${violation.endColumn});\\\\n`;
    sqlStatements += `INSERT INTO VIOLATIONS(MESSAGE, TYPE, RANGE) VALUES('${escapeString(violation.message)}', '${escapeString(violation.type)}', '${id}');\\\\n`;
    });

fs.writeFileSync('/thread/insert.sql', sqlStatements);

ツールの実行

お気づきでないかもしれませんが、プロンプトの最後に、「コンテナでJavaScriptコードを実行してください」という最後の指示を追加しました。 これは、結果を見ることができることを意味するため、私たちのセッションへの素晴らしい追加です。

ここで、ツールコールが再び登場します。 AIが作成したばかりのプログラムを実行しようとする能力を与えるために、新しいツールを試すための分離されたランタイムサンドボックスを作成する新しい関数を定義しました。

エージェントの新しいツール定義を次に示します。

tools:
  - name: run-javascript-sandbox
    description: execute javascript code in a container
    parameters:
      type: object
      properties:
        javascript:
          type: string
          description: the javascript code to run
    container:
      image: vonwig/javascript-runner
      command:
        - "{{javascript|safe}}"

AI アシスタントに、そのツールの説明からツールを生成するように依頼しました。 ツールの説明が変更されない限り、ワークフローは AI に戻って新しいツール バージョンの構築を依頼する必要はありません。

このパターンでの Docker の役割は、このコードを実行するためのサンドボックスを作成することです。 この関数は実際にはランタイムをあまり必要としないため、かなり小さなサンドボックスを提供します。

  • ネットワークにアクセスできません。
  • ホスト・ファイル・システムへのアクセスはありません (ツール間でデータを共有するための分離ボリュームへのアクセスは可能です)。
  • GPU にアクセスできません。
  • Node.jsランタイム以外のソフトウェアにはほとんどアクセスできません(たとえば、シェルはありません)。

1 つのツールが別のツールを作成する機能は、単なるトリックではありません。 これは、LLMに送信されるデータの量を制御する方法を提供し、アシスタントがループから自分自身を「自動化」する方法を提供するため、構築できるワークフローの種類に非常に実用的な影響を及ぼします。

次のステップ

この例は少し抽象的でしたが、次の投稿では、プロンプトが新しいツールを生成するというこのアイデアを検討するようになった実際のシナリオについて説明します。 私たちが検討しているワークフローのほとんどは、Pylint、SQLite、tree_sitter (もちろん Docker を使用して埋め込んでいます) などの既製のツールにすぎません。 例えば:

  1. 私のコードベースから違反を抽出するためにpylintを使用してください。
  2. 違反をSQLに変換し、それを新しいSQLiteに送信します。
  3. 最も一般的な error 型の違反を見つけ、それらを含む最上位のコード ブロックを表示します。

ただし、この種のワークフローを作成できるのは、カスタムツールをミックスに追加する必要があるタイミングを認識できることでもあります。

Docker Labs の GenAI シリーズを読んで、私たちが取り組んできたことの詳細をご覧ください。

さらに詳しく