🔫

ローカルLLM用の簡易版スキルとしてトリガーという機能を考えてみました

に公開

最近、DGX Spark上でローカルLLMをどれだけ使えるかという実験をしています。

https://zenn.dev/karaage0703/articles/fcca40c614dffd

ローカルLLM、単純におしゃべりをさせたり、単一の作業をさせるだけなら、使える場面も増えてきたのですが、Claude CodeやCodexみたいに、スキルを使って汎用的に様々な作業をさせようとすると、うまくいかないことが多いです(自分のユースケースでは)。

なんか、もうちょっとうまくできないかなーと考えてみたのが、トリガーという機能です。

言ってしまえば、簡易版のスキルでtriggersディレクトリにシェルスクリプトを置くだけで、LLMが使えるカスタムツールを追加できるというものです。スキルほどの柔軟性はないですが、ローカルLLMでもある程度柔軟に機能を追加する仕組みが考えられないかなと思い考えたものです。

スキルとツールユースの中間みたいなものって、ありそうで(調べた限りでは)なかったので名前をつけてみました。ただ、もし全く同じものあったらごめんなさい。気づいてないだけです。

ちなみに「トリガー」という名前は、ロックバンドLUNA SEAの「ROSIER」から取りました。

I am the trigger

ROSIERの歌詞にある「I am the trigger」——自分自身が引き金となって運命を切り拓く、という力強いメッセージ。トリガー機能も同じように、ユーザーの言葉が引き金となってシェルスクリプトが発動し、AIアシスタントの可能性を広げます※

※ LLMの妄言(ハルシネーション)です

トリガーの試行

トリガーの効果を確認するために、自作のAIアシスタントフレームワークxangiにトリガー機能を組み込みました。

xangiは、Claude Code / Codex / Gemini CLI / Local LLMをバックエンドに、DiscordやSlackから利用できるAIアシスタントフレームワークです。

以下のような形でトリガーが動作します。

  1. xangi起動時にワークスペースのtriggers/をスキャンしてツール定義を自動生成
  2. LLMにカスタムツールとして登録
  3. ユーザーが関連する質問をすると、LLMがFunction Callingでトリガーを呼び出し
  4. handler.shが実行され、結果がLLMに返される
  5. LLMが結果を踏まえて自然な文章で応答

スキルと同じ用に、ユーザーがAIと話しかけると、たまにトリガーが発動してツールが使われるという感じです。スキルほどの柔軟性ない代わりに、確実に再現性高くタスクを実行できます。

トリガーセットアップ

ディレクトリ構成

ワークスペースにtriggers/ディレクトリを作成し、コマンドごとにサブディレクトリを配置します。

workspace/
  triggers/
    weather/
      trigger.yaml    # トリガー定義
      handler.sh      # 実行スクリプト
    technews/
      trigger.yaml
      handler.sh

trigger.yaml

各トリガーにはtrigger.yamlという定義ファイルが必要です。

name: weather
description: "天気予報を取得する(例: weather 名古屋)"
handler: handler.sh
フィールド 必須 説明
name Yes ツール名(LLMがFunction Callingで呼ぶ名前)
description No ツールの説明(LLMに渡されるツール定義に含まれる)
handler Yes 実行スクリプトのファイル名

descriptionはLLMがツールを選択する判断材料になるので、具体例を含めて書くのがおすすめです。

handler.shの仕様

  • ワークスペースルートをcwdとしてbash handler.sh [引数...]で実行されます
  • 引数はLLMがFunction Callingで渡したargsをスペース区切りで渡します
  • stdoutの内容がLLMに返され、LLMが自然な文章で応答を生成します

トリガーの実例

ai-assistant-workspaceに含まれているトリガーの実例を紹介します。

1. 天気予報トリガー

最もシンプルな例です。wttr.inというCLI向け天気サービスを呼ぶだけです。

trigger.yaml:

name: weather
description: "天気予報を取得する(例: weather 名古屋)"
handler: handler.sh

handler.sh:

#!/bin/bash
CITY="${1:-Tokyo}"
curl -s "wttr.in/${CITY}?format=3&lang=ja" 2>/dev/null || echo "天気情報の取得に失敗しました"

ユーザーが「名古屋の天気教えて」と話しかけると、LLMがこのトリガーを呼び出して天気情報を取得し、自然な文章で教えてくれます。

2. テックニューストリガー

RSSフィードから最新のテックニュースを取得するトリガーです。

trigger.yaml:

name: technews
description: "最新テックニュースを取得する"
handler: handler.sh

handler.sh:

#!/bin/bash
curl -s "https://karaage0703.github.io/tech-blog-rss-feed/feeds/rss.xml" 2>/dev/null | \
  uv run --python 3.12 python -c "
import sys, xml.etree.ElementTree as ET
tree = ET.parse(sys.stdin)
items = tree.findall('.//item')[:5]
for item in items:
    title = item.find('title').text if item.find('title') is not None else ''
    link = item.find('link').text if item.find('link') is not None else ''
    print(f'- {title}\n  {link}\n')
" 2>/dev/null || echo "ニュースの取得に失敗しました"

curlでRSSを取得し、Pythonでパースして上位5件を表示しています。uv runを使うことで、仮想環境を気にせずPythonスクリプトを実行できます。

とりあえずxangiでトリガーを使ってみる

手元の環境でとりあえずトリガーを試してみたいという人は、xangiのquickstartスクリプトを使用してみてください。

xangiはOpenClaw的なAIアシスタントソフトなので、インストールしたPCを操作しますが、Dockerを使うことで、環境を隔離して実行することができます。そのままセットアップするより、いくぶん安全に使えます(絶対ではないです)。

Dockerをセットアップした環境で、以下コマンドを実行するとすぐxangiとローカルLLMがセットアップできます。環境はLinux OS(WSL2でも多分大丈夫)を想定しています。APIは一切不要です。

git clone https://github.com/karaage0703/xangi.git
cd xangi
./quickstart.sh

Macの場合は、最後のquickstartコマンドに以下のようにオプションをつけたらOKです。

./quickstart.sh --mac

Dockerに関しては、以下記事を参照ください。

https://zenn.dev/mkj/articles/33befbaf38c693

実行して localhost:18888にアクセスするとブラウザでxangiを使うことができます。以下はテックニュースのトリガーを使っている様子です。

今のところはデフォルトでGemma4:26bを使っています。ある程度のスペックのPCなら動くと思いますが、動かなかったら適宜変更してください。

まとめ

ローカルLLMをエージェンティックに使うために、トリガーというアイデアを考えてみました。「ローカルLLMだと、全然スキルとか使えないじゃん!」って思っている人は試してみてもよいのではないかと思います。

ただ、ちょうど最近出たQwen3.6-35B-A3Bの性能高く、スキルも結構いい感じに使ってくれるので、すぐにトリガーなんて中途半端なものはいらなくなりそうな気配も感じています(なので、この記事も今のうちに出しておくことにしました)。

性能低いLLMにうまくツールを使わせるために、LLMが簡単に使えるツールを用意する、みたいな考え方は今後も重要なんじゃないかなと思ったりしていますので、何かの参考になれば幸いです。

xangiについて詳しく知りたい人は、以下参考にしてみてください。

https://karaage.hatenadiary.jp/entry/2026/02/25/073000

参考

Discussion