👻

OpenHands+ローカルLLMをGitHub Actionsセルフホステッドランナーで

に公開

OpenHandsのGitHub Actionsを設定すると、GitHubのPRやIssueのコメントからOpenHandsへタスクの指示を出せるようになります。しかし、GitHubホステッドランナーでOpenHandsを動かす場合、自宅で動かしているローカルLLMを使用するのが難しいです(自宅のローカルLLMをネットからアクセスできるようにすればできますが、セキュリティ上それは望ましくありません)。

自宅のローカルLLMと同じマシンや同じLAN内でGitHub Actionsセルフホステッドランナーを動かせば、GitHub Actionsで動くOpenHandsから安全にローカルLLMを使用できます。以下、その手順について書きます。

なお、この記事ではローカルLLMにgpt-oss-120Bまたはgpt-oss-20Bを使用します。他のモデルでも手順はほぼ同じですが、一部gpt-oss特有の設定があります。

ローカルLLMの導入

LM Studioのインストール

https://lmstudio.ai/ からダウンロードしてインストールしてください。

gpt-ossのダウンロード

LM Studioを開いて、

  1. 虫眼鏡アイコンをクリック
  2. 「Model Search」をクリック
  3. テキストフィールドに「gpt-oss」とタイプ
  4. 「OpenAI's gpt-oss 120B」または「OpenAI's gpt-oss 20B」を選択
  5. 右下の「Download」ボタンをクリック

LM Studioをサーバモードで動かす

  1. LM Studioのウインドウ左端のアイコン、上から2番目をクリック
  2. 「Status: Stopped」と書かれているところのスイッチをオンにする
  3. 「Settings」をクリックし、「Serve on Local Network」をオンにする
  4. ウインドウ上部の「Select a model to load」をクリック
  5. 「OpenAI's gpt-oss 20B」または「OpenAI's gpt-oss 120B」を選択
  6. 「Context Length」を適当に大きくしておく
  7. 「Load Model」をクリック

curlで何か適当な質問をして動作確認します。

curl http://192.168.0.167:1234/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-oss-120b",
    "messages": [
      { "role": "user", "content": "あなたの名前は?" }
    ]
  }'

IPアドレスとポート番号は、LM Studioのウインドウ内の Reachable at: http://192.168.0.167:1234 のように書かれているものを使用してください。gpt-oss-20Bを使用する場合は "model" の値を "openai/gpt-oss-20b" にします。

GitHub Actionsセルフホステッドランナーの準備

https://docs.github.com/ja/actions/how-tos/manage-runners/self-hosted-runners/add-runners

上記ドキュメントに書かれている通りですが、リポジトリの画面で Settings → Actions → Runners と進み「new self-hosted runner」をクリックします。

すると、次のような画面が表示されるので、「Download」と「Configure」に書かれているコマンドをセルフホステッドランナーを動かすマシン上で実行すると、セルフホステッドランナーが立ち上がりGitHubに接続されます。

OpenHands GitHub Actionsの設定

https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/resolver/README.md#openhands-github-gitlab--bitbucket-issue-resolver-

上記ドキュメントに書かれている手順に従って設定を行います。

パーソナルアクセストークンの作成

GitHubのパーソナルアクセストークンを作成し、"contents", "issues", "pull requests", "workflows" の4つに読み書き両方を許可します。

openhands-resolver.ymlをコピー

openhands-resolver.yml をリポジトリの .github/workflows/ にコピーします。

リポジトリのパーミッション設定

Settings → Actions → General と進み、Workflow permissions を「Read and write permissions」にします。また「Allow GitHub Actions to create and approve pull requests」を有効にします。

secretsとvariablesの設定

Settings → Secrets and variables → Actions と進み、secrets には次の4つを設定します。

  • LLM_API_KEY
    • ローカルLLMを使用するので適当なダミー値を設定しておきます
  • LLM_BASE_URL
    • LM Studioの画面上で Reachable at: http://192.168.0.167:1234 のように表示されているURLに /vi/ を付けて http://192.168.0.167:1234/vi/ のようにして設定します
  • PAT_TOKEN
    • 先ほど作成したパーソナルアクセストークンです
  • PAT_USERNAME
    • 先ほど作成したパーソナルアクセストークンのGitHubユーザー名です

variables には次の2つを設定します。

  • LLM_MODEL
    • openai/gpt-oss-120b または openai/gpt-oss-20b(他のモデルを使用する場合はそのモデル名)
  • TARGET_RUNNER
    • self-hosted

gpt-oss特有の設定

別の記事に、OpenHandsでgpt-ossを正常に動作させるために必要な設定を書きました。

https://zenn.dev/sonicmoov/articles/370f904b9787b3

しかし、GitHub ActionsでOpenHandsを使う場合は、この記事のようにコマンド引数で設定することができません。workflowファイルを書き換えて設定します。

https://github.com/All-Hands-AI/OpenHands/blob/main/.github/workflows/openhands-resolver.yml を名前を変えて .github/workflows/ にコピーします。先ほどコピーしたファイルと同じ名前で紛らわしいですが別のもののため、名前を変えてください。

コピーしたファイルを、次のように編集します。

       PAT_USERNAME:
         required: false
 
-  issues:
-    types: [labeled]
-  pull_request:
-    types: [labeled]
-  issue_comment:
-    types: [created]
-  pull_request_review_comment:
-    types: [created]
-  pull_request_review:
-    types: [submitted]
+  #issues:
+  #  types: [labeled]
+  #pull_request:
+  #  types: [labeled]
+  #issue_comment:
+  #  types: [created]
+  #pull_request_review_comment:
+  #  types: [created]
+  #pull_request_review:
+  #  types: [submitted]
 
 permissions:
   contents: write
@@ -84,6 +84,8 @@ jobs:
         )
       )
     runs-on: "${{ inputs.runner || 'ubuntu-latest' }}"
+    env:
+      LLM_NATIVE_TOOL_CALLING: "true"
     steps:
       - name: Checkout repository
         uses: actions/checkout@v4

最初にコピーした方の openhands-resolver.yml を次のように編集します。

@@ -19,7 +19,7 @@

 jobs:
   call-openhands-resolver:
-    uses: All-Hands-AI/OpenHands/.github/workflows/openhands-resolver.yml@main
+    uses: <user>/<repo>/.github/workflows/<filename>.yml@main
     with:
       macro: ${{ vars.OPENHANDS_MACRO || '@openhands-agent' }}
       max_iterations: ${{ fromJson(vars.OPENHANDS_MAX_ITER || 50) }}

<user>、<repo> はGitHubユーザー名とリポジトリ名です。<filename>は、名前を変えてコピーした方のファイル名です。

OpenHandsにタスクを指示する

以上で準備は完了です。OpenHandsにタスクを指示する基本的な流れは次のようになります。

  1. リポジトリにIssueを作成する
  2. Issueに fix-me ラベルを付けるか、@openhands-agent から始まるコメントをする
  3. OpenHandsがタスクを実行しPRを作成するので、それを確認する
  4. PRにコメントする
  5. PRに fix-me ラベルを付けるか、@openhands-agent から始まるコメントをする

しかし、必ずしもこの流れに従う必要はありません。すでにあるIssueやPRの途中から fix-me ラベルを付けたり @openhands-agent で始まるコメントをすることもできます。

fix-me ラベルと @openhands-agent の違い

  • fix-me ラベルはIssue全体またはPR全体に対処する指示となります
  • @openhands-agent で始まるコメントは、そのコメントの内容とIssueまたはPRの説明文のみを考慮してタスクを行う指示となります
株式会社ソニックムーブ

Discussion