【AgentScope Java新手村系列】(20)文字转语音TTS

简介: AgentScope 2.0 移除内置 TTS,教你用 @Tool 包装 DashScope CosyVoice,把文字合成 mp3 写入文件,运行后可直接播放验证。

第二十章 文字转语音 TTS:用 @Tool 包装上游 SDK,框架不再内置 TTS

原因:TTS 的业务差异太大。有的团队用火山引擎,有的用阿里云 CosyVoice,有的用 OpenAI。硬塞进框架等于绑死用户。

2.0 的做法:你自己包装上游 SDK 为一个 @Tool,agent 调工具拿到音频字节,框架只管调度、不管语音合成。

20.1 包装上游 TTS SDK 为 @Tool

业务方做三件事:

  1. 引入上游 SDK(火山 / 阿里云 CosyVoice / OpenAI TTS / Azure Speech)
  2. 写一个 @Tool 调用该 SDK
  3. 注册到 agent / subagent

例:包装阿里云 CosyVoice SDK 为工具:

import com.alibaba.dashscope.audio.tts.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.tts.SpeechSynthesizer;
import io.agentscope.core.message.DataBlock;
import io.agentscope.core.message.Base64Source;
import io.agentscope.core.tool.Tool;
import io.agentscope.core.tool.ToolParam;

import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;

public class TtsTool {
   

    private final String apiKey;

    public TtsTool(String apiKey) {
   
        this.apiKey = apiKey;
    }

    @Tool(name = "tts_synthesize", description = "把文字转成 mp3 音频,返回 DataBlock")
    public DataBlock synthesize(
            @ToolParam(name = "text") String text,
            @ToolParam(name = "voice", required = false) String voice) {
   

        SpeechSynthesisParam param = SpeechSynthesisParam.builder()
                .apiKey(apiKey)
                .model("cosyvoice-v1")
                .text(text)
                .build();

        SpeechSynthesizer synthesizer = new SpeechSynthesizer();
        ByteBuffer result = synthesizer.call(param);
        byte[] mp3 = new byte[result.remaining()];
        result.get(mp3);

        String b64 = Base64.getEncoder().encodeToString(mp3);
        return DataBlock.builder()
                .source(Base64Source.builder()
                        .data(b64)
                        .mediaType("audio/mp3")
                        .build())
                .name("tts.mp3")
                .build();
    }

    @Tool(name = "tts_to_file", description = "把文字转成 mp3 并写入文件")
    public String synthesizeToFile(
            @ToolParam(name = "text") String text,
            @ToolParam(name = "outPath") String outPath) {
   

        SpeechSynthesisParam param = SpeechSynthesisParam.builder()
                .apiKey(apiKey)
                .model("cosyvoice-v1")
                .text(text)
                .build();

        SpeechSynthesizer synthesizer = new SpeechSynthesizer();
        ByteBuffer result = synthesizer.call(param);
        byte[] mp3 = new byte[result.remaining()];
        result.get(mp3);

        Files.write(Path.of(outPath), mp3);
        return "已写入 " + outPath;
    }
}

注册到 agent:

Toolkit toolkit = new Toolkit();
toolkit.registerTool(new TtsTool());                                 // 注册 TTS 包装工具

HarnessAgent agent = HarnessAgent.builder()
        .name("voice_assistant")
        .sysPrompt("你是语音助理,可以用 tts_to_file 把文字转成 mp3 文件。")  // 告诉 LLM 有 TTS 工具可用
        .model(model())
        .workspace(Path.of("./workspace"))
        .toolkit(toolkit)                                            // 传入注册好的工具
        .build();

LLM 看到用户要"念给我听"时,自动调用 tts_to_file 工具——生成的 tts_output.mp3 直接用播放器打开就能听到。

提示:tts_synthesize 返回 DataBlock,适合把音频推给前端播放;tts_to_file 写入磁盘,适合本地验证。

20.2 与前端的流式推送

Web 端拿到 DataBlock 后:

ws.onmessage = (event) => {
   
  const data = JSON.parse(event.data);
  if (data.toolName === "tts_synthesize") {
   
    const audioB64 = data.result.media.base64;
    const audioBlob = base64ToBlob(audioB64, "audio/mp3");
    const audio = new Audio(URL.createObjectURL(audioBlob));
    audio.play();
  }
};

agent.streamEvents(...) 会把 ToolResultBlock 推给前端;前端按 mediaType 决定是否转成 <audio>

20.3 多 TTS 提供商的策略

如果业务同时接火山、OpenAI、CosyVoice,可以在 tools.json 里按工具名分别包装:

{
   
  "tools": [
    {
   
      "name": "tts_cosyvoice",
      "class": "demo.tts.CosyVoiceTool",
      "description": "中文 TTS(CosyVoice)"
    },
    {
   
      "name": "tts_openai",
      "class": "demo.tts.OpenAiTtsTool",
      "description": "英文 TTS(OpenAI)"
    }
  ]
}

