diff --git a/.migpt.example.js b/.migpt.example.js index 47d2ce7..280769c 100644 --- a/.migpt.example.js +++ b/.migpt.example.js @@ -147,7 +147,7 @@ export default { */ // 是否启用连续对话功能,部分小爱音箱型号无法查询到正确的播放状态,需要关闭连续对话 - streamResponse: true, + streamResponse: false, // 连续对话时,无响应多久后自动退出 exitKeepAliveAfter: 30, // 默认 30 秒,建议不要超过 1 分钟 // 连续对话时,下发 TTS 指令多长时间后开始检测设备播放状态(默认 3 秒) @@ -164,6 +164,6 @@ export default { // 是否跟踪 Mi Service 相关日志(打开后可以查看设备 did) enableTrace: false, // 一般情况下不要打开 // 网络请求超时时长(单位毫秒,默认 5 秒) - timeout: 5000, + timeout: 5000, }, }; diff --git a/README.md b/README.md index d1d5037..216835c 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ [![npm version](https://badge.fury.io/js/mi-gpt.svg)](https://www.npmjs.com/package/mi-gpt) [![Docker Image Version](https://img.shields.io/docker/v/idootop/mi-gpt?color=%23086DCD&label=docker%20image)](https://hub.docker.com/r/idootop/mi-gpt) - - > 👉 查看完整演示视频:【[整活!将小爱音箱接入 ChatGPT 和豆包,改造成你的专属语音助手~](https://www.bilibili.com/video/BV1N1421y7qn/?share_source=copy_web&vd_source=5d4e78ff2a0dc6a661baa65f479199c1)】 + + ## 👋 项目简介 在这个数字化的世界里,家已不仅仅是一个居住的地方,而是我们数字生活的延伸。 @@ -29,7 +29,7 @@ - **💬 流式响应**。爱情来得太快就像龙卷风,而你的小爱音箱也是,对你的爱意秒回,爱你不会让你等太久。 - **🧠 长短期记忆**。小爱音箱现在能记住你们之间的每一次对话,越聊越默契,就像是你身边的老朋友。 - **🔊 自定义 TTS**。厌倦了小爱同学的语音?帮你解锁[「豆包」](https://doubao.com)同款音色,就像真人在回你的消息。 -- **🤖️ 智能家居 Agent**。心情不好?小爱立刻懂你,自动帮你播放喜欢的音乐,调节灯光,逗你开心。_TODO_ +- ~**🤖️ 智能家居 Agent**。心情不好?小爱立刻懂你,自动帮你播放喜欢的音乐,调节灯光,逗你开心。~ ## 🦄 Sponsors @@ -39,15 +39,9 @@ ## ⚡️ 快速开始 -> 查看视频教程 👉 【[MiGPT 光速入门教程,从零教你调教小爱音箱~](https://www.bilibili.com/video/BV1zb421H7cS)】 +### 视频教程 -`MiGPT` 有两种启动方式: [Docker](#docker) 和 [Node.js](#nodejs)。 - -启动成功后,你可以通过以下方式来召唤 AI 回答问题: - -- **小爱同学,请 xxx**。比如 `小爱同学,请问地球为什么是圆的?` -- **小爱同学,你 xxx**。比如 `小爱同学,你喜欢我吗?` -- **小爱同学,召唤 xxx**。比如 `小爱同学,召唤傻妞` +👉 [MiGPT 光速入门视频教程,手把手教你调教小爱音箱~](https://www.bilibili.com/video/BV1zb421H7cS) ### 设备要求 @@ -57,6 +51,16 @@ > 注意:本项目暂不支持小度音箱、天猫精灵、HomePod 等智能音箱设备,亦无相关适配计划。 +### 使用方式 + +`MiGPT` 有两种启动方式: [Docker](#docker) 和 [Node.js](#nodejs)。 + +启动成功后,你可以通过以下方式来召唤 AI 回答问题: + +- **小爱同学,请 xxx**。比如 `小爱同学,请问地球为什么是圆的?` +- **小爱同学,你 xxx**。比如 `小爱同学,你喜欢我吗?` +- **小爱同学,召唤 xxx**。比如 `小爱同学,召唤傻妞` + ### Docker [![Docker Image Version](https://img.shields.io/docker/v/idootop/mi-gpt?color=%23086DCD&label=docker%20image)](https://hub.docker.com/r/idootop/mi-gpt) @@ -104,7 +108,7 @@ main(); ## 📖 使用文档 -以下为更详细的使用教程,大多数问题都可在 [💬 常见问题](https://github.com/idootop/mi-gpt/blob/main/docs/faq.md) 中找到答案。 +提示:大多数问题都可在 [💬 常见问题](https://github.com/idootop/mi-gpt/blob/main/docs/faq.md) 中找到答案。 - [🔥 官方视频教程](https://www.bilibili.com/video/BV1zb421H7cS) - [⚙️ 参数设置](https://github.com/idootop/mi-gpt/blob/main/docs/settings.md) @@ -116,14 +120,14 @@ main(); - [✨ 更新日志](https://github.com/idootop/mi-gpt/blob/main/docs/changelog.md) - [🚀 Roadmap](https://github.com/idootop/mi-gpt/blob/main/docs/roadmap.md) -## 👍 推荐项目与教程 +## 🤩 推荐项目与教程 | 项目链接 | 简介 | 来源 | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | | **相关项目** | | | -| [@shinedlc/mi-gpt](https://github.com/shinedlc/mi-gpt) | 一个接入了摄像头硬件 + 本机搭建 Ollama 模型的 MiGPT 分支,让小爱同学可以看到和理解现实世界 | @shinedlc | -| [@lmk123/migpt-cli](https://github.com/lmk123/migpt-cli) | 通过图形化界面的方式创建并管理 MiGPT,支持运行多个账号。 | @lmk123 | -| [@lmk123/migpt-cli/gui](https://migptgui.com/gui/) | 直接在网页上更方便的编辑和生成 `.migpt.js` 和 `.env` 配置文件 | @lmk123 | +| [MiGPT GUI](https://migptgui.com/) | 通过图形化界面的方式创建并管理 MiGPT,支持运行多个账号 | @lmk123 | +| - | 直接在网页上更方便的编辑和生成 `.migpt.js` 和 `.env` 配置文件 | @lmk123 | +| [@shinedlc/mi-gpt](https://github.com/shinedlc/mi-gpt) | 支持摄像头模块的 MiGPT 分支,让小爱同学可以看到和理解现实世界 | @shinedlc | | **使用教程** | | | | [MiGPT 官方视频教程](https://www.bilibili.com/video/BV1zb421H7cS) | 官方视频教程配套 PPT 文件 👉 [MiGPT 官方教程.pdf](https://github.com/idootop/mi-gpt/blob/main/assets/pdf/MiGPT%E5%AE%98%E6%96%B9%E6%95%99%E7%A8%8B.pdf) | @idootop | | [MiGPT 接入豆包等大模型教程](https://migptgui.com/docs/apply/) | 豆包、Moonshot(Kimi)等常见大模型的详细接入教程 | @lmk123 | diff --git a/docs/changelog.md b/docs/changelog.md index f4c18a7..cce89ea 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,21 @@ # ✨ 更新日志 +## v4.2.0 + +### ✨ 新功能 + +- ✅ 新增对小爱音箱 LLM 消息的支持 + +### 🐛 修复 + +- ✅ 修复 LLM 返回值格式与预期不符的问题 by @yanyao2333 + +### ❤️ 感谢 + +- @yanyao2333 让 LLM 返回值的解析更加健壮 https://github.com/idootop/mi-gpt/pull/160 +- @LyCecilion 对小爱音箱丢消息问题的详细反馈 https://github.com/idootop/mi-gpt/issues/177 +- @Jasonzhu1207 在 telegram 群中帮忙解答问题 + ## v4.1.0 ### 🐛 修复 diff --git a/package.json b/package.json index a3f5278..720a53e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mi-gpt", - "version": "4.1.0", + "version": "4.2.0", "type": "module", "description": "将小爱音箱接入 ChatGPT 和豆包,改造成你的专属语音助手。", "homepage": "https://github.com/idootop/mi-gpt", @@ -25,16 +25,17 @@ "scripts": { "start": "node ./app.js", "dev": "node --env-file=.env ./app.js", - "build": "npx -y prisma generate && tsup", + "build": "npx -y prisma generate && rm -rf dist && tsup", "db:gen": "npx -y prisma migrate dev --name init", "db:reset": "rm -f .mi.json .bot.json prisma/app.db prisma/app.db-journal", + "prepublish": "npm run build", "postinstall": "npx -y prisma migrate dev --name hello" }, "dependencies": { "@prisma/client": "^5.14.0", "fs-extra": "^11.2.0", - "mi-service-lite": "^3.0.0", - "openai": "^4.52.2", + "mi-service-lite": "^3.1.0", + "openai": "^4.56.0", "prisma": "^5.14.0", "proxy-agent": "^6.4.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6ce39ae..c6b7d9b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,11 +15,11 @@ importers: specifier: ^11.2.0 version: 11.2.0 mi-service-lite: - specifier: ^3.0.0 - version: 3.0.0 + specifier: ^3.1.0 + version: 3.1.0 openai: - specifier: ^4.52.2 - version: 4.52.2 + specifier: ^4.56.0 + version: 4.56.0 prisma: specifier: ^5.14.0 version: 5.14.0 @@ -561,8 +561,8 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - axios@1.7.2: - resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} + axios@1.7.5: + resolution: {integrity: sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -863,8 +863,8 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - mi-service-lite@3.0.0: - resolution: {integrity: sha512-Fbz3lGPNp1Jbqqlj4EK1vya9zj3WCWXeW6+mnpcQi9RTMMPmGXC+126HHmw8WWUWj4G0tNHHP/ApHMCAIzzENQ==} + mi-service-lite@3.1.0: + resolution: {integrity: sha512-WOMK8poZZ4nvXezETGdJHiqOOMON/+8prv8Hi9kjODBGCkdUtp+Q1w9OJRYxZUiB+ZaH5f9okaqAq5TBRpg1VA==} engines: {node: '>=16'} micromatch@4.0.5: @@ -933,9 +933,14 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - openai@4.52.2: - resolution: {integrity: sha512-mMc0XgFuVSkcm0lRIi8zaw++otC82ZlfkCur1qguXYWPETr/+ZwL9A/vvp3YahX+shpaT6j03dwsmUyLAfmEfg==} + openai@4.56.0: + resolution: {integrity: sha512-zcag97+3bG890MNNa0DQD9dGmmTWL8unJdNkulZzWRXrl+QeD+YkBI4H58rJcwErxqGK6a0jVPZ4ReJjhDGcmw==} hasBin: true + peerDependencies: + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true pac-proxy-agent@7.0.2: resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==} @@ -1173,10 +1178,6 @@ packages: v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - web-streams-polyfill@4.0.0-beta.3: resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} engines: {node: '>= 14'} @@ -1566,7 +1567,7 @@ snapshots: asynckit@0.4.0: {} - axios@1.7.2: + axios@1.7.5: dependencies: follow-redirects: 1.15.6 form-data: 4.0.0 @@ -1905,9 +1906,9 @@ snapshots: merge2@1.4.1: {} - mi-service-lite@3.0.0: + mi-service-lite@3.1.0: dependencies: - axios: 1.7.2 + axios: 1.7.5 pako: 2.1.0 transitivePeerDependencies: - debug @@ -1961,7 +1962,7 @@ snapshots: dependencies: mimic-fn: 2.1.0 - openai@4.52.2: + openai@4.56.0: dependencies: '@types/node': 18.19.33 '@types/node-fetch': 2.6.11 @@ -1970,7 +1971,6 @@ snapshots: form-data-encoder: 1.7.2 formdata-node: 4.4.1 node-fetch: 2.7.0 - web-streams-polyfill: 3.3.3 transitivePeerDependencies: - encoding @@ -2230,8 +2230,6 @@ snapshots: v8-compile-cache-lib@3.0.1: optional: true - web-streams-polyfill@3.3.3: {} - web-streams-polyfill@4.0.0-beta.3: {} webidl-conversions@3.0.1: {} diff --git a/src/services/speaker/speaker.ts b/src/services/speaker/speaker.ts index 0e23e5d..93b95b5 100644 --- a/src/services/speaker/speaker.ts +++ b/src/services/speaker/speaker.ts @@ -199,7 +199,7 @@ export class Speaker extends BaseSpeaker { private async _fetchFirstMessage() { const msgs = await this.getMessages({ limit: 1, - filterTTS: false, + filterAnswer: false, }); this.currentQueryMsg = msgs[0]; } @@ -285,23 +285,26 @@ export class Speaker extends BaseSpeaker { async getMessages(options?: { limit?: number; timestamp?: number; - filterTTS?: boolean; + filterAnswer?: boolean; }): Promise { - const filterTTS = options?.filterTTS ?? true; + const filterAnswer = options?.filterAnswer ?? true; const conversation = await this.MiNA!.getConversations(options); this._lastConversation = conversation; let records = conversation?.records ?? []; - if (filterTTS) { + if (filterAnswer) { // 过滤有小爱回答的消息 records = records.filter( - (e) => e.answers.length > 0 && e.answers.some((e) => e.type === "TTS") + (e) => + ["TTS", "LLM"].includes(e.answers[0]?.type) && // 过滤 TTS 和 LLM 消息 + e.answers.length === 1 // 播放音乐时会有 TTS、Audio 两个 Answer ); } return records.map((e) => { - const ttsAnswer = e.answers.find((e) => e.type === "TTS") as any; + const msg: any = e.answers[0]; + const answer = msg?.tts?.text?.trim() ?? msg?.llm?.text?.trim(); return { + answer, text: e.query, - answer: ttsAnswer?.tts?.text?.trim(), timestamp: e.time, }; }); diff --git a/tests/bot.ts b/tests/bot.ts deleted file mode 100644 index 38f08fe..0000000 --- a/tests/bot.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { MyBot } from "../src/services/bot"; -import { AISpeaker } from "../src/services/speaker/ai"; - -export async function testMyBot() { - // await testStreamResponse(); - await testRunBot(); -} - -async function testRunBot() { - const name = "傻妞"; - const speaker = new AISpeaker({ - name, - tts: "custom", - userId: process.env.MI_USER!, - password: process.env.MI_PASS!, - did: process.env.MI_DID, - }); - const bot = new MyBot({ - speaker, - bot: { - name, - profile: `性别女,性格乖巧可爱,喜欢搞怪,爱吃醋。`, - }, - master: { - name: "陆小千", - profile: `性别男,善良正直,总是舍己为人,是傻妞的主人。`, - }, - }); - const res = await bot.run(); - console.log("✅ done"); -} - -async function testStreamResponse() { - const stream = await MyBot.chatWithStreamResponse({ - user: "地球为什么是圆的?", - onFinished: (text) => { - console.log("\nFinal result 111:\n", text); - }, - }); - const config: any = { - userId: process.env.MI_USER!, - password: process.env.MI_PASS!, - did: process.env.MI_DID, - tts: "custom", - }; - const speaker = new AISpeaker(config); - await speaker.initMiServices(); - await speaker.response({ stream }); - const res = await stream.getFinalResult(); - console.log("\nFinal result 222:\n", res); -} diff --git a/tests/db.ts b/tests/db.ts deleted file mode 100644 index 8232ed2..0000000 --- a/tests/db.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { assert } from "console"; -import { - ConversationManager, - MessageContext, -} from "../src/services/bot/conversation"; -import { println } from "../src/utils/base"; -import { MessageCRUD } from "../src/services/db/message"; - -export async function testDB() { - const manager = new ConversationManager({ - bot: { - name: "小爱同学", - profile: "我是小爱同学,机器人", - }, - master: { - name: "陆小千", - profile: "我是陆小千,人类", - }, - room: { - name: "客厅", - description: "陆小千的客厅,小爱同学放在角落里", - }, - }); - const { room, bot, master, memory } = await manager.get(); - assert(room, "❌ 初始化用户失败"); - const ctx = { bot, master, room } as MessageContext; - let message = await manager.onMessage(ctx, { - sender: master!, - text: "你好!", - }); - assert(message?.text === "你好!", "❌ 插入消息失败"); - message = await manager.onMessage(ctx, { - sender: bot!, - text: "你好!很高兴认识你", - }); - await manager.onMessage(ctx, { - sender: master!, - text: "你是谁?", - }); - await manager.onMessage(ctx, { - sender: bot!, - text: "我是小爱同学,你可以叫我小爱!", - }); - const messages = await manager.getMessages({ take: 100 }); - assert(messages.length === 4, "❌ 查询消息数量异常"); - assert(messages[0].text === "你好!", "❌ 查询消息排序异常"); - const newMessages = await MessageCRUD.gets({ - take: 100, - cursorId: message!.id, - order: "asc", - }); - assert(newMessages.length === 2, "❌ 查询消息数量异常(游标)"); - assert( - newMessages[1].text === "我是小爱同学,你可以叫我小爱!", - "❌ 查询消息排序异常(游标)" - ); - println("✅ hello world!"); -} diff --git a/tests/log.ts b/tests/log.ts deleted file mode 100644 index 40a1c7e..0000000 --- a/tests/log.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Logger } from "../src/utils/log"; - -export function testLog() { - Logger.log("你好", ["世界"], { hello: "world!" }); - Logger.success("你好", ["世界"], { hello: "world!" }); - Logger.error("你好", ["世界"], { hello: "world!" }); - Logger.assert(true, "你好 111", ["世界"], { hello: "world!" }); - Logger.assert(false, "你好 222", ["世界"], { hello: "world!" }); -} diff --git a/tests/openai.ts b/tests/openai.ts deleted file mode 100644 index 6fb4319..0000000 --- a/tests/openai.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { randomUUID } from "crypto"; -import { openai } from "../src/services/openai"; - -export async function testOpenAI() { - await testChat(); - // await testStreamChat(); -} - -async function testChat() { - const res = await openai.chat({ user: "地球为什么是圆的?" }); - console.log("\nFinal result:\n", res?.content); -} - -async function testStreamChat() { - const requestId = randomUUID(); - const res = await openai.chatStream({ - requestId, - user: "地球为什么是圆的?", - onStream: (text) => { - console.log(text); - }, - }); - console.log("\nFinal result:\n", res); -} - -async function testAbortStreamChat() { - const requestId = randomUUID(); - const res = await openai.chatStream({ - requestId, - user: "hello!", - onStream: (text) => { - console.log(text); - openai.cancel(requestId); - }, - }); - console.log("xxx", res); -} diff --git a/tests/speaker.ts b/tests/speaker.ts deleted file mode 100644 index f2771c9..0000000 --- a/tests/speaker.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { AISpeaker } from "../src/services/speaker/ai"; -import { StreamResponse } from "../src/services/speaker/stream"; -import { sleep } from "../src/utils/base"; - -export async function testSpeaker() { - const speaker = new AISpeaker({ - userId: process.env.MI_USER!, - password: process.env.MI_PASS!, - did: process.env.MI_DID, - tts: "xiaoai", - debug: true, - }); - await speaker.initMiServices(); - await testTTS(speaker); - // await testAISpeakerStatus(speaker); - // await testSpeakerResponse(speaker); - // await testSpeakerStreamResponse(speaker); - // await testSpeakerGetMessages(speaker); - // await testSwitchSpeaker(speaker); - // await testSpeakerUnWakeUp(speaker); - // await testAISpeaker(speaker); -} - -async function testTTS(speaker: AISpeaker) { - const res1 = await speaker.MiIOT!.doAction(5, 1, "你好,很高兴认识你"); - const res2 = await speaker.MiNA!.play({ tts: "你好,很高兴认识你" }); - console.log("finished"); -} - -async function testAISpeakerStatus(speaker: AISpeaker) { - const playingCommand = [5, 3, 1]; - const res1 = await speaker.MiIOT!.getProperty( - playingCommand[0], - playingCommand[1] - ); - const res2 = await speaker.MiNA!.getStatus(); - console.log("finished"); -} - -async function testAISpeaker(speaker: AISpeaker) { - speaker.askAI = async (msg) => { - return { text: "你说:" + msg.text }; - }; - await speaker.run(); - console.log("finished"); -} - -async function testSpeakerUnWakeUp(speaker: AISpeaker) { - await speaker.wakeUp(); - await sleep(1000); - await speaker.unWakeUp(); - console.log("hello"); -} - -async function testSwitchSpeaker(speaker: AISpeaker) { - await speaker.response({ text: "你好,我是傻妞,很高兴认识你!" }); - const success = await speaker.switchSpeaker("魅力苏菲"); - console.log("switchSpeaker 魅力苏菲", success); - await speaker.response({ text: "你好,我是傻妞,很高兴认识你!" }); - console.log("hello"); -} - -async function testSpeakerGetMessages(speaker: AISpeaker) { - let msgs = await speaker.getMessages({ filterTTS: true }); - console.log("filterTTS msgs", msgs); - msgs = await speaker.getMessages({ filterTTS: false }); - console.log("no filterTTS msgs", msgs); -} - -async function testSpeakerResponse(speaker: AISpeaker) { - let status = await speaker.MiNA!.getStatus(); - console.log("curent status", status); - await speaker.response({ text: "你好,我是傻妞,很高兴认识你!" }); - sleep(1000); - status = await speaker.MiNA!.getStatus(); - console.log("tts status", status); -} - -async function testSpeakerStreamResponse(speaker: AISpeaker) { - const stream = new StreamResponse(); - const text = ` -明朝是中国历史上一个极具影响力的王朝,它涌现了众多杰出的历史人物和令人感动的故事。下面我会为你介绍一些明朝的主要历史人物和故事。 - -### 明朝的主要历史人物 - -1. **朱元璋(太祖)**:明朝的开国皇帝,他出身于农家,后来成为了农民起义军的领袖,最终建立了明朝,并自称为皇帝,即洪武帝。他实行了一系列改革,开创了明朝初期的盛世。 - -2. **永乐皇帝**:明朝第三位皇帝,朱棣之子,被誉为明朝的“盛世之君”。他以永乐大典著称,是中国古代历史上最宏大的一次全国性修订和总结,同时也是世界上最早的百科全书之一。 - -3. **郑和**:明朝的航海家和探险家,他率领庞大的船队七次下西洋,到达东南亚、南亚、阿拉伯半岛和非洲东岸。他的航海活动开拓了明朝的海外贸易,加强了中国与其他国家的交流。 - -4. **文征明**:明朝的杰出将领,他在抵御蒙古族的入侵、收复失地等方面做出了重大贡献。他曾率领明军成功收复了被蒙古族侵占的大片土地,为明朝的稳定和发展立下了汗马功劳。 - -5. **张居正**:明朝中期的重要政治家和改革者,他实行了一系列政治、经济和军事改革,加强了中央集权,提高了国家的统治效率,被誉为“明代的政治家典范”。 - -### 明朝的主要历史故事 - -1. **洪武三年征诏案**:这是明朝开国之初发生的一起重大政治事件,朱元璋在此案中发布了“永久大赦”的诏书,显示了他对国家的信任和恢复法制的决心。 - -2. **郑和下西洋**:郑和率领的七次下西洋活动是明朝海上远洋活动的高峰,展现了中国古代航海技术的高超水平,也促进了中外贸易和文化交流。 - -3. **靖难之役**:这是明朝中期的一场内乱,明英宗与明成祖之间的争斗导致了一场激烈的军事冲突,最终明成祖战胜了明英宗,稳固了自己的统治地位。 - -4. **杨廷和献计征蒙**:在明朝初期,面对蒙古族的入侵,杨廷和献计建议明太祖采取防御策略,最终成功挫败了蒙古族的进攻,保卫了明朝的疆土。 - -5. **文官武将齐聚南京**:明朝的一场盛会,明成祖为了加强中央集权,特意邀请了全国的文官武将前来南京,通过研讨国家大政方针来稳固统治。 - -以上是一些明朝的主要历史人物和故事,展现了这个伟大王朝的兴衰荣辱。 -`; - const add = async (text: string) => { - stream.addResponse(text); - await sleep(100); - }; - setTimeout(async () => { - for (const s of text.split(",")) { - await add(s); - } - stream.finish(); - }); - await speaker.response({ stream }); - console.log("hello!"); -}