Spaces:
Running
Running
| # Clare AI Agent:前后端配合说明 & Podcast 集成到独立网站 | |
| ## 一、前后端需要配合做什么 | |
| 整体上:**前端负责 UI 和用户操作,后端负责会话状态、RAG、LLM 调用和音频生成**。具体分工如下。 | |
| ### 1. 会话与身份 | |
| | 能力 | 后端 | 前端 | | |
| |------|------|------| | |
| | 登录 | `POST /api/login` 校验并建立会话(内存里存 `user_id`) | 收集姓名/ID,调用 login,存 `user`(如 `user_id` = email) | | |
| | 会话状态 | 按 `user_id` 存 history、course_outline、weaknesses、cognitive_state、上传文件等 | 所有需要会话的请求都带 `user_id`(如 `user.email`) | | |
| ### 2. 对话与内容 | |
| | 能力 | 后端 | 前端 | | |
| |------|------|------| | |
| | 对话 | `POST /api/chat`:RAG 检索 + LLM 生成,返回回复 + 可选 references | 发 message、learning_mode、language_preference,渲染回复和引用 | | |
| | 上传 | `POST /api/upload`:解析文件、更新该用户的 course_outline / RAG chunks | 选文件、doc_type,上传后刷新侧边栏/状态 | | |
| | 导出 | `POST /api/export`:根据当前会话 history 生成 Markdown 文本 | 调 export,展示或下载文本 | | |
| | 总结 | `POST /api/summary`:对当前会话做总结,返回 Markdown | 调 summary,展示在右侧或弹窗 | | |
| ### 3. 音频(TTS / Podcast) | |
| | 能力 | 后端 | 前端 | | |
| |------|------|------| | |
| | TTS | `POST /api/tts`:接收一段 text,调用 OpenAI TTS,返回 **MP3 二进制**(`audio/mpeg`) | 传 `user_id` + `text`,用 `response.blob()` 得到 Blob → `URL.createObjectURL(blob)` 给 `<audio src>` 播放或下载 | | |
| | Podcast | `POST /api/podcast`:根据 `source=summary|conversation` 用当前会话生成脚本,再 TTS 合成,返回 **MP3 二进制**(`audio/mpeg`) | 传 `user_id` + `source`,同样用 Blob → Object URL 播放/下载 | | |
| ### 4. 其他 | |
| - Memory Line、Profile 状态、Quiz 等:前端按需调对应 API,后端按 `user_id` 读会话状态并返回。 | |
| - 前端需统一配置 **API 基地址**(如 `VITE_API_BASE`),保证请求发到 Clare 后端。 | |
| **小结**:后端不写前端 UI,只提供 REST API 和会话状态;前端负责所有展示、表单和把「当前用户」(`user_id`) 带到每次请求里。Podcast 当前是「请求一次、返回一段 MP3 流」,不落盘。 | |
| --- | |
| ## 二、把 Podcast 集成到「独立网站」的两种方式 | |
| 当前实现里,**Podcast 不会生成可下载的 MP3 文件链接**,而是接口**直接返回 MP3 字节流**,由调用方在内存里播放或触发下载。 | |
| 若你要在**独立网站**(与当前 React 应用分离的站点)里集成 podcast,有两种常见做法。 | |
| ### 方式 A:不落盘,直接用 API 返回的 MP3 流(当前能力即可) | |
| **流程:** | |
| 1. 独立网站(同源或配置好 CORS 的后端代理)向 Clare 后端发: | |
| `POST /api/podcast`,body:`{ "user_id": "...", "source": "summary" | "conversation", "voice": "nova" }`。 | |
| 2. 后端生成 podcast,**直接在响应体里返回 MP3**(`Content-Type: audio/mpeg`)。 | |
| 3. 独立网站用 `fetch` 拿到 `response.blob()`,然后: | |
| - **播放**:`URL.createObjectURL(blob)` 得到临时 URL,赋给 `<audio src="...">`; | |
| - **下载**:用 `<a download>` + Object URL,或 Blob 转成文件让用户保存。 | |
| **特点:** | |
| - **不需要**在后端生成「可下载的 MP3 文件」再让网站访问; | |
| - 不占磁盘、不涉及文件 URL;适合「当前会话、一次性听/下」; | |
| - 独立网站只要能用 JavaScript 调 Clare 的 API(或通过自己的后端转发)即可。 | |
| **结论:** | |
| 若独立网站可以发 POST 并处理 Blob,**不需要**先生成可下载的 MP3 文件再给网站访问;直接使用接口返回的 MP3 流即可播放和下载。 | |
| --- | |
| ### 方式 B:生成可下载的 MP3 文件,网站用「链接」播放/下载 | |
| 适用于: | |
| - 需要**固定、可分享的链接**(例如发给别人、放 RSS、嵌入第三方播放器); | |
| - 希望**缓存**同一脚本的 MP3,避免重复生成; | |
| - 独立网站只能提供「一个 MP3 地址」,不能发 POST(例如纯静态页、外嵌 `<audio src="https://...">`)。 | |
| **做法:** | |
| 1. **后端**在生成完 podcast 后: | |
| - 把 MP3 写入**文件**(例如 `/static/podcasts/<session_id>_<timestamp>.mp3`)或对象存储(如 S3/OSS), | |
| - 返回一个**可访问的 URL**,例如: | |
| `https://your-api.com/static/podcasts/xxx.mp3` 或存储的 public URL。 | |
| 2. **独立网站**: | |
| - 用该 URL 做 `<audio src="...">` 播放; | |
| - 或用 `<a href="..." download>下载</a>` 提供下载。 | |
| 这样就是「**生成可下载的 MP3,再让网站通过文件 URL 访问/播放**」。 | |
| 若要采用方式 B,后端需要增加逻辑(例如): | |
| - 在 `POST /api/podcast` 里,生成 MP3 后写入指定目录或上传到存储; | |
| - 在响应里不再只返回 `audio/mpeg` 流,而是返回 JSON,例如: | |
| `{ "url": "https://.../podcasts/xxx.mp3", "expires_in": 3600 }`; | |
| 或保留当前「直接返回 MP3」的行为,并**额外**提供一个 `GET /api/podcast/<id>` 或静态路径,用于通过 URL 访问已生成的 MP3。 | |
| --- | |
| ## 三、简要对比 | |
| | 维度 | 方式 A:直接用 API 返回的 MP3 流 | 方式 B:生成 MP3 文件 + URL | | |
| |------|----------------------------------|-----------------------------| | |
| | 是否需要生成可下载文件 | **否** | **是**(写盘或对象存储) | | |
| | 网站如何播放/下载 | 用 Blob + Object URL 或 `<a download>` | 用返回的 URL 做 `<audio src>` / 下载链接 | | |
| | 是否可分享/固定链接 | 否(每次请求新生成) | 是 | | |
| | 是否适合纯静态页「只填 URL」 | 否(需能发 POST) | 是 | | |
| | 当前 Clare 是否支持 | **是**(现有 `/api/podcast` 即可) | 需在后端增加「落盘/存储 + 返回 URL」 | | |
| **总结:** | |
| - **不需要**「先生成可下载的 MP3 再让网站访问」:用现有 `/api/podcast`,在独立网站里用 POST + Blob 即可播放和下载(方式 A)。 | |
| - **需要**「可分享链接、外嵌播放器、纯 URL 访问」时:再在后端增加「生成 MP3 文件并返回 URL」的能力(方式 B)。 | |