Hermes Agentなるものに触れてみたよ

Artificial Intelligence

最近いろいろささやかれてる中で、Hermes Agentというツールがあり、要はここ最近登場してるClaudeCodeだったり、Codexだったりーなものに似たようなものってざっくり理解してるのだけど、これを投入してみたなど。

GitHub – NousResearch/hermes-agent: The agent that grows with you
The agent that grows with you. Contribute to NousResearch/hermes-agent development by creating an account on GitHub.

冒頭文を日本語に訳してみる(Hy-MT2-1.8Bで翻訳)と

Nous Researchが開発した自己改善型のAIエージェントです。内蔵された学習ループを持つ唯一のエージェントであり、経験からスキルを生み出し、使用中にそれを改善し、知識を保持するために自らを促進し、過去の会話を検索し、セッション間で自分が誰であるかに関する深いモデルを構築します。無料で利用できるVPSやGPUクラスター、サーバーレスインフラ上で実行できます。ノートパソコンに依存することはなく、クラウドVM上で動作している間はTelegramから通信することができます。

必要なモデルを自由に使用できます——Nous Portal、OpenRouter(200以上のモデル)、NovitaAI(Model API、エージェントサンドボックス、GPUクラウド向けのAIネイティブクラウド)、NVIDIA NIM(Nemotron)、Xiaomi MiMo、z.ai/GLM、Kimi/Moonshot、MiniMax、Hugging Face、OpenAI、または自分のエンドポイントです。Hermesモデルを切り替えるだけでよく、コード変更もロックインも必要ありません。

ある一定のセキュリティリスク回避実装が行われてるようですし、ライセンスもMITのようですので、扱いに禁止シチュエーションがあるというわけでもないようですんで、ちょっと触れてみようかなという感覚で入れてます。

インストール

インストール自体は以下のコマンドで実行可能のようです。あ、当方Ubuntu 24.04環境ですんで。

curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash

すると、コンテンツがダウンロードされてシェルスクリプトが実行され、以下のようなところから対話型でのセットアップになります。クイックセットアップでやります。

How would you like to set up Hermes?
  ↑↓ navigate  ENTER/SPACE select  ESC cancel

  (●) Quick setup ? provider, model & messaging (recommended)
   () Full setup ? configure everything

LLMプロバイダの選択を求められます。今回はローカルインストールしてますllama.cppのOpenAI互換APIを使います。
以下のように Custom endpoint を選択するとよいようです。

Select provider:
  ↑↓ navigate  ENTER/SPACE select  ESC cancel

   () Nous Portal (Nous Research subscription)
   () OpenRouter (100+ models, pay-per-use)
   () NovitaAI (AI-native cloud: Model API, Agent Sandbox, GPU Cloud)
   () LM Studio (local desktop app with built-in model server)
   () Anthropic (Claude models ? API key or Claude Code)
   () OpenAI Codex
   () OpenAI API (api.openai.com, API key)
   () Qwen Cloud / DashScope Coding (Qwen + multi-provider)
   () xAI Grok OAuth (SuperGrok / Premium+)
   () Xiaomi MiMo (MiMo-V2.5 and V2 models ? pro, omni, flash)
   () Tencent TokenHub (Hy3 Preview ? direct API via tokenhub.tencentmaas.com)
   () NVIDIA NIM (Nemotron models ? build.nvidia.com or local NIM)
   () GitHub Copilot (uses GITHUB_TOKEN or gh auth token)
   () GitHub Copilot ACP (spawns `copilot --acp --stdio`)
   () Hugging Face Inference Providers (20+ open models)
			:
			:
			:
   () Alibaba Cloud Coding Plan ? dedicated coding tier
   () custom (direct API)
  (●) Custom endpoint (enter URL manually)
   () Configure auxiliary models...

すると、エンドポイントのURLを求められますので、それに応じてURLを入力します。

 Inference Provider
  Choose how to connect to your main chat model.
     Guide: https://hermes-agent.nousresearch.com/docs/integrations/providers


Custom OpenAI-compatible endpoint configuration:

