日常をセンサーで記録してみたブログ

IoTの技術を使って日常をデジタルデータにして気づかなかった面白そうなことを探してみるページ。

「エージェント設計ベース」: aichat で組む軽量ワークスペース

PCを立ち上げて最初にする作業は「メールの返信」でも「コードの編集」でもなく、「今日の業務をどのAIエージェントに分担させるか設計すること」。

生成AIの活用が進むにつれ、タスクを直接AIに処理させる対話型から、「自分専属のエージェントチームを編成して現場を回す」 というスタイルへ仕事の進め方そのものがシフトするのではないか?

今回は、Rust製の超軽量CLI「aichat」と最小限のWeb UI(Streamlit) を組み合わせ、「エージェント設計工房」を立ち上げた実践を紹介します。

1. 「エージェント設計ベース」の仕事術

エージェント設計ベースのワークフローでは、仕事を以下のように進めます。

  1. メタ対話(設計相談):
    「今日は午前中に溜まった未読メールを捌き、午後はGitの差分からコミットログを整理したい」と、まずは設計士AI(Architect AI)に今日の課題を投げる。

  2. 役割(Role)と道具(Tool)の切り出し:
    AIが対話の中で「トリアージ役」「返信下書き役」「Git差分抽出ツール」といった最小構成のパーツを定義・提案する。

  3. チーム編成と安全な実行(Human-in-the-loop):
    パーツを組み合わせて現場用エージェントを召喚。エージェントはメールの下書きを作っても勝手に送信せず、必ず「この内容で承認しますか?」と人間に判断を委ねる。

  4. チームの保存と翌朝の再利用:
    よく機能した編成はYAMLファイルとして保存。翌朝は1クリックで同じ体制を復元する。

自分が手を動かすのではなく、「まず今日の現場チームを設計し、自分は指示と最終承認に徹する」。これがエージェント設計ベースのワークフローです。

2. aichat の恩恵

このワークフローを試す際、LangChainや重たいPythonオーケストレーション基盤をフルに積む必要はありません。要となるのが Rust製CLIツール「aichat」 です。

  • 驚異的な軽さと低リソース消費:
    単一バイナリで動作し、起動は一瞬。重たいランタイムや常駐プロセスでメモリを圧迫しないため、サブ機やファンレスの省電力PCでもキビキビ動きます。

  • 標準入出力(UNIX哲学)の扱いやすさ:
    CLIのパイプライン処理と相性が抜群。Pythonの subprocess から標準出力を叩くだけで、ブラウザへの低遅延なリアルタイム・ストリーミング描画が簡単に実現できます。

  • マルチプロバイダ対応:
    設定ファイル1枚で Gemini、Claude、各種ローカルLLMまで透過的に切り替え可能。クラウドの高速APIを使えば、計算機側の非力さは何らハンデになりません。

3. ミニマムな「エージェント設計工房」のアーキテクチャ

システム構成は極めてシンプルです。

 

[ ブラウザ (Streamlit UI) ]
        │
[ aichat CLI (v0.30.0) ] ───> [ 各種LLM API (Gemini 等) ]
        │
[ ハイブリッド正規表現パーサー ]
        │
[ Role / Tool ] 
(動的アセット登録)   (構成の保存・再読込)

画面上で設計士AIとチャットすると、返答テキストから自動でRoleやToolの定義をパーサーが抽出し、サイドバーに動的ストックします。

ユーザーはストックされたアセットを選んで「召喚ボタン」を押すだけ。その場でチャットの宛先を特化エージェントに切り替え、実務の代行を依頼できます。

おわりに

自律型AIエージェントの運用には、必ずしも巨大なマシンパワーや複雑な基盤は必要ありません。

「推論は軽量なCLI(aichat)に任せ、UI(Streamlit)とプレーンテキスト(YAML)で薄く糊付けする」。これだけで、手元の非力なPCは十分、業務を劇的に変える「エージェント設計スタジオ」へと進化します。

Core 2 Duoで動くAI補助付きコード開発環境

「AIのコード補完は便利だけど、勝手に関連ファイルを見に行ってトークンを消費したり、意図しない文脈を拾ってハルシネーションを起こすのが気になる……」

「手動で『このファイルとこのファイルだけ見て、この機能を追加して』と、1ステップずつ手綱を握りながら開発したい」

そんな思いから、ブラックボックスな自動化を排し、人間が主導権を握ってコンテキストを渡すCUIツール を作りました。

