1
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?

LangChain × SingleStoreでインフラ構築を楽にするAIエージェント: planner-shell

1
Last updated at Posted at 2026-03-21

開発の背景

学習目的で、SingleStoreを使ったAIエージェントを作成してみようと思い、Vibeコーディングで作成しました。

どうせ作るなら自分にとって便利なものをと思い、テスト環境構築時のソフトウェアインストールを楽にするエージェントを作ることに。

OpenClawでも、依頼したらソフトウェアを自動的にインストールしてくれますが、いま何をやっているのかが見えないので不安になります(可視性の欠如)。

Open-InterpreterやOpenHandsなど既存のOSSもいくつか試してみたのですが、こういった用途にはいまいち使いづらかったので、自分にとって使いやすいものを目指しました。

免責事項: planner-shellはあくまで使い捨てのテスト環境を作成する目的のもので、本番環境での使用は想定していません。AIが生成したコマンドを実行するため、環境を破壊する可能性もあります。自己責任でご利用ください。

planner-shell "install superset and load sample data" —— これだけで、AIエージェントが自身のOS種別に沿った情報をWeb検索し、実行プランを考え、インストールからサンプルデータのロードまでを実行します。

planner-shellを使ってサクッとインストールしたApache Superset(BIツール)の画面

image.png

ChatGPTが提案する段階を超えて、実際にLinuxコマンドを実行し、状況を判断し、自分自身で失敗を修復する—— これが planner-shell です。

単なる「AIヘルパー」ではなく、自ら学習するインフラエンジニアのような存在を目指しています。

planner-shell起動後の画面

============================================================
🚀 Practical AI DevOps Agent Initializing...
============================================================
💾 Using SingleStore plan cache backend (host=svc-3xxx-shared-dml.aws-virginia-6.svc.singlestore.com, db=db_yo_05dae)
💾 Plan cache initialized (30-day TTL)

🧹 Cleaning up expired plan cache entries...
✅ No expired plan cache entries to clean
✅ No expired plan entries found

⚡ Running database optimizations...
✅ System Ready. Persistent shell session established.
🤖 Agent Modes: Planner (with Tavily search) + Executor

============================================================
⚠️  IMPORTANT WARNING
============================================================
This tool is designed EXCLUSIVELY for creating disposable test
environments. It is NOT intended for production use.
The agent executes AI-generated commands which may DESTROY your
environment. Use entirely at your own risk.
============================================================

============================================================
📋 Quick Guide:
  • Enter a task description (e.g., 'Install Nginx')
  • Enter a URL to fetch content (e.g., 'https://example.com/guide')
  • Enter a markdown file path (e.g., './setup.md')
  • Type 'exit' or 'quit' to exit
============================================================

🤖 Enter your task (or 'exit'): 

核となるコンセプト: 計画 × 記憶 × 実行

planner-shellは以下の3つの要素で構成されています:

1. Planner (計画エージェント):
Tavily検索で最新情報を得て、LLMに実行プランを生成させます

2. Cache (記憶):
生成したプランを保存し、将来の同一または類似タスクで再利用可能にします。これにより、LLMトークンとTavilyトークンの両方を節約できます

3. Executor (実行エージェント):
プランに記載されたコマンドを永続的PTYシェルで1つずつ実行し、結果を観察し、エラーになれば自律的に修正します

なぜSingleStoreなのか?

planner-shellは、上記の Cache(記憶) としてデフォルトではローカルのSQLiteを使用しますが、SingleStoreも使えるように作っています。

planner-shellの真の価値は、組織内で知識が自然に蓄積される点にあります。SQLiteだと個人のローカルで完結してしまいますが、SingleStoreに替えると——-

チームの集合知が貯まる

  • Aさんの生成した「Redis構築手順」
  • Bさんの修正した「Docker composeアップグレード手順」
  • Cさんが検証した「SSL証明書更新の自動化」

これらがすべてSingleStoreの plans テーブルに蓄積され、ベクトル検索BM25全文検索を組み合わせたハイブリッド検索で引き出せます。

-- SingleStore内のplanキャッシュ構造
CREATE TABLE IF NOT EXISTS {self.table} (
    task_hash      VARCHAR(64),           -- タスクのハッシュ値(一意識別用)
    task_text      TEXT NOT NULL,         -- タスク内容の件名(ハイブリッド検索の対象)
    plan           LONGTEXT NOT NULL,     -- LLMが生成した詳細な実行プラン
    timestamp      DATETIME NOT NULL,     -- プランの生成日時(時系列管理用)
    embedding      VECTOR(1536) NULL,     -- OpenAI埋め込み(類似プランの検索用)
    url            TEXT NULL,             -- URL入力時のURL
    markdown_file  TEXT NULL,             -- Markdownファイルのパス
    PRIMARY KEY (task_hash),              -- Primaryキーとしてtask_hashを指定
    SORT KEY (timestamp)
);