API base URL [e.g. https://api.example.com/v1]: http://127.0.0.1:8001/v1

API Keyは入力せずにそのままEnterします。

API key [optional]:

どういうタイプのAPIか選択を求められます。どうやら自動検出もできたようなのですが、私の場合うまくいかずに Chat Completions を選択しています。

Select API compatibility mode:
  1. Auto-detect [current]
     Use Hermes URL heuristics; best for standard OpenAI-compatible endpoints.
  2. Chat Completions
     Use /chat/completions for standard OpenAI-compatible servers.
  3. Responses / Codex
     Use /responses for Codex-compatible tool-calling backends.
  4. Anthropic Messages
     Use /v1/messages for Anthropic-compatible endpoints.
Choice [1-4, Enter to keep current/detected]: 2

自動検出したらスキップできそうですが、以下のようにモデル名や最大コンテキスト長、APIサーバの表示名、などを設定していきます。

  API mode: chat_completions
Model name (e.g. gpt-4, llama-3-70b): Qwen3.5-9B
Context length in tokens [leave blank for auto-detect]: 65535
Display name [Local (127.0.0.1:8001)]: LocalLLM
Default model set to: Qwen3.5-9B (via https://127.0.0.1:8001/v1)
  ?? Saved to custom providers as "LocalLLM" (edit in config.yaml)

それが終わると次はターミナルのバックエンドセットアップです。
本来だと、おそらくDockerとかのほうがいいんでしょうね。あるいは、どっか別に実行専用ホストを組んでその中で動かすか。今回あまり難しいプログラムを組んだりしない想定で以下の通り設定しました。

 Terminal Backend
  Choose where Hermes runs shell commands and code.
  This affects tool execution, file access, and isolation.
     Guide: https://hermes-agent.nousresearch.com/docs/developer-guide/environments

Select terminal backend:
  ↑↓ navigate  ENTER/SPACE select  ESC cancel

   () Local - run directly on this machine (default)
   () Docker - isolated container with configurable resources
   () Modal - serverless cloud sandbox
   () SSH - run on a remote machine
   () Daytona - persistent cloud development environment
   () Vercel Sandbox - cloud microVM with snapshot filesystem persistence
   () Singularity/Apptainer - HPC-friendly container
  (●) Keep current (local)

ほかにも以下のような設定が。

? Applied recommended defaults:
    Max iterations: 90
    Tool progress: all
    Compression threshold: 0.50
    Session reset: inactivity (1440 min) + daily (4:00)
    Run `hermes setup agent` later to customize.

最大会話ターン数やToolプログレスは表示するかどうかとか、コンテキスト圧縮の閾値とかセッションの最大時間とか。

そして最後はメッセージングですが、今回はスキップしています。

Connect a messaging platform? (Telegram, Discord, etc.)
  ↑↓ navigate  ENTER/SPACE select  ESC cancel

  (○) Set up messaging now (recommended)
   () Skip ? set up later with 'hermes setup gateway'

それ以降、セットアップが継続されて、最後に以下のようなメッセージが出ます。

?? Commands:

   hermes              Start chatting
   hermes setup        Configure API keys & settings
   hermes config       View/edit configuration
   hermes config edit  Open config in editor
   hermes gateway install Install gateway service (messaging + cron)
   hermes update       Update to latest version

─────────────────────────────────────────────────────────

? Reload your shell to use 'hermes' command:

   source ~/.bashrc

$

指示の通りにsourceコマンドを実行し、.bashrcの内容を反映すれば hermes コマンドが実行できるようになります。

設定情報は以下のところに配置されるようです。

 Paths
  Config:       /home/aiuser/.hermes/config.yaml
  Secrets:      /home/aiuser/.hermes/.env
  Install:      /home/aiuser/.hermes/hermes-agent

起動してみると・・・

██╗  ██╗███████╗██████╗ ███╗   ███╗███████╗███████╗       █████╗  ██████╗ ███████╗███╗   ██╗████████╗
██║  ██║██╔════╝██╔══██╗████╗ ████║██╔════╝██╔════╝      ██╔══██╗██╔════╝ ██╔════╝████╗  ██║╚══██╔══╝
███████║█████╗  ██████╔╝██╔████╔██║█████╗  ███████╗█████╗███████║██║  ███╗█████╗  ██╔██╗ ██║   ██║
██╔══██║██╔══╝  ██╔══██╗██║╚██╔╝██║██╔══╝  ╚════██║╚════╝██╔══██║██║   ██║██╔══╝  ██║╚██╗██║   ██║
██║  ██║███████╗██║  ██║██║ ╚═╝ ██║███████╗███████║      ██║  ██║╚██████╔╝███████╗██║ ╚████║   ██║
╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═╝     ╚═╝╚══════╝╚══════╝      ╚═╝  ╚═╝ ╚═════╝ ╚══════╝╚═╝  ╚═══╝   ╚═╝

╭────────────────────────────────────────────────────────────────────────────────────────── Hermes Agent v0.14.0 (2026.5.16) · upstream 81a4f280 ───────────────────────────────────────────────────────────────────────────────────────────╮
                                   Available Tools                                                                                                                                                                                         
  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⡀⠀⣀⣀⠀⢀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀   browser: browser_back, browser_click, ...                                                                                                                                                               
  ⠀⠀⠀⠀⠀⠀⢀⣠⣴⣾⣿⣿⣇⠸⣿⣿⠇⣸⣿⣿⣷⣦⣄⡀⠀⠀⠀⠀⠀⠀   browser-cdp: browser_cdp, browser_dialog                                                                                                                                                                
  ⠀⢀⣠⣴⣶⠿⠋⣩⡿⣿⡿⠻⣿⡇⢠⡄⢸⣿⠟⢿⣿⢿⣍⠙⠿⣶⣦⣄⡀⠀   clarify: clarify                                                                                                                                                                                        
  ⠀⠀⠉⠉⠁⠶⠟⠋⠀⠉⠀⢀⣈⣁⡈⢁⣈⣁⡀⠀⠉⠀⠙⠻⠶⠈⠉⠉⠀⠀   code_execution: execute_code                                                                                                                                                                            
  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣿⡿⠛⢁⡈⠛⢿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀   computer_use: computer_use                                                                                                                                                                              
  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠿⣿⣦⣤⣈⠁⢠⣴⣿⠿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀   cronjob: cronjob                                                                                                                                                                                        
  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠻⢿⣿⣦⡉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀   delegation: delegate_task                                                                                                                                                                               
  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢷⣦⣈⠛⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀   discord: discord                                                                                                                                                                                        
  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣴⠦⠈⠙⠿⣦⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀   (and 21 more toolsets...)                                                                                                                                                                               │
  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣤⡈⠁⢤⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀                                                                                                                                                                                                           
  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠷⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀   Available Skills                                                                                                                                                                                        
  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⠑⢶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀   autonomous-ai-agents: claude-code, codex, hermes-agent, kanban-codex-...                                                                                                                                
  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠁⢰⡆⠈⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀   creative: architecture-diagram, ascii-art, ascii-video, b...                                                                                                                                            
  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⠈⣡⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀   data-science: jupyter-live-kernel                                                                                                                                                                       
  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀   devops: kanban-orchestrator, kanban-worker, webhook-sub...                                                                                                                                              
                                   email: himalaya                                                                                                                                                                                         

うおおおおお、なんかかっちょええ・・・・
そしてこんな風に最後は表示され、入力を受け付けるようになったみたいです。

Welcome to Hermes Agent! Type your message or /help for commands.
 Tip: hermes chat --image path/to/pic.png attaches a local image to a single -q query without a separate upload step.

この時点で、もうHermes Agentに話しかけてよさそうです。ローカルモデルのQwen3.5-9Bとコンタクトできました。

マルチターン会話のパフォーマンス計測用ツールを作成してみた

そんなHermes Agentにこんなお願いをしてみました。

OpenAI APIに向けて、以下の話の流れを送り、それぞれの消費トークン量やトークン出力速度などの情報を収集して出力するようなスクリプトを作りたい。

  • 言語はPythonとする。
  • OpenAI APIのURLはユーザが自由に指定可能なものとする。
  • それぞれのターンでユーザから投げるメッセージは以下の通り。
    • ブロック崩しが遊べるJavaScriptコードを書いてほしい。HTMLに張り付ける形で。
    • もっとかっこよくしてほしいな!
    • ボールの動きが画面サイズに対して速すぎる気がするんだ。ちょっぴりだけゆっくり目にしてほしいな!

すると、およそ7ターンがかりで処理が完了し、それなりに計測できる代物になったようです。その際、どうやらSkillなるものが誕生したようで、

Self-improvement review: Skill 'benchmark-openai-api-performance' created. · Skill 'benchmark-openai-api-performance' updated.

とかいうメッセージが出てました。成果を出すためのプロセスから、自律的にSkillが構成できるようですね。そして、それはエージェントの機能を通じて自らアップデートすることもできるようです。

途中で打ち切ったセッションを復帰させることも可能なようで、

$ hermes --resume 20260527_050531_592349
██╗  ██╗███████╗██████╗ ███╗   ███╗███████╗███████╗       █████╗  ██████╗ ███████╗███╗   ██╗████████╗
██║  ██║██╔════╝██╔══██╗████╗ ████║██╔════╝██╔════╝      ██╔══██╗██╔════╝ ██╔════╝████╗  ██║╚══██╔══╝
███████║█████╗  ██████╔╝██╔████╔██║█████╗  ███████╗█████╗███████║██║  ███╗█████╗  ██╔██╗ ██║   ██║
██╔══██║██╔══╝  ██╔══██╗██║╚██╔╝██║██╔══╝  ╚════██║╚════╝██╔══██║██║   ██║██╔══╝  ██║╚██╗██║   ██║
██║  ██║███████╗██║  ██║██║ ╚═╝ ██║███████╗███████║      ██║  ██║╚██████╔╝███████╗██║ ╚████║   ██║
╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═╝     ╚═╝╚══════╝╚══════╝      ╚═╝  ╚═╝ ╚═════╝ ╚══════╝╚═╝  ╚═══╝   ╚═╝

╭────────────────────────────────────────────────────────────────────────────────────────── Hermes Agent v0.14.0 (2026.5.16) · upstream 81a4f280 ───────────────────────────────────────────────────────────────────────────────────────────╮
                                   Available Tools                                                                                                                                                                                         
  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⡀⠀⣀⣀⠀⢀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀   browser: browser_back, browser_click, ...                                                                                                                                                               
  ⠀⠀⠀⠀⠀⠀⢀⣠⣴⣾⣿⣿⣇⠸⣿⣿⠇⣸⣿⣿⣷⣦⣄⡀⠀⠀⠀⠀⠀⠀   browser-cdp: browser_cdp, browser_dialog
                         :
                         :
                         :
                         :
──────────────────────────────────────────────────────────────────────────────────────────────────────────╮
    You: OpenAI APIに向けて、以下の話の流れを送り、それぞれの消費トークン量やトークン出力速度などの情報を収集して出力するようなスクリプトを作りたい。                                                                                     
          - 言語はPythonとする。                                                                                                                                                                                                           
          - OpenAI APIのURLはユーザが自由に指定可能なものとする。                                                                                                                                                                          
          - それぞれのターンでユーザから投げるメッセージは以下の通り。                                                                                                                                                                     
            - ブロック崩しが遊べるJavaScriptコードを書いてほしい。HTMLに張り付ける形で。                                                                                                                                                   
            - もっとかっこよくしてほしいな!                                                                                                                                                                                               
            - ボールの動きが画面サイズに対して速すぎる気がするんだ。ちょっぴりだけゆっくり目にしてほしいな!                                                                                                                               
    Hermes: OpenAI API を使って会話のトークン統計を収集する Python スクリプトを作成します。 [1 tool call: write_file]                                                                                                                     
    Hermes: 完成しました!スクリプトを `/home/aiuser/openai_stats_collector.py` に作成しました。                                                                                                                                          
                                                                                                                                                                                                                                           
             ## 特徴 ...                                                                                                                                                                                                                   │
    You: pipでインストールを必要とするライブラリはどれか?                                                                                                                                                                                
    Hermes: 確認しました!このスクリプトは **標準ライブラリのみ** で動作するので、pip インストールは不要です。                                                                                                                            
                                                                                                                                                                                                                                           
             必要なモジュール: ...                                                                                                                                                                                                        
    You: 1ターン目では正常に出力できたようだが、2,3ターン目は応答がおかしな状態になり、最終的にはurllib.error.HTTPError: HTTP Error 400: Bad Requestが出力された。対策を実施し、そのファイルを更新せよ。ファイルは                       
 /home/aiuser/data_collect/ 配下に位置を変えているので注意すること。同じディレクトリ上に更新バンコードを出力せよ。                                                                                                                         
    Hermes: 2 ターン目以降の「Bad Request」エラーは、会話履歴の管理ロジックに問題がありそうです。修正して再作成します。 [1 tool call: terminal]                                                                                           
    Hermes: 完成しました!修正バージョンを作成しました。                                                                                                                                                                                  
                                                                                                                                                                                                                                           
             ## ファイル情報 ...                                                                                                                                                                                                           │
    You: max_tokens                                                                                                                                                                                                                       
 は32768に設定してください。途中でLLMの出力が打ち切られることは避けたいためです。また、このプログラムはよりよいコード出力を目的とするのではなく、あくまで読み取り・書き込みパフォーマンスを計測するためのベンチマークツールであることを念  
 頭においてください。                                                                                                                                                                                                                      
    Hermes: 理解しました。ベンチマークツールとして最適化します。 [1 tool call: patch]                                                                                                                                                     │                         
                         :
                         :
                         :
                         :
                                                                                                                                                                                                                                           
             更新されたファイルを `/home/aiuser/data_collect/openai_stats_collector.py` に保存しました。これで 10 分のタイムアウトで動作します!                                                                                           
    You: help                                                                                                                                                                                                                             
                                                                                                                                                                                                                                           
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
Welcome to Hermes Agent! Type your message or /help for commands.
 Tip: /queue <prompt> queues a message for the next turn without interrupting the current one.

という具合に、直前までのセッションをレジュームして、継続開発ができるようにもなってるようです。まだ、入力トークン速度の正確な値が出力できてませんが、出力トークンの速度は出せるようになってきました。

その時点でのトークン使用状況を見るとなかなかのもので、最大コンテキスト量の6割を使用している状況を見るのはそんなに多くないです。それほどしっかりToolsを使って処理をしたということなのでしょう。まだ何のMCPにもつなげてませんが、内部処理によるプロンプトエンジニアリングでここまで仕上げたのかなと思うと驚きです。

  Qwen3.5-9B  38.5K/65.5K  [██████░░░░] 59% │ 6m │ ⏲ 3m 10s

プログラムの実行結果はこんな感じ。

============================================================
統計要約(平均値)
============================================================
総リクエスト数:3
総入力トークン数:7721
総出力トークン数:10218
総処理時間:326.40 
*** 平均入力トークン/秒:23.66
*** 平均出力トークン/秒:31.31
============================================================

私としては、Enterprise方面でまだ本格的にコーディングをしたことがなく、あくまで内部的な実験でコードを書くだけに過ぎないため、こういうことを通じてコードを生成させ、その中で有益なコードの組み方を学ばせてもらってるというのが実際のところです。

生成AIは何かを生産するためだけのものでなく、何か教えを乞うもの・学ぶものという方面でもうまく何かの役に立てられないかなと考えているのですが、こうしたツールの活用も覚えていかなきゃなりませんねと感じたところであります。

参考:生成されたコード

参考として、Qwen3.5-9BがHermes Agent上で出力したコードを例として表示します。
KVキャッシュ設定がKey-Q8_0、Value-Turbo3bitsで量子化されているため、もしかしたら正確さにかけたコードになってるかもしれませんけれども・・

入力トークン/秒の出力は正確ではありませんが、一々コピーペースとして質問ぶん投げなくていいので楽と言えば楽になりますけども、興味ある人はどぞ。

#!/usr/bin/env python3
"""
OpenAI API 対話統計収集スクリプト(ベンチマーク用)
- 指定された会話パターンで OpenAI API へリクエストを送信
- 各ターンの消費トークン数と処理時間を収集
- 必須項目:入力トークン/秒、出力トークン/秒
- 参考項目:入力/出力トークン数、処理時間
"""

import json
import time
import argparse
from typing import Dict, List, Any, Optional
import urllib.request
import urllib.error


# 標準的な OpenAI API URL
DEFAULT_API_URL = "https://api.openai.com/v1/chat/completions"

# 会話のパターン
CONVERSATION_PROMPTS = [
    "ブロック崩しが遊げる JavaScript コードを書いてほしい。HTML に張り付ける形で。",
    "もっとかっこよくしてほしいな!",
    "ボールの動きが画面サイズに対して速すぎる気がするんだ。ちょっぴりだけゆっくり目にしてほしいな!"
]

# コンテキストウィンドウの制限(メッセージ数)
MAX_CONTEXT_WINDOW = 10

# タイムアウト設定(秒)
TIMEOUT_SECONDS = 720  # 10 分


def calculate_tokens_per_second(input_tokens: int, output_tokens: int, elapsed_time: float) -> Dict[str, float]:
    """
    トークン速度を計算する

    Args:
        input_tokens: 入力トークン数
        output_tokens: 出力トークン数
        elapsed_time: 処理時間(秒)

    Returns:
        入力トークン/秒と出力トークン/秒
    """
    result = {}

    # 必須項目:入力トークン/秒(コンテキスト読み込み速度)
    if elapsed_time > 0:
        result["input_tokens_per_second"] = input_tokens / elapsed_time

    # 必須項目:出力トークン/秒(回答生成速度)
    if elapsed_time > 0:
        result["output_tokens_per_second"] = output_tokens / elapsed_time

    return result


def main():
    """メイン関数"""
    parser = argparse.ArgumentParser(
        description="OpenAI API 対話統計収集ツール(ベンチマーク用)"
    )
    parser.add_argument(
        "--url",
        type=str,
        default=DEFAULT_API_URL,
        help=f"OpenAI API URL(デフォルト:{DEFAULT_API_URL})"
    )
    parser.add_argument(
        "--key",
        type=str,
        required=True,
        help="OpenAI API キー"
    )
    parser.add_argument(
        "--model",
        type=str,
        default="gpt-3.5-turbo",
        help="使用するモデル(デフォルト:gpt-3.5-turbo)"
    )
    parser.add_argument(
        "--context-window",
        type=int,
        default=5,
        help="コンテキストウィンドウサイズ(デフォルト:5)"
    )
    parser.add_argument(
        "--json",
        action="store_true",
        help="結果を JSON 形式で出力"
    )
    parser.add_argument(
        "--timeout",
        type=int,
        default=TIMEOUT_SECONDS,
        help=f"タイムアウト時間(秒)(デフォルト:{TIMEOUT_SECONDS} 秒)"
    )

    args = parser.parse_args()

    # 統計情報の蓄積
    global_stats = {
        "total_requests": 0,
        "total_input_tokens": 0,
        "total_output_tokens": 0,
        "total_elapsed_time": 0.0,
        "turns": []
    }

    print("=" * 60)
    print("OpenAI API 対話統計収集開始(ベンチマーク用)")
    print("=" * 60)
    print(f"API URL: {args.url}")
    print(f"モデル:{args.model}")
    print(f"コンテキストウィンドウ:{args.context_window} メッセージ")
    print(f"タイムアウト:{args.timeout} 秒")
    print("-" * 60)
    print("測定項目:")
    print("  - 入力トークン/秒(コンテキスト読み込み速度)")
    print("  - 出力トークン/秒(回答生成速度)")
    print("=" * 60)
    print()

    api_key = args.key
    messages = []

    for i, prompt in enumerate(CONVERSATION_PROMPTS):
        print(f"[ターン {i + 1}/{len(CONVERSATION_PROMPTS)}]")
        print(f"ユーザー入力:{prompt}")

        # 現在の会話履歴を構築(システムメッセージは常に最初)
        current_messages = [m for m in messages if m["role"] != "user"]
        current_messages.append({"role": "user", "content": prompt})

        # API にリクエスト
        payload = {
            "model": args.model,
            "messages": current_messages,
            "temperature": 0.7,
            "max_tokens": 32768
        }

        data = json.dumps(payload).encode('utf-8')

        req = urllib.request.Request(
            args.url,
            data=data,
            headers={
                "Content-Type": "application/json",
                "Authorization": f"Bearer {api_key}"
            },
            method="POST"
        )

        start_time = time.time()

        try:
            with urllib.request.urlopen(req, timeout=args.timeout) as response:
                response_data = json.loads(response.read().decode('utf-8'))

                elapsed_time = time.time() - start_time
                usage = response_data['usage']
                content = response_data['choices'][0]['message']['content']

                input_tokens = usage['prompt_tokens']
                output_tokens = usage['completion_tokens']

                # 統計の更新
                global_stats["total_requests"] += 1
                global_stats["total_input_tokens"] += input_tokens
                global_stats["total_output_tokens"] += output_tokens
                global_stats["total_elapsed_time"] += elapsed_time

                turn_data = {
                    "turn": i + 1,
                    "prompt": prompt,
                    "input_tokens": input_tokens,
                    "output_tokens": output_tokens,
                    "elapsed_time": elapsed_time
                }
                global_stats["turns"].append(turn_data)

                # 会話履歴の更新
                messages = current_messages + [{"role": "assistant", "content": content}]

                # コンテキストウィンドウの維持
                if len(messages) > args.context_window * 2:
                    system_messages = [m for m in messages if m["role"] == "system"]
                    limit = args.context_window * 2 - len(system_messages)
                    assistant_user_messages = [m for m in messages if m["role"] != "system"]
                    if len(assistant_user_messages) > limit:
                        assistant_user_messages = assistant_user_messages[-limit:]
                    messages = system_messages + assistant_user_messages

                # 必須項目の計算と表示
                tokens_per_second = calculate_tokens_per_second(input_tokens, output_tokens, elapsed_time)

                print()
                print("統計情報:")
                print(f"  入力トークン数:{input_tokens}")
                print(f"  出力トークン数:{output_tokens}")
                print(f"  処理時間:{elapsed_time:.2f} ")
                print(f"  *** 入力トークン/秒:{tokens_per_second['input_tokens_per_second']:.2f}")
                print(f"  *** 出力トークン/秒:{tokens_per_second['output_tokens_per_second']:.2f}")
                print()
                print(f"[応答]")
                print(content)

        except urllib.error.HTTPError as e:
            print(f"\nエラー:HTTP {e.code}")
            error_body = e.read().decode('utf-8')
            print(f"エラー詳細:{error_body}")
            raise
        except Exception as e:
            print(f"\nエラー:{e}")
            raise

    # 統計要約
    print("\n" + "=" * 60)
    print("統計要約(平均値)")
    print("=" * 60)

    total_turns = global_stats["total_requests"]

    print(f"総リクエスト数:{total_turns}")
    print(f"総入力トークン数:{global_stats['total_input_tokens']}")
    print(f"総出力トークン数:{global_stats['total_output_tokens']}")
    print(f"総処理時間:{global_stats['total_elapsed_time']:.2f} ")

    # 必須項目:平均速度
    if total_turns > 0 and global_stats['total_elapsed_time'] > 0:
        avg_input_per_second = global_stats['total_input_tokens'] / global_stats['total_elapsed_time']
        avg_output_per_second = global_stats['total_output_tokens'] / global_stats['total_elapsed_time']

        print(f"*** 平均入力トークン/秒:{avg_input_per_second:.2f}")
        print(f"*** 平均出力トークン/秒:{avg_output_per_second:.2f}")

    print("=" * 60)

    # JSON 出力
    if args.json:
        print("\nJSON フォーマット:")

        # 平均速度を計算
        result = {
            "total_requests": total_turns,
            "total_input_tokens": global_stats['total_input_tokens'],
            "total_output_tokens": global_stats['total_output_tokens'],
            "total_elapsed_time": global_stats['total_elapsed_time'],
            "average_input_tokens_per_second": global_stats['total_input_tokens'] / global_stats['total_elapsed_time'] if total_turns > 0 and global_stats['total_elapsed_time'] > 0 else 0,
            "average_output_tokens_per_second": global_stats['total_output_tokens'] / global_stats['total_elapsed_time'] if total_turns > 0 and global_stats['total_elapsed_time'] > 0 else 0,
            "turns": global_stats["turns"]
        }

        print(json.dumps(result, indent=2, ensure_ascii=False))


if __name__ == "__main__":
    main()

コメント

タイトルとURLをコピーしました