Qwen 3.5が話題です。Function Callingも出来ます。 NVIDIA GeForce RTX 3060という非力めのGPUでも9Bくらいなら難なく動きます。ということでちょこっと遊んでみます
ローカルLLMの準備
まずllama-serverを準備しました。GPU向けにビルドするだけなので略
ggml_cuda_init: found 1 CUDA devices:
Device 0: NVIDIA GeForce RTX 3060, compute capability 8.6, VMM: yes
version: 8182 (05728db18)
built with GNU 13.3.0 for Linux x86_64
モデルを準備します
とくに深い考慮をせず Qwen3.5-9B-Q4_K_S.gguf をダウンロードして ~/models/qwen3.5-9b/Qwen3.5-9B-Q4_K_S.gguf に置きました。置き方の流儀はわかりません。
Geminiにオプションを聞きつつ起動します。
$ llama-server -m ~/models/qwen3.5-9b/Qwen3.5-9B-Q4_K_S.gguf \
-c 32768 \
-ngl 99 \
-fa on \
--ubatch-size 256 \
--batch-size 1024 \
--jinja
なお、現時点のGeminiでは軽くハルシネーションをおこしてます
追記: Qwen3.5の推奨設定とは指定が違うので、必要なら ここ見て調整ください
サーバは動きましたので、curlで動作確認します。Function Callingのやり方も聞きゃぁ出てきます
curl -sS http://localhost:8080/v1/chat/completions -H "Content-Type: application/json" -d '{
"model": "qwen",
"messages": [
{"role": "user", "content": "東京の天気を教えて?"}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "指定された場所の現在の天気を取得する",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "都市名(例:東京、大阪)"
}
},
"required": ["location"]
}
}
}
],
"tool_choice": "auto"
}' | jq .
{
"choices": [
{
"finish_reason": "tool_calls",
"index": 0,
"message": {
"role": "assistant",
"content": "",
"tool_calls": [
{
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\":\"東京\"}"
},
"id": "x29nNtYMJZF1iU2ujEqPrK5QJFUdR2tl"
}
]
}
}
],
"created": 1773409866,
"model": "Qwen3.5-9B-Q4_K_S.gguf",
"system_fingerprint": "b8182-05728db18",
"object": "chat.completion",
"usage": {
"completion_tokens": 26,
"prompt_tokens": 293,
"total_tokens": 319
},
"id": "chatcmpl-pKl8FBIsDjF4huuvq4hY83RYOlaWx7Zr",
"timings": {
"cache_n": 0,
"prompt_n": 293,
"prompt_ms": 471.413,
"prompt_per_token_ms": 1.6089180887372014,
"prompt_per_second": 621.53568102704,
"predicted_n": 26,
"predicted_ms": 604.79,
"predicted_per_token_ms": 23.261153846153846,
"predicted_per_second": 42.990128805039774
}
}
雰囲気掴みながら進められるのは良いですね
AIエージェントを作らせる
Claude Code で指示するだけです。
細かいところは略しますが、今回のお題は「ブラウザを複数ステップ操作して期待する結果になるか確認してよ」という感じのことです。これ自体、まぁまぁ役に立つケースはあるはずです。
インプットは次のようなファイルであるとします。
# Markdown Editorが開けることの確認
https://mowa-net.jp/markdawn/ には簡易のMarkdownエディタがある。
エディタを開いたときにその画面が表示されることを確認する
## 前提条件
ブラウザが開けること
## 操作
1. https://mowa-net.jp/markdawn/ へアクセスする
2. `New` ボタンがあることを確認し、クリックする
## 期待動作
1. HTTP 400台、500台が発生することなく、エディタ画面と考えられるものが開かれる
2. 新しいタブが開ける。また、開かれたタブに何らかのMardownの入力を行うことができる
結果として次のような設計を提案してきました。
入力 (stdin / ファイル)
↓
InputParser(Markdown解析)
↓
Agent(エージェントループ)
├─→ LLMClient(OpenAI互換 Function Calling)
│ ↑↓ ツール呼び出し/結果
└─→ BrowserTools(Playwright / Chromium)
↓
ResultFormatter
↓
標準出力(JSON) + 標準エラー出力(ログ)
良いよ作って。
できた
uv run tester --input .ai/docs/example_input.md --llm-base-url http://localhost:8080/v1 --model qwe
n
2026-03-13T22:39:51 [INFO] テストシナリオ: Markdown Editorが開けることの確認
2026-03-13T22:39:51 [INFO] --- イテレーション 1 / 20 ---
2026-03-13T22:39:55 [INFO] HTTP Request: POST http://localhost:8080/v1/chat/completions "HTTP/1.1 200 OK"
2026-03-13T22:39:55 [INFO] ツール呼び出し: navigate({'url': 'https://mowa-net.jp/markdawn/'})
2026-03-13T22:39:55 [INFO] ツール結果: ナビゲーション成功: https://mowa-net.jp/markdawn/ (HTTP 200)
2026-03-13T22:39:55 [INFO] --- イテレーション 2 / 20 ---
2026-03-13T22:39:56 [INFO] HTTP Request: POST http://localhost:8080/v1/chat/completions "HTTP/1.1 200 OK"
2026-03-13T22:39:56 [INFO] ツール呼び出し: check_element_exists({'selector': 'text=New'})
2026-03-13T22:39:56 [INFO] ツール結果: 存在します: text=New (count=1)
2026-03-13T22:39:56 [INFO] --- イテレーション 3 / 20 ---
2026-03-13T22:39:58 [INFO] HTTP Request: POST http://localhost:8080/v1/chat/completions "HTTP/1.1 200 OK"
2026-03-13T22:39:58 [INFO] ツール呼び出し: click({'selector': 'text=New'})
2026-03-13T22:39:58 [INFO] ツール結果: クリック成功: text=New
2026-03-13T22:39:58 [INFO] --- イテレーション 4 / 20 ---
2026-03-13T22:39:59 [INFO] HTTP Request: POST http://localhost:8080/v1/chat/completions "HTTP/1.1 200 OK"
2026-03-13T22:39:59 [INFO] ツール呼び出し: get_page_title({})
2026-03-13T22:39:59 [INFO] ツール結果: Markdown Editor
...
{
"success": true,
"title": "Markdown Editorが開けることの確認",
"summary": "テストが成功しました。https://mowa-net.jp/markdawn/ にアクセスし、New ボタンをクリックすることで Markdown Editor が正常に開かれました。HTTP 200 でアクセス成功し、新しいドキュメント \"Document 2\" が作成され、Markdown 入力エリアが表示されています。",
"iteration_count": 6,
"steps": [
{
"iteration": 1,
"tool": "navigate",
"arguments": {
"url": "https://mowa-net.jp/markdawn/"
},
"result": "ナビゲーション成功: https://mowa-net.jp/markdawn/ (HTTP 200)",
"success": true,
"timestamp": "2026-03-13T13:39:55.373469+00:00"
},
{
"iteration": 2,
"tool": "check_element_exists",
"arguments": {
"selector": "text=New"
},
"result": "存在します: text=New (count=1)",
"success": true,
"timestamp": "2026-03-13T13:39:56.847058+00:00"
},
{
"iteration": 3,
"tool": "click",
"arguments": {
"selector": "text=New"
},
"result": "クリック成功: text=New",
"success": true,
"timestamp": "2026-03-13T13:39:58.129820+00:00"
},
{
"iteration": 4,
"tool": "get_page_title",
"arguments": {},
"result": "Markdown Editor",
"success": true,
"timestamp": "2026-03-13T13:39:59.590523+00:00"
},
{
"iteration": 5,
"tool": "get_page_text",
"arguments": {},
"result": "File\nNew\nImport\nSave\nLoad\nEdit\n↶ Undo\n↷ Redo\nFind/Replace\nExport\nMD\nHTML\nPDF\nZIP\nInsert\n📷 Images\nGallery\nSettings\n💾 Auto-Save: ON\nGitHub\nWelcome to Markdown Editor\nDocument 2\nNew document \"Document 2\" created\n📋 Outline\nHide Preview\nMarkdown Editor\nLines: 3\nWords: 7\nChars: 49\n1\n2\n3\n⌄\n# Document 2\n\n\nStart writing your markdown here...\nPreview\nEdit\nDocument 2\n\nStart writing your markdown here...",
"success": true,
"timestamp": "2026-03-13T13:40:00.778586+00:00"
},
{
"iteration": 6,
"tool": "report_result",
"arguments": {
"success": true,
"message": "テストが成功しました。https://mowa-net.jp/markdawn/ にアクセスし、New ボタンをクリックすることで Markdown Editor が正常に開かれました。HTTP 200 でアクセス成功し、新しいドキュメント \"Document 2\" が作成され、Markdown 入力エリアが表示されています。"
},
"result": "SUCCESS: テストが成功しました。https://mowa-net.jp/markdawn/ にアクセスし、New ボタンをクリックすることで Markdown Editor が正常に開かれました。HTTP 200 でアクセス成功し、新しいドキュメント \"Document 2\" が作成され、Markdown 入力エリアが表示されています。",
"success": true,
"timestamp": "2026-03-13T13:40:06.698398+00:00"
}
],
"error": null
}
期待通りですねー。何より嬉しいのは非力めなGPUとモデルでもこんくらいはやりきってくれることを確認できたということです。