類似タスクは「先人の知恵」が使える

タスク入力欄に依頼する文言は人によって様々です。「Install Redis」とだけ入力する人もいれば「I'd like to install Redis database on Ubuntu」と入力する人もいるでしょう。

Planner(計画エージェント) は、セマンティック検索全文検索(BM25) を組み合わせたハイブリッド検索で、「あ、過去のあれと似てる」と検知し、類似したプランをキャッシュから提案します。

🤖 Enter your task (or 'exit'): install redis

============================================================
🎯 TWO-PHASE EXECUTION
============================================================

📋 PHASE 1: PHASE 1: Planning (with Tavily search)

⚡ Searching cache for similar plans...
🔍 Hybrid Search:
   BM25 Query: task_text:(install redis Ubuntu)
   Vector Search: Using embedding similarity (1536 dimensions)

============================================================
🔍 Similar cached plans found (hybrid FTS + vector search)
============================================================

[1] Score: 0.0163  (FTS: 0.2615 | Vector: 0.7436)  — 0d ago
     Task : Install Redis on Linux | Docs
     Plan » Here's a detailed execution plan for installing Redis on Ubuntu 24.04 LTS:
     Plan » 1. Update package lists and install prerequisites:
     Plan »    ```bash

[2] Score: 0.0162  (FTS: 0.1899 | Vector: 0.7659)  — 0d ago
     Task : How to Install and Configure Redis on Ubuntu/Debian
     Plan » # Redis Installation and Configuration Plan for Ubuntu 24.04
     Plan » ## 1. System Preparation
     Plan » ```bash

