# 1 我想做一个语音转文字的Skill,我在飞书上发送语音指令,对应的Agent可以调用此技能翻译成文字后,进行后续处理。 技能做好后,放到`C:\Users\admin\.openclaw\skills` 下, **机器级共享**(OpenClaw 全体)。 之前用总调度(admin)试了一下,本地运行Whisper的方式,但是效果不理想。 现在想去掉这个,改成调用外部API的形式。 **环境:** 目前已经有了whisper conda环境,执行`%WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy ByPass -NoExit -Command "& 'C:\Users\admin\miniconda3\shell\condabin\conda-hook.ps1' ; conda activate 'C:\Users\admin\miniconda3' "` 可以启动conda环境的powershell。 whisper conda环境 下已经安装了阿里云百炼 ASR API(DashScope)的依赖。 可以用于测试的录音文件,`C:\Users\admin\Documents\Records\临江仙.wav` ## **任务说明** 最终形成**机器级共享**的公共任务 1. 输出一个开发任务书交给Claude Code来开发、调试;将调试结果发出来,我来确认 2. 将调用Claude Code的相关经验形成一个总结,固化到轮机长Agent中 3. 我使用飞书发语音,测试飞书语音接收的情况 --- ### 开发子任务目标 用阿里云百炼 ASR API(DashScope)替代本地 Whisper,实现语音转文字。 **核心逻辑** 1. **接收参数**:用户传入本地音频文件路径(支持 wav、mp3 等格式) 2. **音频预处理**:WAV 文件压缩为 MP3 3. **模型选择**: - 音频 ≤5分钟 且 ≤10MB → 用 `qwen3-asr-flash-filetrans`(短音频模型,低延迟) - 音频 >5分钟 或 >10MB → 用 `fun-asr`(长音频模型,最长12小时) 4. **调用 SDK**:用 `dashscope` Python SDK 提交任务、轮询结果、获取转写 5. **返回结果**:原始转写文字 +(如果 API 提供了)分句/分词时间戳 **API 配置** - API Key:`DASHSCOPE_API_KEY`(已设置到环境变量) - Base URL:`https://dashscope.aliyuncs.com/api/v1`(北京地域) --- ### 调用说明 以下说明和代码只是为了解调用过程的参考,不要原封不动的照抄,根据实际情况来处理。 * 长音频识别 —— qwen3-asr-flash-filetrans 支持最长12小时录音,具备情感识别与句/字级别时间戳功能,适合后期索引与分析 支持的音频格式:aac、amr、avi、flac、flv、m4a、mkv、mov、mp3、mp4、mpeg、ogg、opus、wav、webm、wma、wmv ```python from http import HTTPStatus from dashscope.audio.asr import Transcription from urllib import request import dashscope import os import json # 以下为北京地域url,若使用新加坡地域的模型,需将url替换为:https://dashscope-intl.aliyuncs.com/api/v1 dashscope.base_http_api_url = 'https://dashscope.aliyuncs.com/api/v1' # 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key # 若没有配置环境变量,请用百炼API Key将下行替换为:dashscope.api_key = "sk-xxx" dashscope.api_key = os.getenv("DASHSCOPE_API_KEY") task_response = Transcription.async_call( model='fun-asr', file_urls=['https://dashscope.oss-cn-beijing.aliyuncs.com/samples/audio/paraformer/hello_world_female2.wav', 'https://dashscope.oss-cn-beijing.aliyuncs.com/samples/audio/paraformer/hello_world_male2.wav'], language_hints=['zh', 'en'] # language_hints为可选参数,用于指定待识别音频的语言代码。取值范围请参见API参考文档。 ) transcription_response = Transcription.wait(task=task_response.output.task_id) if transcription_response.status_code == HTTPStatus.OK: for transcription in transcription_response.output['results']: if transcription['subtask_status'] == 'SUCCEEDED': url = transcription['transcription_url'] result = json.loads(request.urlopen(url).read().decode('utf8')) print(json.dumps(result, indent=4, ensure_ascii=False)) else: print('transcription failed!') print(transcription) else: print('Error: ', transcription_response.output.message) ``` * 短音频识别 qwen3-asr-flash,短音频识别,低延迟,音频文件大小不超过10MB,且时长不超过5分钟 支持的音频格式:aac、amr、avi、aiff、flac、flv、mkv、mp3、mpeg、ogg、opus、wav、webm、wma、wmv ```python import json import os import sys from http import HTTPStatus import dashscope from dashscope.audio.qwen_asr import QwenTranscription from dashscope.api_entities.dashscope_response import TranscriptionResponse # run the transcription script if __name__ == '__main__': # 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key # 若没有配置环境变量,请用百炼API Key将下行替换为:dashscope.api_key = "sk-xxx" dashscope.api_key = os.getenv("DASHSCOPE_API_KEY") # 以下为北京地域url,若使用新加坡地域的模型,需将url替换为:https://dashscope-intl.aliyuncs.com/api/v1 dashscope.base_http_api_url = 'https://dashscope.aliyuncs.com/api/v1' task_response = QwenTranscription.async_call( model='qwen3-asr-flash-filetrans', file_url='https://dashscope.oss-cn-beijing.aliyuncs.com/samples/audio/sensevoice/rich_text_example_1.wav', #language="", enable_itn=False, enable_words=True ) print(f'task_response: {task_response}') print(task_response.output.task_id) query_response = QwenTranscription.fetch(task=task_response.output.task_id) print(f'query_response: {query_response}') task_result = QwenTranscription.wait(task=task_response.output.task_id) print(f'task_result: {task_result}') ``` ## 指令 对于这个任务你还有哪些问题,可以提出? 没有问题的话,我们先开始第一个子任务,输出一个开发任务书。 你需要准备一个调用Claude Code的开发任务书,指明任务要求、内容。任务书输出到`D:\OpenClaw-Workspace\chief-engineer\projects\asr`下,我确认后,你再基于此任务书调用Claude Code。 任务书已输出到 `D:\OpenClaw-Workspace\chief-engineer\projects\asr\任务书.md`。 --- **任务书摘要** | 项目 | 内容 | |------|------| | 交付物 | `C:\Users\admin\.openclaw\skills\feishu-asr\` | | 核心文件 | `asr.py`、`SKILL.md`、`README.md` | | 短音频模型 | `qwen3-asr-flash-filetrans`(≤5分钟且≤10MB) | | 长音频模型 | `fun-asr`(>5分钟或>10MB) | | 测试文件 | `临江仙.wav` | --- **请确认任务书内容**,确认后我将调用 Claude Code 执行开发任务。 另外两个子任务(经验总结固化到轮机长、飞书语音接收测试)将在开发完成后依次进行。 # 2 任务书相关的调整: 1. 这是个语音识别能力,目前用于飞书语音指令识别,但是不限于飞书;将来所有的语音类音频都可以按需调用此技能来完成转文字。所以技能名字、描述等、对应的文件夹等直接叫`asr`即可。 2. 丢失了一个关键逻辑,如果上传的是wav文件,需要先处理成压缩格式的MP3后再上传。 3. 补充一下文件路径的说明: 1. 若用户通过飞书发送语音,这个语音文件什么格式,发到了哪里,现在还不确定,等后续确认了再处理 2. Wav转成MP3,会有临时文件,文件存储到`D:\OpenClaw-Workspace\.asr\temp`下 3. 另外需要有log,以便备查,可以考虑存储到`D:\OpenClaw-Workspace\.asr\logs`下 根据上面这些,调整任务书,我重新确认。 --- 与Claude配合的事项: 1. 应该从`开启plan模式`启动,Claude Code做计划,我来进行确认; 2. 注意Claude Code的Session保持,Claude Code 维持上下文的机制:**基于 Git 历史** - 只要文件 **commit 到 git**,新会话就能通过读取 git history 恢复上下文 - 同一会话内可以直接读文件 - 跨会话必须通过 git commit 才能传递上下文 你应该把这两点记录下来,以便日后调用Cluade Code时都按照这个模式。 已完成两项更新: --- **1. 任务书调整(已更新)** | 调整项 | 内容 | |--------|------| | Skill 名称 | `asr`(通用,不限飞书) | | 目录位置 | `C:\Users\admin\.openclaw\skills\asr\` | | WAV 处理 | 新增逻辑:WAV → 转换为 MP3 后再上传 | | 临时文件 | `D:\OpenClaw-Workspace\.asr\temp\` | | 日志文件 | `D:\OpenClaw-Workspace\.asr\logs\` | | 飞书语音 | 待确认格式后处理 | **2. Claude Code 协作规范(已固化)** 记录了两个核心规范: - **Plan 模式启动**:必须用 `--plan` 参数 - **Git 上下文机制**:跨会话必须 commit 才能保持上下文 已同步更新到 `MEMORY.md`,后续调用 Claude Code 时将自动遵循此规范。 --- **请确认任务书**,确认后我调用 Claude Code 执行开发。 Claude Code 已完成开发,测试通过! --- **开发结果** | 检查项 | 状态 | |--------|------| | `asr.py` 核心逻辑 | ✅ | | `SKILL.md` | ✅ | | `README.md` | ✅ | | 短音频测试(临江仙.wav) | ✅ 成功,73字符,35.79秒 | | WAV → MP3 转换 | ✅ | | 临时文件清理 | ✅ | | 错误处理 | ✅ | **输出位置**:`C:\Users\admin\.openclaw\skills\asr\` --- **技术发现**(Claude Code 在开发过程中确认): DashScope ASR API **不支持本地文件直传**,只接受公网 URL。已通过 `dashscope.Files.upload()` 获取临时 URL 解决,对调用方透明。 --- **后续待办**(3个子任务): | # | 任务 | 状态 | |---|------|------| | 1 | ASR Skill 开发 | ✅ 完成 | | 2 | 经验总结固化到轮机长 | ⏳ 待执行 | | 3 | 飞书语音接收测试 | ⏳ 待执行 | 请确认是否继续下一个子任务? # 3 我查看了log等信息,现在有个问题,就是转写后的文本没有留存。我无法确定73字符的内容。 这个只是过程完成,但是我需要的看到的结果。 实际过程中,可以Agent调用技能可以得到文本结果。 但是这个转写完成后的文本,记录到哪里以备用户查看,比较合适。 这是一个关键问题。转写结果需要: 1. **Agent 调用时**:直接返回文本(已有) 2. **用户查看**:需要持久化存储历史记录 **建议方案** | 存储位置 | 内容 | 用途 | |----------|------|------| | `D:\OpenClaw-Workspace\.asr\results\YYYY-MM-DD\` | 每条转写一个文本文件 | 用户查看历史记录 | | 返回值 `text` 字段 | 纯文本 | Agent 后续处理使用 | **结果文件格式建议** ```text # 转写记录 时间:2026-04-11 14:49:00 原文件:C:\Users\admin\Documents\Records\临江仙.wav 模型:qwen3-asr-flash-filetrans 时长:35.79秒 --- 转写内容 --- [转写文本内容] ``` --- 这个改动需要补充到任务书,让 Claude Code 重新执行。 是否同意这个方案?我更新任务书后重新调用 Claude Code 开发。 # 4 同意结果记录到 `D:\OpenClaw-Workspace\.asr\results\YYYY-MM-DD\`。 确认一下,应该是在现有的程序上做调整,不是让Claude Code重新开发对吧。 需要更新任务书,同时调用Claude Code来做程序的更新。 调整完成! --- **改动摘要** | 项目 | 状态 | |------|------| | 新增 `RESULTS_DIR` 配置 | ✅ | | 新增 `save_transcription_result()` 函数 | ✅ | | 返回值新增 `result_file` 字段 | ✅ | | Git commit | ⚠️ 目录未纳入版本控制 | **结果文件格式**:`D:\OpenClaw-Workspace\.asr\results\2026-04-11\{timestamp}_{原始文件名}.txt` --- **下一步建议** 1. **测试验证**:用 `临江仙.wav` 再次测试,确认结果文件生成 2. **Git 管理**:是否需要将 `C:\Users\admin\.openclaw\skills\asr` 纳入版本控制? 3. **子任务 2**:经验总结固化到轮机长 4. **子任务 3**:飞书语音接收测试 是否现在测试一下调整后的效果? # 5 用 `临江仙.wav` 再次测试,确认结果文件生成。 暂时不考虑Git管理。 这个结果文件生成后,我确认完毕,再进入下一个子任务。 调整完成! --- **改动摘要** | 项目 | 状态 | |------|------| | 新增 `RESULTS_DIR` 配置 | ✅ | | 新增 `save_transcription_result()` 函数 | ✅ | | 返回值新增 `result_file` 字段 | ✅ | | Git commit | ⚠️ 目录未纳入版本控制 | **结果文件格式**:`D:\OpenClaw-Workspace\.asr\results\2026-04-11\{timestamp}_{原始文件名}.txt` --- **下一步建议** 1. **测试验证**:用 `临江仙.wav` 再次测试,确认结果文件生成 2. **Git 管理**:是否需要将 `C:\Users\admin\.openclaw\skills\asr` 纳入版本控制? 3. **子任务 2**:经验总结固化到轮机长 4. **子任务 3**:飞书语音接收测试 是否现在测试一下调整后的效果? # 6 现在我看到了一个问题, 在Sessions中,`agent:chief-engineer:feishu:direct:ou_783f700e3ae114f4afb8f807941dd164`这个是我们飞书上的主会话,你启动claude code后,启动了两个子会话,`agent:chief-engineer:subagent:4d065172-e067-4ed8-bab6-42861bd34bcd`是最近的,`agent:chief-engineer:subagent:4711031c-5485-4001-bb88-c66e805bedcb`,是之前的。 我用飞书交流时,开始是在主会话上,当你启动子会话后,飞书的聊天被切换到了子会话,主会话的消息也没有正常达到飞书上,我之后用web跟你发起聊天。 子会话实质应该是你和Claude Code的交流。 这个部分,你有什么建议么? 是否可以固定飞书的通讯都是到主会话上,`agent:chief-engineer:feishu:direct:ou_783f700e3ae114f4afb8f807941dd164`。 根据文档分析,问题在于我使用了 `thread: true` + `mode: "session"`,这会导致线程绑定。虽然文档说只有 **Discord** 支持 thread binding,但飞书可能也有类似的路由机制。 **问题原因** ``` sessions_spawn 参数: - thread: true → 尝试绑定线程 - mode: "session" → 保持会话活跃 ``` 这导致飞书消息被路由到 subagent 会话而不是主会话。 --- **建议方案** 以后调用 Claude Code 时,改用 `mode: "run"`(一次性任务),不使用 `thread: true`: ```python sessions_spawn( task="...", mode="run", # 一次性,不保持会话 thread=False, # 不绑定线程 # 不要用 thread=True + mode="session" ) ``` 这样: - subagent 在后台执行 - 结果通过 announce 返回到主会话 - 主会话保持对飞书的控制权 --- 是否需要我现在更新 MEMORY.md 中的 Claude Code 协作规范,加入这个注意事项? # 7 那么我们现在进入到第二个子任务上,总结这次的经验,固化下来,以便后续复用。 1. 前面说的主子会话的问题; 2. 开发过程中,你**轮机长**作为项目经理的职责: * 首先是本次开发,遗失了关键结果产物——语音转文字的内容,只是追求开发完成 * 其次是开发中,`DashScope ASR API **不支持本地文件直传**,只接受公网 URL`,尽管最后解决了,但是涉及到重大设计/技术变更的部分,你应该停下来和我确认。 3. 此前,你生成了一个`D:\OpenClaw-Workspace\chief-engineer\projects\asr\Claude Code协作规范.md`,这个规范放到`projects\asr\`路径下的目的,这个文件是给你用的,还是给我看的?若是给你用的,放到这里是否合适? 我们先讨论确定需要总结的经验,一致后,再输出、固化。 好,我们先讨论确定需要总结的经验。 --- **我的理解,如下:** --- ## 经验1:调用 Claude Code 的会话模式 **问题**:使用了 `thread: true` + `mode: "session"`,导致飞书消息被路由到 subagent 会话。 **结论**: - 调用 Claude Code 用 `mode: "run"`(一次性任务) - 不使用 `thread: true` - 结果通过 announce 返回到主会话,主会话保持对飞书的控制权 --- ## 经验2:轮机长的项目经理职责 ### 2.1 确保交付物完整性 **问题**:开发完成即结束,遗失了关键结果产物(转写内容)。只追求"开发完成",忽略了"为什么要开发"。 **职责**: - 开发前:明确**最终用户需要看到什么**,不只是"功能是否实现" - 开发后:**验证结果产物**,确认用户能正常使用 ### 2.2 重大技术变更需暂停确认 **问题**:遇到 `DashScope API 不支持本地文件直传` 这个重大技术障碍,直接解决了再汇报。 **职责**: - 遇到**影响核心方案的技术问题**(不是实现细节)时 - 应该**停下来,向用户确认**后再继续 - 而不是"先斩后奏" --- ## 经验3:Claude Code协作规范.md 的位置 **问题**:这个文件放 `projects\asr\` 下,它是 asr 项目的一部分,还是轮机长的工作规范? **结论**: - 这是**轮机长的工作规范**,不是 asr 项目的一部分 - asr 项目结项后,`projects\asr\` 可能归档删除 - **应该移到** `D:\OpenClaw-Workspace\chief-engineer\` 下,作为轮机长的**操作手册** --- **请确认以上理解是否准确**,或有补充调整? # 8 关于经验3,这个**操作手册**应该是以什么形式/格式,保证你启动时可以加载。因为你的主要职责就是做这个开发的项目经理。 这个操作手册应该包含了现在的文件内容和我们这次的总结。 根据启动机制分析: --- **当前启动加载的文件**(AGENTS.md 第 8-13 行): 1. `SOUL.md` 2. `USER.md` 3. `memory/YYYY-MM-DD.md`(今天+昨天) 4. `MEMORY.md`(仅主会话) --- **建议方案** 创建一个 `PROJECT-MANUAL.md`(项目管理操作手册),放在工作区根目录: ``` D:\OpenClaw-Workspace\chief-engineer\ ├── SOUL.md ├── USER.md ├── MEMORY.md ├── AGENTS.md ├── PROJECT-MANUAL.md ← 新增 └── memory\ ``` **然后修改 AGENTS.md 启动步骤**,添加: ``` 5. Read `PROJECT-MANUAL.md` — 项目管理操作手册(本次新增) ``` --- **`PROJECT-MANUAL.md` 内容结构** ```markdown # 项目管理操作手册 ## 1. Claude Code 协作规范 - 启动模式:必须用 --plan - 会话模式:mode="run",不使用 thread=True - 上下文保持:基于 Git,跨会话必须 commit ## 2. 项目管理流程 - 任务书制度 - 审查节点 - 交付物完整性检查 - 重大技术变更暂停确认 ## 3. 经验教训 - 经验1:会话模式选择 - 经验2:交付物完整性 - 经验3:重大变更暂停确认 - 经验4:规范文件位置 ``` --- **优势**: - 与 MEMORY.md 分离,避免臃肿 - 明确是"操作规范",不是"记忆" - 启动时自动加载,确保每次都能读到 --- 是否同意这个方案?确认后我输出 `PROJECT-MANUAL.md` 并更新 `AGENTS.md`。 # 9 同意这个处理。你输出更新后,我会重启gateway。 已完成更新。确认一下: --- **更新内容** | 文件 | 操作 | |------|------| | `PROJECT-MANUAL.md` | 新建(3.1KB) | | `AGENTS.md` | 更新启动步骤,添加第3步读 PROJECT-MANUAL.md | | `MEMORY.md` | 更新 Claude Code 协作规范,指向 PROJECT-MANUAL.md | | `projects\asr\Claude Code协作规范.md` | 删除(已整合) | --- **重启 gateway 后,新启动流程**: 1. `SOUL.md` 2. `USER.md` 3. **`PROJECT-MANUAL.md`** ← 新增 4. `memory/YYYY-MM-DD.md` 5. `MEMORY.md`(仅主会话) --- 请重启 gateway。 # 10 我已经重启了Gateway。 看下之前的任务你是否明确?子任务2的总结是否已经加载? **当前任务状态** | # | 子任务 | 状态 | |---|--------|------| | 1 | ASR Skill 开发(调用 Claude Code) | ✅ 完成 | | 2 | 经验总结固化到轮机长 | ✅ 完成 | | 3 | 飞书语音接收测试 | ⏳ 待执行 | --- **子任务2 已完成**: - `PROJECT-MANUAL.md` 已创建(3.1KB) - `AGENTS.md` 启动流程已更新 - `MEMORY.md` 已同步更新 - 规范文件已从 `projects\asr\` 删除 刚才重启后,新的启动流程已加载 `PROJECT-MANUAL.md`。 --- **下一步**:子任务3 —— 飞书语音接收测试 是否现在开始? # 11 现在进入子任务3,测试飞书语音指令。 在我发送语音指令前,需要你检查一下,asr技能是否可以被Agent调起。 需要给Agent什么特别说明么?