@h-ai/ai 0.1.0-alpha.34 → 0.1.0-alpha.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @h-ai/ai
2
2
 
3
- AI 能力模块,提供统一的 `ai` 服务对象,覆盖 LLM 对话、工具调用、MCP、Embedding、记忆、检索/RAG、知识库、上下文管理、文件解析、Rerank 与 A2A。Node.js 侧通过 `ai.init()` 初始化,浏览器侧通过 API/client 代理访问。
3
+ AI 能力模块,提供统一的 `ai` 服务对象,覆盖 LLM 对话、工具调用、MCP、Embedding、记忆、检索/RAG、知识库、上下文管理、文件解析、Rerank、语音(ASR/TTS)与 A2A。Node.js 侧通过 `ai.init()` 初始化,浏览器侧通过 API/client 代理访问。
4
4
 
5
5
  ## 能力概览
6
6
 
@@ -10,10 +10,12 @@ AI 能力模块,提供统一的 `ai` 服务对象,覆盖 LLM 对话、工具
10
10
  - `ai.mcp` / `createMcpServer`:内置 MCP 注册表与独立 MCP Server。
11
11
  - `ai.embedding`:单条/批量文本向量化。
12
12
  - `ai.memory`:记忆提取、存储、召回、注入。
13
+ - `ai.persona`:AI 角色人格档案(系统提示词 + 特征)与系统提示词组合。
13
14
  - `ai.retrieval` / `ai.rag`:多源向量检索与检索增强问答。
14
15
  - `ai.knowledge`:文档入库、实体增强检索、知识问答。
15
16
  - `ai.context`:LLM + Memory + RAG + 压缩的一体化会话管理。
16
17
  - `ai.file` / `ai.rerank`:文件解析/OCR 与文本重排序。
18
+ - `ai.audio`:语音识别(ASR)与语音合成(TTS),支持完整与流式调用,覆盖 OpenAI / MiMo / Qwen / 豆包平台。
17
19
  - `ai.a2a`:Agent-to-Agent 请求处理与远端调用。
18
20
  - `@h-ai/ai/client`:前端轻量客户端(配合 API 服务)。
19
21
  - `AIStoreProvider`:统一存储抽象;默认 DB Provider 基于 reldb + vecdb。
@@ -155,6 +157,49 @@ if (setup.success) {
155
157
  }
156
158
  ```
157
159
 
160
+ ### 语音(Audio)
161
+
162
+ 先在 `ai.init()` 中注册语音模型并映射默认识别/合成模型(凭据可回退到平台环境变量):
163
+
164
+ ```ts
165
+ await ai.init({
166
+ audio: {
167
+ models: [
168
+ { id: 'asr', provider: 'qwen', model: 'qwen3-asr-flash-realtime' },
169
+ { id: 'tts', provider: 'qwen', model: 'qwen3-tts-flash-realtime' },
170
+ ],
171
+ transcribeModel: 'asr',
172
+ synthesizeModel: 'tts',
173
+ },
174
+ })
175
+
176
+ // 完整识别(可选热词提示提升专有名词识别率)
177
+ const result = await ai.audio.transcribe({ audio: { data: wavBytes, format: 'wav' }, language: 'zh', contextHints: ['专有名词'] })
178
+ if (result.success) {
179
+ const text = result.data.text
180
+ }
181
+
182
+ // 实时识别(持续音频输入 → 领域事件流:speech_started / transcript / speech_stopped)
183
+ for await (const event of ai.audio.transcribeStream({
184
+ audio: { chunks: microphoneChunks, format: 'pcm16', sampleRate: 16000 },
185
+ })) {
186
+ if (event.type === 'speech_started')
187
+ onSpeechStart() // 支持服务端 VAD 的平台会在检测到说话时立即产出,可据此取消上游生成
188
+ else if (event.type === 'transcript')
189
+ updateTranscript(event.text, event.final)
190
+ }
191
+
192
+ // 流式合成:可带自然语言风格指令,并直接连接 LLM 文本流边生成边合成;signal 可随时打断
193
+ const controller = new AbortController()
194
+ for await (const audio of ai.audio.synthesizeStream({ text: ai.llm.askStream(question), voice: 'Cherry', instruction: '用轻快的语气', signal: controller.signal })) {
195
+ await player.write(audio)
196
+ }
197
+ ```
198
+
199
+ 取消/超时/连接错误统一为领域错误:`AbortSignal` 触发 → `AUDIO_CANCELLED`(超时 → `AUDIO_TIMEOUT`),连接失败 → `AUDIO_CONNECTION_FAILED`。实时连接时长受 `audio.maxStreamDurationMs`(默认 5 分钟)限制。
200
+
201
+ 浏览器 / 移动端通过 `@h-ai/serv` 暴露的统一语音 WebSocket 入口访问,`@h-ai/ai/client` 提供与 Node 端一致的 `audio.*` API(传输细节内部隐藏)。
202
+
158
203
  ### Context 管理器
159
204
 
160
205
  ```ts
@@ -169,7 +214,72 @@ if (manager.success) {
169
214
  }