[3] Score: 0.0159  (FTS: 0.1899 | Vector: 0.7050)  — 0d ago
     Task : How To Install and Secure Redis on Ubuntu | DigitalOcean
     Plan » # Redis Installation and Security Hardening Execution Plan
     Plan » ## 1. System Preparation
     Plan » ```bash

[4] Generate a new plan instead
============================================================

Use a cached plan? [1-3 / 4 for new]: 

SingleStoreのハイブリッド検索ではSQLiteと違い、RRF(Reciprocal Rank Fusion:逆順位融合) を使用しています。一言で言えば 「複数の検索結果(ランキング)を、公平かつ強力にひとつのリストにまとめるアルゴリズム」 です。

ドット積を使ったベクトル検索や

embedding <*> (%s):>VECTOR(1536) AS vec_score

RRFをPython側ではなく、SingleStore側のSQL処理としてオフロードできるのが便利です。

SELECT
    COALESCE(f.task_hash, v.task_hash) AS task_hash,
    COALESCE(f.task_text, v.task_text) AS task_text,
    COALESCE(f.plan, v.plan) AS plan,
    COALESCE(f.timestamp, v.timestamp) AS timestamp,
    IFNULL(f.ft_score, 0) AS ft_score,
    IFNULL(v.vec_score, 0) AS vec_score,
    0.3 * (1.0 / (NVL(f.fts_rank, 1000.0) + 60)) +
    0.7 * (1.0 / (NVL(v.vs_rank, 1000.0) + 60)) AS combined_score
FROM fts_ranked f

SingleStoreの本質的な強み

1. マルチモデル対応

一つのテーブルに、構造化データだけでなくJSONなどの半構造化データ、今回のような全文テキストやベクトルデータも格納できます。

Agentic AI(エージェント型AI)では、用途ごとにベクトルデータベースなどの専用データベースを用意するのではなく、管理の容易さからもSingleStoreのようなマルチモデルデータベースの活用が主流になりつつあります。

2. スケーラビリティ

「このプラン、全社のサーバーで使えますか?」という問いに対して——-

SQLite: 1MB → 10MB → 100MB と肥大化すると単一ファイルがボトルネックに
SingleStore: 分散クラスタで数億プランを保持可能、秒単位で検索

3. 高可用性

チームが「え、さっきのタスクをもう一度?」となったとき——-

SQLite: ローカルならそのホストが死んだら終わり。
SingleStore: マルチノードレプリケーション、Read Replicaで負荷分散も可能

4. クラウドネイティブ

Kubernetes上にデプロイする場合——-

SingleStore Heliosなら、PLAN_CACHE_BACKEND=singlestore と環境変数を設定するだけで接続。DB管理はフルマネージドサービスのHelios任せ。

PLAN_CACHE_BACKEND=singlestore
SINGLESTORE_HOST=my-cluster.singlestore.com
SINGLESTORE_PORT=3306
SINGLESTORE_USER=agent
SINGLESTORE_PASSWORD=••••••
SINGLESTORE_DATABASE=planner_shell_shared

planner-shell の技術的な特徴

「職人的なこだわり」でplanner-shellを作成しました。

1. 状態維持の徹底

class PersistentShell:
    def __init__(self):
        self.master_fd, self.slave_fd = pty.openpty()
        # bash単一プロセスを維持

cdした後も、exportした後も、すべて次のコマンドに引き継がれます。人間がbashを叩くのと同じ感覚。これにより、venvを作成してからインストールが必要なソフトウェアでも問題なくインストール可能に。

2. 自動修復ループ

def build_replan_task(...):
    if last_exit_code != 0:
        instruction = f"Fix error: {error_tail}"
        # Tavilyで修正方法を検索
        search_query = f"fix error: {error_tail}"

エラーが出たら、LLMがなぜ失敗したかを分析し、修正案を検索してコマンドを再生成。私たちが普段やっているエラーメッセージを元にググってリトライする作業を完全に自動化。

LLMが提示するコマンドが気に入らなければ、却下して指示をだすことも、提案されたコマンドを直接書き換えることも可能です。

実際の例として、下記ではApache Supersetのインストール中に、SECRET_KEYが誰でも知っている初期設定(デフォルト)のままになっているという警告によって、superset db upgradeコマンドの実行に失敗しています。

============================================================
🤖 AI Proposes Command:
------------------------------------------------------------
superset db upgrade
============================================================

Execute this?[Y/n/edit/skip/quit] (default: Y): y

🚀 Executing...

--------------------------------------------------------------------------------
                                    WARNING
--------------------------------------------------------------------------------
A Default SECRET_KEY was detected, please use superset_config.py to override it.
Use a strong complex alphanumeric string and use a tool to help you generate 
a sufficiently random sequence, ex: openssl rand -base64 42 
For more info, see: https://superset.apache.org/docs/configuration/configuring-superset#specifying-a-secret_key
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Refusing to start due to insecure SECRET_KEY

[Exit Code: 1]

planner-shellは、すかさずエラー内容を元にWeb検索を実行し、opensslを使った堅牢なSECRET_KEYを作成するコマンド実行を提案してきました。

============================================================
🔍 Tavily Search:
------------------------------------------------------------
Query: apache superset SECRET_KEY configuration superset_config.py
============================================================
⏳ Searching... (this may take a few seconds)
✅ Search completed

============================================================
🔍 Tavily Search:
------------------------------------------------------------
Query: apache superset create superset_config.py with SECRET_KEY
============================================================
⏳ Searching... (this may take a few seconds)
✅ Search completed

============================================================
🔍 Tavily Search:
------------------------------------------------------------
Query: apache superset generate SECRET_KEY openssl rand base64
============================================================
⏳ Searching... (this may take a few seconds)
✅ Search completed

============================================================
🤖 AI Proposes Command:
------------------------------------------------------------
echo "SECRET_KEY = '$(openssl rand -base64 42)'" > superset_config.py
============================================================

このように、自律的にエラー対応してくれます。

3. 進化し続けるプラン

planner-shellは、タスク実行後に refine コマンドで実行ログからクリーンなプランを再生成する機能を備えています。

仕組み

  1. タスク実行時、Executor(実行エージェント)は各コマンドの成功/失敗の結果を記録
  2. 実行完了後、ユーザーがrefine コマンドを実行
  3. LLMに各コマンド結果を送信し、以下のルールでプランを再構成:
    • 失敗したコマンドは除外
    • 同一目的でのリトライは最終成功コマンドのみ残す
    • 非対話フラグ(-yDEBIAN_FRONTEND=noninteractive)を自動付与
  4. 元のプランとの diff を表示、内容を edit で修正可能
  5. 保存時は元のキャッシュエントリを上書き

ユースケース

  • プランの品質向上: テスト環境での試行錯誤を経て、信頼性の高いプランをキャッシュ
  • チーム知識の結晶化: 共有キャッシュのエントリを定期的にアップデート

実例

🤖 Enter your task: Install Nginx with SSL
# ... 実行完了 ...
💡 Execution log: 12 commands total (11 succeeded, 1 failed).
   Type 'refine' to distil a clean plan from the successful path.

🤖 Enter your task: refine
# LLMがログを分析し、失敗を除外したクリーンなプランを生成
# diff 表示 → edit → 保存 が可能

この refine 機能により、planner-shellは「単なる自動化ツール」から試行錯誤から学習して進化する自律的エージェントへと進化しています。

一言で言うと

「失敗を恐れず、学習し、チームの知恵を貯め込むDevOpsエージェント」

コマンド実行前に人間が確認できる安全性(ヒューマン・イン・ザ・ループ)、同じタスクは再度Web検索しない効率性、失敗したら自分で直そうとする自律性、そして進化し続けるプランを SingleStore という共有DBに保存する集合知

これが、本格的な「自律型AIエージェント」のあるべき姿だと考えています。

始め方

planner-shellをインストールします

git clone https://github.com/bobo3977/planner-shell.git && cd planner-shell && chmod +x setup.sh && ./setup.sh

環境変数ファイルを編集します

vi .env

.envファイルの記載項目(最低限 OPENAI_API_KEY があれば動きます)

# 任意: Tavily検索(無くても動作するがWeb検索機能が使えない)
# TAVILY_API_KEY=your_tavily_key_here

# 必須: どちらか1つ(OpenAI or OpenRouter)
OPENAI_API_KEY=your_openai_key_here
# または
# OPENROUTER_API_KEY=your_openrouter_key_here

# 任意: LLMモデル選択
# OPENAI_MODEL=gpt-5-nano  # デフォルト
# OPENROUTER_MODEL=openai/gpt-5-nano  # デフォルト

# 任意: 埋め込めモデル選択 
# EMBEDDING_MODEL="text-embedding-3-small"  # デフォルト

# 任意: 埋め込みの無効化 (ベクトル検索はスキップ)
# DISABLE_EMBEDDINGS=0  # 1で無効化

# 任意: 自動承認モード(automation/CI用)
# AUTO_APPROVE=1  # true/yes も可

# 任意: SingleStoreキャッシュバックエンドを使用する場合
# PLAN_CACHE_BACKEND=singlestore  # デフォルト: sqlite
# SINGLESTORE_HOST=<SINGLESTORE_HOSTNAME>
# SINGLESTORE_PORT=3306(Standard Workspace) or 3333(Starter Workspace)
# SINGLESTORE_USER=<USER_NAME>
# SINGLESTORE_PASSWORD=<PASSWORD>
# SINGLESTORE_DATABASE=<DATABASE_NAME>
# PLAN_CACHE_TTL_DAYS=30  # デフォルト: 30

Tavily APIは必須ではなく、なければLLMの内部知識オンリーで動作します。ただ、Web検索なしだとハルシネーションが起きやすいので、後述のURLやマークダウンを参照するモードでのみ使用するのがよさそうです。

トークン料金を節約するためにgpt-5-nanoという小さなモデルをデフォルトにしていますが、賢いモデルを選択するとより快適に使えます。

実行方法

  • 対話モード
planner-shell
  • タスク内容を引数として指定して起動
planner-shell "Install redis"
  • URLを引数として指定して起動(ベンダー公式ドキュメントのURLなど) ⇐オススメ
planner-shell https://example.com/guide
  • マークダウンファイルを引数として指定して起動(過去に誰かがまとめた手順書など)
planner-shell ./setup.md
  • サンドボックス・モード(コンテナを作成してその中にソフトウェアをインストール)
planner-shell --sandbox docker --image ubuntu --ports 8088:8088
planner-shell --sandbox podman --image almalinux --ports 8080:8080

サンドボックス・モードはdockerもしくはpodmanの環境が必要です

  • 自動承認モード(コマンド実行確認にすべてYESと自動的に回答)
AUTO_APPROVE=1 planner-shell ./setup.md

自動承認モードはヒューマン・イン・ザ・ループを完全に排除するので危険です。使用するときは十分ご注意ください。

注意点

planner-shellは設計のコンセプト上、冪等性の担保(毎回同じ処理を実行することの保証)が弱いです。

キャッシュされた実行プラン自体は毎回同じものを使いますが、Excecutorエージェントによる実行時には、プランを参照しつつ毎回コマンドを生成します。そのため、プランと同じコマンドが実行される完全な保証はありません。LLMに対するプロンプトで冪等性を担保するように強く指示していますが、限界があります。

planner-shellは、Ansibleのコードを書くほどでもない手軽なテスト環境の構築や、あまり試したことのないソフトウェアのインストールといった用途に向いています。

完全に同じテスト環境を何十回も構築するなど冪等性の担保が重要な場合には、Ansibleを使うのが望ましいでしょう。

planner-shellの詳細な使い方は

以下の記事を参照ください。

SingleStoreを使ってみたい方は

以下の記事を参考にHeliosアカウントを作成ください。

1
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
1
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?