実行環境は、Core 2 Duo(GPUなし) のオールドマシン。ローカルで重いモデルを動かすのではなく、軽量CLI「aichat」と「Google Gemini API」を組み合わせることで、軽快かつ快適なキーボード完結の開発環境が仕上がったので紹介します。

1. 「全自動」に対する問題意識

現代のAI開発環境は「全自動」がトレンドです。しかし、実際に使っていると以下のような違和感や疲れを感じることはないでしょうか。

  • ブラックボックス化するプロンプト
    チャット形式やエージェント機能は裏で何をしているのかが見えません。

  • 意図しないファイル巻き込みとハルシネーション
    プロジェクト全体を勝手に走査・検索するため、関係のない設定ファイルや古いテストコードまでプロンプトに含まれます。

  • 「思考のアウトソーシング」による疲弊
    先回りでコードが次々と提案され続けると、人間は設計者ではなく「AIが出したコードのレビュー係」に追いやられます。

便利な道具のはずが、気づけばAIに振り回されている――。

2. 制約を味方にする:Core 2 Duo × クラウドAPI

今回のターゲットマシンは Core 2 Duo世代のPCです。

当然ながら、現代の重厚なIDEを立ち上げるだけでメモリとCPUを食いつぶし、ローカルLLMランタイム(llama.cppやOllama)はAVX命令がないためまともに動きません。

しかし、この物理的な制約こそが「余計なものを削ぎ落とす」開発背景になりました。

 

[ キーボード操作 (端末 / tmux) ]
          │
          ▼
    ~/dev_menu.py (Python標準ライブラリのみ)
          │ (手動で選んだファイル+要件プロンプト)
          ▼
      aichat (Rust製の超軽量CLI)
          │
          ▼ (HTTPS通信のみ / ローカル負荷ゼロ)
    Google Gemini API (gemini-1.5-flash & gemini-embedding-001)

ローカル環境が行うのは「標準入出力の整形」と「TLS接続の確立」だけです。推論やベクトル検索といった重たい処理はすべてGoogle Gemini APIにオフロードします。

結果として、Core 2 Duo機であっても実用的な応答性が手に入りました。

3. 作ったもの:最小限のCUIオーケストレーター「dev_menu.py」

作ったのは、Pythonの標準ライブラリだけで書かれた対話型のCUIスクリプト dev_menu.py です。

端末上で起動すると、シンプルな番号選択メニューが表示されます。

 

=== Local AI Dev Menu ===
[1] 新規コード生成 (MVP作成)
[2] コンテキスト指定で機能追加 / リファクタ
[3] エラーログ診断 (Traceback解析)
[4] クイック質問 (aichat直接対話)
[q] 終了
Select:

こだわったポイント:手動コンテキスト選択

最大の肝は [2] コンテキスト指定 です。このメニューを選ぶと、カレントディレクトリの対象ファイルを一覧表示し、「AIに読ませたいファイルだけ」を人間が番号で指定します。

  1. main.py と models.py だけを選択。

  2. 「models.py のスキーマ変更に合わせて main.py にバリデーション処理を追加して」と1行プロンプトを入力。

  3. dev_menu.py が指定ファイルの中身とプロンプトを整形し、パイプ経由で aichat に渡す。

  4. ストリーミングでターミナルに変更案が流れてくる。

渡している情報が「人間が指定した2ファイル」だけなので、余計なノイズは一切混ざりません。

4. 実際の開発リズム:tmuxで回す心地よいサイクル

普段の開発は tmux で画面を2分割して進めています。

  • 左ペイン: テキストエディタ(Vim等)でコードを書く

  • 右ペイン: dev_menu.py を開き、AIとやり取りする

  1. 小さく作る: 仕様を決めて、まずは最小限の動くコードを出力させる。

  2. 手で動かす: ターミナルで実行し、挙動やテストを確認する。

  3. 必要な分だけ足す: 修正したいファイルだけを dev_menu で選んで機能追加を投げる。

  4. コピペで納得しながら適用: 出てきたdiffやコードを目を通しながら反映する。

1ステップずつ足場を固めながらコードを育てていく感覚が戻ってきました。

おわりに:「道具」としてのAIを取り戻す

生成AIの進化によって「プログラミングの完全自動化」が語られることが増えました。しかし、何が起きているか分からないブラックボックスに身を委ねる開発は、便利である一方でどこか頼りなく、疲れるものでもあります。