170
215
  ```
171
216
 
172
- ## 配置
217
+ #### 真实对话状态(Conversation Commit Layer)
218
+
219
+ 默认(`turnCommit: 'auto'`)下,`chat` / `chatStream` 会把**模型生成的完整文本**写入上下文。但在「模型生成 → TTS 合成 → 实际播放」链路中,AI 可能说到一半就被打断——此时进入下一轮所有参与者可见的对话状态,应当是**实际播放出去的部分**,而非模型本想说完的全文。
220
+
221
+ 设置 `turnCommit: 'manual'` 后,生成结果不会自动写入上下文,而是返回一个 `turnId`;由调用方在确定「实际发生了什么」后显式提交真实文本:
222
+
223
+ ```ts
224
+ const m = ai.context.createManager({ turnCommit: 'manual' /* ... */ }).data
225
+
226
+ for await (const ev of m.chatStream('请展开讲讲')) {
227
+ if (ev.type === 'delta') {
228
+ feedTts(ev.text)
229
+ } // 边生成边合成播放
230
+ else if (ev.type === 'done') {
231
+ m.markTurnSpeaking(ev.turnId) // 可选:标记进入播放
232
+ if (interrupted)
233
+ await m.interruptTurn(ev.turnId, { text: actuallySpokenText }) // 只提交播放出去的部分
234
+ else
235
+ await m.commitTurn(ev.turnId) // 完整提交
236
+ }
237
+ }
238
+
239
+ // 观测每一轮的 generated / committed / status
240
+ const turns = m.getTurns()
241
+ ```
242
+
243
+ - `commitTurn(turnId, { text? })` — 提交真实文本(缺省用完整生成文本),状态转 `completed`。
244
+ - `interruptTurn(turnId, { text? })` — 只写入实际表达出去的部分(缺省视为未表达,不写入),状态转 `interrupted`。
245
+ - 只有 `committed` 的内容进入上下文与记忆提取;未提交/被打断丢弃的部分不会污染后续轮次。
246
+
247
+ #### 会话固化(Memory 生命周期)
248
+
249
+ 会话进行中,每轮对话按 `scope: { sessionId }` 提取**短期会话记忆**;会话结束时用 `consolidate()`
250
+ 把「短期记忆 + 摘要」沉淀为**跨会话长期记忆**,形成 `Session Memory → Summary → Long-term Memory` 闭环:
251
+
252
+ ```ts
253
+ // 会话结束时固化:整合摘要 → 提取长期记忆(写入不含 sessionId 的持久作用域)
254
+ const result = await manager.consolidate({ scope: { userId: 'user-001', personaId: 'xiaoq' } })
255
+ if (result.success) {
256
+ // result.data.summary —— 本次会话整合摘要
257
+ // result.data.memories —— 固化到长期记忆的条目
258
+ }
259
+ ```
260
+
261
+ ### Persona(AI 角色人格)
262
+
263
+ Memory 用 `objectId` / `scope` 回答「谁的记忆」;Persona 回答「AI 是谁」——为每个 AI 角色定义
264
+ 稳定的系统提示词与性格特征,并通过 `scope: { personaId }` 关联其长期记忆:
265
+
266
+ ```ts
267
+ await ai.persona.save({
268
+ id: 'xiaoq',
269
+ name: '小Q',
270
+ systemPrompt: '你是一位经济学家,善于从长期视角分析问题。',
271
+ traits: ['数据驱动', '偏好引用真实案例'],
272
+ })
273
+
274
+ // 组合出可直接喂给 ContextManager 的系统提示词(systemPrompt + traits)
275
+ const composed = await ai.persona.compose('xiaoq')
276
+ const manager = ai.context.createManager({
277
+ systemPrompt: composed.data,
278
+ memory: { enable: true, enableExtract: true, scope: { personaId: 'xiaoq' } },
279
+ })
280
+ ```
281
+
282
+ `ai.persona` 提供 `save` / `get` / `update` / `remove` / `list` / `compose`;角色档案全局共享(不按 `objectId` 隔离)。
173
283
 
174
284
  ```yaml
175
285
  llm:
@@ -210,6 +320,7 @@ memory:
210
320
  recencyDecay: 0.95
211
321
  embeddingEnabled: true
212
322
  defaultTopK: 10
323
+ candidateMultiplier: 5 # 候选池倍数:先取回 topK×倍数 条候选,再按 scope/重要性过滤,最后截取 topK
213
324
  writebackRelatedTopK: 20
214
325
  ```
215
326
 
@@ -221,11 +332,22 @@ memory:
221
332
  defaultTopK: 10
222
333
  ```
223
334
 
224
- - **`native`(默认,推荐)**:HAI 原生引擎,复用同一套 vecdb(向量库)、reldb(关系库)、LLM 与 Embedding。`extract` 采用 **Mem0 式批量合并**——一次 LLM 调用对整批抽取事实与相关既有记忆做 ADD / UPDATE / DELETE / NONE 决策,实现增量更新、跨条去重与矛盾删除,并支持 `category` 主题标签。`maxEntriesPerObject`、`maxEntriesGlobal`、`recencyDecay`、`embeddingEnabled`、`writebackRelatedTopK` 均作用于此后端;淘汰按 `objectId` 分区触发,不会因某一主体写入过多而淘汰其他主体的记忆。
335
+ - **`native`(默认,推荐)**:HAI 原生引擎,复用同一套 vecdb(向量库)、reldb(关系库)、LLM 与 Embedding。`extract` 采用 **Mem0 式批量合并**——一次 LLM 调用对整批抽取事实与相关既有记忆做 ADD / UPDATE / DELETE / NONE 决策,实现增量更新、跨条去重与矛盾删除,并支持 `category` 主题标签。`maxEntriesPerObject`、`maxEntriesGlobal`、`recencyDecay`、`embeddingEnabled`、`writebackRelatedTopK` 均作用于此后端;淘汰按 `objectId` 分区触发,不会因某一主体写入过多而淘汰其他主体的记忆。native 后端的 `scope` 过滤:候选集已被 `objectId` 索引收窄(≤ `maxEntriesPerObject`),PostgreSQL 上还会把 scope 下推为 `data @> '{"scope":...}'::jsonb` 包含查询并命中 JSONB **GIN 索引**(SQLite / MySQL 退回内存匹配,结果一致)。
225
336
  - **`mem0`(真·mem0ai/oss)**:直接使用 `mem0ai/oss` 的 `Memory` 引擎(嵌入式,无云服务)。LLM / Embedder 从 `llm` 配置提取(OpenAI 兼容,走 `baseUrl` / `apiKey` / 场景模型);向量库从底层 vecdb 后端提取——`qdrant` / `pgvector` 直接复用同一后端,`lancedb` / `chroma`(mem0 TS 不支持)则退回 mem0 自带的 in-memory 存储。历史记录默认禁用。需安装 `mem0ai`(已内置为依赖);复用 qdrant/pgvector 时需对应客户端。
