<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Ornith on Roguer</title>
        <link>https://roguer.info/tags/ornith/</link>
        <description>Recent content in Ornith on Roguer</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>ja</language>
        <lastBuildDate>Sat, 11 Jul 2026 11:20:00 +0900</lastBuildDate><atom:link href="https://roguer.info/tags/ornith/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>【Hermesシリーズ #1】ローカルLLM Ornith-1.0をHermes AgentからDockerで安全に使う</title>
            <link>https://roguer.info/p/hermes-1-ornith-hermes-agent-docker/</link>
            <pubDate>Sat, 11 Jul 2026 11:20:00 +0900</pubDate>
            <guid>https://roguer.info/p/hermes-1-ornith-hermes-agent-docker/</guid>
            <description>&lt;img src=&#34;https://roguer.info/&#34; alt=&#34;Featured image of post 【Hermesシリーズ #1】ローカルLLM Ornith-1.0をHermes AgentからDockerで安全に使う&#34; /&gt;&lt;h2 id=&#34;はじめに&#34;&gt;はじめに&#xA;&lt;/h2&gt;&lt;p&gt;HX370(CachyOS)上でローカルLLMをいくつも動かしてきたが、今回は「自己スキャフォールディング型」という触れ込みのエージェント特化モデル &lt;strong&gt;Ornith-1.0&lt;/strong&gt; と、自律実行クライアント &lt;strong&gt;Hermes Agent&lt;/strong&gt; を組み合わせて動かしてみた。単に動かすだけでなく、エージェントにファイル操作・コード実行・ブラウザ操作を任せる以上、&lt;strong&gt;安全に隔離した状態で使う&lt;/strong&gt;ことを最初から意識して構成を組んだので、その一連の流れを記録しておく。&lt;/p&gt;&#xA;&lt;p&gt;この構成は今後もいろいろ試していきたいので、「Hermesシリーズ」として何回かに分けて書いていく予定。今回はその第1回として、導入からコンテナで動くところまでをまとめる。&lt;/p&gt;&#xA;&lt;h2 id=&#34;何を組み合わせたか&#34;&gt;何を組み合わせたか&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Ornith-1.0&lt;/strong&gt;: Gemma 4 / Qwen 3.5 をベースにRLで再学習された、エージェント用途特化のOSSモデル(MIT license)。9B/31B/35B MoE/397B MoEのサイズ展開があり、タスクを受け取るとまず自分でPythonの実行ハーネスを書き、それを使ってタスクを解く「self-scaffolding」という設計が特徴。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Hermes Agent&lt;/strong&gt;: Nous Researchによる自律エージェントクライアント。モデル本体は持たず、OpenAI互換のどんなエンドポイントにも接続できる。スキル・記憶を蓄積しながら学習していく「学習ループ」を軸に据えている。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;つまり構成としては「Ornithをlama.cppでローカルサーブし、Hermes Agentがそこに接続してタスクを実行する」という形になる。&lt;/p&gt;&#xA;&lt;h2 id=&#34;環境&#34;&gt;環境&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;CPU: Ryzen AI 9 HX370 (Radeon 890M iGPU)&lt;/li&gt;&#xA;&lt;li&gt;OS: CachyOS (linux-cachyos kernel)&lt;/li&gt;&#xA;&lt;li&gt;推論バックエンド: Vulkan / llama.cpp&lt;/li&gt;&#xA;&lt;li&gt;モデル: &lt;code&gt;ornith-1.0-35b-Q4_K_M.gguf&lt;/code&gt; (35B MoE, Q4_K_M量子化, 約21GB)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;既存でQwen3.5-35B-A3B(11434)、LFM2-24B-A2B(11435)、GLM-4.7-Flash(11436)をsystemdサービスとして動かしている環境なので、そこに新しくOrnithを追加する形になる。&lt;/p&gt;&#xA;&lt;h2 id=&#34;ornithをlamacppでサーブする&#34;&gt;Ornithをlama.cppでサーブする&#xA;&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;llama-server -m /home/hideaki/src/llama.cpp/models/ornith-1.0-35b-Q4_K_M.gguf &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --host 0.0.0.0 &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --port &lt;span class=&#34;m&#34;&gt;11434&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -c &lt;span class=&#34;m&#34;&gt;131072&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --jinja&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;いくつかハマったポイントがあるので共有しておく。&lt;/p&gt;&#xA;&lt;h3 id=&#34;--jinjaは必須&#34;&gt;&lt;code&gt;--jinja&lt;/code&gt;は必須&#xA;&lt;/h3&gt;&lt;p&gt;llama.cppでツール呼び出し(tool-calling)を機能させるには&lt;code&gt;--jinja&lt;/code&gt;フラグが必要。これを忘れるとHermes側からのfunction callingがそもそも動かない。&lt;/p&gt;&#xA;&lt;h3 id=&#34;--host-0000にしないとコンテナから届かない&#34;&gt;&lt;code&gt;--host 0.0.0.0&lt;/code&gt;にしないとコンテナから届かない&#xA;&lt;/h3&gt;&lt;p&gt;Hermes Agent自体をDockerコンテナで動かす予定だったので、コンテナからホストのllama-serverに接続する必要がある。しかしllama-serverはデフォルトで&lt;code&gt;127.0.0.1&lt;/code&gt;のみをlistenするため、コンテナ側の&lt;code&gt;host.docker.internal&lt;/code&gt;経由の接続がそもそも届かない。&lt;code&gt;--host 0.0.0.0&lt;/code&gt;を明示することで解決した。&lt;/p&gt;&#xA;&lt;p&gt;ただし&lt;code&gt;0.0.0.0&lt;/code&gt;でlistenすると同一LAN内の他端末からもアクセスできてしまうため、ufw/nftablesでDockerブリッジのサブネット(&lt;code&gt;172.17.0.0/16&lt;/code&gt;)以外からのアクセスを遮断しておくのが安全。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ufw allow from 172.17.0.0/16 to any port &lt;span class=&#34;m&#34;&gt;11434&lt;/span&gt; proto tcp&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;コンテキスト長の要件に注意&#34;&gt;コンテキスト長の要件に注意&#xA;&lt;/h3&gt;&lt;p&gt;Hermes Agentは「64,000トークン未満のコンテキストを持つモデルは起動時に拒否する」という制約がある。デフォルトのままだとllama.cppは32768(32K)でlistenしてしまい、この最低要件を満たさない。&lt;code&gt;-c&lt;/code&gt;オプションで明示的に引き上げる必要がある(Ornithの学習時コンテキストは256Kまであるので、メモリと相談しつつ128K程度まで上げた)。&lt;/p&gt;&#xA;&lt;h2 id=&#34;dockerでhermes-agentを動かす&#34;&gt;DockerでHermes Agentを動かす&#xA;&lt;/h2&gt;&lt;h3 id=&#34;なぜdockerか&#34;&gt;なぜDockerか&#xA;&lt;/h3&gt;&lt;p&gt;Ornithの最大の特徴である「self-scaffolding」は、裏を返せば&lt;strong&gt;モデルが自分でPythonコードを書いて実行する&lt;/strong&gt;ということでもある。ホストに直接その権限を与えるのは避けたいので、コンテナに閉じ込めて動かすことにした。&lt;/p&gt;&#xA;&lt;p&gt;Hermes公式のDockerイメージは都合よく、コアの実行ファイル(&lt;code&gt;/opt/hermes&lt;/code&gt;)がroot所有・読み取り専用で、書き込み可能なのは&lt;code&gt;/opt/data&lt;/code&gt;(ホストの&lt;code&gt;~/.hermes&lt;/code&gt;にマウント)だけという設計になっている。つまり「ディレクトリを絞る」という要件がイメージの構造自体で半分満たされている。&lt;/p&gt;&#xA;&lt;h3 id=&#34;つまずいた点-dockerのネットワーキングが壊れていた&#34;&gt;つまずいた点: Dockerのネットワーキングが壊れていた&#xA;&lt;/h3&gt;&lt;p&gt;セットアップ中、コンテナ起動時に以下のエラーに遭遇した。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;failed to set up container networking: failed to create endpoint ... on network bridge:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;failed to add the host (vethXXXXX) &amp;lt;=&amp;gt; sandbox (vethYYYYY) pair interfaces: operation not supported&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;原因を辿ると、&lt;code&gt;pacman -Syu&lt;/code&gt;でカーネル(&lt;code&gt;linux-cachyos&lt;/code&gt;)が更新された直後にまだ再起動しておらず、稼働中のカーネル(&lt;code&gt;7.0.12-1-cachyos&lt;/code&gt;)に対応するモジュールディレクトリが&lt;code&gt;/lib/modules/&lt;/code&gt;からすでに削除されていた、というオチだった。&lt;code&gt;veth&lt;/code&gt;・&lt;code&gt;bridge&lt;/code&gt;・&lt;code&gt;br_netfilter&lt;/code&gt;のいずれもロードできず、Dockerのbridgeネットワーク作成(veth pair作成)が失敗していた。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 稼働中カーネルとディスク上のモジュールが食い違っていないか確認&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;uname -r&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ls /lib/modules/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;再起動一発で解決。CachyOSはローリングリリースでカーネル更新が頻繁なので、「Dockerが急に動かなくなったら、まずカーネル更新後に再起動を忘れていないか確認する」というのは今後の教訓にしたい。&lt;/p&gt;&#xA;&lt;h3 id=&#34;データディレクトリを作ってセットアップウィザードを実行&#34;&gt;データディレクトリを作ってセットアップウィザードを実行&#xA;&lt;/h3&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p ~/.hermes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker run -it --rm &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --add-host&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;host.docker.internal:host-gateway &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -v ~/.hermes:/opt/data &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  nousresearch/hermes-agent setup&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;LinuxのDockerでは&lt;code&gt;host.docker.internal&lt;/code&gt;がデフォルトで名前解決されないため、&lt;code&gt;--add-host=host.docker.internal:host-gateway&lt;/code&gt;を明示的に付ける必要がある。&lt;/p&gt;&#xA;&lt;h3 id=&#34;セットアップウィザードでの選択&#34;&gt;セットアップウィザードでの選択&#xA;&lt;/h3&gt;&lt;p&gt;対話式のウィザードが進み、以下を選択した。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;セットアップモード&lt;/strong&gt;: &lt;code&gt;Full setup&lt;/code&gt;(Nous Portalのクラウドではなく、自前のOrnithエンドポイントに接続したいため)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;プロバイダ&lt;/strong&gt;: &lt;code&gt;Custom endpoint (enter URL manually)&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Base URL&lt;/strong&gt;: &lt;code&gt;http://host.docker.internal:11434/v1&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;API互換モード&lt;/strong&gt;: &lt;code&gt;Chat Completions&lt;/code&gt;(llama.cppの標準的な&lt;code&gt;/chat/completions&lt;/code&gt;実装に合わせて明示指定)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;モデル名&lt;/strong&gt;: &lt;code&gt;curl http://localhost:11434/v1/models&lt;/code&gt;で確認した実際の&lt;code&gt;id&lt;/code&gt;(今回はファイルパスそのものだった)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;コンテキスト長&lt;/strong&gt;: &lt;code&gt;131072&lt;/code&gt;(実際にllama-serverへ指定した値と一致させる)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;ツール構成&lt;/strong&gt;: Web検索・TTS・画像生成・Cron・Computer Useはオフ、ブラウザ・ファイル操作・コード実行・メモリ等は有効&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;ブラウザプロバイダ&lt;/strong&gt;: &lt;code&gt;Local Browser&lt;/code&gt;(無料・APIキー不要のヘッドレスChromium、コンテナ内で完結)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;terminal.backend&lt;/strong&gt;: &lt;code&gt;Local&lt;/code&gt;のまま&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;最後の「terminal.backend」の選択は少し引っかかったポイントなので補足しておく。&lt;/p&gt;&#xA;&lt;h3 id=&#34;terminalbackendはdockerではなくlocalを選ぶ&#34;&gt;terminal.backendは&lt;code&gt;Docker&lt;/code&gt;ではなく&lt;code&gt;Local&lt;/code&gt;を選ぶ&#xA;&lt;/h3&gt;&lt;p&gt;一見「Dockerで隔離したい」なら&lt;code&gt;terminal.backend: Docker&lt;/code&gt;を選びたくなるが、これは避けた。Hermes自体を既にDockerコンテナの中で動かしている状態でさらに&lt;code&gt;Docker&lt;/code&gt;を選ぶと、&lt;strong&gt;コンテナの中からさらに別のコンテナを起動する&lt;/strong&gt;、いわゆるDocker-in-Docker構成になってしまう。これを実現するには&lt;code&gt;docker.sock&lt;/code&gt;をコンテナにマウントする必要があり、それは実質的にそのコンテナへホスト相当の権限を渡すことを意味する。これまで積み上げてきた「権限を絞る」という方針と矛盾してしまうため、&lt;code&gt;Local&lt;/code&gt;のままにした。Hermes自体がコンテナの中にいる以上、外側のコンテナが既に隔離層として機能している。&lt;/p&gt;&#xA;&lt;h2 id=&#34;動作確認&#34;&gt;動作確認&#xA;&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker run -it --rm &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  --add-host&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;host.docker.internal:host-gateway &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -v ~/.hermes:/opt/data &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  nousresearch/hermes-agent&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;起動すると、モデル名(&lt;code&gt;ornith-1.0-35b-Q4_K_M&lt;/code&gt;)、26個のツール、68個のスキルが認識された状態でCLIが立ち上がった。簡単な会話とブラウザ操作タスクを試したところ、問題なく動作した。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;⚕ ornith-1.0-35b-Q4_K_M │ ctx -- │ [░░░░░░░░░░] -- │ 2s │ ⏲ 0s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;今後の課題&#34;&gt;今後の課題&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;起動時に&lt;code&gt;tirith security scanner enabled but not available — command scanning will use pattern matching only&lt;/code&gt;という警告が出ている。危険コマンドの検知が単純なパターンマッチングのみにフォールバックしている状態なので、原因を追って本来のスキャナを有効化したい。&lt;/li&gt;&#xA;&lt;li&gt;監査ログ(&lt;code&gt;audit_log_enabled&lt;/code&gt;)をまだ有効化していないので、次に触るタイミングで設定する。&lt;/li&gt;&#xA;&lt;li&gt;実際のChromeブラウザ(ログイン済みセッション)への接続や、Google Workspace連携も試してみたいが、認証情報の権限を絞った専用アカウントを用意してから進める予定。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;途中で踏んだDockerネットワーキング障害の原因が「カーネル更新後の再起動忘れ」だったのは若干拍子抜けだったが、ローリングリリース環境では起こりがちな話。同じ轍を踏む人の参考になれば。次回はセキュリティスキャナ周りを掘っていく予定。&lt;/p&gt;&#xA;</description>
        </item></channel>
</rss>
