Closed11

Qwen-Code を試す

kun432kun432

GitHubレポジトリ

https://github.com/QwenLM/qwen-code

Qwen Code


referred from https://github.com/QwenLM/qwen-code

開発者向けAI搭載コマンドラインワークフローツール

Qwen Codeは、Gemini CLI (詳細) をベースにし、Qwen3-Coder モデル向けに最適化された強力なコマンドラインAIワークフローツールです。高度なコード理解、自動化タスク、インテリジェントアシストにより、開発ワークフローを向上させます。

[!WARNING]
トークン使用に関する注意: Qwen Codeはサイクルごとに複数のAPIコールを行う可能性があり、トークン使用量が増大する場合があります (Claude Codeと同様)。API効率の最適化に努めています。

💡 無料オプション: ModelScopeは中国本土のユーザーに1日2,000回の無料APIコールを提供しています。OpenRouterは全世界で1日1,000回の無料APIコールを提供しています。設定方法はAPI構成を参照してください。

主な機能

  • コード理解 & 編集 - 従来のコンテキストウィンドウ制限を超えて大規模なコードベースをクエリ・編集できます
  • ワークフロー自動化 - プルリクエストの処理や複雑なリベースなどの運用タスクを自動化します
  • 拡張パーサー - Qwen-Coderモデル向けに最適化されたパーサーを採用

Gemini-CLIがベースなのね。

ライセンスは Apache-2.0

kun432kun432

一般的には、OpenRouterで使うパターンが多いのだろうけど、以下でQwen3-Coder-30B-A3B-Instructをllama.cppで動かしたので、これを使ってローカルで完結できるか確認してみたい。

https://zenn.dev/kun432/scraps/b3207edfe2815f

以下のような構成でやってみたい。

  • Ubuntuサーバ
    • Ubuntu-22.04 + RTX4090
    • llama-server(llama.cpp)を起動し、Qwen3-Coder-30B-A3B−Instruct(UD-Q4_K_XL)をロード。
  • Mac
    • Qwen-Codeをインストール
    • モデルアクセスを上記サーバに向ける。
kun432kun432

まず、Ubuntuサーバでllama-serverを起動してモデルをロード。

./build/bin/llama-server \
    -hf unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF:Q4_K_XL \
    --jinja \
    -ngl 99 \
    --threads -1 \
    --ctx-size 32684 \
    --temp 0.7 \
    --min-p 0.0 \
    --top-p 0.80 \
    --top-k 20 \
    --repeat-penalty 1.05 \
    --host 0.0.0.0
出力
main: server is listening on http://0.0.0.0:8080 - starting the main loop

次にMac。Dockerコンテナを立ち上げてその中でQwen-Codeを試すことにする。devcontainerイメージを使わせてもらう。

作業ディレクトリ作成

mkdir qwen-code-work && cd $_

コンテナ起動

docker run \
    --rm \
    -it \
    -u node \
    -v "$PWD:/workspace:cached" \
    -w "/workspace" \
    mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm \
    bash

以下はDockerコンテナ内で。

Qwen-Codeをインストール

npm install -g @qwen-code/qwen-code@latest

確認

qwen --version
出力
0.0.2

Usageも。

qwen --help
出力
qwen [options]

Qwen Code - Launch an interactive CLI, use -p/--prompt for non-interactive mode

Options:
  -m, --model                     Model             [string] [default: "qwen3-coder-plus"]
  -p, --prompt                    Prompt. Appended to input on stdin (if any).    [string]
  -i, --prompt-interactive        Execute the provided prompt and continue in interactive
                                  mode                                            [string]
  -s, --sandbox                   Run in sandbox?                                [boolean]
      --sandbox-image             Sandbox image URI.                              [string]
  -d, --debug                     Run in debug mode?            [boolean] [default: false]
  -a, --all-files                 Include ALL files in context? [boolean] [default: false]
      --all_files                 Include ALL files in context?
       [deprecated: Use --all-files instead. We will be removing --all_files in the coming
                                                        weeks.] [boolean] [default: false]
      --show-memory-usage         Show memory usage in status bar
                                                                [boolean] [default: false]
      --show_memory_usage         Show memory usage in status bar
  [deprecated: Use --show-memory-usage instead. We will be removing --show_memory_usage in
                                             the coming weeks.] [boolean] [default: false]
  -y, --yolo                      Automatically accept all actions (aka YOLO mode, see
                                  https://www.youtube.com/watch?v=xvFZjo5PgG0 for more
                                  details)?                     [boolean] [default: false]
      --telemetry                 Enable telemetry? This flag specifically controls if
                                  telemetry is sent. Other --telemetry-* flags set
                                  specific values but do not enable telemetry on their
                                  own.                                           [boolean]
      --telemetry-target          Set the telemetry target (local or gcp). Overrides
                                  settings files.       [string] [choices: "local", "gcp"]
      --telemetry-otlp-endpoint   Set the OTLP endpoint for telemetry. Overrides
                                  environment variables and settings files.       [string]
      --telemetry-log-prompts     Enable or disable logging of user prompts for telemetry.
                                  Overrides settings files.                      [boolean]
  -c, --checkpointing             Enables checkpointing of file edits
                                                                [boolean] [default: false]
      --allowed-mcp-server-names  Allowed MCP server names                         [array]
  -e, --extensions                A list of extensions to use. If not provided, all
                                  extensions are used.                             [array]
  -l, --list-extensions           List all available extensions and exit.        [boolean]
      --ide-mode                  Run in IDE mode?                               [boolean]
      --openai-logging            Enable logging of OpenAI API calls for debugging and
                                  analysis                                       [boolean]
      --openai-api-key            OpenAI API key to use for authentication        [string]
      --openai-base-url           OpenAI base URL (for custom endpoints)          [string]
  -v, --version                   Show version number                            [boolean]
  -h, --help                      Show help                                      [boolean]

