GraphAI で SakanaAIのAI-Scientist
はじめに
現代の科学研究は、膨大なデータと高度な分析手法の活用により、かつてない速度で進展しています。こうした中で、SakanaAI が提供する「AI-Scientist」は、研究者の創造性と効率性を飛躍的に向上させる革新的なツールとして注目されています。AI-Scientist は、大規模言語モデル(LLM)や各種ツールを駆使して、研究アイデアの生成から論文作成までのプロセスを自動化・支援します。
本記事では、AI-Scientist の概要に触れた後、最新の取り組みとして Python ベースの実装から GraphAI への移植による generate_ideas プロセスの詳細を解説します。
SakanaAI の AI-Scientist とは?
AI-Scientist は、科学的探求を補助または部分的に自動化することを目的としたツール群を備えています。単なるコーパスからの情報抽出やデータ分析に留まらず、科学的な発想(アイデアジェネレーション)、仮説構築、実験計画、さらには論文執筆など、研究サイクルの重要なパートを LLM や関連ツールで支援します。これにより、研究者が本来費やすべき創造的思考時間を最大化することが可能になります。
公式サイト:https://sakana.ai/ai-scientist-jp/
GraphAI とは?
GraphAI は、複雑なデータフローやプロセスを視覚的かつ直感的に管理・実行できるフレームワークです。ノード(タスク)とエッジ(データの流れ)で構成されるグラフ構造を利用し、ワークフローの設計・実行を容易にします。これにより、複雑なプロセスの可視化、再利用性の向上、拡張性の確保が可能となります。
AI-Sientist の GraphAI への移植
AI-Scientist のオリジナルは Python ベースで実装されていますが、TypeScript ベースの実装で GraphAI に移植されています。
この移植により、アイデア生成プロセスの効率化と柔軟性が大幅に向上しています。
リポジトリは以下になります。(@isamu 氏のご厚意によりリポジトリを公開させてもらいました。筆者のリポジトリではありません。)
以下の手順でセットアップと実行が可能です。
- OpenAIのキーを .env ファイルに設定します。
OPENAI_API_KEY=sk-xxx
- 以下のコマンドでインストールと実行ができます。
git clone https://github.com/isamu/ai-scientist.git
yarn install
yarn run run
- 実行結果はコンソールに出力されます。
実行の過程で各ノードの状態を出力し、最後に結果をJSON形式で出力します。
GraphAI を用いた generate_ideas プロセスの詳細解説
- プロジェクト構成と依存関係
まず、GraphAI を利用するために必要な依存関係やプロジェクト構成を確認します。以下の TypeScript コードは、AI-Scientist のアイデア生成プロセスを GraphAI 上で実行するためのものです。- generate_ideas: アイデア生成を実行する関数です。最大世代数: maxNumGenerations, リフレクション回数: numReflections を指定します。
- GraphAI の主要な設計部分は、getGraphData 関数内で定義されます。
- エントリーポイントの指定で、templates 配下の題目を選べます。以下のコードでは grokking を選んでいます。
import fs from "fs";
import "dotenv/config";
import { idea_first_prompt, idea_reflection_prompt } from "./text";
import { getBaseDir, loadJsonFile, agents } from "./utils";
import { GraphAI } from "graphai";
// GraphAI用のグラフデータを生成する関数
const getGraphData = (maxNumGenerations: number, numReflections: number) => {
const graphData = {
version: 0.5,
loop: {
count: maxNumGenerations,
},
nodes: {
idea_str_archive: {
value: [], // 配列。値を注入
update: ":nextIdeas.array",
},
ideaSystemPrompt: {
value: "", // 文字列。値を注入
},
taskDescription: {
value: "", // 文字列。値を注入
},
code: {
value: "", // 文字列。値を注入
},
ideaPrompt: {
agent: "stringTemplateAgent",
params: {
template: idea_first_prompt,
},
inputs: {
taskDescription: ":taskDescription",
code: ":code",
numReflections,
prev_ideas_string: ":idea_str_archive.join(,)",
},
isResult: true,
},
task1: {
agent: "openAIAgent",
params: {
prompt: ":ideaPrompt",
system: ":ideaSystemPrompt",
},
},
jsonParse: {
agent: "jsonParserAgent", // データ検証用
inputs: { text: ":task1.text" },
isResult: true,
},
improveTask: {
agent: "nestedAgent",
inputs: {
history: [
{ role: "user", content: ":ideaPrompt" },
{ role: "assistant", content: ":task1.text" },
],
ideaSystemPrompt: ":ideaSystemPrompt",
},
graph: {
version: 0.5,
loop: {
count: numReflections - 1,
},
nodes: {
history: {
value: "",
update: ":task2.messages",
},
counter: {
value: 2, // j + 2, jはループカウンタ
update: ":counter.add(1)",
},
prompt: {
agent: "stringTemplateAgent",
params: {
template: idea_reflection_prompt,
},
inputs: {
current_round: [":counter"],
numReflections,
},
},
task2: {
agent: "openAIAgent",
params: {
system: ":ideaSystemPrompt",
model: "gpt-4o-mini",
},
inputs: {
messages: ":history",
prompt: ":prompt",
},
isResult: true,
},
},
},
},
nextIdeas: {
agent: "pushAgent",
inputs: {
array: ":idea_str_archive",
item: ":improveTask.task2.text.codeBlock()",
},
},
debug: {
agent: "copyAgent",
console: { after: true },
inputs: { last_history: ":improveTask.nextHistory" },
},
},
};
return graphData;
};
// アイデア生成を実行する関数
const generate_ideas = async (
baseDir: string,
skipGeneration = false,
maxNumGenerations = 10,
numReflections = 5
) => {
if (skipGeneration) {
const ideas = loadJsonFile(baseDir + "/ideas.json");
return ideas;
}
const seedIdeas = loadJsonFile(baseDir + "/seed_ideas.json");
const ideaStrArchive = seedIdeas.map((m: unknown) => JSON.stringify(m));
const code = fs.readFileSync(baseDir + "/experiment.py", "utf8");
const prompt = loadJsonFile(baseDir + "/prompt.json");
try {
const graphData = getGraphData(maxNumGenerations, numReflections);
const graph = new GraphAI(graphData, agents);
graph.injectValue("idea_str_archive", ideaStrArchive);
graph.injectValue("ideaSystemPrompt", prompt["system"]);
graph.injectValue("taskDescription", prompt["task_description"]);
graph.injectValue("code", code);
graph.onLogCallback = ({ nodeId, state, inputs }) => {
console.log(nodeId, state, inputs);
};
const result = (await graph.run()) as any;
console.log(result);
} catch (e) {
console.log(e);
}
};
// エントリーポイント
const main = async () => {
const experiment = "grokking";
const baseDir = getBaseDir(experiment);
await generate_ideas(baseDir);
};
main();
- getGraphData 関数の解説
getGraphData 関数は、GraphAI で実行されるグラフの構造を定義します。以下に主要なノードとその役割を解説します。
- idea_str_archive: 生成されたアイデアをアーカイブする配列。
- ideaSystemPrompt、taskDescription、code: プロンプトや実験コードなどの基本情報を保持。
- ideaPrompt: idea_first_prompt `テンプレートを用いて、初期のアイデア生成プロンプトを作成。
- task1: OpenAI エージェントを使用して初期アイデアを生成。
- jsonParse: 生成されたテキストから JSON データを抽出・検証。
- improveTask: 反映プロセスを管理するネストされたグラフ。numReflections - 1回の反映を実行。
- history: ユーザーとアシスタントのメッセージ履歴。
- counter: 現在のラウンド数を管理。
- prompt: idea_reflection_prompt テンプレートを用いて反映プロンプトを作成。
- task2: OpenAI エージェントを使用してアイデアの改善。
- nextIdeas: 改善されたアイデアをアーカイブに追加。
- debug: デバッグ用に最終的な履歴をコンソールに出力。
- generate_ideas 関数の解説
generate_ideas 関数は、GraphAI を用いてアイデア生成プロセスを実行します。以下にその主要なステップを解説します。
- 生成スキップの判定:
- skipGeneration が true の場合、既存の ideas.json を読み込み、生成をスキップします。
- それ以外の場合、シードアイデア(seed_ideas.json)と実験コード(experiment.py)、プロンプト(prompt.json)を読み込み、アイデアアーカイブを初期化します。
- グラフデータの取得と GraphAI インスタンスの作成:
- getGraphData 関数を呼び出し、指定された生成回数と反映回数に基づいたグラフデータを取得。
- GraphAI コンストラクタにグラフデータとエージェントの設定を渡してインスタンスを生成。
- 値の注入:
- idea_str_archive、ideaSystemPrompt、taskDescription、code などの必要な値をグラフに注入。
- ログコールバックの設定:
- 各ノードの状態や入力をコンソールに出力するためのコールバックを設定。
- グラフの実行:
- graph.run() を呼び出して、グラフ全体のプロセスを実行。生成されたアイデアは ideas.json に保存されます。
- 実行の流れとデバッグ
GraphAI を用いることで、アイデア生成プロセス全体を視覚的に管理・実行できます。特に、onLogCallback を設定することで、各ノードの動作や入力データをリアルタイムで監視可能です。これにより、プロセス中の問題点を迅速に特定・修正できます。
graph.onLogCallback = ({ nodeId, state, inputs }) => {
console.log(nodeId, state, inputs);
};
- この設定により、各ノードのID、現在の状態、入力データがコンソールに出力され、プロセスの各ステップを詳細に追跡できます。
GraphAI 移植のメリットと今後の展望
メリット
- モジュール性の向上
- 各タスクを独立したノードとして定義することで、機能の追加や変更が容易になります。
- 可視化による理解の促進
- プロセス全体の流れを視覚的に把握できるため、デバッグや最適化が効率的に行えます。
- 再利用性の向上
- 共通のタスクやプロセスを他のプロジェクトでも再利用可能になり、一貫性のあるワークフローを維持できます。
- 拡張性の確保
- 新たなエージェントやノードを容易に追加できるため、AI-Scientistの機能拡張がスムーズに行えます。
今後の展望
GraphAI への移植により、AI-Scientist は以下のような新たな可能性を手に入れます:
- 高度なエラーハンドリング
- 各ノードごとにエラー処理を定義できるため、プロセス全体の堅牢性が向上します。
- 並行処理の効率化
- 複数のノードを並行して実行できるため、アイデア生成と反映プロセスの効率がさらに向上します。
- ユーザーによるカスタマイズ性の向上
- ユーザーが自身のニーズに合わせてプロセスをカスタマイズできる柔軟性が増加し、より多様な研究シナリオに対応可能となります。
- 統合と連携の強化
- 他のツールやフレームワークとの統合が容易になり、既存のワークフローにシームレスに組み込むことが可能となります。
まとめ
SakanaAIのAI-Scientist は、AIとツールの力を最大限に活用し、科学研究の未来を切り拓く強力なパートナーです。Python ベースの実装から GraphAI への移植を通じて、アイデア生成プロセスの効率化と拡張性の向上を実現しています。GraphAI の視覚的かつ柔軟なフレームワークにより、AI-Scientist はよりモジュール化され、再利用可能なツールへと進化しました。
今後も、AI-Scientist は研究者の創造性と効率性を支援し、革新的な研究アイデアの発掘を促進することで、科学の発展に貢献し続けることでしょう。GraphAI を基盤としたさらなる機能拡張や最適化が期待され、AI-Scientist はますます多様な研究ニーズに対応できるツールへと成長していくことが予想されます。
GraphAI に移植された AI-Scientist は、科学研究におけるプロセスの可視化と効率化を実現しました。コード全体の拡張性が向上し、研究者にとってさらに強力なツールとなるでしょう。
人工知能を活用したアプリケーションやサービスを活用し、内発的動機付けで行動するエンジニア、起業家、社会起業家をサポートするコミュニティーです。 singularitysociety.org
Discussion