From 7b55e8d117c438e1e0de798d2dfc533d15edd9f1 Mon Sep 17 00:00:00 2001 From: hilr Date: Sat, 27 Jun 2026 14:50:05 +0000 Subject: [PATCH] feat: prefer QQ platform asr_refer_text for voice transcription MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QQ voice messages carry the platform's own ASR result in the attachment payload (asr_refer_text). Use it directly — no audio download or SILK transcoding needed. Falls back to GLM ASR only when the field is absent. Co-Authored-By: Claude Opus 4.7 --- CLAUDE.md | 2 +- src/agent_box/channels/qq.py | 26 ++++++++++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 8739ed6..0fc1f33 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -70,7 +70,7 @@ src/agent_box/ - `WEIXIN_ACCOUNT_ID` — weixin_sdk account id (from login) - `QQBOT_APP_ID` — QQ Bot application ID - `QQBOT_CLIENT_SECRET` — QQ Bot client secret -- `GLM_API_KEY` — ZhipuAI (GLM) API key for voice-to-text. Empty skips QQ voice transcription (falls back to file path only). +- `GLM_API_KEY` — ZhipuAI (GLM) API key for voice-to-text (fallback). QQ voice messages prefer the platform-provided `asr_refer_text`; this key is used only when that field is absent. - `GLM_ASR_MODEL` — GLM ASR model id (default: `glm-asr-2512`) - `PROJECTS_DIR` — where project folders live (default: `data/projects`) - `ROUTER_MODEL` — model override for router (optional) diff --git a/src/agent_box/channels/qq.py b/src/agent_box/channels/qq.py index 52a0e3f..702e030 100644 --- a/src/agent_box/channels/qq.py +++ b/src/agent_box/channels/qq.py @@ -591,8 +591,9 @@ async def _download_atts( ) -> list[tuple[str, str, str | None]]: """Download each attachment. Returns list of (label, local_path, transcription). - transcription is populated for audio attachments when ASR is configured - and succeeds; otherwise None. + Voice attachments prefer the platform-provided ``asr_refer_text`` (QQ's + own ASR result) — when present, no download or transcoding is needed. + Falls back to downloading the audio and running GLM ASR. """ results: list[tuple[str, str, str | None]] = [] for att in atts: @@ -600,9 +601,26 @@ async def _download_atts( label = self._att_label(att) local = "" transcription: str | None = None - if url: + + # Voice: prefer QQ platform ASR text (no download/transcode needed) + if label == "语音": + asr_text = (att.get("asr_refer_text") or "").strip() + if asr_text: + transcription = asr_text + log.info( + "QQ voice: using platform asr_refer_text (%d chars)", + len(asr_text), + ) + else: + log.debug( + "QQ voice attachment has no asr_refer_text; keys=%s", + list(att.keys()), + ) + + # Download when no platform transcription (voice fallback or non-voice) + if url and transcription is None: local = await self._download_image(url, att.get("filename")) or "" - # Transcribe voice attachments when ASR is available + # Fall back to GLM ASR for voice when platform text unavailable if label == "语音" and local and self._asr is not None: transcription = await self._asr.transcribe(local) results.append((label, local, transcription))