APIの設定

https://github.com/QwenLM/qwen-code?tab=readme-ov-file#api-configuration

モデル名はこんな感じで確認できる。

curl -s http://<ローカルサーバのIP>:8080/models | jq -r .models[0].name | awk -F"/" '{ print $NF }'
出力
unsloth_Qwen3-Coder-30B-A3B-Instruct-GGUF_Qwen3-Coder-30B-A3B-Instruct-UD-Q4_K_XL.gguf
export OPENAI_API_KEY="dummy"
export OPENAI_BASE_URL="http://<ローカルサーバのIP>:8080/v1"
export OPENAI_MODEL="unsloth_Qwen3-Coder-30B-A3B-Instruct-GGUF_Qwen3-Coder-30B-A3B-Instruct-UD-Q4_K_XL.gguf"

では実行

qwen

最初にテーマの選択と適用範囲の設定。テーマを選択して、タブで適用範囲に移動して、適用範囲を指定という感じ。自分の場合は元のターミナル側との兼ね合いで、ANSI以外は見えない感じだった。適用範囲は、今回はコンテナなのでどれでも同じかなと思ったけど、"Workspace Settings"にした。選択したらEnterで確定。

出力
ClearcutLogger disabled - no data collection.

██╗       ██████╗ ██╗    ██╗███████╗███╗   ██╗
╚██╗     ██╔═══██╗██║    ██║██╔════╝████╗  ██║
 ╚██╗    ██║   ██║██║ █╗ ██║█████╗  ██╔██╗ ██║
 ██╔╝    ██║▄▄ ██║██║███╗██║██╔══╝  ██║╚██╗██║
██╔╝     ╚██████╔╝╚███╔███╔╝███████╗██║ ╚████║
╚═╝       ╚══▀▀═╝  ╚══╝╚══╝ ╚══════╝╚═╝  ╚═══╝


Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
3. Create QWEN.md files to customize your interactions with Qwen Code.
4. /help for more information.

╭───────────────────────────────────────────────────────────────────────────────╮
│                                                                               │
│ > Select Theme                       Preview                                  │
│ ▲                                    ┌──────────────────────────────────────┐ │
│ ○ Qwen Light Light                   │                                      │ │
│ ○ Qwen Dark Dark                     │ 1 # function                         │ │
│ ● ANSI Dark                          │ 2 -def fibonacci(n):                 │ │
│ ○ Atom One Dark                      │ 3 -    a, b = 0, 1                   │ │
│ ○ Ayu Dark                           │ 4 -    for _ in range(n):            │ │
│ ○ Default Dark                       │ 5 -        a, b = b, a + b           │ │
│ ○ Dracula Dark                       │ 6 -    return a                      │ │
│ ○ GitHub Dark                        │                                      │ │
│ ▼                                    │ ╭──────────────────────────────────╮ │ │
│                                      │ │                                  │ │ │
│   Apply To                           │ │ No changes detected.             │ │ │
│ ● User Settings                      │ │                                  │ │ │
│ ○ Workspace Settings                 │ ╰──────────────────────────────────╯ │ │
│ ○ System Settings                    │                                      │ │
│                                      └──────────────────────────────────────┘ │
│                                                                               │
│ (Use Enter to select, Tab to change focus)                                    │
│                                                                               │
╰───────────────────────────────────────────────────────────────────────────────╯

/workspace      no sandbox (see /docs)       qwen3-coder-plus (100% context left)

次にモデルプロバイダの設定。環境変数セットしたものが適用されてるのかな?とりあえずEnter。

出力
╭───────────────────────────────────────────────────────────────────────────────╮
│                                                                               │
│ Get started                                                                   │
│                                                                               │
│ How would you like to authenticate for this project?                          │
│                                                                               │
│ ● OpenAI                                                                      │
│                                                                               │
│ (Use Enter to Set Auth)                                                       │
│                                                                               │
│ Terms of Services and Privacy Notice for Qwen Code                            │
│                                                                               │
│ https://github.com/QwenLM/Qwen3-Coder/blob/main/README.md                     │
│                                                                               │
╰───────────────────────────────────────────────────────────────────────────────╯

/workspace      no sandbox (see /docs)       qwen3-coder-plus (100% context left)

一応モデル名とかが見えたので環境変数は効いてるみたい。メッセージを入力すれば進めそう。

出力
╭───────────────────────────────────────────────────────────────────────────────╮
│ >   Type your message or @path/to/file                                        │
╰───────────────────────────────────────────────────────────────────────────────╯

