Skip to Content
ユーザーガイド機能チャンネルQQ Bot

QQ Bot (QQ机器人)

このガイドでは、公式の QQ Bot Open Platform API を介して QQ 上に Qwen Code チャンネルを設定する方法を説明します。

前提条件

  • QQ アカウント(QR コードをスキャンするためのモバイルアプリ)

セットアップ

QR コードログイン

チャンネルを起動します。初回起動時に QR コードが表示されます。QQ アプリでスキャンして有効化します。開発者アカウントや手動登録は不要です。認証情報は自動的に保存され、再利用されます。

{ "channels": { "my-qq": { "type": "qq" } } }
qwen channel start my-qq # Scan the QR code in the terminal with your QQ app

手動設定(開発者ポータル)

QQ Bot Open Platform  の開発者ポータルにアプリを登録している場合は、そこから取得した認証情報を使用することもできます。

{ "channels": { "my-qq": { "type": "qq", "appID": "YOUR_APP_ID", "appSecret": "$QQ_APP_SECRET" } } }

シークレットを環境変数として設定します。

export QQ_APP_SECRET=<your-app-secret>

設定

{ "channels": { "my-qq": { "type": "qq", "appID": "YOUR_APP_ID", "appSecret": "$QQ_APP_SECRET", "sandbox": false, "privatePolicy": "open", "sessionScope": "thread", "cwd": "/path/to/your/project", "instructions": "你是一个通过 QQ Bot 对话的 AI 助手。回复控制在 2000 字符以内。", "groupPolicy": "disabled", "groups": { "*": { "requireMention": true } } } } }

sessionScope のデフォルトは "thread" です。groupPolicy: "disabled" の場合、これは DM のみのセットアップであり、"thread" と "user" の両方が各ダイレクトメッセージに独自のコンテキストを提供します。"thread" は、以下のセッション分離セクションとの一貫性のためにここに示されています — 既存のチャンネルでスコープを変更する前に、そのセクションのアップグレードノートを読んでください。

QQ 固有のオプション

オプションデフォルト説明
appID—開発者ポータルの QQ Bot AppID。省略すると QR コードログインが使用されます。
appSecret—QQ Bot AppSecret。$ENV_VAR 構文をサポート。省略すると QR コードログインが使用されます。
sandboxfalsetrue に設定すると、QQ サンドボックス API 環境(sandbox.api.sgroup.qq.com)が使用されます。
purgeLegacySessionsfalseレガシーな single-scope および user-scope セッションルートを削除します — 以下を参照。

すべての標準チャンネルオプション(チャンネル概要 を参照)もサポートされています: privatePolicy, allowedUsers, sessionScope, cwd, instructions, groupPolicy, groups, dispatchMode。

実行

# QQ チャンネルのみ起動 qwen channel start my-qq # または、設定済みのすべてのチャンネルを一緒に起動 qwen channel start

QQ を開いて、ボットにメッセージを送信してください。チャットに応答が表示されるはずです。

グループチャット

QQ グループでボットを使用するには:

  1. チャンネル設定で groupPolicy を "allowlist"、"pairing"、または "open" に設定します
  2. QQ Bot Open Platform のダッシュボードから、またはグループ管理者に招待してもらって、ボットを QQ グループに追加します
  3. グループメンバーは応答をトリガーするためにボットを @メンション する必要があります
  4. groupPolicy: "pairing" を使用している場合、応答が開始される前にグループのペアリングリクエストを一度承認してください。グループが承認されると、そのグループのどのメンバーでもデフォルトでボットを使用できることに注意してください(グループの senders: "allowlist" と allowedUsers で制限)。privatePolicy と最上位の allowedUsers は、承認されたグループのメンバーを制限しません。

QQ Bot API V2 は、ボットを @メンションしたグループメッセージのみを配信します。ボットはすべてのグループメッセージを認識しません。デフォルトでは requireMention は true であり、QQ ではそのままにしておく必要があります。

セッション分離