Core 2 Duoという古いマシンで動かした dev_menu.py は、単なるスペック不足への対抗策ではなく、「開発の主導権を自分の手に取り戻す」ためのフィルターとして機能しています。

AIを魔法のブラックボックスにするのではなく、万年筆やノミのように「手応えのある道具」として使う。そんな開発スタイルも、悪くない選択肢だと思います。

AIエージェントをSSHで飛ばす:ローカルAI × MCPによる遠隔コード修正ワークフロー

研究開発業務でリモート計算機にSSH接続して作業している人は多いと思います。ただ、セキュリティなど制約があり、自律型コーディングAIを直接インストールできないケースがあります。

 

そこで、ローカルのAIエージェントに「リモート計算機へのSSHトンネル」をMCP(Model Context Protocol)経由で渡し、自律的なコード解析・修正を行わせる方法を紹介します。

 

手元AI × SSH踏み台のアーキテクチャ

リモート計算機側には特別なAIツールを一切インストールしません。手元のローカルマシンで動くAIエージェントに対し、「リモートのファイルを読み書きする」「リモートでコマンドを実行する」という最小限の道具(MCPブリッジ)を渡すだけです。

 

  • ローカル側: 自律型AIエージェントが思考・状況判断・コード修正方針の策定を担当

  • 接続部(MCP): ローカルの標準的なSSHクライアントをAIの「手足」としてバインド

  • リモート計算機: 通常通りSSHログインを受け付けるだけ(環境汚染ゼロ)

 

実際のデバッグ現場でのAIとの協調フロー

実際に発生した「Pythonスクリプトの検索・データ取得パイプラインが動かない」というトラブルを例に、AIへどのように指示を出して解決に導いたかの流れです。

 

  • Step 1: まず現状把握と原因究明だけを任せる(書き換え禁止)
    いきなり「直して」と丸投げするのではなく、まずはファイル構成と直近のエラーログを読ませ、「何が起きているか」「根本原因は何か」の仮説検証だけを指示します。
    AIはリモートの実機ログとコードを照らし合わせ、「外部ライブラリの仕様変更によるバックエンド不整合」と「クエリ生成ルールの過剰な絞り込み」という、実装とプロンプト両面の原因を特定しました。

  • Step 2: 修正方針の合意とピンポイント適用
    AIが提示してきた修正案(ライブラリ更新とバックエンド明示、ルール記述の微調整)を確認し、納得した段階で初めて「ファイルへの書き込み」を許可します。

  • Step 3: 非対話コマンドでの自律テスト検証
    修正後、リモート上で単体テストを実行させて正常終了を確認します。対話型メニュー(キーボード入力待ち)があるスクリプトの場合はタイムアウトするため、「非対話のテスト用関数を実行して」とテスト方法を指定するのがスムーズに進めるコツです。

 

この使い方のメリットと応用範囲

計算機本体に手を加えず、手元のAIから「リモートのシェルとエディタを遠隔操作させる」感覚で使えるため、以下のような理系・研究現場特有の課題と非常に相性が良いアプローチです。

 

  • GPUクラスタでの学習スクリプト調整: CUDA Out of Memory やデータローダーの例外発生時に、バッチサイズやテンソル形状の不整合をログから自動追跡させる

  • 環境分離された解析パイプラインの保守: 依存関係が厳密に固定された仮想環境(.venv やコンテナ)を壊さずに、最小限のパッチ適用とテストを回す

  • ログ解析から修正レポートの自動生成: トラブルシューティング完了後、何が原因でどこを変更したかの実施報告書をそのままAIに出力させて実験ノートや開発記録に残す

 

自律型AIを「コードを生成させるツール」としてだけでなく、「手元の安全な環境からリモート実機の調査・検証を代行させるエージェント」として使うことで、リモート環境でのデバッグ作業にかかる時間を大幅に圧縮できます。

 

自律型コード生成エージェントをLangGraphで開発

1. はじめに:コード生成AI開発

 

LLMにコードを書かせる際、AIとのチャット画面に「〇〇のコードを書いて」と1回投げるだけでは、以下のような壁にぶつかります。

  • 文法は合っているが、動かすと実行時エラーが出る

  • エッジケース(空データ、型違い、巨大な数値)で落ちる

  • 人間が毎回コピペしてテストし、手動で修正指示を出す手間がかかる

 

