From 8f5fb32b37fe814b05ff78dc84173e9bc89155c2 Mon Sep 17 00:00:00 2001
From: seaislee1209
Date: Mon, 9 Feb 2026 23:11:58 +0800
Subject: [PATCH] =?UTF-8?q?feat(story,music,server):=20=E8=B1=86=E5=8C=85?=
=?UTF-8?q?=E6=95=85=E4=BA=8B=E7=94=9F=E6=88=90=20+=20=E5=8E=86=E5=8F=B2?=
=?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=8C=81=E4=B9=85=E5=8C=96=20+=20=E5=B0=81?=
=?UTF-8?q?=E9=9D=A2=E5=8D=A0=E4=BD=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 接入火山引擎豆包 Chat API 生成儿童故事(SSE 流式进度)
- 新增 /api/stories 接口加载历史故事到书架
- 新增 /api/playlist 接口加载历史歌曲到唱片架
- 书架排序:预设故事在前,AI 生成在后
- AI 生成的故事显示"暂无封面"淡紫渐变占位
- 保存故事时传回真实标题+内容(不再用 mock)
- 修复 Windows GBK 编码导致的中文乱码问题
- 新增 MusicGenerationService 单例管理音乐生成
- 音乐页心情卡片 UI 重做 + 歌词可读性优化
- 添加豆包 API 参考文档和故事创作 prompt
Co-authored-by: Cursor
---
API相关/离在线语音合成SDK概览.md | 45 +
API相关/豆包大模型-故事生成.md | 1039 +++++++++++++++++
API相关/豆包大模型-文本生成.md | 551 +++++++++
API相关/豆包精品长文本语音合成-API接口文档.md | 315 +++++
API相关/豆包语音模型-音色列表.md | 47 +
FLUTTER_WEB_DEV_GUIDE.md | 130 +++
airhub_app/lib/pages/device_control_page.dart | 143 ++-
airhub_app/lib/pages/music_creation_page.dart | 784 +++++++++----
.../lib/pages/profile/notification_page.dart | 54 +-
airhub_app/lib/pages/story_loading_page.dart | 227 +++-
.../services/music_generation_service.dart | 221 ++++
.../lib/widgets/story_generator_modal.dart | 26 +-
airhub_app/pubspec.lock | 2 +-
airhub_app/pubspec.yaml | 1 +
prompts/music_director.md | 12 +-
prompts/story_director.md | 34 +
server.py | 346 +++++-
阶段总结/session_progress.md | 55 +-
18 files changed, 3705 insertions(+), 327 deletions(-)
create mode 100644 API相关/离在线语音合成SDK概览.md
create mode 100644 API相关/豆包大模型-故事生成.md
create mode 100644 API相关/豆包大模型-文本生成.md
create mode 100644 API相关/豆包精品长文本语音合成-API接口文档.md
create mode 100644 API相关/豆包语音模型-音色列表.md
create mode 100644 FLUTTER_WEB_DEV_GUIDE.md
create mode 100644 airhub_app/lib/services/music_generation_service.dart
create mode 100644 prompts/story_director.md
diff --git a/API相关/离在线语音合成SDK概览.md b/API相关/离在线语音合成SDK概览.md
new file mode 100644
index 0000000..feecf52
--- /dev/null
+++ b/API相关/离在线语音合成SDK概览.md
@@ -0,0 +1,45 @@
+本文档对语音合成SDK支持的能力进行说明。
+
+* **SDK名称**:语音合成SDK
+* **SDK开发者**:北京火山引擎科技有限公司
+* **主要功能**:语音合成SDK支持将文字实时合成语音,适用于实时语音播报的场景,如有声阅读、导航、语音助手等等。
+
+
+## SDK接入
+
+| | | | \
+|平台/语言 |集成指南 |调用流程 |
+|---|---|---|
+| | | | \
+|Android |[集成指南](/docs/6561/79832) |[调用流程](/docs/6561/79834) |
+| | | | \
+|iOS |[集成指南](/docs/6561/79835) |[调用流程](/docs/6561/79837) |
+
+**其他相关信息**:
+
+* [SDK版本信息](/docs/6561/79830)
+* [SDK隐私政策](/docs/6561/116696)
+* [开发者使用合规规范](/docs/6561/116711)
+
+
+# 合成能力
+**在线合成**:云端合成,发起网络请求,边合成边播放(支持TTS的websocket接口,能够使用声音复刻音色以及TTS大小模型音色)
+**离线合成**:本地离线引擎合成,需要相关资源文件,边合成边播放;
+
+# 合成策略
+离在线语音合成SDK,除了可以单独使用的在线合成及离线合成外,提供了在线合成发生网络超时后,切换离线合成的两种策略,用户可以通过配置建连超时和接收超时两个参数来控制切换的敏感程度。
+
+* **在线优先**:优先发起在线合成,失败后(网络超时),启动离线合成引擎开始合成;
+* **并发合成**:同时发起在线合成与离线合成,在线请求失败的情况下,使用离线合成数据,该模式下,可以配置更短的超时时间以提升效果,但会消耗更多系统性能;
+
+
+# 合成场景
+语音合成SDK提供了两种种合成场景,以满足不同的需求:
+
+* **普通场景**:又称单句场景,引擎每次启动,只合成、播放一句音频的模式。
+* **小说场景**:适用于听书业务,每次启动引擎后可以根据需求合成多句音频。
+
+
+# 合成效果
+通过对发音人、音调、音量和语速等参数的调整,可以获得不同的发声效果,更好满足您业务场景中的播报需求。
+
diff --git a/API相关/豆包大模型-故事生成.md b/API相关/豆包大模型-故事生成.md
new file mode 100644
index 0000000..d3f92ea
--- /dev/null
+++ b/API相关/豆包大模型-故事生成.md
@@ -0,0 +1,1039 @@
+数分钟内完成你的首次 API 调用。
+
+
+
+
+
+
+
+**体验中心**
+“0”代码,交互式体验模型能力
+
+
+
+
+
+
+
+
+
+
+
+
+**业务迁移**
+兼容OpenAI API,快速迁移业务至方舟
+
+
+
+
+
+
+
+
+
+
+# 1 获取并配置 API Key
+
+1. 获取 API Key:访问[API Key 管理](https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey) ,创建你的 API Key。
+2. 配置环境变量:在终端中运行下面命令,配置 API Key 到环境变量。
+> 配置持久化环境变量方法参见 [环境变量配置指南](/docs/82379/1820161)。
+
+
+```mixin-react
+return (
+
+
+
+);
+```
+
+
+
+# 2 开通模型服务
+访问 [开通管理页面](https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement) 开通模型服务。
+
+# 3 安装 SDK
+安装官方或三方 SDK。
+
+```mixin-react
+return (
+ 运行环境中需安装 [Python](https://www.python.org/downloads/) 版本 3.7 或以上。
+
+* 安装方舟 SDK:
+ \`\`\`Bash
+ pip install 'volcengine-python-sdk[ark]'
+ \`\`\`
+
+* 安装 OpenAI SDK:
+ \`\`\`Bash
+ pip install openai
+ \`\`\`
+
+`}>
+ 环境中安装 [Go](https://golang.google.cn/doc/install) 版本 1.18 或以上。
+
+在代码中通过下方方法引入 Go SDK
+\`\`\`Go
+import (
+ "github.com/volcengine/volcengine-go-sdk"
+)
+\`\`\`
+
+`}>
+ 环境中安装 [Java](https://www.java.com/en/download/help/index_installing.html) 版本 1.8 或以上。
+
+在项目的\`pom.xml\`文件中添加以下依赖配置。
+\`\`\`XML
+
+ com.volcengine
+ volcengine-java-sdk-ark-runtime
+ LATEST
+
+\`\`\`
+
+`}>);
+```
+
+
+# 4 发起 API 请求
+
+## 文本生成
+传入文本类信息给模型,进行问答、分析、改写、摘要、编程、翻译等任务,并返回文本结果。
+
+```mixin-react
+return (
+
+
+
+
+);
+```
+
+
+* [文本生成](/docs/82379/1399009):文本生成使用指南。
+* [深度思考](/docs/82379/1956279):深度思考能力使用指南。
+* [迁移至 Responses API](/docs/82379/1585128):新用户推荐,更简洁的上下文管理能力、强大的工具调用能力。
+* [Chat API](https://www.volcengine.com/docs/82379/1494384):存量业务迭代推荐,广泛使用的 API。
+
+
+## 多模态理解
+传入图片、视频、PDF文件给模型,进行分析、内容审核、问答、视觉定位等基于多模态理解相关任务,并返回文本结果。
+
+
+|输入 |输出预览 |
+|---|---|
+| |* 思考:用户现在需要找支持输入图片的模型系列,看表格里的输入列中的图像列,哪个模型对应的图像输入是√。看表格,Doubao\-1.5\-vision那一行的输入图像列是√,其他两个Doubao\-1.5\-pro和lite的输入图像都是×,所以答案是Doubao\-1.5\-vision。|\
+|> 支持输入图片的模型系列是哪个? |* 回答:支持输入图片的模型系列是Doubao\-1.5\-vision |
+
+
+```mixin-react
+return (
+
+
+
+
+);
+```
+
+
+* [多模态理解](/docs/82379/1958521):多模态理解详细使用指南。
+* [视觉定位 Grounding](/docs/82379/1616136):图片中找到对应目标并返回坐标任务。
+* [GUI 任务处理](/docs/82379/1584296):在计算机/移动设备中完成自动化任务。
+* [文件输入(File API)](/docs/82379/1885708):传入图片、视频、文档接口。
+
+
+## 图片生成
+传入图片、文字给模型,进行:广告、海报、组图等图片生成;增改元素、颜色更换等图片编辑;油墨、水墨等风格切换。
+
+
+|提示词 |输出预览 |
+|---|---|
+|充满活力的特写编辑肖像,模特眼神犀利,头戴雕塑感帽子,色彩拼接丰富,眼部焦点锐利,景深较浅,具有Vogue杂志封面的美学风格,采用中画幅拍摄,工作室灯光效果强烈。 | |
+
+
+```mixin-react
+return (
+
+
+
+
+);
+```
+
+
+* [Seedream 4.0-4.5 教程](/docs/82379/1824121):主流生图模型能力以及如何通过 API 调用。
+* [Seedream 4.0-4.5 提示词指南](/docs/82379/1829186):使用生图模型时,如何编写提示词。
+
+
+## 视频生成
+通过文本描述、图像素材,快速生成高质量、风格多样的视频内容。
+
+
+|提示词 |输出画面预览 |
+|---|---|
+|一位身穿绿色亮片礼服的女性站在粉红色背景前,周围飘落着五彩斑斓的彩纸 | |
+
+
+```mixin-react
+return (
+
+ contents = new ArrayList<>();
+ contents.add(Content.builder()
+ .type("text")
+ .text("一位身穿绿色亮片礼服的女性站在粉红色背景前,周围飘落着五彩斑斓的彩纸 --wm true --dur 5")
+ .build());
+
+ // Create a video generation task
+ CreateContentGenerationTaskRequest createRequest = CreateContentGenerationTaskRequest.builder()
+ .model("doubao-seedance-1-0-pro-250528") // Replace with Model ID
+ .content(contents)
+ .build();
+
+ CreateContentGenerationTaskResult createResult = service.createContentGenerationTask(createRequest);
+ System.out.println(createResult);
+
+ // Get the details of the task
+ String taskId = createResult.getId();
+ GetContentGenerationTaskRequest getRequest = GetContentGenerationTaskRequest.builder()
+ .taskId(taskId)
+ .build();
+
+ System.out.println("----- polling task status -----");
+ while (true) {
+ try {
+ GetContentGenerationTaskResponse getResponse = service.getContentGenerationTask(getRequest);
+ String status = getResponse.getStatus();
+ if ("succeeded".equalsIgnoreCase(status)) {
+ System.out.println("----- task succeeded -----");
+ System.out.println(getResponse);
+ service.shutdownExecutor();
+ break;
+ } else if ("failed".equalsIgnoreCase(status)) {
+ System.out.println("----- task failed -----");
+ System.out.println("Error: " + getResponse.getStatus());
+ service.shutdownExecutor();
+ break;
+ } else {
+ System.out.printf("Current status: %s, Retrying in 3 seconds...\\n", status);
+ TimeUnit.SECONDS.sleep(3);
+ }
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ System.err.println("Polling interrupted");
+ service.shutdownExecutor();
+ break;
+ }
+ }
+ }
+}
+\`\`\`
+
+`}>
+);
+```
+
+
+* [视频生成](/docs/82379/1366799):学习如何使用模型的视频生成能力,包括文本生成视频、首尾帧生视频、首帧生成视频等。
+* [Seedance-1.0-pro&pro-fast 提示词指南](/docs/82379/1631633):使用生视频模型时,如何编写提示词。
+
+
+## 工具使用
+通过工具/插件让模型具体读取外部数据及函数的能力,包括
+
+* 内置工具:联网搜索、图片处理、知识库检索等已集成至方舟平台的工具。
+* 三方工具:兼容MCP 的三方工具。
+* 自定义工具:您自行定义及开发的工具。
+
+
+```mixin-react
+return (
+
+
+ buildTools() {
+ ToolWebSearch t = ToolWebSearch.builder().build();
+ System.out.println(Arrays.asList(t));
+ return Arrays.asList(t);
+ }
+
+ public static void main(String[] args) throws JsonProcessingException {
+ String apiKey = System.getenv("ARK_API_KEY");
+
+ ArkService arkService = ArkService.builder().apiKey(apiKey).baseUrl("https://ark.cn-beijing.volces.com/api/v3").build();
+ CreateResponsesRequest req = CreateResponsesRequest.builder()
+ .model("doubao-seed-1-6-251015")
+ .input(ResponsesInput.builder().addListItem(
+ ItemEasyMessage.builder().role(ResponsesConstants.MESSAGE_ROLE_USER).content(
+ MessageContent.builder()
+ .addListItem(InputContentItemText.builder().text("What's the weather like in Beijing?").build())
+ .build()
+ ).build()
+ ).build())
+ .tools(buildTools())
+ .build();
+ ResponseObject resp = arkService.createResponse(req);
+ System.out.println(resp);
+
+ arkService.shutdownExecutor();
+ }
+}
+\`\`\`
+
+`}>
+
+);
+```
+
+
+* [工具调用](/docs/82379/1958524):学习如何让模型使用内置工具,如网页搜索、知识库检索、豆包助手等能力。
+* [函数调用 Function Calling](/docs/82379/1262342):学习如何让模型调用自定义的工具。
+* [云部署 MCP / Remote MCP](/docs/82379/1827534):学习如何让模型使用 MCP 服务。
+
+
+# 5 下一步
+现在你已经完成了首次方舟模型服务的 API 调用,你可以探索模型的更多能力,包括:
+
+* [平台能力速览](/docs/82379/1108216):探索方舟平台提供的提示词优化、权限管理、模型管理等高阶能力。
+* [模型列表](/docs/82379/1330310):快速浏览方舟提供的模型全集以及各个模型所具备的能力,快速根据你的实际场景匹配到合适的模型。
+
+
+
diff --git a/API相关/豆包大模型-文本生成.md b/API相关/豆包大模型-文本生成.md
new file mode 100644
index 0000000..15eb5d4
--- /dev/null
+++ b/API相关/豆包大模型-文本生成.md
@@ -0,0 +1,551 @@
+根据输入的提示词生成连贯清晰的文本内容。
+:::tip
+方舟平台的新用户?获取 API Key 及 开通模型等准备工作,请参见 [快速入门](/docs/82379/1399008)。
+:::
+
+# 快速开始
+
+
+|输入 |输出预览 |
+|---|---|
+|```Plain|```Plain|\
+|请将下面内容进行结构化处理:火山方舟是火山引擎推出的大模型服务平台,提供模型训练、推理、评测、精调等全方位功能与服务,并重点支撑大模型生态。 火山方舟通过稳定可靠的安全互信方案,保障模型提供方的模型安全与模型使用者的信息安全,加速大模型能力渗透到千行百业,助力模型提供方和使用者实现商业新增长。|# 火山方舟大模型服务平台结构化信息|\
+|```||\
+| |## 一、基本属性|\
+| |1. **推出主体**:火山引擎|\
+| |2. **平台定位**:大模型服务平台|\
+| ||\
+| |## 二、核心功能与服务|\
+| |提供模型训练、推理、评测、精调等全方位大模型相关功能与服务,核心定位包括:|\
+| |1. 支撑大模型生态建设|\
+| |2. 推动大模型能力向千行百业渗透|\
+| ||\
+| |## 三、安全保障方案|\
+| |通过稳定可靠的安全互信方案,双向保障信息安全:|\
+| |1. 模型提供方:保障其模型安全|\
+| |2. 模型使用者:保障其信息安全|\
+| ||\
+| |## 四、价值目标|\
+| |助力模型提供方和使用者实现商业新增长,构建大模型生态下的商业共赢模式。|\
+| |```|\
+| | |
+
+
+```mixin-react
+return (
+
+
+
+ messages = new ArrayList<>();
+ final ChatMessage userMessage = ChatMessage.builder().role(ChatMessageRole.USER).content("请将下面内容进行结构化处理:火山方舟是火山引擎推出的大模型服务平台,提供模型训练、推理、评测、精调等全方位功能与服务,并重点支撑大模型生态。 火山方舟通过稳定可靠的安全互信方案,保障模型提供方的模型安全与模型使用者的信息安全,加速大模型能力渗透到千行百业,助力模型提供方和使用者实现商业新增长。").build();
+ messages.add(userMessage);
+
+ ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
+ .model("doubao-seed-1-6-251015")//Replace with Model ID
+ .messages(messages)
+ // .thinking(new ChatCompletionRequest.ChatCompletionRequestThinking("disabled")) // Manually disable deep thinking
+ .build();
+ service.createChatCompletion(chatCompletionRequest).getChoices().forEach(choice -> System.out.println(choice.getMessage().getContent()));
+ // shutdown service
+ service.shutdownExecutor();
+ }
+}
+\`\`\`
+
+`}>);
+```
+
+:::tip
+使用 Responses API 实现单轮对话的示例,请参见[快速开始](/docs/82379/1958520#17377051)。
+:::
+
+# 模型与API
+支持的模型:[文本生成能力](/docs/82379/1330310#b318deb2)
+支持的API :
+
+* [Responses API](https://www.volcengine.com/docs/82379/1569618):新推出的 API,简洁上下文管理,增强工具调用能力,缓存能力降低成本,新业务及用户推荐。
+* [Chat API](https://www.volcengine.com/docs/82379/1494384):使用广泛的 API,存量业务迁移成本低。
+
+
+# 使用示例
+
+## 多轮对话
+实现多轮对话,需将包含系统消息、模型消息和用户消息的对话历史组合成一个列表,以便模型理解上下文,并延续之前的话题进行问答。
+
+
+|传入方式 |手动管理上下文 |通过ID管理上下文 |
+|---|---|---|
+|使用示例 |```JSON|```JSON|\
+| |...|...|\
+| | "model": "doubao-seed-1-6-251015",| "model": "doubao-seed-1-6-251015",|\
+| | "messages":[| "previous_response_id":"",|\
+| | {"role": "user", "content": "Hi, tell a joke."},| "input": "What is the punchline of this joke?"|\
+| | {"role": "assistant", "content": "Why did the math book look sad? Because it had too many problems! 😄"},|...|\
+| | {"role": "user", "content": "What's the punchline of this joke?"}|```|\
+| | ]| |\
+| |...| |\
+| |```| |\
+| | | |
+|API |[Chat API](https://www.volcengine.com/docs/82379/1494384) |[Responses API](https://www.volcengine.com/docs/82379/1569618) |
+
+> 更多说明及完整示例请参见 [上下文管理](/docs/82379/2123288)。
+
+
+## 流式输出
+
+
+|预览 |优势 |
+|---|---|
+||* **改善等待体验**:无需等待完整内容生成完毕,可立即处理过程内容。|\
+| |* **实时过程反馈**:多轮交互场景,实时了解任务当前的处理阶段。|\
+| |* **更高的容错性**:中途出错,也能获取到已生成内容,避免非流式输出失败无返回的情况。|\
+| |* **简化超时管理**:保持客户端与服务端的连接状态,避免复杂任务耗时过长而连接超时。 |
+
+通过配置 **stream** 为 `true`,来启用流式输出。
+```JSON
+...
+ "model": "doubao-seed-1-6-251015",
+ "messages": [
+ {"role": "user", "content": "深度思考模型与非深度思考模型区别"}
+ ],
+ "stream": true
+ ...
+```
+
+> 完整示例及更多说明请参见 [流式输出](/docs/82379/2123275)。
+
+
+## 设置最大回答
+当控制成本或者回答问题时间,可通过限制模型回答长度实现。当回答篇幅较长,如翻译长文本,避免中途截断,可通过设置`max_tokens`更大值实现。
+```JSON
+...
+ "model": "doubao-seed-1-6-251015",
+ "messages": [
+ {"role": "user","content": "What are some common cruciferous plants?"}
+ ],
+ "max_tokens": 300
+...
+```
+
+> 完整示例代码,请参见 [控制回答长度](/docs/82379/2123288#c7fbdbe3)。
+
+
+## 异步输出
+当任务较为复杂或者多个任务并发等场景下,可使用 Asyncio 接口实现并发调用,提高程序的效率,优化体验。
+
+* Chat API 代码示例:
+
+
+```mixin-react
+return (
+ None:
+ stream = await client.chat.completions.create(
+ # Replace with Model ID
+ model = "doubao-seed-1-6-251015",
+ messages=[
+ {"role": "system", "content": "你是 AI 人工智能助手"},
+ {"role": "user", "content": "常见的十字花科植物有哪些?"},
+ ],
+ stream=True
+ )
+ async for completion in stream:
+ print(completion.choices[0].delta.content, end="")
+ print()
+
+if __name__ == "__main__":
+ asyncio.run(main())
+\`\`\`
+
+`}>);
+```
+
+
+* Responses API 代码示例:
+
+
+```mixin-react
+return (
+);
+```
+
+
+# 更多使用
+
+## 深度思考
+模型在输出回答前,先对输入问题进行系统性分析与逻辑拆解,再基于拆解结果生成回答。
+可以显著提升回复质量,但会增加 token 消耗,详细信息请参见[深度思考](/docs/82379/1449737)。
+
+## 提示词工程
+正确设计和编写提示词,如提供说明、示例、好的规范等方法可提高模型输出的质量和准确性。进行提示词优化的工作也被称为提示词工程(Prompt Engineering)。详细信息请参见[提示词工程](/docs/82379/1221660)。
+
+## 工具调用
+通过集成内置工具或连接远程 MCP 服务器,您可以扩展模型的功能,以便更好回答问题或执行任务。当前支持:
+
+* 内置工具:搜索网络、检索数据、图片处理等。
+* 调用自定义函数。
+* 访问三方MCP服务。
+
+详细信息请参见[工具概述](/docs/82379/1827538)。
+
+## 续写模式
+通过预填(Prefill)部分 **assistant** 角色的内容,引导和控制模型从已有的文本片段继续输出,以及控制模型在角色扮演场景中保持一致性。
+
+* [续写模式 Prefill Response](/docs/82379/1359497):使用[Chat API](https://www.volcengine.com/docs/82379/1494384)实现续写模式。
+* [续写模式](/docs/82379/1958520#a1384090):使用[Responses API](https://www.volcengine.com/docs/82379/1569618)实现续写模式。
+
+
+## 结构化输出(beta)
+控制模型输出程序可处理的标准格式(主要是 JSON)而非自然语言,方便标准化处理或展示。
+
+* [结构化输出(beta)](/docs/82379/1568221):使用[Chat API](https://www.volcengine.com/docs/82379/1494384)实现结构化输出。
+* [结构化输出(beta)](/docs/82379/1568221):使用[Responses API](https://www.volcengine.com/docs/82379/1569618)实现结构化输出。
+
+
+## 批量推理
+方舟为您提供批量推理的能力,当您有大批量数据处理任务,可使用批量推理能力,以获得更大吞吐量和更低的成本。详细介绍和使用,请参见 [批量推理](/docs/82379/1399517)。
+
+## 异常处理
+增加异常处理,帮助定位问题。
+
+```mixin-react
+return (
+
+ 0 {
+ fmt.Print(recv.Choices[0].Delta.Content)
+ }
+ }
+}
+\`\`\`
+
+`}>
+ streamMessages = new ArrayList<>();
+ final ChatMessage streamSystemMessage = ChatMessage.builder().role(ChatMessageRole.SYSTEM).content("你是 AI 人工智能助手").build();
+ final ChatMessage streamUserMessage = ChatMessage.builder().role(ChatMessageRole.USER).content("常见的十字花科植物有哪些?").build();
+ streamMessages.add(streamSystemMessage);
+ streamMessages.add(streamUserMessage);
+
+ ChatCompletionRequest streamChatCompletionRequest = ChatCompletionRequest.builder()
+ .model("doubao-seed-1-6-251015")//Replace with Model ID
+ .messages(streamMessages)
+ .build();
+
+ try {
+ service.streamChatCompletion(streamChatCompletionRequest)
+ .doOnError(Throwable::printStackTrace)
+ .blockingForEach(
+ choice -> {
+ if (choice.getChoices().size() > 0) {
+ System.out.print(choice.getChoices().get(0).getMessage().getContent());
+ }
+ }
+ );
+ } catch (ArkHttpException e) {
+ System.out.print(e.toString());
+ }
+
+ // shutdown service
+ service.shutdownExecutor();
+ }
+
+}
+\`\`\`
+
+`}>);
+```
+
+
+## 对话加密
+除了默认的网络层加密,火山方舟还提供免费的应用层加密功能,为您的推理会话数据提供更强的安全保护。您只需增加一行代码即可启用。完整示例代码请参见 [加密数据](/docs/82379/1544136#23274b89);更多原理信息,请参见[推理会话数据应用层加密方案](/docs/82379/1389905)。
+
+# 使用说明
+
+* 模型关键限制:
+ * 最大上下文长度(Context Window):即单次请求模型能处理的内容长度,包括用户输入和模型输出,单位 token 。超出最大上下文长度的内容时,会截断并停止输出。如碰到上下文限制导致的内容截断,可选择支持更大上下文长度规格的模型。
+ * 最大输出长度(Max Tokens):即单次模型输出的内容的最大长度。如碰到这种情况,可参考[续写模式 Prefill Response](/docs/82379/1359497),通过多次续写回复,拼接出完整内容。
+ * 每分钟处理内容量(TPM):即账号下同模型(不区分版本)每分钟能处理的内容量限制,单位 token。如默认 TPM 限制无法满足您的业务,可通过[工单](https://console.volcengine.com/workorder/create?step=2&SubProductID=P00001166)联系售后提升配额。举例:某模型的 TPM 为 500w,一个主账号下创建的该模型的所有版本接入点共享此配额。
+ * 每分钟处理请求数(RPM):即账号下同模型(不区分版本)每分钟能处理的请求数上限,与上面 TPM 类似。如默认 RPM 限制无法满足您的业务,可通过[工单](https://console.volcengine.com/workorder/create?step=2&SubProductID=P00001166)联系售后提升配额。
+ * 各模型详细的规格信息,请参见 [模型列表](/docs/82379/1330310)。
+* 用量查询:
+ * 对于某次请求 token 用量:可在返回的 **usage** 结构体中查看。
+ * 输入/输出内容的 token 用量:可使用 [Tokenization API](https://www.volcengine.com/docs/82379/1528728) 或 [Token 计算器](https://console.volcengine.com/ark/region:ark+cn-beijing/tokenCalculator)来估算。
+ * 账号/项目/接入点维度 token 用量:可在 [用量统计](https://console.volcengine.com/ark/region:ark+cn-beijing/usageTracking) 页面查看。
+
+
+# 常见问题
+[常见问题](/docs/82379/1359411)\-[在线推理](/docs/82379/1359411#aa45e6c0):在线推理的常见问题,如遇到错误,可尝试在这里找解决方案。
+
+
diff --git a/API相关/豆包精品长文本语音合成-API接口文档.md b/API相关/豆包精品长文本语音合成-API接口文档.md
new file mode 100644
index 0000000..5f2d9fd
--- /dev/null
+++ b/API相关/豆包精品长文本语音合成-API接口文档.md
@@ -0,0 +1,315 @@
+
+# 接口说明
+精品长文本语音合成为异步合成服务,提供“创建合成任务”和“查询合成结果”两个接口,也可通过http回调获取合成结果。
+请确认是否可满足业务需求再进行接入,本产品适用于需要批量合成较长文本,且对返回时效性无强需求的场景,单次可支持10万字符以内文本,异步返回音频。对于输入的文本请求,会进入集群排队处理,返回时长会受集群负载影响波动,通常返回时间会在数十分钟,最长返回时延3小时以内。如出现长时间未返回情况,如无报错,请耐心等待。
+长文本合成分为“普通版”和“情感预测版”,两者需要开通不同的服务,接口地址不同,支持的音色列表也不相同,请仔细阅读文档。
+:::warning
+创建合成任务的频率限制为10 QPS,请勿一次性提交过多任务。
+本产品不适合对于时效性有强需求的场景,如有需求建议接入语音合成(短文本)接口。
+:::
+
+# 鉴权
+请求接口时,需要携带`Resource-Id`和`Authorization`两个header,缺一不可。
+> 参考文档:[鉴权方法](/docs/6561/1105162)
+
+
+# 创建合成任务
+
+## 请求参数
+
+| | | \
+|服务类型 |接口地址 |
+|---|---|
+| | | \
+|普通版 |https://openspeech.bytedance.com/api/v1/tts_async/submit |
+| | | \
+|情感预测版 |https://openspeech.bytedance.com/api/v1/tts_async_with_emotion/submit |
+
+**请求方式:`POST`**
+**Content-Type:** `application/json`
+**请求参数说明:**
+
+| | | | | \
+|参数名称 |参数类型 |是否必需 |描述 |
+|---|---|---|---|
+| | | | | \
+|appid |string |Y |Appid从控制台获取 |
+| | | | | \
+|reqid |string |Y |Request ID,不可重复,长度20~64,建议使用uuid |
+| | | | | \
+|text |string |Y |合成文本,长度小于10万字符,支持SSML。SSML需要以开头和结束,且全文只出现一组标签,支持的SSML标签可参考[SSML标记语言](/docs/6561/104897) |
+| | | | | \
+|format |string |Y |输出音频格式,支持pcm/wav/mp3/ogg_opus |
+| | | | | \
+|voice_type |string |Y |音色voice_type,见[音色列表](/docs/6561/1108211) |
+| | | | | \
+|voice |string |N |音色voice,情感预测版voice为空时,使用预测结果;voice不为空时,使用指定的voice;其余情况使用默认voice |
+| | | | | \
+|language |string |N |语种,与音色有关,具体值参考[音色列表](/docs/6561/1108211),默认为中文 |
+| | | | | \
+|sample_rate |int |N |采样率,默认为24000 |
+| | | | | \
+|volume |float |N |音量,范围0.1~3,默认为1 |
+| | | | | \
+|speed |float |N |语速,范围0.2~3,默认为1 |
+| | | | | \
+|pitch |float |N |语调,范围0.1~3,默认为1 |
+| | | | | \
+|enable_subtitle |int |N |是否开启字幕时间戳,0表示不开启,1表示开启**句级别**字幕时间戳,2表示开启**字词级别**时间戳,3表示开启**音素级别**时间戳 |
+| | | | | \
+|sentence_interval |int |N |句间停顿,单位毫秒,范围0~3000,默认为预测值 |
+| | | | | \
+|style |string |N |指定情感,“情感预测版”默认为预测值,“普通版”默认为音色默认值,音色支持的情感见[音色列表](/docs/6561/1108211) |
+| | | | | \
+|callback_url |string |N |回调返回地址建议使用域名方式; |
+
+:::warning
+在 “情感预测版”接口中使用不支持多情感的音色,将会合成失败。是否支持多情感见[音色列表](/docs/6561/1108211)
+:::
+**请求参数示例:**
+```json
+{
+ "appid": "123456",
+ "text": "火山引擎异步长文本合成。",
+ "format": "mp3",
+ "voice_type": "BV701_streaming",
+ "sample_rate": 24000,
+ "volume": 1.2,
+ "speed": 0.9,
+ "pitch": 1.1,
+ "enable_subtitle": 1,
+ "callback_url": "http://x.y.z/callback"
+}
+```
+
+
+## 返回结果
+**返回结果示例:**
+请求成功:
+```json
+{
+ "task_id": "bd0c2171-4b38-4c05-b685-11f3d240ee8d",
+ "task_status": 0,
+ "text_length": 12
+}
+```
+
+请求失败:
+```json
+{
+ "reqid": "e8f41275-72a3-45b5-af3c-61047f406cac",
+ "code": 40000,
+ "message": "请求参数错误:text不能为空"
+}
+```
+
+**返回参数说明:**
+
+| | | | \
+|参数名称 |类型 |描述 |
+|---|---|---|
+| | | | \
+|task_id |string |任务ID,**注意保存,用于查询合成结果** |
+| | | | \
+|task_status |int |任务状态,0-合成中,1-合成成功,2-合成失败 |
+| | | | \
+|text_length |int |合成需要消耗的字符数,含标点符号 |
+| | | | \
+|code |int |错误码,参考[错误码说明](/docs/6561/1096680#错误码说明) |
+| | | | \
+|message |string |错误信息 |
+
+
+# 查询合成结果
+
+## 请求参数
+
+| | | \
+|服务类型 |接口地址 |
+|---|---|
+| | | \
+|普通版 |https://openspeech.bytedance.com/api/v1/tts_async/query |
+| | | \
+|情感预测版 |https://openspeech.bytedance.com/api/v1/tts_async_with_emotion/query |
+
+**请求方式:`GET`**
+**请求参数说明:**
+
+| | | | | \
+|参数名称 |参数类型 |是否必需 |描述 |
+|---|---|---|---|
+| | | | | \
+|appid |string |Y |Appid从控制台获取 |
+| | | | | \
+|task_id |string |Y |创建合成任务时返回的task_id |
+
+**请求参数示例:**
+```GET
+https://openspeech.bytedance.com/api/v1/tts_async/query?appid=123456&task_id=bd0c2171-4b38-4c05-b685-11f3d240ee8d
+```
+
+
+## 返回结果
+**返回结果示例:**
+请求成功:
+```json
+{
+ "task_id": "bd0c2171-4b38-4c05-b685-11f3d240ee8d",
+ "task_status": 1,
+ "text_length": 12,
+ "audio_url": "https://lf9-lab-speech-tt-sign.bytetos.com/tos-cn-o-14155/aef41ebf89124edba16d4e97e455e007?x-expires=1687778318&x-signature=SJub692wmwsxboJTgl2VX55tIzY%3D",
+ "url_expire_time": 1687777943,
+ "sentences": [
+ {
+ "text": "火山引擎异步长文本合成。",
+ "origin_text": "火山引擎异步长文本合成。",
+ "paragraph_no": 1,
+ "begin_time": 0,
+ "end_time": 4211,
+ "emotion": "neutral"
+ "words": [
+ {
+ "text": "火",
+ "begin": 25,
+ "end": 235,
+ "phonemes": [
+ { "ph": "C0h", "begin": 25, "end": 130 },
+ { "ph": "C0uo", "begin": 130, "end": 235 }
+ ]
+ },
+ {
+ "text": "山",
+ "begin": 235,
+ "end": 495,
+ "phonemes": [
+ { "ph": "C0sh", "begin": 235, "end": 345 },
+ { "ph": "C0an", "begin": 345, "end": 495 }
+ ]
+ },
+ ...
+ ]
+ }
+ ]
+}
+```
+
+请求失败:
+```json
+{
+ "reqid": "bd0c2171-4b38-4c05-b685-11f3d240ee8d",
+ "code": 40001,
+ "message": "没有可以合成的有效字符"
+}
+```
+
+**返回参数说明:**
+
+| | | | \
+|参数名称 |类型 |描述 |
+|---|---|---|
+| | | | \
+|task_id |string |任务ID |
+| | | | \
+|task_status |int |任务状态,0-合成中,1-合成成功,2-合成失败 |
+| | | | \
+|text_length |int |合成消耗的字符数,含标点符号 |
+| | | | \
+|audio_url |string |音频URL,**有效期为1个小时,请及时下载** |
+| | | | \
+|url_expire_time |int |音频URL过期时间(UNIX时间戳) |
+| | | | \
+|sentences |List |分句信息,enable_subtitle≥1才会返回 |
+| | | | \
+|sentences.text |string |实际合成的文本,会过滤掉一些符号、表情和无法合成的字符 |
+| | | | \
+|sentences.origin_text |string |原文分句,所有句子拼起来与输入文本完全一致 |
+| | | | \
+|sentences.paragraph_no |int |分句所属段落,以换行符\n或
划分段落 |
+| | | | \
+|sentences.begin_time |int |分句开始时间,单位:毫秒 |
+| | | | \
+|sentences.end_time |int |分句结束时间,单位:毫秒 |
+| | | | \
+|sentences.emotion |string |分句情感,“情感预测版”才会返回 |
+| | | | \
+|sentences.words |List |字词信息,enable_subtitle≥2才会返回 |
+| | | | \
+|sentences.words.text |string |字词文本 |
+| | | | \
+|sentences.words.begin |int |字词开始时间,单位:毫秒 |
+| | | | \
+|sentences.words.end |int |字词结束时间,单位:毫秒 |
+| | | | \
+|sentences.words.phonemes |List |音素信息,enable_subtitle=3才会返回 |
+| | | | \
+|sentences.words.phonemes.ph |string |音素 |
+| | | | \
+|sentences.words.phonemes.begin |int |音素开始时间,单位:毫秒 |
+| | | | \
+|sentences.words.phonemes.end |int |音素结束时间,单位:毫秒 |
+
+:::warning
+1. 合成结果保留7天,7天内都可以通过该接口查询合成结果,过期后自动删除。
+2. 下载URL有效期为1小时,请勿直接保存audio_url,应及时下载音频或转存至你的云存储中。
+3. audio_url过期后(状态码401或403),可重新请求查询接口获取新的URL。
+:::
+
+# 错误码说明
+
+| | | | \
+|错误码 |错误码描述 |解决办法 |
+|---|---|---|
+| | | | \
+|40000 |请求参数错误 |根据返回的message检查请求参数 |
+| | | | \
+|40001 |没有可以合成的有效字符 |检查请求参数中的text |
+| | | | \
+|40002 |该音色不支持多情感 |可用音色见[音色列表](/docs/6561/1108211) ,或使用“普通版”合成 |
+| | | | \
+|40300 |试用额度不足 |开通正式版服务 |
+| | | | \
+|40400 |任务不存在或已过期 |检查task_id是否正确 |
+| | | | \
+|50000 |服务器错误 |建议先重试,重试无效请联系客服 |
+| | | | \
+|50001 |合成失败 |建议先重试,重试无效请联系客服 |
+| | | | \
+|50002 |生成下载URL失败 |建议先重试,重试无效请联系客服 |
+
+
+# 结果回调
+如果“创建合成任务”时传入了**callback_url**,服务器将会在合成成功/失败时,以接口回调的方式通知用户。
+**请求方式:`POST`**
+**Content-Type:** `application/json`
+**回调参数示例:**
+合成成功:
+```json
+{
+ "code": 0,
+ "message": "Success"
+ "task_id": "bd0c2171-4b38-4c05-b685-11f3d240ee8d",
+ "task_status": 1,
+ "text_length": 12,
+ "audio_url": "https://lf9-lab-speech-tt-sign.bytetos.com/tos-cn-o-14155/aef41ebf89124edba16d4e97e455e007?x-expires=1687778318&x-signature=SJub692wmwsxboJTgl2VX55tIzY%3D",
+ "url_expire_time": 1687777943,
+ "sentences": [
+ ...
+ ]
+}
+```
+
+合成失败:
+```json
+{
+ "code": 40001,
+ "message": "没有可以合成的有效字符",
+ "task_id": "bd0c2171-4b38-4c05-b685-11f3d240ee8d",
+ "task_status": 2,
+ "text_length": 12
+}
+```
+
+:::warning
+不保证回调成功,建议在提交任务一定时间后(如3个小时)仍未收到回调,则主动请求“查询合成结果”接口。
+:::
+
diff --git a/API相关/豆包语音模型-音色列表.md b/API相关/豆包语音模型-音色列表.md
new file mode 100644
index 0000000..14f8333
--- /dev/null
+++ b/API相关/豆包语音模型-音色列表.md
@@ -0,0 +1,47 @@
+:::warning
+精品长文本合成包含两种方案,分别为“**普通版(不支持情感预测)**”和“**情感预测版**”
+:::
+
+# **情感预测版**-音色列表
+
+* 多情感配置信息请详见:[音色列表--豆包语音-火山引擎](https://www.volcengine.com/docs/6561/97465)
+
+
+| | | \
+|推荐音色 |voice_type |
+|---|---|
+| | | \
+|擎苍 |BV701_streaming |
+| | | \
+|阳光青年 |BV123_streaming |
+| | | \
+|反卷青年 |BV120_streaming |
+| | | \
+|通用赘婿 |BV119_streaming |
+| | | \
+|古风少御 |BV115_streaming |
+| | | \
+|霸气青叔 |BV107_streaming |
+| | | \
+|质朴青年 |BV100_streaming |
+| | | \
+|温柔淑女 |BV104_streaming |
+| | | \
+|开朗青年 |BV004_streaming |
+| | | \
+|甜宠少御 |BV113_streaming |
+| | | \
+|儒雅青年 |BV102_streaming |
+
+
+# **普通版(不支持情感预测)**-音色列表
+
+* 普通版音色与语音合成中的**音色一致**,音色信息请详见:[音色列表--豆包语音-火山引擎](https://www.volcengine.com/docs/6561/97465)
+
+
+# FAQ
+**Q1:精品长文本语音合成产品支持哪些情感预测**
+可以自动区分旁白和对话。其中,对话可以支持七大情感:开心、悲伤、愤怒、害怕、厌恶、惊讶、平和
+**Q2:精品长文本语音合成产品是否可以支持ssml标签**
+精品长文本语音支持ssml标签
+
diff --git a/FLUTTER_WEB_DEV_GUIDE.md b/FLUTTER_WEB_DEV_GUIDE.md
new file mode 100644
index 0000000..a24754e
--- /dev/null
+++ b/FLUTTER_WEB_DEV_GUIDE.md
@@ -0,0 +1,130 @@
+# Flutter Web 本地调试启动指南
+
+> 本文档供 AI 编码助手阅读,用于在本项目中正确启动 Flutter Web 调试环境。
+
+## 项目结构
+
+- Flutter 应用目录:`airhub_app/`
+- 后端服务入口:`server.py`(根目录,FastAPI + Uvicorn,端口 3000)
+- 前端端口:`8080`
+
+## 环境要求
+
+- Flutter SDK(3.x)
+- Python 3.x(后端服务)
+- PowerShell(Windows 环境)
+
+## 操作系统
+
+Windows(所有命令均为 PowerShell 语法)
+
+---
+
+## 启动流程(严格按顺序执行)
+
+### 1. 杀掉旧进程并确认端口空闲
+
+```powershell
+# 杀掉占用 8080 和 3000 的旧进程
+Get-NetTCPConnection -LocalPort 8080 -ErrorAction SilentlyContinue | ForEach-Object { taskkill /F /PID $_.OwningProcess 2>$null }
+Get-NetTCPConnection -LocalPort 3000 -ErrorAction SilentlyContinue | ForEach-Object { taskkill /F /PID $_.OwningProcess 2>$null }
+
+# 等待端口释放
+Start-Sleep -Seconds 3
+
+# 确认端口已空闲(无输出 = 空闲)
+Get-NetTCPConnection -LocalPort 8080 -ErrorAction SilentlyContinue
+Get-NetTCPConnection -LocalPort 3000 -ErrorAction SilentlyContinue
+```
+
+### 2. 启动后端服务器(音乐生成功能依赖此服务)
+
+```powershell
+# 工作目录:项目根目录
+cd d:\Airhub
+python server.py
+```
+
+成功标志:
+```
+INFO: Uvicorn running on http://0.0.0.0:3000 (Press CTRL+C to quit)
+[Server] Music Server running on http://localhost:3000
+```
+
+### 3. 设置国内镜像源 + 启动 Flutter Web Server
+
+```powershell
+# 工作目录:airhub_app 子目录
+cd d:\Airhub\airhub_app
+
+# 设置镜像源(必须,否则网络超时)
+$env:PUB_HOSTED_URL = "https://pub.flutter-io.cn"
+$env:FLUTTER_STORAGE_BASE_URL = "https://storage.flutter-io.cn"
+
+# 启动 web-server 模式
+flutter run -d web-server --web-port=8080 --no-pub
+```
+
+成功标志:
+```
+lib\main.dart is being served at http://localhost:8080
+```
+
+### 4. 访问应用
+
+浏览器打开:`http://localhost:8080`
+
+---
+
+## 关键规则
+
+### 必须使用 `web-server` 模式
+- **禁止**使用 `flutter run -d chrome`(会弹出系统 Chrome 窗口,不可控)
+- **必须**使用 `flutter run -d web-server`(只启动 HTTP 服务,手动用浏览器访问)
+
+### `--no-pub` 的使用条件
+- 仅修改 Dart 代码(无新依赖、无新 asset)→ 加 `--no-pub`,编译更快
+- 新增了 `pubspec.yaml` 依赖或 `assets/` 资源文件 → **不能**加 `--no-pub`
+
+### 端口管理
+- 固定使用 8080(Flutter)和 3000(后端),不要换端口绕过占用
+- 每次启动前必须先确认端口空闲
+- 停止服务后等 3 秒再重新启动
+
+### 热重载
+- 在 Flutter 终端按 `r` = 热重载(保留页面状态)
+- 按 `R` = 热重启(重置页面状态)
+- 浏览器 `Ctrl+Shift+R` = 强制刷新
+
+---
+
+## 停止服务
+
+```powershell
+# 方法1:在 Flutter 终端按 q 退出
+
+# 方法2:强制杀进程
+Get-NetTCPConnection -LocalPort 8080 | ForEach-Object { taskkill /F /PID $_.OwningProcess }
+Get-NetTCPConnection -LocalPort 3000 | ForEach-Object { taskkill /F /PID $_.OwningProcess }
+```
+
+---
+
+## 常见问题排查
+
+| 问题 | 原因 | 解决方案 |
+|------|------|---------|
+| 端口被占用 | 旧进程未退出 | 执行第1步杀进程,等3秒 |
+| 编译报错找不到包 | 使用了 `--no-pub` 但有新依赖 | 去掉 `--no-pub` 重新编译 |
+| 网络超时 | 未设置镜像源 | 设置 `PUB_HOSTED_URL` 和 `FLUTTER_STORAGE_BASE_URL` |
+| 页面白屏 | 缓存问题 | 浏览器 `Ctrl+Shift+R` 强刷 |
+| 音乐功能不工作 | 后端未启动 | 先启动 `python server.py` |
+
+---
+
+## 编译耗时参考
+
+- 首次完整编译(含 pub get):90-120 秒
+- 增量编译(`--no-pub`):60-90 秒
+- 热重载(按 r):3-5 秒
+- 热重启(按 R):10-20 秒
diff --git a/airhub_app/lib/pages/device_control_page.dart b/airhub_app/lib/pages/device_control_page.dart
index a0ca392..4ec693a 100644
--- a/airhub_app/lib/pages/device_control_page.dart
+++ b/airhub_app/lib/pages/device_control_page.dart
@@ -1,8 +1,10 @@
+import 'dart:convert';
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import 'package:http/http.dart' as http;
import 'story_detail_page.dart';
import 'product_selection_page.dart';
import 'settings_page.dart';
@@ -89,6 +91,45 @@ class _DeviceControlPageState extends State
_bookshelfScrollOffset = _bookshelfController.page ?? 0.0;
});
});
+
+ // Load historical stories from backend
+ _loadHistoricalStories();
+ }
+
+ /// Fetch saved stories from backend and prepend to bookshelf
+ Future _loadHistoricalStories() async {
+ try {
+ final resp = await http.get(Uri.parse('http://localhost:3000/api/stories'));
+ if (resp.statusCode == 200) {
+ final data = jsonDecode(resp.body);
+ final List stories = data['stories'] ?? [];
+ if (stories.isEmpty) return;
+
+ // Collect titles already in the mock list to avoid duplicates
+ final existingTitles = _mockStories.map((s) => s['title'] as String).toSet();
+
+ final newStories =