226
337
 
227
338
  两个 Provider 对外 `ai.memory.*` API 完全一致(`extract` / `recall` / `injectMemories` / `add` / `update` / `get` / `remove` / `list` / `listPage` / `clear`),均支持 `objectId`(主体隔离)与 `scope`(业务作用域 key-value 过滤,如 `{ topicId, personaId }`)。`recall` / `list` / `listPage` / `clear` 均按 `scope` 严格过滤,`clear` 在传入 `types` / `scope` 时仅删除同时匹配项(避免误删)。一个差异:mem0 后端在 `update` 涉及 type/importance/metadata 时会重建记忆并重新分配 `id`(native 后端保持 id 稳定)。
228
339
 
340
+ **候选池与 scope 漏召回**:`scope` 过滤在内存中完成,若先按 `topK` 截断再过滤,同一主体下相关度较高的其它主题/角色记忆会把目标 scope 的记忆挤出候选池,导致「明明有却召回 0 条」。为此 `recall` / `injectMemories` 先取回 `topK × candidateMultiplier`(默认 5)条候选,过滤后再截取 `topK`。scope 隔离越细(如按 `topicId` + `personaId`),可将 `candidateMultiplier` 调大:
341
+
342
+ ```ts
343
+ const memories = await ai.memory.recall('经济发展', {
344
+ objectId: 'user-001',
345
+ scope: { topicId: 'C' },
346
+ topK: 10,
347
+ candidateMultiplier: 8, // 覆盖配置默认值,扩大候选池
348
+ })
349
+ ```
350
+
229
351
  `ai.config` 返回脱敏后的配置快照;`apiKey`、`privateKey`、URL 内嵌凭证等敏感字段不会原样暴露。
230
352
 
231
353
  ## 错误处理
@@ -1,6 +1,6 @@
1
1
  import * as _h_ai_core from '@h-ai/core';
2
2
  import { HaiResult } from '@h-ai/core';
3
- import { aj as ChatMessage, at as InteractionScope, v as MemoryType, b0 as RagOptions, b4 as ReasoningOptions, bx as ToolRegistryOperations, bh as SessionInfo, aL as LLMOperations, aU as MemoryOperations, a$ as RagOperations, b3 as ReasoningOperations, c as AIConfig, d as AIConfigInput, a8 as AIStoreProvider, by as ToolsOperations, bl as StreamOperations, ba as RetrievalOperations, aF as KnowledgeOperations, a2 as A2AOperations } from './ai-reasoning-types-Cm3-HVVN.js';
3
+ import { aC as ChatMessage, aN as InteractionScope, J as MemoryType, bk as RagOptions, bo as ReasoningOptions, bR as ToolRegistryOperations, b6 as MemoryEntry, bB as SessionInfo, b3 as LLMOperations, bc as MemoryOperations, bj as RagOperations, bn as ReasoningOperations, c as AIConfig, d as AIConfigInput, ar as AIStoreProvider, bS as ToolsOperations, bF as StreamOperations, bu as RetrievalOperations, aZ as KnowledgeOperations, al as A2AOperations, o as AudioOperations, k as AudioFormat } from './ai-reasoning-types-BGf-x2Qj.js';
4
4
  import { z } from 'zod';
5
5
  import { Buffer } from 'node:buffer';
6
6
 