これを解決するのが、「AI自身にテストを実行させ、エラーが出たら自分で直させる」自律型コード生成エージェント です。そのフロー設計と、オーケストレーションツールとしてLangGraphを採用する方法を解説します。

 

2. 自律型コード生成のフロー設計(4段階の責務分離)

 

1つの巨大なプロンプトに「要件定義も設計もコードもテストも全部やって」と頼むと、LLMの思考がパンクしてバグが増えます。人間の開発チームと同じように 「責務の分離」 を行うフロー設計です。

Plaintext

[ユーザーの自然言語要望]
      ▼
│  Planner  │ 1. 要件定義:何を作るか、どんな例外を考慮すべきかを言語化
      ▼
│ Architect │ 2. 構造設計:ファイル構成、関数シグネチャ、テスト方針を決定
      ▼
│   Coder   │ 3. 実装:設計書に沿ってコードと自動テスト(pytest)を生成
      ▼
│  Sandbox  │ 4. 検証:安全な隔離環境(Docker)でテストを実行
  (テスト結果)
      ├── PASS ──► 【完成・成果物保存】
      └── FAIL
                    │ Debugger  │ 5. 自己治癒:エラーログを読んで修正
                         └──► [再度 Sandbox でテスト実行へ ]

なぜこの分割が効くのか?

  • Planner ⇄ Architect の分離: 「仕様の検討(何を作るか)」と「技術的な構造決定(どう作るか)」を分けることで、設計漏れを防ぎます。

  • テストファーストの思想: 実装と同時に「どうやってテストするか」を Architect と Coder に設計させることで、検証可能な成果物を作らせます。

  • 実行環境(Sandbox)の独立: AIに直接マシンを触らせず、使い捨ての Docker コンテナ内でテストを実行することで安全性を担保します。

  •  

3. LangGraph

 

従来の「直列パイプライン(A → B → C)」だけならシンプルなスクリプトでも組めますが、自律プログラミングには 「条件分岐」 と 「ループ(巡回)」 が不可欠です。

LangGraph が自律エージェントの構築に選ばれる理由は主に3点あります。

 

① 「状態(State)」の一元管理

各エージェントは独立していますが、「現在のコード群」「テスト実行ログ」「リトライ回数」 などの共通状態を1つの State(辞書)として受け渡しながら処理を進めます。

 

② 「条件分岐エッジ(Conditional Edge)」の簡潔さ

「テストが通ったら終了」「落ちたらデバッガーへ」「最大N回失敗したら諦める」といったフロー制御を、コード数行のルーター関数で直感的に定義できます。

 

③ 自己治癒ループ(サイクリックなグラフ構造)

従来の LangChain(LCEL)などのチェーン構造は一方向の有向非巡回グラフ(DAG)が基本でしたが、LangGraph は グラフの巡回(ループ) をネイティブにサポートしているため、「デバッグ → 再テスト → デバッグ」のサイクルを極めて美しく表現できます。

 

4. まとめ

 

  • 自律型コード生成は、「Planner / Architect / Coder / Debugger」の責務分離

  • テスト実行環境(Docker)とエラーフィードバック を組み合わせることで、AI自身にバグを修正させるループが作れる。

  • LangGraph を使うことで、状態管理とループ制御を含む複雑なマルチエージェントフローをシンプルに実装できる。

 

「AIにコードを書かせる」から「AI同士で協調させて動くソフトウェアを作らせる」ステップへ進むための基本パターンです。

 

議論系AIエージェントの構築:Role / Tool / Agent 指向の設計

AIに対話や議論をさせようとすると、「単に同意するイエスマン」になるか、「正論でマウントをとってくるウザい論破bot」になりえます。

今回、「良き壁打ち相手」として動作する議論系AIエージェントを構築した。

システム設計における「責務分離の概念」と「プロンプトパイプライン」の要点に絞って解説します。

1. ディベートAIの課題:なぜ「1つのLLM」では上手くいかないのか?

LLMに「私の主張に反対意見を出して」と一発で頼むと、どうしても表面的な反論や無礼な論破に陥ります。

対人議論で重要なのは「相手の論理構造を客観的に分解し、言外の前提をあぶり出し、受容した上で別の視点を提示する」という多段階の思考プロセスです。

これを単一のプロンプトで処理させると内部状態でタスクが競合するため、本システムでは3段階の「Role(役割)」に思考をパイプライン化しました。

2. 思考のパイプライン(3つのRole)

入力された主張に対し、以下の3ステップでデータを精査していきます。