/workspno sandbox (see  unsloth_Qwen3-Coder-30B-A3B-Instruct-GGUF_Qwen3-Coder-30B
ace    /docs)          -A3B-Instruct-UD-Q4_K_XL.gguf (100% context left)

以下のメッセージを入力

> ターミナルで動く三目並べを作って。Pythonで。

一応なんか進んでるっぽいけど、下の方にエラーが起きてるメッセージが見える・・・

出力
╭──────────────────────────────────────────────────╮
│  > ターミナルで動く三目並べを作って。Pythonで。  │
╰──────────────────────────────────────────────────╯


⠇ Greasin' the cogs of the machine... (esc to cancel, 52s)



/work no sandbox   unsloth_Qwen3-Coder-30B-A3B-Instruct-GGUF
space (see /docs) _Qwen3-Coder-30B-A3B-Instruct-UD-Q4_K_XL.| ✖ 8 errors (ctrl+o
                  guf (100% context left)                    for details)
出力
✕ [API Error: OpenAI API error: 500 Value is not callable: null at row 62, column
  114:
                  {%- if json_key not in handled_keys %}
                      {%- set normed_json_key = json_key | replace("-", "_") |
  replace(" ", "_") | replace("$", "") %}

                                  ^
                      {%- if param_fields[json_key] is mapping %}
   at row 62, column 21:
                  {%- if json_key not in handled_keys %}
                      {%- set normed_json_key = json_key | replace("-", "_") |
  replace(" ", "_") | replace("$", "") %}
                      ^
                      {%- if param_fields[json_key] is mapping %}
   at row 61, column 55:
              {%- for json_key in param_fields %}
                  {%- if json_key not in handled_keys %}
                                                        ^
                      {%- set normed_json_key = json_key | replace("-", "_") |
  replace(" ", "_") | replace("$", "") %}
   at row 61, column 17:
              {%- for json_key in param_fields %}
                  {%- if json_key not in handled_keys %}
                  ^
                      {%- set normed_json_key = json_key | replace("-", "_") |
  replace(" ", "_") | replace("$", "") %}
   at row 60, column 48:
              {%- set handled_keys = ['type', 'description', 'enum', 'required'] %}
              {%- for json_key in param_fields %}
                                                 ^
                  {%- if json_key not in handled_keys %}
   at row 60, column 13:
              {%- set handled_keys = ['type', 'description', 'enum', 'required'] %}
              {%- for json_key in param_fields %}
              ^
                  {%- if json_key not in handled_keys %}
   at row 49, column 80:
          {{- '\n<parameters>' }}
          {%- for param_name, param_fields in tool.parameters.properties|items %}
                                                                                 ^
              {{- '\n<parameter>' }}
   at row 49, column 9:
          {{- '\n<parameters>' }}
          {%- for param_name, param_fields in tool.parameters.properties|items %}
          ^
              {{- '\n<parameter>' }}
   at row 42, column 29:
      {{- "<tools>" }}
      {%- for tool in tools %}
                              ^
          {%- if tool.function is defined %}
   at row 42, column 5:
      {{- "<tools>" }}
      {%- for tool in tools %}
      ^
          {%- if tool.function is defined %}
   at row 39, column 51:
  {%- endif %}
  {%- if tools is iterable and tools | length > 0 %}
                                                    ^
      {{- "\n\nYou have access to the following functions:\n\n" }}
   at row 39, column 1:
  {%- endif %}
  {%- if tools is iterable and tools | length > 0 %}
  ^
      {{- "\n\nYou have access to the following functions:\n\n" }}
   at row 1, column 69:
  {#- Copyright 2025-present the Unsloth team. All rights reserved. #}
                                                                      ^
  {#- Licensed under the Apache License, Version 2.0 (the "License") #}
  ]

んー、チャットテンプレートが正しくない?

llama-server側のログ見てると

出力
srv  params_from_: Chat format: Hermes 2 Pro

とか出てる。

kun432kun432

チャットテンプレートはこれかな?

https://huggingface.co/unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/blob/main/template

ダウンロード

wget --content-disposition https://huggingface.co/unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/resolve/main/template?download=true

このテンプレートを指定してllama-serverを起動し直し。

./build/bin/llama-server \
    -hf unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF:Q4_K_XL \
    --jinja \
    -ngl 99 \
    --threads -1 \
    --ctx-size 32684 \
    --temp 0.7 \
    --min-p 0.0 \
    --top-p 0.80 \
    --top-k 20 \
    --repeat-penalty 1.05 \
    --host 0.0.0.0 \
    --chat-template-file template

再度Qwen-Codeを実行

> ターミナルで動く三目並べを作って。Pythonで。

いけてそう?

出力
╭──────────────────────────────────────────────────╮
│  > ターミナルで動く三目並べを作って。Pythonで。  │
╰──────────────────────────────────────────────────╯

 ╭────────────────────────────────────────────────────────────────────────────────────────╮
 │ ⊶  WriteFile Writing to tic_tac_toe.py                                                 │
 ╰────────────────────────────────────────────────────────────────────────────────────────╯

⠧ Polishing the pixels... (esc to cancel, 19s)

一応それっぽくは動いてる。

出力
╭──────────────────────────────────────────────────╮
│  > ターミナルで動く三目並べを作って。Pythonで。  │
╰──────────────────────────────────────────────────╯

 ╭─────────────────────────────────────────────────────────────────────────────────╮
 │ ✔  WriteFile Writing to tic_tac_toe.py                                          │
 │                                                                                 │
 │      1 import random                                                            │
 │      2                                                                          │
 │      3 class TicTacToe:                                                         │
 │      4     def __init__(self):                                                  │
 │      5         self.board = [' ' for _ in range(9)]  # 3x3 board                │
 │      6         self.current_player = 'X'                                        │
 │      7                                                                          │
 │      8     def print_board(self):                                               │
 │      9         print("\n")                                                      │
 │     10         for i in range(3):                                               │
 │     11             print(f" {self.board[i*3]} | {self.board[i*3+1]} |           │
 │        {self.board[i*3+2]} ")                                                   │
 │     12             if i < 2:                                                    │
 │     13                 print("-----------")                                     │
 │     14         print("\")                                                       │
 │     15                                                                          │
 │     16     def make_move(self, position):                                       │
 │     17         if self.board[position] == ' ':                                  │
 │     18             self.board[position] = self.current_player                   │
 │     19             return True                                                  │
 │     20         return False                                                     │
 │     21                                                                          │
 │     22     def check_winner(self):                                              │
 │     23         # Check rows                                                     │
 │     24         for i in range(0, 9, 3):                                         │
 │     25             if self.board[i] == self.board[i+1] == self.board[i+2] != '  │
 │        ':                                                                       │
 │     26                 return self.board[i]                                     │
 │     27                                                                          │
 │     28         # Check columns                                                  │
 │     29         for i in range(3):                                               │
 │     30             if self.board[i] == self.board[i+3] == self.board[i+6] != '  │
 │        ':                                                                       │
 │     31                 return self.board[i]                                     │
 │     32                                                                          │
 │     33         # Check diagonals                                                │
 │     34         if self.board[0] == self.board[4] == self.board[8] != ' ':       │
 │     35             return self.board[0]                                         │
 │     36         if self.board[2] == self.board[4] == self.board[6] != ' ':       │
 │     37             return self.board[2]                                         │
 │     38                                                                          │
 │     39         return None                                                      │
 │     40                                                                          │
 │     41     def is_board_full(self):                                             │
 │     42         return ' ' not in self.board                                     │
 │     43                                                                          │
 │     44     def switch_player(self):                                             │
 │     45         self.current_player = 'O' if self.current_player == 'X' else     │
 │        'X'                                                                      │
 │     46                                                                          │
 │     47     def play(self):                                                      │
 │     48         print("Welcome to Tic Tac Toe!")                                 │
 │     49         print("You are X, computer is O.")                               │
 │     50                                                                          │
 │     51         while True:                                                      │
 │     52             self.print_board()                                           │
 │     53                                                                          │
 │     54             # Player's turn                                              │
 │     55             try:                                                         │
 │     56                 move = int(input("Enter position (1-9): ")) - 1          │
 │     57                 if move < 0 or move > 8:                                 │
 │     58                     print("Invalid position. Please enter a number       │
 │        between 1 and 9.")                                                       │
 │     59                     continue                                             │
 │     60                                                                          │
 │     61                 if not self.make_move(move):                             │
 │     62                     print("Position already taken. Try again.")          │
 │     63                     continue                                             │
 │     64             except ValueError:                                           │
 │     65                 print("Please enter a valid number.")                    │
 │     66                 continue                                                 │
 │     67                                                                          │
 │     68             # Check for winner or tie                                    │
 │     69             winner = self.check_winner()                                 │
 │     70             if winner:                                                   │
 │     71                 self.print_board()                                       │
 │     72                 print(f"{winner} wins!")                                 │
 │     73                 break                                                    │
 │     74                                                                          │
 │     75             if self.is_board_full():                                     │
 │     76                 self.print_board()                                       │
 │     77                 print("It's a tie!")                                     │
 │     78                 break                                                    │
 │     79                                                                          │
 │     80             # Computer's turn                                            │
 │     81             self.switch_player()                                         │
 │     82             self.computer_move()                                         │
 │     83                                                                          │
 │     84             # Check for winner or tie after computer move                │
 │     85             winner = self.check_winner()                                 │
 │     86             if winner:                                                   │
 │     87                 self.print_board()                                       │
 │     88                 print(f"{winner} wins!")                                 │
 │     89                 break                                                    │
 │     90                                                                          │
 │     91             if self.is_board_full():                                     │
 │     92                 self.print_board()                                       │
 │     93                 print("It's a tie!")                                     │
 │     94                 break                                                    │
 │     95                                                                          │
 │     96             # Switch back to player                                      │
 │     97             self.switch_player()                                         │
 │     98                                                                          │
 │     99     def computer_move(self):                                             │
 │    100         # Simple AI: Try to win, block player, or make random move       │
 │    101         # Check if computer can win                                      │
 │    102         for i in range(9):                                               │
 │    103             if self.board[i] == ' ':                                     │
 │    104                 self.board[i] = 'O'                                      │
 │    105                 if self.check_winner() == 'O':                           │
 │    106                     return                                               │
 │    107                 self.board[i] = ' '                                      │
 │    108                                                                          │
 │    109         # Block player from winning                                      │
 │    110         for i in range(9):                                               │
 │    111             if self.board[i] == ' ':                                     │
 │    112                 self.board[i] = 'X'                                      │
 │    113                 if self.check_winner() == 'X':                           │
 │    114                     self.board[i] = 'O'                                  │
 │    115                     return                                               │
 │    116                 self.board[i] = ' '                                      │
 │    117                                                                          │
 │    118         # Make a random move                                             │
 │    119         empty_positions = [i for i in range(9) if self.board[i] == ' ']  │
 │    120         if empty_positions:                                              │
 │    121             move = random.choice(empty_positions)                        │
 │    122             self.board[move] = 'O'                                       │
 │    123                                                                          │
 │    124 if __name__ == "__main__":                                               │
 │    125     game = TicTacToe()                                                   │
 │    126     game.play()                                                          │
 ╰─────────────────────────────────────────────────────────────────────────────────╯
 ╭─────────────────────────────────────────────────────────────────────────────────╮
 │ ✔  Shell python /workspace/tic_tac_toe.py (Running the Tic Tac Toe game)        │
 │                                                                                 │
 │    bash: line 1: python: command not found                                      │
 │                                                                                 │
 ╰─────────────────────────────────────────────────────────────────────────────────╯
 ╭────────────────────────────────────────────────────────────────────────────────────────╮
 │ ?  Shell python3 /workspace/tic_tac_toe.py (Running the Tic Tac Toe game with pytho… ← │
 │                                                                                        │
 │   python3 /workspace/tic_tac_toe.py                                                    │
 │                                                                                        │
 │ Allow execution?                                                                       │
 │                                                                                        │
 │ ● Yes, allow once                                                                      │
 │ ○ Yes, allow always "python3 ..."                                                      │
 │ ○ No (esc)                                                                             │
 │                                                                                        │
 ╰────────────────────────────────────────────────────────────────────────────────────────╯

⠏ Waiting for user confirmation...

ただ、llama-server側のログにはテンプレートのエラーっぽいものはちらほら見えるし、モデルページのDiscussions見てるとどうやらllama.cpp側にも問題があるっぽくて(上でHermes 2 Proとかでてたのがそれみたい)、パッチで直してる人がいる模様。

https://huggingface.co/unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/discussions/1

もうしばらく待ったほうがいいかも。

kun432kun432

チャットテンプレートは正しくはこれか(HuggingFaceのファイルの管理、いまいちわかってない)

https://huggingface.co/unsloth/Qwen3-30B-A3B-Thinking-2507-GGUF?chat_template=default

ダウンロードのやり方がわかんなくて、ローカルのtemplateファイルにコピペして、再度llama-serverを起動。

Qwen-Codeから試してみる。

> ターミナルで動く三目並べを作って。Pythonで。 

llama-server側でコアダンプした・・・自分の環境の問題なのかどうなのかも、ちょっとわからない。

出力
(snip)
<tool_call>
<function=write_file' not found at start of '# 三目並べ (Tic-Tac-Toe) ゲームの作成

では、ターミナルで動作する三目並べゲームをPythonで作ってみましょう。

このゲームは、2人のプレイヤーが交互にマスを選択して、3つを一列に並べるゲームです。以下のような機能を実装します:

1. 3x3のボードを表示
2. プレイヤーの入力を受け取る
3. 入力された位置にマークを置く
4. 勝敗を判定する
5. ボードが埋まった場合、引き分けを判定する

以下は実装コードです:

<tool_call>

'
中止 (コアダンプ)
kun432kun432

一旦ペンディングかな。

とりあえずはOpenRouterでやったほうが良さそう。

kun432kun432

Discussions進んでて、llama.cpp側のPR投げられてるので、これ待ちだな。

https://huggingface.co/unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/discussions/1

https://github.com/ggml-org/llama.cpp/pull/14962

https://github.com/ggml-org/llama.cpp/issues/15012

https://github.com/ggml-org/llama.cpp/pull/15019

2025/08/03追加

その後をざっと追いかけてみた感じ、

  • llama.cppへのPRは一部マージされたが、ツール呼び出しはまだ上手くいかない場合がある
  • 原因はCoderモデルのツール呼び出しのパーサーがInstruct/Thinkingモデルとは異なる独自のものになっているため
  • Instruct/Thinkingモデルだと問題なく動作する
  • vLLMで動かした場合でもOpenCodeなどでクラッシュした?
  • QwenもしくはUnsloth側での修正が必要

という感じ。CoderモデルをQwen-Codeで使うのは現時点では厳しそう。

とりあえずOpenRouterに変えて試してみる予定。

kun432kun432

どうやら直ってるっぽい?

https://huggingface.co/unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/discussions/10

GGUFが全部更新されてるので再ダウンロード、あとllama.cppも最新版にする必要がありそう。

ってことでllama.cppを最新でビルドしなおし。

git clone https://github.com/ggml-org/llama.cpp && cd llama.cpp
cmake -B build -DGGML_CUDA=ON
cmake --build build --config Release -j 16

llama-serverで起動。なお、-hf でモデル指定している場合、モデルが更新されていれば勝手に再ダウンロードされる様子。

./build/bin/llama-server \
    -hf unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF:Q4_K_XL \
    --jinja \
    -ngl 99 \
    --threads -1 \
    --ctx-size 32684 \
    --temp 0.7 \
    --min-p 0.0 \
    --top-p 0.80 \
    --top-k 20 \
    --repeat-penalty 1.05 \
    --host 0.0.0.0

ローカルのMacでコンテナを立ち上げてQwen-Codeをインストール

mkdir qwen-code-work && cd $_
docker run \
    --rm \
    -it \
    -u node \
    -v "$PWD:/workspace:cached" \
    -w "/workspace" \
    mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm \
    bash
npm install -g @qwen-code/qwen-code@latest

バージョン少し上がってた。

qwen --version
出力
0.0.4
kun432kun432

実行

export OPENAI_API_KEY="dummy"
export OPENAI_BASE_URL="http://<ローカルサーバのIP>:8080/v1"
export OPENAI_MODEL="unsloth_Qwen3-Coder-30B-A3B-Instruct-GGUF_Qwen3-Coder-30B-A3B-Instruct-UD-Q4_K_XL.gguf"
qwen

作ったアプリを動かすところまでやりたいので、Node.jsにした。

> ターミナルで動く三目並べを作って。Node.jsで。
出力
██╗       ██████╗ ██╗    ██╗███████╗███╗   ██╗
╚██╗     ██╔═══██╗██║    ██║██╔════╝████╗  ██║
 ╚██╗    ██║   ██║██║ █╗ ██║█████╗  ██╔██╗ ██║
 ██╔╝    ██║▄▄ ██║██║███╗██║██╔══╝  ██║╚██╗██║
██╔╝     ╚██████╔╝╚███╔███╔╝███████╗██║ ╚████║
╚═╝       ╚══▀▀═╝  ╚══╝╚══╝ ╚══════╝╚═╝  ╚═══╝

Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
3. Create QWEN.md files to customize your interactions with Qwen Code.
4. /help for more information.

╭───────────────────────────────────────────────────╮
│  > ターミナルで動く三目並べを作って。Node.jsで。  │
╰───────────────────────────────────────────────────╯

✦ 了解しました。Node.jsでターミナル上で動作する三目並べ(Tic-Tac-Toe)ゲームを作成します。

  まず、プロジェクトの構造を設定し、必要なファイルを作成します。
  <tool_call>
  <function=run_shell_command
 ╭──────────────────────────────────────────────────────────────────────────╮
 │ ✔  Shell mkdir -p /workspace/tictactoe (プロジェクトディレクトリを作成)  │
 ╰──────────────────────────────────────────────────────────────────────────╯
✦ 次に、package.jsonファイルを作成してプロジェクトの依存関係を設定します。
  <tool_call>
  <function=write_file
 ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮
 │ ✔  WriteFile Writing to tictactoe/package.json                                                     │
 │                                                                                                    │
 │     1 {                                                                                            │
 │     2   "name": "tictactoe-terminal",                                                              │
 │     3   "version": "1.0.0",                                                                        │
 │     4   "description": "A terminal-based Tic-Tac-Toe game",                                        │
 │     5   "main": "index.js",                                                                        │
 │     6   "scripts": {                                                                               │
 │     7     "start": "node index.js"                                                                 │
 │     8   },                                                                                         │
 │     9   "keywords": ["game", "tic-tac-toe", "terminal"],                                           │
 │    10   "author": "Your Name",                                                                     │
 │    11   "license": "MIT"                                                                           │
 │    12 }                                                                                            │
 ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ {
    "tool_calls": [],
    "content": "次に、ゲームのメインロジックを含むindex.jsファイルを作成します。このファイルには、ゲームの状態
  管理、ボード表示、入力処理、勝敗判定などの機能が含まれます。\n<tool_call>\n<function=write_file"
  }
 ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮
 │ ✔  WriteFile Writing to tictactoe/index.js                                                         │
 │                                                                                                    │
 │    ... first 83 lines hidden ...                                                                   │
 │     84     if (this.board[0][2] !== ' ' &&                                                         │
 │     85         this.board[0][2] === this.board[1][1] &&                                            │
 │     86         this.board[1][1] === this.board[2][0]) {                                            │
 │     87       this.gameOver = true;                                                                 │
 │     88       this.winner = this.board[0][2];                                                       │
 │     89       return;                                                                               │
 │     90     }                                                                                       │
 │     91                                                                                             │
 │     92     // 引き分けのチェック                                                                   │
 │     93     let isDraw = true;                                                                      │
 │     94     for (let i = 0; i < 3; i++) {                                                           │
 │     95       for (let j = 0; j < 3; j++) {                                                         │
 │     96         if (this.board[i][j] === ' ') {                                                     │
 │     97           isDraw = false;                                                                   │
 │     98           break;                                                                            │
 │     99         }                                                                                   │
 │    100       }                                                                                     │
 │    101       if (!isDraw) break;                                                                   │
 │    102     }                                                                                       │
 │    103                                                                                             │
 │    104     if (isDraw) {                                                                           │
 │    105       this.gameOver = true;                                                                 │
 │    106       this.winner = 'Draw';                                                                 │
 │    107     }                                                                                       │
 │    108   }                                                                                         │
 │    109                                                                                             │
 │    110   // ゲームをリセットするメソッド                                                           │
 │    111   reset() {                                                                                 │
 │    112     this.board = [                                                                          │
 │    113       [' ', ' ', ' '],                                                                      │
 │    114       [' ', ' ', ' '],                                                                      │
 │    115       [' ', ' ', ' ']                                                                       │
 │    116     ];                                                                                      │
 │    117     this.currentPlayer = 'X';                                                               │
 │    118     this.gameOver = false;                                                                  │
 │    119     this.winner = null;                                                                     │
 │    120   }                                                                                         │
 │    121 }                                                                                           │
 │    122                                                                                             │
 │    123 // ゲームのメインロジック                                                                   │
 │    124 function main() {                                                                           │
 │    125   const game = new TicTacToe();                                                             │
 │    126                                                                                             │
 │    127   // readlineインターフェースを作成                                                         │
 │    128   const rl = readline.createInterface({                                                     │
 │    129     input: process.stdin,                                                                   │
 │    130     output: process.stdout                                                                  │
 │    131   });                                                                                       │
 │    132                                                                                             │
 │    133   console.log('Welcome to Terminal Tic-Tac-Toe!');                                          │
 │    134   console.log('Enter row and column (0-2) separated by space. Example: "1 2"');             │
 │    135                                                                                             │
 │    136   // ゲームループ                                                                           │
 │    137   function gameLoop() {                                                                     │
 │    138     if (game.gameOver) {                                                                    │
 │    139       game.displayBoard(); // 最後のボードを表示                                            │
 │    140                                                                                             │
 │    141       if (game.winner === 'Draw') {                                                         │
 │    142         console.log('It\'s a draw!');                                                       │
 │    143       } else {                                                                              │
 │    144         console.log(`Player ${game.winner} wins!`);                                         │
 │    145       }                                                                                     │
 │    146                                                                                             │
 │    147       // ゲームをリセットするか確認                                                         │
 │    148       rl.question('\nDo you want to play again? (y/n): ', (answer) => {                     │
 │    149         if (answer.toLowerCase() === 'y') {                                                 │
 │    150           game.reset();                                                                     │
 │    151           console.log('\nNew game started!');                                               │
 │    152           gameLoop();                                                                       │
 │    153         } else {                                                                            │
 │    154           console.log('Thanks for playing!');                                               │
 │    155           rl.close();                                                                       │
 │    156         }                                                                                   │
 │    157       });                                                                                   │
 │    158       return;                                                                               │
 │    159     }                                                                                       │
 │    160                                                                                             │
 │    161     // 現在のボードを表示                                                                   │
 │    162     game.displayBoard();                                                                    │
 │    163                                                                                             │
 │    164     // 現在のプレイヤーを表示                                                               │
 │    165     console.log(`Player ${game.currentPlayer}\'s turn`);                                    │
 │    166                                                                                             │
 │    167     // 入力を受け取る                                                                       │
 │    168     rl.question('Enter row and column (0-2): ', (input) => {                                │
 │    169       const [row, col] = input.split(' ').map(Number);                                      │
 │    170                                                                                             │
 │    171       if (isNaN(row) || isNaN(col) || row < 0 || row > 2 || col < 0 || col > 2) {           │
 │    172         console.log('Invalid input! Please enter numbers between 0 and 2.');                │
 │    173         gameLoop();                                                                         │
 │    174         return;                                                                             │
 │    175       }                                                                                     │
 │    176                                                                                             │
 │    177       if (!game.makeMove(row, col)) {                                                       │
 │    178         console.log('Invalid move! That position is already taken or out of bounds.');      │
 │    179         gameLoop();                                                                         │
 │    180         return;                                                                             │
 │    181       }                                                                                     │
 │    182                                                                                             │
 │    183       // 次のターンへ                                                                       │
 │    184       gameLoop();                                                                           │
 │    185     });                                                                                     │
 │    186   }                                                                                         │
 │    187                                                                                             │
 │    188   // ゲームを開始                                                                           │
 │    189   gameLoop();                                                                               │
 │    190 }                                                                                           │
 │    191                                                                                             │
 │    192 // プログラムのエントリーポイント                                                           │
 │    193 main();                                                                                     │
 ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ {
    "tool_calls": [],
    "content": "次に、Node.jsの依存関係をインストールします。このゲームは標準ライブラリのみを使用するため、特
  に追加のパッケージは必要ありません。\n<tool_call>\n<function=run_shell_command"
  }
 ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮
 │ ✔  Shell cd /workspace/tictactoe && npm install (Node.jsの依存関係をインストール)                  │
 │                                                                                                    │
 │                                                                                                    │
 │    up to date, audited 1 package in 144ms                                                          │
 │                                                                                                    │
 │    found 0 vulnerabilities                                                                         │
 │                                                                                                    │
 ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ {
    "tool_calls": [],
    "content":
  "それでは、ゲームを起動して実際にプレイしてみましょう。\n<tool_call>\n<function=run_shell_command"
  }
 ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮
 │ ✔  Shell cd /workspace/tictactoe && npm start (ゲームを起動)                                       │
 │                                                                                                    │
 │                                                                                                    │
 │    > tictactoe-terminal@1.0.0 start                                                                │
 │    > node index.js                                                                                 │
 │                                                                                                    │
 │    Welcome to Terminal Tic-Tac-Toe!                                                                │
 │    Enter row and column (0-2) separated by space. Example: "1 2"                                   │
 │                                                                                                    │
 │                                                                                                    │
 │       |   |                                                                                        │
 │       |   |                                                                                        │
 │    ___|___|___                                                                                     │
 │       |   |                                                                                        │
 │       |   |                                                                                        │
 │    ___|___|___                                                                                     │
 │       |   |                                                                                        │
 │       |   |                                                                                        │
 │       |   |                                                                                        │
 │                                                                                                    │
 │                                                                                                    │
 │    Player X's turn                                                                                 │
 │    Enter row and column (0-2):                                                                     │
 ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ 三目並べゲームが正常に起動しました!

  ゲームの説明:
   - ターミナル上で動作する三目並べゲームです
   - プレイヤーはXとOのどちらかを選びます
   - ボードは3x3のグリッドで、各マスは0から2までの行番号と列番号で指定します
   - 例: "1 2" は2行目の3列目を意味します

  ゲームの流れ:
   1. ゲーム開始時にXのプレイヤーが先手になります
   2. 行番号と列番号をスペースで区切って入力してください(例: "1 2")
   3. 入力された位置にマークが置かれます
   4. 3つのマークが一直線になったらそのプレイヤーの勝ちです
   5. すべてのマスが埋まった場合、引き分けになります

  ゲームを終了するには、ゲーム終了時に「y」または「n」を入力してください。

  それでは、ゲームを楽しんでください!
kun432kun432
出力
╭──────────────────╮
│  > 起動方法は?  │
╰──────────────────╯

✦ 三目並べゲームを起動するには、以下の手順を行ってください:

   1. ターミナルで以下のコマンドを実行します:

   1    cd /workspace/tictactoe && npm start

   2. または、package.jsonに記載されているスクリプトを使って起動することもできます:

   1    cd /workspace/tictactoe && node index.js

  これでゲームが開始されます。ゲームの説明は前回の回答をご参照ください。

╭───────────╮
│  > /quit  │
╰───────────╯

╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│                                                                                                                       │
│  Agent powering down. Goodbye!                                                                                        │
│                                                                                                                       │
│  Interaction Summary                                                                                                  │
│  Tool Calls:                 5 ( ✔ 5 ✖ 0 )                                                                            │
│  Success Rate:               100.0%                                                                                   │
│  User Agreement:             100.0% (5 reviewed)                                                                      │
│                                                                                                                       │
│  Performance                                                                                                          │
│  Wall Time:                  2m 50s                                                                                   │
│  Agent Active:               2m 14s                                                                                   │
│    » API Time:               1m 33s (69.8%)                                                                           │
│    » Tool Time:              40.7s (30.2%)                                                                            │
│                                                                                                                       │
│                                                                                                                       │
│  Model Usage                  Reqs   Input Tokens  Output Tokens                                                      │
│  ───────────────────────────────────────────────────────────────                                                      │
│  unsloth_Qwen3-Coder-30B-A      17        132,516          6,948                                                      │
│  3B-Instruct-GGUF_Qwen3-Co                                                                                            │
│  der-30B-A3B-Instruct-UD-Q                                                                                            │
│  4_K_XL.gguf                                                                                                          │
│                                                                                                                       │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

実行してみた。

cd tictactoe/
npm start
出力
> tictactoe-terminal@1.0.0 start
> node index.js

Welcome to Terminal Tic-Tac-Toe!
Enter row and column (0-2) separated by space. Example: "1 2"


   |   |
   |   |
___|___|___
   |   |
   |   |
___|___|___
   |   |
   |   |
   |   |


Player X's turn
Enter row and column (0-2): 2 2


   |   |
   |   |
___|___|___
   |   |
   |   |
___|___|___
   |   |
   |   | X
   |   |


Player O's turn
Enter row and column (0-2): 1 1


   |   |
   |   |
___|___|___
   |   |
   | O |
___|___|___
   |   |
   |   | X
   |   |


Player X's turn
Enter row and column (0-2): 2 0


   |   |
   |   |
___|___|___
   |   |
   | O |
___|___|___
   |   |
 X |   | X
   |   |


Player O's turn
Enter row and column (0-2): 2 1


   |   |
   |   |
___|___|___
   |   |
   | O |
___|___|___
   |   |
 X | O | X
   |   |


Player X's turn
Enter row and column (0-2): 1 0


   |   |
   |   |
___|___|___
   |   |
 X | O |
___|___|___
   |   |
 X | O | X
   |   |


Player O's turn
Enter row and column (0-2): 0 1


   |   |
   | O |
___|___|___
   |   |
 X | O |
___|___|___
   |   |
 X | O | X
   |   |


Player O wins!

Do you want to play again? (y/n): n
Thanks for playing!
このスクラップは1ヶ月前にクローズされました