主 agent 在 system prompt 里描述路由规则:

中文场景用 tts_cosyvoice,英文场景用 tts_openai。

20.4 完整可运行示例

本地验证时,建议让 agent 直接调用 tts_to_file,把 mp3 写到磁盘,这样就能用播放器听到真实语音:

public class Chapter20_FullTts {
   

    public static void main(String[] args) {
   
        TtsTool ttsTool = new TtsTool();                            // 业务方包装的 TTS 工具(见 20.1 节)

        Toolkit toolkit = new Toolkit();
        toolkit.registerTool(ttsTool);                              // 通过 Toolkit 注册

        String outPath = Path.of("./workspace").resolve("tts_output.mp3").toString();

        HarnessAgent agent = HarnessAgent.builder()
                .name("voice_assistant")
                .sysPrompt("""
                        你是语音助理。
                        用户让你"念" / "读" / "播放"某段文字时,
                        调 tts_to_file 把 mp3 保存到:
                        """ + outPath)
                .model(model())
                .workspace(Path.of("./workspace"))
                .toolkit(toolkit)                                   // 传入注册好的工具
                .build();

        String reply = agent.call(
                        new UserMessage("把'杭州今天 22 度'念给我听,保存成 mp3 文件。"),
                        RuntimeContext.empty())                     // 无 session,单次调完即走
                .block()
                .getTextContent();

        System.out.println(reply);
        System.out.println("音频已保存:" + outPath);                  // 可直接用播放器打开
    }
}

运行后打开 ./workspace/tts_output.mp3 即可听到合成语音。

20.5 本章小结

  • 2.0 移除了内置 TTS 模块。推荐业务方包装上游 SDK 为 @Tool
  • 本地验证用 tts_to_file 把 mp3 写到磁盘,可直接播放;上线后可用 tts_synthesize 返回 DataBlock 推给前端。
  • 多个 TTS 提供商共存时,按工具名路由即可。
目录
相关文章
|
3月前
|
JSON 前端开发 NoSQL
【AgentScope Java新手村系列】(11)中断与恢复
中断与恢复 — AgentStateStore 按 sessionId 持久化上下文,浏览器关闭后秒级恢复对话与 todo 状态。
419 1
|
2月前
|
中间件 Java API
【AgentScope Java新手村系列】(17)长期记忆系统
长期记忆 — 废弃 LongTermMemory,改用 MEMORY.md + Compaction 压缩 + MemoryFlush 冲刷 + @Tool 主动写。三层机制互补,框架自动维护,业务方按需定制。
455 0
|
3月前
|
自然语言处理 Java API
【AgentScope Java新手村系列】(7)子Agent编排
子Agent编排 — SubagentDeclaration 描述子 agent,主 agent 通过 agent_spawn 工具同步/异步委派子任务。
709 0
|
前端开发 NoSQL Java
【AgentScope Java新手村系列】(2)第一个Agent-基础对话
第一个Agent-基础对话 — 演示 HarnessAgent 的 Builder 模式创建、ReAct 推理循环、流式事件与思考模式三个核心能力。
865 2
|
3月前
|
前端开发 安全 中间件
【AgentScope Java新手村系列】(14)人机交互
人机交互 — Permission 系统五种模式配合 ALLOW/DENY/ASK 规则,运行时 HITL 自动拦截与决策收集。
482 6
【AgentScope Java新手村系列】(14)人机交互
|
前端开发 Java 中间件
【AgentScope Java新手村系列】(1)框架简介与环境搭建
本章带你快速入门AgentScope Java 2.0:从GitHub拉取v2.0.0-RC2源码、Maven编译安装,到纯Java构建HarnessAgent,接入DeepSeek等主流LLM,跑通首个可对话智能体——完成学习之旅的“第0步”。
292 0
【AgentScope Java新手村系列】(1)框架简介与环境搭建
|
3月前
|
SQL JSON Java
【AgentScope Java新手村系列】(15)MCP协议工具
MCP协议工具 — tools.json 一行声明一个 MCP server,支持 stdio/sse/ws 协议,McpMeta 传递调用元数据。
491 1
|
3月前
|
前端开发 NoSQL Java
【AgentScope Java新手村系列】(9)SpringBoot集成
SpringBoot集成 — 工厂方法将 HarnessAgent 注册为单例 Bean,WebFlux 流式输出 streamEvents 到 SSE 端点。
546 3
|
3月前
|
Java 中间件 API
【AgentScope Java新手村系列】(10)实战-多Agent天气助手
实战-多Agent天气助手 — 文件驱动 subagent 实战,主 agent 自主编排天气查询、航班搜索、景点推荐三个子任务并行调研。
689 1
|
前端开发 Java 中间件
【AgentScope Java新手村系列】(3)工具系统
工具系统 — @Tool/@ToolParam 注解将 Java 方法注册为 Agent 能力,自主决定调用时机,支持同步/异步返回。
526 0

热门文章

最新文章