QQ チャンネルのデフォルトは sessionScope: "thread" です:同じグループのメンバーは <channel>:<group_openid> でキー付けされた単一の会話コンテキストを共有し、異なるグループは互いに分離されます。各ダイレクトメッセージは <channel>:<user_openid> でキー付けされた独自のコンテキストを持ちます。

グループスレッドセッションは共有セッションです:グループの全メンバーが同じ会話履歴を読み取り、継続します。そのセッションで動作するコマンド(/clear、/cancel、/who、/status、/loop、/btw)、許可の回答(/approve、/approve-always、/deny)、および進行中のターンのステアリング(メンバーのプレーンメッセージはステアリングではなく、実行中のターンの背後にキューに入れられます)は、チャンネルの operators にリストされたメンバーに制限されます。allowedUsers と senderPolicy はそれらを付与しません。operators はデフォルトでは設定されていないため、デフォルトでは共有グループセッションにはそれらのコマンドを実行できるメンバーがいません。また、グループで発生した許可リクエストに回答できるメンバーもいません。operators をグループセッションを操作できるメンバーの QQ ID に設定してください。グループメッセージの場合、ゲートはそのメンバーの member_openid — 上記のグループスコープの allowedUsers と同じ ID スペース — に対して比較され、ダイレクトメッセージの user_openid ではありません。user_openid で埋められたリストは誰もマッチせず、リストが空ではないため起動時の警告は静かなままとなり、上記のすべてのコマンドが単に拒否されます。single または chat_thread スコープのチャンネルも同様に影響を受けます。それらのスコープもダイレクトメッセージセッションを共有するためです。! ホストシェルプレフィックスは、スコープに関係なく、すべての QQ グループチャットで無効です。ダイレクトメッセージでは、同じスコープルールが決定します — ! はセッションを1人の送信者にプライベートに保つスコープ("thread" または "user")でのみ実行され、"single" または "chat_thread" の下での DM およびすべてのグループメッセージでも拒否されます。グループメンバーが履歴を共有したり、互いのターンを制御したりすべきでない場合は、sessionScope: "user" を設定するか(各メンバーがプライベートセッションを取得します — 既存のチャンネルを切り替える前に以下のアップグレードノートを参照)、グループスコープの senders / allowedUsers 制御でボットと会話できるメンバーを制限します。例えば groups: {"*": {"senders": "allowlist", "allowedUsers": ["<member_openid>"]}}。最上位の senderPolicy、allowedUsers、privatePolicy はダイレクトメッセージのみを管理し、承認されたグループのメンバーを制限しません。

フルメッセージモードの場合、このデフォルトはすでに必要なものです — groupAllPolicy: "all" で、"thread" を維持すると、グループ内の共有コンテキストとグループ間の分離が得られます。代わりに sessionScope: "user" を設定すると、フルメッッセージトラフィックは送信者ごとにフラグメント化され(メンバーごとに個別のセッション)、グループフルメッセージシナリオには適していません。

他の共有スコープは同等ではありません。sessionScope: "chat_thread" はグループメッセージを "thread" とまったく同じようにルーティングします(QQ はスレッド ID を提供しないため、両方とも <channel>:<chatId> でキー付けされます)が、すべてのチャットに対して共有として扱われます。各ダイレクトメッセージは operators ゲートの影響を受ける共有セッションとなり、DM での /clear は拒否され、DM で発生したツールコールに回答するメンバーがいません。sessionScope: "single" もダイレクトメッセージを共有し、さらにすべてのグループとすべてのダイレクトメッセージを1つのコンテキスト <channel>:__single__ でキー付けするため、グループ A の履歴がグループ B の履歴や DM の履歴で回答されます — このセクションで問題としているクロスグループ漏洩です。チャンネルは、groupAllPolicy が "keyword" または "all" でスコープが "thread" でない場合、起動時に警告します。multiSession が有効な場合、スコープは "user" に固定されるため、その警告は2つの設定が相互に排他的であることを示し、オペレーターに groupAllPolicy を変更するか multiSession をオフにするよう指示します。"thread" を推奨するのではなく。