[ユーザーの主張]
       │
   1. Analyzer (主張・隠れた前提の解剖)
       │  └─ JSON (結論・根拠・暗黙の前提・論理的課題)
       │
   2. Reframer (対抗論点・思考フレーム構築)
       │  └─ JSON (前提の揺さぶり・トレードオフ・代替アプローチ)
       │
   3. Responder (対話文合成)
       │  └─ テキスト (受容 ➔ 視点提示 ➔ オープンクエスチョン)
       │
[ユーザーへの返答]

Step 1: Analyzer(論理解剖)

ユーザーの出力を真に受けず、「結論 (Claim)」「根拠 (Reason)」「隠れた前提 (Implicit Assumption)」「論理的課題 (Vulnerabilities)」 に分解します。

特に「言及されていないが、論理が成り立つために暗黙のうちに仮定されている前提」を抽出するのが最大のポイントです。

Step 2: Reframer(リフレーミング)

Analyzerが引きずり出した「隠れた前提」や「論理的課題」に対し、「前提の揺さぶり」「トレードオフの提示」「条件限定(ハイブリッド案)」 の3方向から問いかけの切り口を設計します。

Step 3: Responder(応答合成)

論破を避け、認知の枠組み(Frame)を広げるため、応答文を「受容 (Acknowledge) ➔ 視点提示 (Pivot) ➔ 問いかけ (Question)」 の固定シークエンスで合成します。

3. アーキテクチャ設計:Role / Tool / Agent の分離

システムのメンテナンス性と拡張性を高めるため、概念を3つの層にカプセル化しています。

  • Role(.md):思考の定義

    「何者としてどう考えるか」を記述したMarkdownプロンプト。コードからプロンプトを完全に切り離すことで、Markdownエディタ上でプロンプトエンジニアリングに集中できる。

  • Tool(.py):実行手段

    LLM APIの呼び出し、レスポンスからのJSON抽出(ガードレール)、Web検索などの純粋な「機能」。

  • Agent(.py):実行主体

    特定領域のRoleとToolを組み合わせ、入出力を制御して自律動作するプログラム単位。

この「Role/Tool/Agent」の分離は、将来的にLangChainといった既存のフレームワークに移行する際にもそのまま移植できる汎用的な構造です。

4. 実際に動かしてみた手応え

例えば、「投資信託と個別株には優劣があり、戦略によって使い分けるべき」という主張を入力すると、以下のような思考プロセスが動きます。

  1. Analyzer: 「使い分けが必要」という主張の裏にある*「投資家が自身の戦略を明確に定義でき、管理コストを支払える」という隠れた前提*を発見。

  2. Reframer: *「初心者や多忙な投資家における学習コスト・手間というトレードオフ」*を問いかけの軸に設定。

  3. Responder: 主張に共感・受容を示した上で、「管理の手間や学習コストがメリットを上回る可能性」を優しく提示し、「初心者にとって選択基準となる要素は何か?」と問いかける。

単に「その通りですね」と同意するでもなく、「いや、初心者には投信一択です」と決めつけるでもない、相手に「次の思考」を促す良き壁打ち相手として機能させることができました。

まとめ

重いモデルをローカルで動かさずとも、軽量なLLM APIとPythonスクリプトの組み合わせで「思考の責務分離」を徹底するだけで、ここまでの対話を構築できます。

エージェント開発の本質は、複雑なフレームワークを使うことではなく「LLMにどの順番で、何を考えさせるか」というパイプライン設計にあると考える。

【LangChain】自作ツールをAIエージェントに組み込む

【LangChain】Tool登録の仕組みとLLMが関数を呼び出す(Tool Calling)内部の流れ

LangChainを使っているとよく目にする @tool デコレータ。

単に関数につけているだけに見えますが、「なぜLLMが適切なツールを選べるのか?」「チャットの文章からどうやって関数の引数を取り出しているのか?」 その裏側の仕組みは非常に洗練されています。

今回は、LangChainにおける Tool登録のメカニズム と、「ユーザーの入力 → LLMの判断 → ツール実行 → 結果の解釈」 という一連の流れを詳しく解説します。

1. Tool登録(@tool)の裏側で起きていること

Pythonの関数に @tool をつけると、LangChain内部では単なる関数から 「LLMが理解できる形式(JSON Schema)に変換されたオブジェクト」 へと拡張されます。

コード例

from langchain_core.tools import tool

