asr Plugin
详细说明 asr 内建 plugin 如何安装转写依赖、配置模型、显式转写音频,以及在 chat 入站链路里自动增强语音消息
asr Plugin
asr 是当前最能体现“plugin 不只是 action 集合,还能直接参与运行链路”的内建 plugin 之一。
它的核心目标很直接:
- 让语音或音频附件能被转成文本
- 让 agent 能像处理普通文字消息一样处理语音输入
它到底做什么
asr 主要负责三类事情:
- 安装和配置语音转写依赖
- 提供显式的本地音频转写 action
- 在 chat 入站时自动把语音附件增强成文本片段
所以它既是:
- 显式工具能力
- 运行时中间层增强能力
它使用了哪些 plugin 能力
asr 用到了:
configsetupusageavailabilityactionshooks.pipelinesystem
这里最值得注意的是 hooks.pipeline。
这意味着 asr 不只等你手动调用 transcribe,它还可以主动参与消息进入 agent 之前的增强流程。
它为什么是很重要的参考实现
如果你想设计这种能力:
- 输入先预处理
- 预处理结果再交给 agent
- 预处理失败时尽量不要把主链路直接打死
那 asr 是非常好的参考。
因为它当前就采用了这种策略:
- 有语音附件时尝试转写
- 转写成功则把文本追加进消息增强区
- 转写失败则 best-effort 跳过,不阻塞主流程
典型运行流
在自动增强模式下,一条语音消息通常这样流动:
- chat 入站消息带着
voice或audio附件进入 asr的 pipeline hook 检查附件列表- 对每个有效音频文件尝试调用转写依赖
- 若成功,把转写文本追加到
pluginSections - 追加后的增强消息继续交给后续 agent 处理
这个链路的关键不是“替代原消息”,而是“在原消息上增加可读文本上下文”。
典型场景
场景一:Telegram 语音消息希望直接让 agent 理解
这是 asr 最典型的使用方式。
用户发来语音,系统先转写,再把文字交给 agent。
这样后面的规划、工具调用和回答逻辑都可以继续以文本心智工作。
场景二:你手上有一个本地音频文件,只想单独拿到转写结果
这时不需要走完整 chat 链路,直接调用 transcribe action 即可。
场景三:你想保留语音能力,但暂时不想自动增强每条消息
这时可以保留 plugin 启用,同时关闭 augmentMessage。
这样 transcribe 仍然能显式调用,但入站消息不会自动附加转写文本。
在 SDK 里怎么挂载
import { Agent } from "@downcity/agent";
import { AsrPlugin } from "@downcity/plugins";
const agent = new Agent({
id: "voice-agent",
path: "/path/to/project",
tools: {},
plugins: [new AsrPlugin()],
});关键配置项
| 配置项 | 作用 | 说明 |
|---|---|---|
injectPrompt | 是否注入 ASR 提示词 | 建议大多数语音场景开启 |
augmentMessage | 是否自动增强入站消息 | 自动转写链路的关键开关 |
provider | 转写 provider | 当前默认是本地相关 provider |
modelId | 默认模型 | 例如 SenseVoiceSmall |
language | 默认语言提示 | 例如 auto、zh、en |
timeoutMs | 超时时间 | 用于限制转写等待时间 |
主要动作
| Action | 作用 | 什么时候用 |
|---|---|---|
status | 查看配置与依赖状态 | 想先判断当前环境 |
install | 安装转写依赖 | 首次启用语音转写 |
configure | 更新 ASR 配置 | 日常调整模型和策略 |
on | 启用 plugin,可选顺带安装 | 一步打开语音能力 |
off | 关闭 plugin | 暂停语音能力 |
use | 切换当前模型 | 安装多个模型后切换 |
transcribe | 转写一个本地音频文件 | 显式调用场景 |
models | 查看支持模型 | 给 UI 或控制面做选择器 |
doctor | 诊断 plugin 与依赖 | 怀疑环境不完整时 |
场景化用法示例
查看当前状态
const status = await agent.plugins.runAction({
plugin: "asr",
action: "status",
});安装转写依赖
const result = await agent.plugins.runAction({
plugin: "asr",
action: "install",
payload: {
modelIds: ["SenseVoiceSmall"],
activeModel: "SenseVoiceSmall",
installDeps: true,
},
});切换当前模型
const result = await agent.plugins.runAction({
plugin: "asr",
action: "use",
payload: {
modelId: "SenseVoiceSmall",
},
});显式转写一个本地音频文件
const result = await agent.plugins.runAction({
plugin: "asr",
action: "transcribe",
payload: {
audioPath: "/path/to/message.wav",
language: "zh",
},
});查看支持模型
const models = await agent.plugins.runAction({
plugin: "asr",
action: "models",
});做依赖诊断
const diagnosis = await agent.plugins.runAction({
plugin: "asr",
action: "doctor",
});pipeline 在这里到底起什么作用
这是理解 asr 的关键。
它在 chat 入站增强点上做的事情不是“修改原始渠道消息”,而是:
- 找出语音附件
- 做转写
- 把转写文本附加到增强区
所以它更像在告诉 agent:
- 原始消息之外,还有一段可读文本上下文
这种设计很适合:
- 保留原消息语义
- 补充额外可读信息
- 不强耦合到某个具体 chat 渠道实现
为什么转写失败不会阻塞主流程
当前实现刻意采用 best-effort 策略。
原因很现实:
- 语音转写依赖可能偶发失败
- 某个附件坏掉,不代表整条消息就应该完全失效
所以 asr 更倾向于:
- 能转就转
- 转不出来就跳过
- 让主消息继续进入 agent
这非常适合真实生产环境。
system 在这里为什么也重要
即使 asr 已经能自动增强消息,system prompt 仍然有价值。
因为 agent 需要知道:
- 当前环境具备语音转写能力
- 遇到音频相关需求时可以调用
asraction - 自动增强文本应该如何理解
它的自然边界
asr 虽然背后要调用模型和依赖,但从架构角色看,它仍然更像 plugin:
- 暴露 action
- 参与 pipeline
- 管理配置
如果你要设计“能力参与消息流水线”的机制,plugin 通常就是最自然的边界。
需要注意的边界
自动增强只针对语音 / 音频附件
它不是任意附件预处理框架。
关闭 augmentMessage 后,显式转写仍然可用
这两件事不要混为一谈。
use 切模型前,最好先确认模型已安装
否则会遇到“配置指向某个模型,但环境实际上没准备好”的问题。
什么时候优先参考它
当你要设计这些能力时,优先参考 asr:
- 入站消息增强
- best-effort pipeline hook
- 显式 action 与自动链路并存
- 语音输入转文本