古い QQ チャンネルからのアップグレード。 デフォルトスコープは "user" から "thread" に変更されました。(groupAllPolicy: "keyword" または "all" で設定されたチャンネルは、以前は "single" — 1つのグローバルセッション — に強制されていました。sessionScope を固定しなかったチャンネルは、multiSession がオンでない限り "thread" に解決されるようになりました。multiSession がオンの場合は "user" に解決されます — プリセッションセッションは multiSession がサポートする唯一の形状であるため、プラグインのデフォルトに勝ちます。multiSession のないチャンネルの場合、実効の変更はそのグローバルセッションからグループごとのセッションへの変更です。sessionScope を明示的に固定したチャンネルはもはや上書きされないため、明示的な "user" が有効になり、グループフルメッッセージトラフィックを送信者ごとにフラグメント化します — 手動で "thread" に変更してください。)レガシーセッションルートは自動的に削除されません。purgeLegacySessions がオフの間、各起動時に削除対象のルート — single-scope 時代の <channel>:__single__ キーと、このチャンネルのレガシー user-scope <channel>:<senderId>:<chatId> キー — をカウントし、それらとデーモン側のセッションをそのまま残します。chat_thread 時代の <channel>:<chatId>:<threadId> ルートは対象外です。カウントも削除もされません。対象のルートを削除するには、チャンネルで purgeLegacySessions を true に設定します。有効にすると、そのようなルートはすべて解放され、デーモン側のセッションは破棄されるため、古い会話マッピングは存在しなくなります。削除は、レスキューコピーがディスクに到達しない限り実行されません。最初の削除の直前に、チャンネルは削除対象のルートをチャンネル状態ディレクトリの <channel>-sessions-purged.json に追加します(例:~/.qwen/channels/my-qq-sessions-purged.json。チャンネル名の A-Za-z0-9_- 以外の文字は _ になります)。その書き込みが失敗した場合、何も削除せず、ルートをそのまま残します。ファイルは削除が実際に発生する場合にのみ書き込まれます — すべてをそのままにする起動時には決して書き込まれません。これは purge レコードの JSON リストで、各レコードには purgedAt タイムスタンプ、sessionScope、routes があります。各ルートは kind、key、sessionId、target、および(ルーターの永続化されたセッション状態にまだ記録されている場合)cwd、turns、startedAt、isolation、workspaceCwd を持ちます。レコードは上書きではなく追加されるため、以前の purge のレコードは後の purge でも生き残ります(リストが20を超えると、最も古いレコードは削除されます)。読み取れないファイル、または purge レコードリストを保持していないファイルは、ログで報告され、新しいレコードが書き込まれる前に兄弟の <path>.corrupt-<timestamp> ファイルに名前が変更されるため、損傷したコピーは手動 recovery 用に保持され、新しいレコードが失われることはありません。これはルーティングメタデータのみで、会話コンテンツではないため、何が削除されたかを示し、マッピングを手動で再作成できます。レコードに cwd が含まれ、isolation がないルートは、復元時に同じワークスペースに解決されます。レコードに isolation: "worktree" も含まれている場合、cwd は(通常はすでに削除された)セッションごとのワークツリーであるため、レコードの workspaceCwd に対して復元し、isolation を保持して、ルーターが管理されたロードパスを通じて再アタッチできるようにします。ルートの保存された状態に cwd がない場合(エントリがないか、フィールドより前のもの)、レコードは推測せずに cwd を省略するため、そのようなルートを手動で復元するにはチャンネルのワークスペースパスも必要です。purge 後に sessionScope: "user" を設定しても、古い会話はそれ自体では戻りません — それらのルートはすでに削除されており、自動的に復元されません。

グループポリシーとメンション制限の詳細については、グループチャット を参照してください。

Markdown サポート