@tool
def run_simulation(temp: float, pressure: float) -> str:
    """物理・熱シミュレーションを実行します。設定温度(temp: ℃)と設定圧力(pressure: kPa)を受け取ります。"""
    # (実際の処理)
    return result

このとき、LangChainは以下の3つの情報を自動的に抽出・生成しています。

  1. 関数名 (name): run_simulation

  2. 説明文 (description): docstring("""物理・熱...""")に書かれた文章

  3. 引数の型情報 (args_schema): 型ヒント(temp: float, pressure: float)から生成された JSON Schema

LLMに渡される実際のデータ(JSON Schema)

llm.bind_tools([run_simulation]) を呼び出したとき、LLM(ChatOllamaやOpenAI等)にはPythonコードそのものではなく、以下のような JSONデータ が送信されています。

{
  "name": "run_simulation",
  "description": "物理・熱シミュレーションを実行します。設定温度(temp: ℃)と設定圧力(pressure: kPa)を受け取ります。",
  "parameters": {
    "type": "object",
    "properties": {
      "temp": { "type": "number", "title": "Temp" },
      "pressure": { "type": "number", "title": "Pressure" }
    },
    "required": ["temp", "pressure"]
  }
}

💡 ポイント

LLMはプログラムを実行しているわけではなく、「自分が使えるツールの説明書(JSON)」 を読んで「どれを使うべきか」を判断しています。そのため、docstring(説明文)と型ヒントを丁寧に書くことがツール呼び出しの精度に直結します。

2. LLMがToolを呼び出して回答するまでの全体フロー

チャットのテキスト入力から最終回答が生成されるまで、やり取りは 2往復 行われます。

【1往復目:ツールの選定と引数抽出】
ユーザー: 「温度110度、圧力200kPaでシミュレーションして」
   │
   ▼
[ LLM ] (登録されたツール一覧のJSONを参照)
   │
   └─► 「run_simulation を temp=110, pressure=200 で呼び出すべき」と判断
       (テキスト回答ではなく Tool Call オブジェクトを返却)

【ローカル実行:Python側での処理】
[ Python ]
   │
   ├─► LLMからの指示に基づき run_simulation.invoke() を実行
   └─► シミュレーション結果 (JSON文字列など) を取得

【2往復目:結果の解釈と最終回答】
[ LLM ] (元々の会話 + ツール実行結果 を受け取る)
   │
   └─► 「シミュレーション結果によると、危険状態(DANGER)です...」
       (人間にわかりやすい最終文章を生成)

3. コードで辿る内部データの変化

実際にコードを動かしたとき、メッセージ(messages)がどのように変化していくかを見ていきます。

Step 1: LLMへの入力(1回目)

ユーザーからの自然言語プロンプトを HumanMessage として渡します。

messages = [
    HumanMessage(content="温度を 110 度、圧力を 200 kPa にしてシミュレーションを実行して")
]
response = llm_with_tools.invoke(messages)

Step 2: LLMからの返答(Tool Call の検知)

LLMは文章を返さず、「ツール呼び出し命令」 が入った AIMessage を返します。

# response (AIMessage) の中身
AIMessage(
    content="",  # テキスト本文は空
    tool_calls=[{
        "name": "run_simulation",
        "args": {"temp": 110.0, "pressure": 200.0}, # 文章から数値を自動抽出!
        "id": "call_12345678"                       # 呼び出し識別ID
    }]
)

Step 3: Python側でのツール実行と結果のセット

response.tool_calls が存在することを検知し、Python側で関数を実行して、その結果を ToolMessage として会話履歴に追加します。

# ツールの実行
tool_result = run_simulation.invoke(tool_call['args'])

# 履歴に追加(どの呼び出しIDに対する結果かを明記する)
messages.append(response) # AIMessage を追加
messages.append(
    ToolMessage(
        content=str(tool_result), # シミュレータから返ってきたJSONなどの結果
        tool_call_id=tool_call['id']
    )
)

この時点での messages の中身(会話履歴)は以下の3要素の配列になっています。

  1. HumanMessage: 「温度110度、圧力200kPaで〜」

  2. AIMessage: 「run_simulation(temp=110, pressure=200) を実行して」(ID: call_1234)

  3. ToolMessage: 「ID: call_1234 の結果は {"status": "DANGER", ...} でした」

Step 4: 実行結果をLLMに渡して最終回答を得る(2回目)

この履歴(messages)をそのままもう一度 LLM に渡します。