@@ -177,6 +177,82 @@ interface ContextDeps {
177
177
  rag?: RagOperations;
178
178
  /** Reasoning 操作(推理引擎需要) */
179
179
  reasoning?: ReasoningOperations;
180
+ /** Summary 操作(会话固化 consolidate 需要) */
181
+ summary?: SummaryOperations;
182
+ }
183
+ /**
184
+ * 会话固化选项(`ContextManager.consolidate` 使用)
185
+ *
186
+ * 把「短期会话记忆 + 摘要」固化为「长期记忆」,形成
187
+ * `Session Memory → Summary → Long-term Memory` 的生命周期闭环。
188
+ */
189
+ interface ConsolidateOptions {
190
+ /**
191
+ * 长期记忆作用域。
192
+ *
193
+ * 默认使用管理器 `memory.scope`。通常应传入**不含 sessionId** 的作用域
194
+ * (如 `{ userId, personaId }`),使固化后的记忆跨会话持久,而非绑定单次会话。
195
+ */
196
+ scope?: Record<string, unknown>;
197
+ /** 固化提取的记忆类型限制 */
198
+ types?: MemoryType[];
199
+ /** 摘要 / 提取使用的模型 */
200
+ model?: string;
201
+ /** 固化提取的自定义 systemPrompt */
202
+ extractionSystemPrompt?: string;
203
+ }
204
+ /**
205
+ * 会话固化结果
206
+ */
207
+ interface ConsolidateResult {
208
+ /** 本次会话的整合摘要 */
209
+ summary: string;
210
+ /** 固化到长期记忆的条目 */
211
+ memories: MemoryEntry[];
212
+ }
213
+ /**
214
+ * 对话轮次状态
215
+ *
216
+ * 描述一次 assistant 生成从「模型产出」到「真实进入对话」的生命周期:
217
+ * - `generating` — 模型正在/已生成,但尚未确定实际对外表达的内容
218
+ * - `speaking` — 已进入下游表达(如 TTS 合成 / 播放)
219
+ * - `completed` — 已提交,完整生成文本即为真实文本
220
+ * - `interrupted` — 被打断,仅实际表达出去的部分(committed)进入对话
221
+ */
222
+ type ConversationTurnStatus = 'generating' | 'speaking' | 'completed' | 'interrupted';
223
+ /**
224
+ * 对话轮次
225
+ *
226
+ * 区分「模型生成的文本」与「真实进入对话的文本」,解决多智能体 / 语音访谈等场景中
227
+ * 「AI 说到一半被打断,下一轮所有参与者应看到真实发生了什么」的问题。
228
+ */
229
+ interface ConversationTurn {
230
+ /** 轮次唯一标识 */
231
+ id: string;
232
+ /** 发言者 */
233
+ speaker: 'user' | 'assistant';
234
+ /** 模型生成的完整文本 */
235
+ generated: string;
236
+ /** 实际提交进入上下文的文本(`completed` 时通常等于 generated;`interrupted` 时为真实表达部分) */
237
+ committed: string;
238
+ /** 轮次状态 */
239
+ status: ConversationTurnStatus;
240
+ /** 创建时间(Unix 毫秒) */
241
+ createdAt: number;
242
+ /** 提交时间(Unix 毫秒,未提交时为 undefined) */
243
+ committedAt?: number;
244
+ }
245
+ /**
246
+ * 提交 / 打断轮次的输入
247
+ */
248
+ interface CommitTurnInput {
249
+ /**
250
+ * 实际进入对话的文本。
251
+ *
252
+ * - `commitTurn` 不传时默认使用模型生成的完整文本(generated)。
253
+ * - `interruptTurn` 不传时默认视为「未表达任何内容」(空串,不写入上下文)。
254
+ */
255
+ text?: string;
180
256
  }
181
257
  /**
182
258
  * 有状态上下文管理器配置
@@ -192,6 +268,17 @@ interface ContextManagerOptions {
192
268
  model?: string;
193
269
  /** 温度覆盖 */
194
270
  temperature?: number;
271
+ /**
272
+ * 对话提交模式(默认 `auto`)
273
+ *
274
+ * - `auto`:`chat` / `chatStream` 生成结束后,自动把**模型生成的完整文本**写入上下文并触发记忆提取。
275
+ * - `manual`:生成结束后**不写入**上下文,仅登记一个待提交轮次并返回 `turnId`;由调用方在确定
276
+ * 「实际发生了什么」后,通过 `commitTurn` / `interruptTurn` 写入**真实文本**。
277
+ *
278
+ * 用于「模型生成 → TTS 合成 → 实际播放」链路:AI 说到一半被打断时,只有真正播放出去的
279
+ * 部分才应进入下一轮所有参与者可见的对话状态,而不是模型本想说完的全文。
280
+ */
281
+ turnCommit?: 'auto' | 'manual';
195
282
  /**
196
283
  * 压缩配置(覆盖全局 compress 配置)
197
284
  *
@@ -284,6 +371,13 @@ interface ContextChatResult {
284
371
  reply: string;
285
372
  /** 使用的模型 */
286
373
  model: string;
374
+ /**
375
+ * 本次生成对应的对话轮次 ID
376
+ *
377
+ * `turnCommit: 'manual'` 时,用于后续 `commitTurn` / `interruptTurn` 提交真实文本;
378
+ * `auto` 模式下该轮已自动提交(`completed`)。
379
+ */
380
+ turnId: string;
287
381
  /** Token 使用统计 */