QQ Bot チャンネルは Markdown 書式(msg_type=2)をサポートしています。エージェントの Markdown 応答はそのまま送信され、QQ はそれをリッチな書式(太字、斜体、コードブロック、リンク、リスト)でレンダリングします。

何らかの理由で QQ サーバーが Markdown メッセージを拒否した場合、チャンネルは自動的にプレーンテキストとして再試行します。そのため、ボットの Markdown 機能がサーバー側で制限されていても、メッセージは常に配信されます。

これは、すべての Markdown を削除する WeChat チャンネルとは逆です。QQ チャンネルでは、エージェントに完全な Markdown を使用させることができます。

画像、動画、ファイル、音声

ユーザーはボットに画像、動画、ファイル、音声メッセージを送信できます。画像は vision input としてエージェントに渡されるため、エージェントは画像自体を認識します — スクリーンショット、エラーメッセージ、図表などすべて機能します — また、同じファイルはモデルが画像を受け取れない場合のために一時的なローカルパスにも保存されます。動画、ファイル、または音声メッセージは一時的なローカルパスに保存され、そのパスがエージェントに伝えられます。

メディアメッセージにテキストは不要です — 画像のみ、動画のみ、ファイルのみ、または音声のみのメッセージでもターンが開始され、チャンネルはプレースホルダーとして (image)、(video)、(file)、または (voice) を使用します。メッセージにテキストが含まれている場合、そのテキストがキャプションとして保持されます。

ファイルとは、プラットフォームが content_type: file として配信する任意のものです — Word 文書、PDF、スプレッドシート、アーカイブ、ソースファイルなど。QQ はファイルの MIME タイプを報告しないため、チャンネルは送信者の拡張子を保持し(英数字に制限され、存在しない場合は .bin にフォールバック)、システム一時ディレクトリの channel-files/<uuid>/ 以下にバイトを保存します。エージェントの読み取りツールはその拡張子を使用してドキュメントの開き方を判断します。

音声メッセージは content_type: voice として届きます。元の SILK URL に加えて、QQ は voice_wav_url — 同じオーディオを WAV に変換したもの — を提供し、チャンネルはこれを優先します。プラットフォームが送信しない場合、チャンネルは元の URL にフォールバックします。バイトは channel-files/<uuid>/ 以下に qq_voice.wav として保存され、元の SILK URL が使用された場合は qq_voice.silk として保存され、そのパスがエージェントに伝えられます。プラットフォームの ASR テキストは使用されず、エージェントは保存されたオーディオファイルから処理します。

  • 画像は 8 MB、動画・ファイル・音声メッセージは 20 MB に制限されており、1 メッセージあたり最大 5 つの添付ファイルまで処理されます。それより大きいものはスキップされ、チャンネルの stderr にログが記録されます。
  • ダウンロードされたファイルは削除されません。システムの一時ディレクトリに保持され、システムのスケジュールに従ってクリアされます。

画像入力には画像を受け付けるモデルが必要です。モデルがマルチモーダルだが名前で認識されない場合は、機能を宣言してください。このフィールドはモデルを提供するプロバイダーエントリに配置する必要があります。トップレベルの model.generationConfig の値はプロバイダーバックモデルでは無視されるためです。

{ "modelProviders": { "openai": [ { "id": "your-model-id", "baseUrl": "https://example.invalid/v1", "generationConfig": { "modalities": { "image": true } } } ] } }

テキストのみのモデルでは、エージェントは画像の代わりに「サポートされていない画像」のメモと保存されたパスを受け取ります。

QQ に画像、動画、ファイル、または音声メッセージを送信することはサポートされていません。

トークン管理

アクセストークンは約2時間で期限切れになります。チャンネルは TTL の80%(通常約1.6時間)で自動的に更新します。更新に失敗した場合、60秒後に再試行します。

トークン更新は WebSocket の再接続をまたいで継続されます。AppID と AppSecret が有効である限り、トークン期限切れによってチャンネルがオフラインになることはありません。