final_response = llm_with_tools.invoke(messages)
print(final_response.content)

LLMは「自分が要求したツール(ID: call_1234)の実行結果(ToolMessage)が返ってきた」と認識し、その結果(DANGERや警告文)を読解して「安全ではありません。温度と圧力が閾値を超えています」 という最終的な回答を生成してくれます。

まとめ

LangChainにおける Tool Calling の核心は以下の通りです。

  1. @tool は関数を JSON Schema に変換する仕組み: LLMはコードではなく「関数の説明書(JSON)」を読んで判断している。

  2. 会話履歴(messages)をバトンリレーする: HumanMessage ➔ AIMessage(ToolCall) ➔ ToolMessage(結果) の順で履歴を構築して再度LLMに渡すことで、結果を踏まえた解釈が可能になる。

この流れさえ押さえておけば、DB検索やWeb検索、API連携など、あらゆる自作ツールをAIエージェントに組み込めるようになります!

AIChat × Ollama × Gemini で作るハイブリッドRAG環境

日常のターミナル作業の中で「ローカルのメモやドキュメントを即座にAI検索したい」と思ったことはありませんか?

今回は、軽量で高速なCLI AIツールである AIChat と、ローカルLLM実行環境 Ollama、そして高性能な Gemini API を組み合わせた快適なRAG(Retrieval-Augmented Generation:検索拡張生成)環境の魅力と実際の使い勝手をご紹介します。

💡 この構成のメリット

  1. コスト&プライバシーの最適化

    • Embedding(ベクトル化): ローカルのOllama(nomic-embed-text)で行うため、トークン費用が一切かからず大容量ドキュメントも安心。

    • 回答生成(Chat): 高精度かつ高速な Gemini API を使用。

  2. CLI完結の快適操作

    • ターミナルから一発でRAGの初期化・切り替え・検索が可能。

  3. ローカルとクラウドのいいとこ取り

    • 機密情報のベクトル化はローカルCPUで安全に行い、文章生成のみ強力なクラウドLLMに任せるハイブリッド運用を実現。

🛠 システム構成

今回活用する環境のイメージは以下の通りです。

[ ローカルドキュメント (demo.txt) ]
             │
             ▼ (ベクトル化)
 [ Ollama (nomic-embed-text) ] ── (ローカルCPU動作)
             │
             ▼ (コンテキスト抽出)
     [ AIChat CLI ]
             │
             ▼ (プロンプト + コンテキスト送信)
   [ Google Gemini API ] ──► 高精度な回答を出力!

  • OS: Linux (Ubuntu想定)

  • LLM (Chat): Gemini API

  • Embedding: Ollama (nomic-embed-text)

  • CLI Tool: AIChat

🔍 RAGインデックスの活用と検証

インデックスの作成と切り替え

AIChatでは、ターミナル上のREPLモードからシンプルなコマンドでドキュメントのベクトル化や参照の切り替えが行えます。

default) .rag my_docs

対象のドキュメント(例: demo.txt)を指定するだけで、ローカル上で一瞬にしてベクトルデータベースがアクティブになります。

動作検証:セマンティック検索の威力を試す

例えば、直接「コーヒー」という単語が含まれていないドキュメントに対して、次のように質問してみます。

Q: 「コーヒーを飲みたいときはどこに行けばいいですか?」

AIChatは内部でコンテキストを検索し、ドキュメント内の「全自動の高級エスプレッソマシン」という表記を見つけ出して、以下のように正確な回答を生成してくれます。

A: 本社の3階にあるリフレッシュスペースに行けば、全自動の高級エスプレッソマシンが設置されています。マイボトルを持参してご利用ください。

💡 根拠(ソース)の追跡も可能

回答の根拠となったファイルや該当箇所を確認したい時は、AIChat内で .sources rag と入力するだけです。

default@my_docs) .sources rag
demo.txt (0-0, 0-1)

どのドキュメントのどのチャンクを参照したのかが透明化されているため、ハルシネーション(嘘の生成)のチェックも容易です。

📝 まとめ

AIChat と Ollama を組み合わせることで、「コストゼロのローカルEmbedding」 と 「クラウドLLMの圧倒的生成能力」 をイイトコ取りしたRAG環境が簡単に手に入ります。

ターミナル作業がメインのエンジニアにとって、自分のメモ帳や対応手順書をそのままAIナレッジ化できるこの構成は非常におすすめです。