288
382
  usage?: {
289
383
  prompt_tokens: number;
@@ -310,6 +404,7 @@ type ContextStreamEvent = {
310
404
  type: 'done';
311
405
  reply: string;
312
406
  model: string;
407
+ turnId: string;
313
408
  usage?: {
314
409
  prompt_tokens: number;
315
410
  completion_tokens: number;
@@ -404,6 +499,57 @@ interface ContextManager {
404
499
  * 重置管理器(清空所有消息和摘要)
405
500
  */
406
501
  reset: () => void;
502
+ /**
503
+ * 获取对话轮次列表(Conversation Commit Layer)
504
+ *
505
+ * 记录每次 chat/chatStream 生成的轮次及其 `generated` / `committed` / `status`,
506
+ * 供应用侧观测「模型生成」与「真实进入对话」的差异。
507
+ *
508
+ * @returns 轮次列表(按发生顺序)
509
+ */
510
+ getTurns: () => HaiResult<ConversationTurn[]>;
511
+ /**
512
+ * 标记某轮次进入「表达中」(如 TTS 开始播放)
513
+ *
514
+ * 仅更新状态用于观测,不改变上下文内容。
515
+ *
516
+ * @param turnId - 轮次 ID
517
+ * @returns 成功返回 ok(undefined);轮次不存在返回 CONTEXT_TURN_NOT_FOUND
518
+ */
519
+ markTurnSpeaking: (turnId: string) => HaiResult<void>;
520
+ /**
521
+ * 提交轮次(`turnCommit: 'manual'` 场景)
522
+ *
523
+ * 把真实文本写入上下文并触发记忆提取;`text` 缺省时使用模型生成的完整文本。
524
+ * 提交后该轮 `status` 变为 `completed`。
525
+ *
526
+ * @param turnId - 轮次 ID
527
+ * @param input - 提交内容(可覆盖为真实表达文本)
528
+ * @returns 成功返回 ok(undefined);轮次不存在或已提交返回对应错误
529
+ */
530
+ commitTurn: (turnId: string, input?: CommitTurnInput) => Promise<HaiResult<void>>;
531
+ /**
532
+ * 打断轮次(`turnCommit: 'manual'` 场景)
533
+ *
534
+ * 只把「实际表达出去的部分」写入上下文;`text` 缺省时视为未表达任何内容(不写入)。
535
+ * 打断后该轮 `status` 变为 `interrupted`。
536
+ *
537
+ * @param turnId - 轮次 ID
538
+ * @param input - 实际表达出去的文本
539
+ * @returns 成功返回 ok(undefined);轮次不存在或已提交返回对应错误
540
+ */
541
+ interruptTurn: (turnId: string, input?: CommitTurnInput) => Promise<HaiResult<void>>;
542
+ /**
543
+ * 将当前会话固化为长期记忆(Memory 生命周期)
544
+ *
545
+ * 流程:整合会话摘要(历史摘要 + 当前消息)→ 从摘要中提取长期记忆 → 以持久作用域写入。
546
+ * 需要 deps.summary + deps.memory 可用。用于会话结束时把「短期会话记忆」沉淀为
547
+ * 「跨会话长期记忆」,形成 Session → Summary → Long-term Memory 闭环。
548
+ *
549
+ * @param options - 固化选项(长期作用域、类型、模型等)
550
+ * @returns 会话摘要与固化的记忆条目
551
+ */
552
+ consolidate: (options?: ConsolidateOptions) => Promise<HaiResult<ConsolidateResult>>;
407
553
  /**
408
554
  * 发送消息并获取回复(需 deps.llm 可用)
409
555
  *
@@ -841,6 +987,164 @@ interface AIMCPFunctionsDeps {
841
987
  config: AIConfig;
842
988
  }
843
989
 
990
+ /**
991
+ * @h-ai/ai — Persona 子功能类型
992
+ *
993
+ * 定义「AI 角色人格」的持久化与组合接口。Persona 解决的是「AI 是谁」的问题:
994
+ * 多智能体场景(如多位专家同台访谈)中,每个 AI 有稳定的系统提示词、性格特征与
995
+ * 长期人格。它与 Memory 正交——Memory 用 `objectId` / `scope` 回答「谁的记忆」,
996
+ * Persona 回答「这个 AI 的身份与人设」,二者通过 `scope: { personaId }` 关联。
997
+ * @module ai-persona-types
998
+ */
999
+
1000
+ /**
1001
+ * Persona 档案输入(创建 / 覆盖保存时使用)
1002
+ *
1003
+ * @example
1004
+ * ```ts
1005
+ * const input: PersonaProfileInput = {
1006
+ * id: 'xiaop',
1007
+ * name: '小P',
1008
+ * systemPrompt: '你是一位社会学家,善于从群体行为视角分析问题。',
1009
+ * traits: ['谨慎', '喜欢引用真实案例'],
1010
+ * }
1011
+ * ```
1012
+ */
1013
+ interface PersonaProfileInput {
1014
+ /** 角色唯一标识(业务侧稳定 ID,如 `xiaop`) */
1015
+ id: string;
1016
+ /**
1017
+ * 所属主体 ID(多租户隔离)
1018
+ *
1019
+ * 不同主体可创建同名角色而互不覆盖;不传时归为平台内置角色 `system`。
1020
+ */
1021
+ objectId?: string;
1022
+ /** 角色显示名(如「小P」) */
1023
+ name?: string;
1024
+ /** 角色系统提示词(定义身份、职责、语气) */
1025
+ systemPrompt: string;
1026
+ /** 性格 / 风格特征(组合进系统提示词,如「谨慎」「喜欢引用案例」) */
1027
+ traits?: string[];
1028
+ /** 附加元数据 */
1029
+ metadata?: Record<string, unknown>;
1030
+ }
1031
+ /**
1032
+ * 完整的 Persona 档案
1033
+ */
1034
+ interface PersonaProfile {
1035
+ /** 角色唯一标识 */
1036
+ id: string;
1037
+ /** 所属主体 ID(多租户隔离;平台内置角色为 `system`) */
1038
+ objectId: string;
1039
+ /** 角色显示名 */
1040
+ name?: string;
1041
+ /** 角色系统提示词 */
1042
+ systemPrompt: string;
1043
+ /** 性格 / 风格特征 */
1044
+ traits: string[];
1045
+ /** 附加元数据 */
1046
+ metadata?: Record<string, unknown>;
1047
+ /** 创建时间(Unix 毫秒) */
1048
+ createdAt: number;
1049
+ /** 更新时间(Unix 毫秒) */
1050
+ updatedAt: number;
1051
+ }
1052
+ /**
1053
+ * Persona 档案更新输入
1054
+ *
1055
+ * 所有字段可选,仅更新传入的字段。
1056
+ */
1057
+ interface PersonaProfileUpdate {
1058
+ /** 更新显示名 */
1059
+ name?: string;
1060
+ /** 更新系统提示词 */
1061
+ systemPrompt?: string;
1062
+ /** 更新特征列表(整体替换) */
1063
+ traits?: string[];
1064
+ /** 更新元数据 */
1065
+ metadata?: Record<string, unknown>;
1066
+ }
1067
+ /**
1068
+ * Persona 操作接口(通过 `ai.persona` 访问)
1069
+ *
1070
+ * 管理 AI 角色人格的持久化与系统提示词组合。需要先调用 `ai.init()` 初始化后使用。
1071
+ *
1072
+ * @example
1073
+ * ```ts
1074
+ * // 定义一个 AI 角色
1075
+ * await ai.persona.save({
1076
+ * id: 'xiaoq',
1077
+ * name: '小Q',
1078
+ * systemPrompt: '你是一位经济学家。',
1079
+ * traits: ['数据驱动', '偏好长期视角'],
1080
+ * })
1081
+ *
1082
+ * // 组合出可直接喂给 ContextManager 的系统提示词
1083
+ * const composed = await ai.persona.compose('xiaoq')
1084
+ * const manager = ai.context.createManager({
1085
+ * systemPrompt: composed.data,
1086
+ * // 该角色的长期记忆用 scope 关联
1087
+ * memory: { enable: true, enableExtract: true, scope: { personaId: 'xiaoq' } },
1088
+ * })
1089
+ * ```
1090
+ */
1091
+ interface PersonaOperations {
1092
+ /**
1093
+ * 创建或覆盖保存一个角色档案(upsert 语义)
1094
+ *
1095
+ * @param profile - 角色档案输入
1096
+ * @returns 保存后的完整档案
1097
+ */
1098
+ save: (profile: PersonaProfileInput) => Promise<HaiResult<PersonaProfile>>;
1099
+ /**
1100
+ * 按 ID 获取角色档案
1101
+ *
1102
+ * @param id - 角色 ID
1103
+ * @returns 角色档案,不存在时返回 PERSONA_NOT_FOUND
1104
+ */
1105
+ get: (id: string, options?: PersonaScopeOptions) => Promise<HaiResult<PersonaProfile>>;
1106
+ /**
1107
+ * 更新角色档案(仅更新传入字段)
1108
+ *
1109
+ * @param id - 角色 ID
1110
+ * @param updates - 需要更新的字段
1111
+ * @param options - 主体作用域(默认 `system`)
1112
+ * @returns 更新后的完整档案
1113
+ */
1114
+ update: (id: string, updates: PersonaProfileUpdate, options?: PersonaScopeOptions) => Promise<HaiResult<PersonaProfile>>;
1115
+ /**
1116
+ * 删除角色档案
1117
+ *
1118
+ * @param id - 角色 ID
1119
+ * @param options - 主体作用域(默认 `system`)
1120
+ * @returns 成功返回 ok(undefined)
1121
+ */
1122
+ remove: (id: string, options?: PersonaScopeOptions) => Promise<HaiResult<void>>;
1123
+ /**
1124
+ * 列出指定主体的角色档案
1125
+ *
1126
+ * @param options - 主体作用域(默认 `system`)
1127
+ * @returns 角色档案列表
1128
+ */
1129
+ list: (options?: PersonaScopeOptions) => Promise<HaiResult<PersonaProfile[]>>;
1130
+ /**
1131
+ * 组合角色的完整系统提示词
1132
+ *
1133
+ * 将 `systemPrompt` 与 `traits` 拼装为一段可直接作为 system 消息的文本,
1134
+ * 供 `ai.context.createManager({ systemPrompt })` 使用。
1135
+ *
1136
+ * @param id - 角色 ID
1137
+ * @param options - 主体作用域(默认 `system`)
1138
+ * @returns 组合后的系统提示词,角色不存在时返回 PERSONA_NOT_FOUND
1139
+ */
1140
+ compose: (id: string, options?: PersonaScopeOptions) => Promise<HaiResult<string>>;
1141
+ }
1142
+ /** Persona 操作的主体作用域选项 */
1143
+ interface PersonaScopeOptions {
1144
+ /** 所属主体 ID(不传时归为平台内置角色 `system`) */
1145
+ objectId?: string;
1146
+ }
1147
+
844
1148
  /**
845
1149
  * @h-ai/ai — Rerank 子功能类型
846
1150
  *
@@ -1030,14 +1334,29 @@ declare const HaiAIError: {
1030
1334
  readonly MEMORY_RECALL_FAILED: _h_ai_core.HaiErrorDef;
1031
1335
  readonly MEMORY_NOT_FOUND: _h_ai_core.HaiErrorDef;
1032
1336
  readonly MEMORY_ENRICH_FAILED: _h_ai_core.HaiErrorDef;
1337
+ readonly MEMORY_PROMOTE_FAILED: _h_ai_core.HaiErrorDef;
1338
+ readonly PERSONA_NOT_FOUND: _h_ai_core.HaiErrorDef;
1339
+ readonly PERSONA_SAVE_FAILED: _h_ai_core.HaiErrorDef;
1033
1340
  readonly FILE_PARSE_FAILED: _h_ai_core.HaiErrorDef;
1034
1341
  readonly FILE_UNSUPPORTED_FORMAT: _h_ai_core.HaiErrorDef;
1035
1342
  readonly FILE_OCR_FAILED: _h_ai_core.HaiErrorDef;
1036
1343
  readonly FILE_INVALID_CONTENT: _h_ai_core.HaiErrorDef;
1344
+ readonly AUDIO_INVALID_REQUEST: _h_ai_core.HaiErrorDef;
1345
+ readonly AUDIO_MODEL_NOT_FOUND: _h_ai_core.HaiErrorDef;
1346
+ readonly AUDIO_PROVIDER_NOT_FOUND: _h_ai_core.HaiErrorDef;
1347
+ readonly AUDIO_UNSUPPORTED_INPUT: _h_ai_core.HaiErrorDef;
1348
+ readonly AUDIO_UPSTREAM_ERROR: _h_ai_core.HaiErrorDef;
1349
+ readonly AUDIO_PROTOCOL_ERROR: _h_ai_core.HaiErrorDef;
1350
+ readonly AUDIO_CONNECTION_FAILED: _h_ai_core.HaiErrorDef;
1351
+ readonly AUDIO_TIMEOUT: _h_ai_core.HaiErrorDef;
1352
+ readonly AUDIO_INPUT_TOO_LARGE: _h_ai_core.HaiErrorDef;
1353
+ readonly AUDIO_CANCELLED: _h_ai_core.HaiErrorDef;
1037
1354
  readonly CONTEXT_COMPRESS_FAILED: _h_ai_core.HaiErrorDef;
1038
1355
  readonly CONTEXT_SUMMARIZE_FAILED: _h_ai_core.HaiErrorDef;
1039
1356
  readonly CONTEXT_TOKEN_ESTIMATE_FAILED: _h_ai_core.HaiErrorDef;
1040
1357
  readonly CONTEXT_BUDGET_EXCEEDED: _h_ai_core.HaiErrorDef;
1358
+ readonly CONTEXT_TURN_NOT_FOUND: _h_ai_core.HaiErrorDef;
1359
+ readonly CONTEXT_TURN_INVALID_STATE: _h_ai_core.HaiErrorDef;
1041
1360
  readonly STORE_FAILED: _h_ai_core.HaiErrorDef;
1042
1361
  readonly STORE_NOT_AVAILABLE: _h_ai_core.HaiErrorDef;
1043
1362
  readonly SESSION_NOT_FOUND: _h_ai_core.HaiErrorDef;
@@ -1121,6 +1440,8 @@ interface AIFunctions {
1121
1440
  readonly knowledge: KnowledgeOperations;
1122
1441
  /** Memory 操作(记忆提取、存储、检索、注入),需要先调用 `init()` */
1123
1442
  readonly memory: MemoryOperations;
1443
+ /** Persona 操作(AI 角色人格档案与系统提示词组合),需要先调用 `init()` */
1444
+ readonly persona: PersonaOperations;
1124
1445
  /** Token 操作(Token 估算),需要先调用 `init()` */
1125
1446
  readonly token: TokenOperations;
1126
1447
  /** Summary 操作(消息摘要生成),需要先调用 `init()` */
@@ -1135,6 +1456,91 @@ interface AIFunctions {
1135
1456
  readonly file: FileOperations;
1136
1457
  /** A2A 操作(Agent-to-Agent 协议),需要先调用 `init()` 并配置 `a2a` */
1137
1458
  readonly a2a: A2AOperations;
1459
+ /** Audio 操作(语音识别 / 语音合成),需要先调用 `init()` 并配置 `audio` */
1460
+ readonly audio: AudioOperations;
1461
+ }
1462
+
1463
+ /**
1464
+ * @h-ai/ai — 统一语音 WebSocket 协议
1465
+ *
1466
+ * 定义浏览器 / 远程客户端与 `@h-ai/serv` 语音入口之间的统一 WebSocket 消息协议。
1467
+ * 客户端与服务端共享此协议,客户端不接收任何厂商原生事件;音频以二进制帧传输,
1468
+ * 控制与文本以 JSON 帧传输。
1469
+ * @module audio/ai-audio-ws-protocol
1470
+ */
1471
+
1472
+ /** 统一语音入口的默认路径(相对 API 前缀) */
1473
+ declare const AUDIO_WS_PATH = "/ai/audio";
1474
+ /**
1475
+ * 会话起始消息(客户端首个 JSON 帧)
1476
+ *
1477
+ * 表达本次语音操作:识别或合成,及可选的模型 / 语言 / 音色 / 格式等参数。
1478
+ */
1479
+ interface AudioWsStartMessage {
1480
+ type: 'start';
1481
+ /** 操作类型 */
1482
+ operation: 'transcribe' | 'synthesize';
1483
+ /**
1484
+ * 是否流式返回增量结果
1485
+ *
1486
+ * 识别操作:`true` 时服务端桥接为持续音频输入并流式返回临时结果;
1487
+ * `false`(默认)时服务端缓冲完整音频后返回单条最终结果。
1488
+ */
1489
+ stream?: boolean;
1490
+ /** 模型 ID(不传时使用服务端默认模型) */
1491
+ model?: string;
1492
+ /** 识别语言提示 */
1493
+ language?: string;
1494
+ /** 领域提示词 / 热词(识别) */
1495
+ contextHints?: string[];
1496
+ /** 合成音色 */
1497
+ voice?: string;
1498
+ /** 合成自然语言风格指令 */
1499
+ instruction?: string;
1500
+ /** 音频格式(识别时为输入格式,合成时为输出格式) */
1501
+ format?: AudioFormat;
1502
+ /** 采样率 */
1503
+ sampleRate?: number;
1504
+ /** 声道数 */
1505
+ channels?: 1 | 2;
1506
+ }
1507
+ /** 文本输入帧(合成操作时携带待合成文本) */
1508
+ interface AudioWsTextMessage {
1509
+ type: 'text';
1510
+ /** 待合成文本片段 */
1511
+ text: string;
1512
+ }
1513
+ /** 输入结束帧(音频 / 文本输入全部发送完毕) */
1514
+ interface AudioWsDoneMessage {
1515
+ type: 'done';
1516
+ }
1517
+ /** 客户端 JSON 控制消息(音频输入以二进制帧发送,不走 JSON) */
1518
+ type AudioWsClientMessage = AudioWsStartMessage | AudioWsTextMessage | AudioWsDoneMessage;
1519
+ /** 语音起止事件(识别操作时服务端 VAD 检测到语音开始 / 结束) */
1520
+ interface AudioWsSpeechMessage {
1521
+ type: 'speech_started' | 'speech_stopped';
1522
+ }
1523
+ /** 识别结果帧(识别操作时返回当前语句的完整文本) */
1524
+ interface AudioWsTranscriptMessage {
1525
+ type: 'transcript';
1526
+ /** 当前语句的完整识别文本 */
1527
+ text: string;
1528
+ /** 是否为该语句的最终结果 */
1529
+ final: boolean;
1530
+ }
1531
+ /** 错误帧(领域语义错误码,不暴露厂商协议细节) */
1532
+ interface AudioWsErrorMessage {
1533
+ type: 'error';
1534
+ /** 领域错误码(如 `hai:ai:054`) */
1535
+ code: string;
1536
+ /** 错误消息 */
1537
+ message: string;
1538
+ }
1539
+ /** 结束帧(服务端已发送全部结果,随后关闭连接) */
1540
+ interface AudioWsEndMessage {
1541
+ type: 'end';
1138
1542
  }
1543
+ /** 服务端 JSON 消息(合成音频以二进制帧返回,不走 JSON) */
1544
+ type AudioWsServerMessage = AudioWsSpeechMessage | AudioWsTranscriptMessage | AudioWsErrorMessage | AudioWsEndMessage;
1139
1545
 
1140
- export { type AIFunctions as A, type MCPProvider as B, type CompressionStrategy as C, type MCPResource as D, type EmbeddingItem as E, type FileOperations as F, type MCPResourceContent as G, HaiAIError as H, type MCPToolDefinition as I, type MCPToolHandler as J, type McpServerOptions as K, type RerankItem as L, type MCPContext as M, type RerankOperations as N, type OutputFormat as O, type RerankRequest as P, type RerankResponse as Q, type RerankDocument as R, type SummaryOperations as S, type SummaryOptions as T, type SummaryResult as U, type TokenOperations as V, type AIInitOptions as a, CompressionStrategySchema as b, type AIMCPFunctionsDeps as c, type CompressOperations as d, type CompressOptions as e, type CompressResult as f, type ContextChatOptions as g, type ContextChatResult as h, type ContextDeps as i, type ContextManager as j, type ContextManagerOptions as k, type ContextOperations as l, type ContextStreamEvent as m, type EmbeddingOperations as n, type EmbeddingProvider as o, type EmbeddingRequest as p, type EmbeddingResponse as q, type FileParseMethod as r, type FileParseOptions as s, type FileParseRequest as t, type FileParseResult as u, type MCPOperations as v, type MCPPrompt as w, type MCPPromptArgument as x, type MCPPromptContent as y, type MCPPromptMessage as z };
1546
+ export { type OutputFormat as $, type AIFunctions as A, type ConversationTurn as B, type CompressionStrategy as C, type ConversationTurnStatus as D, type EmbeddingItem as E, type EmbeddingOperations as F, type EmbeddingProvider as G, HaiAIError as H, type EmbeddingRequest as I, type EmbeddingResponse as J, type FileOperations as K, type FileParseMethod as L, type FileParseOptions as M, type FileParseRequest as N, type FileParseResult as O, type MCPContext as P, type MCPOperations as Q, type MCPPrompt as R, type MCPPromptArgument as S, type MCPPromptContent as T, type MCPPromptMessage as U, type MCPProvider as V, type MCPResource as W, type MCPResourceContent as X, type MCPToolDefinition as Y, type MCPToolHandler as Z, type McpServerOptions as _, type AIInitOptions as a, type PersonaOperations as a0, type PersonaProfile as a1, type PersonaProfileInput as a2, type PersonaProfileUpdate as a3, type PersonaScopeOptions as a4, type RerankDocument as a5, type RerankItem as a6, type RerankOperations as a7, type RerankRequest as a8, type RerankResponse as a9, type SummaryOperations as aa, type SummaryOptions as ab, type SummaryResult as ac, type TokenOperations as ad, AUDIO_WS_PATH as b, type AudioWsClientMessage as c, type AudioWsDoneMessage as d, type AudioWsEndMessage as e, type AudioWsErrorMessage as f, type AudioWsServerMessage as g, type AudioWsSpeechMessage as h, type AudioWsStartMessage as i, type AudioWsTextMessage as j, type AudioWsTranscriptMessage as k, CompressionStrategySchema as l, type AIMCPFunctionsDeps as m, type CommitTurnInput as n, type CompressOperations as o, type CompressOptions as p, type CompressResult as q, type ConsolidateOptions as r, type ConsolidateResult as s, type ContextChatOptions as t, type ContextChatResult as u, type ContextDeps as v, type ContextManager as w, type ContextManagerOptions as x, type ContextOperations as y, type ContextStreamEvent as z };