接続の回復力

  • 自動再接続: WebSocket 切断時、チャンネルは指数バックオフで再試行します(最大20回、再試行間隔最大30秒)
  • セッション再開: WebSocket が一時的に切断された場合、チャンネルは QQ の RESUME オペコードを使用して、進行中のメッセージを失うことなくセッションを復元します
  • クロスサーバーコンテキストの継続: チャットセッションとルーティング状態はディスクに永続化されます。デーモンが再起動しても、会話は中断したところから継続されます。
  • ハートビート監視: HEARTBEAT_ACK のタイムアウトを検出し、ゾンビ接続を回避するために強制的に再接続します。
  • メッセージ重複排除: 再接続後のリプレイメッセージを検出してスキップします。

ヒント

  • Markdown を自由に使う — WeChat とは異なり、QQ は Markdown をネイティブにレンダリングします。太字、コードブロック、リスト、リンクがすべて機能します。
  • 応答は2000文字以内に抑える — 長い応答は自動的に分割されます。指示に長さのヒントを追加すると、エージェントが簡潔さを保つのに役立ちます。
  • テスト用サンドボックス — 開発中にサンドボックス API を使用するには "sandbox": true を設定します。本番メッセージには影響しません。
  • アクセス制限 — 固定の QQ ユーザーセットには privatePolicy: "allowlist" を、CLI から新しいユーザーを承認するには "pairing" を使用します。詳細は DM ペアリング を参照してください。

Telegram との主な違い

項目QQ BotTelegram
認証QR コードログインまたは AppID/AppSecretBotFather からの静的ボットトークン
Markdownネイティブ QQ Markdown(プレーンテキストフォールバック付き)エージェント Markdown からの HTML 形式
トークンライフサイクル2時間 TTL、80%で自動更新永続的ボットトークン
グループメッセージ@メンションされたメッセージのみボットに配信ボットは全メッセージを認識(プライバシーモードオフ時)
入力中インジケーター利用不可(QQ API の制限)“作業中…” メッセージ
サンドボックスモードテスト用にサポート利用不可

トラブルシューティング

ボットが応答しない

  • ターミナル出力でエラーを確認
  • チャンネルが実行中であることを確認(qwen channel status)
  • privatePolicy: "allowlist" を使用している場合、QQ ユーザー ID が allowedUsers に含まれていることを確認
  • 初回起動時、ターミナルに QR コードが表示されます。QQ アプリでスキャンしてください。

グループ内でボットが応答しない

  • groupPolicy が "allowlist"、"pairing"、または "open" に設定されていることを確認(デフォルトは "disabled")
  • "pairing" を使用している場合、グループのペアリングリクエストが承認されていることを確認してください
  • ボットを @メンションする必要があります — QQ はボットをタグ付けしたメッセージのみ配信します
  • ボットがグループに追加されていることを確認

QR コードログインが停止する

  • QR コードはターミナルに表示されます。QQ モバイルアプリでスキャンしてください(私 → スキャン)
  • QR コードが期限切れになった場合(通常数分後)、チャンネルを再起動して新しいコードを取得してください。

Markdown メッセージがプレーンテキストで表示される

  • QQ サーバーが Markdown メッセージを拒否し、チャンネルが自動的にプレーンテキストにフォールバックした可能性があります。ターミナルで "Markdown rejected" のログメッセージを確認してください。
  • QQ Bot Open Platform では珍しいですが、ボットの Markdown 機能がサーバー側で制限されている場合に発生する可能性があります。

長時間のダウンタイム後にトークンが期限切れになる

  • チャンネルが2時間以上オフラインになると、アクセストークンが期限切れになります。チャンネルは再接続時に新しいトークンを取得します。操作は不要です。
  • AppSecret 自体が無効な場合(例:開発者ポータルでローテーションされた場合)、appSecret フィールドを更新するか、~/.qwen/channels/<name>-credentials.json を削除して QR コードログインを再トリガーしてください。
Last updated on