@h-ai/cli 0.1.0-alpha.19 → 0.1.0-alpha.20

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.
Files changed (63) hide show
  1. package/dist/{chunk-BPO34NRL.js → chunk-3BBXDQ4Y.js} +5 -5
  2. package/dist/chunk-3BBXDQ4Y.js.map +1 -0
  3. package/dist/cli-config-templates-IGGO7P45.js +5 -0
  4. package/dist/{cli-config-templates-JCS7T3NU.js.map → cli-config-templates-IGGO7P45.js.map} +1 -1
  5. package/dist/index.js +67 -39
  6. package/dist/index.js.map +1 -1
  7. package/dist/{src-N4NPS7AG.js → src-QNEJJ3BV.js} +261 -103
  8. package/dist/src-QNEJJ3BV.js.map +1 -0
  9. package/package.json +2 -2
  10. package/templates/apps/admin/messages/en-US.json +5 -0
  11. package/templates/apps/admin/messages/zh-CN.json +5 -0
  12. package/templates/apps/admin/src/routes/+layout.svelte.hbs +5 -5
  13. package/templates/apps/admin/src/routes/+page.svelte.hbs +32 -12
  14. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +37 -20
  15. package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +33 -24
  16. package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +16 -13
  17. package/templates/apps/android-app/src/lib/api.ts.hbs +4 -3
  18. package/templates/apps/android-app/src/lib/capacitor.ts +1 -1
  19. package/templates/apps/android-app/src/routes/+layout.svelte.hbs +11 -10
  20. package/templates/apps/android-app/src/routes/+page.svelte.hbs +17 -6
  21. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +4 -1
  22. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +14 -0
  23. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +13 -0
  24. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +3 -0
  25. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server-app.ts.hbs +23 -3
  26. package/templates/apps/h5/messages/en-US.json +3 -0
  27. package/templates/apps/h5/messages/zh-CN.json +3 -0
  28. package/templates/apps/h5/src/routes/+layout.svelte.hbs +30 -29
  29. package/templates/apps/h5/src/routes/+page.svelte.hbs +17 -6
  30. package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +17 -6
  31. package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +17 -6
  32. package/templates/apps/website/src/routes/+layout.svelte.hbs +35 -26
  33. package/templates/apps/website/src/routes/+page.svelte.hbs +28 -10
  34. package/templates/apps/website/src/routes/about/+page.svelte.hbs +11 -6
  35. package/templates/apps/website/src/routes/contact/+page.svelte.hbs +37 -34
  36. package/templates/apps/website/src/routes/services/+page.svelte.hbs +27 -40
  37. package/templates/base/package.json.hbs +26 -21
  38. package/templates/base/src/app.html.hbs +4 -0
  39. package/templates/base/src/hooks.server.ts.hbs +17 -7
  40. package/templates/base/src/lib/server/init.ts.hbs +20 -12
  41. package/templates/base/static/favicon.png +0 -0
  42. package/templates/base/tests/scaffold.test.ts.hbs +9 -1
  43. package/templates/base/vite.config.ts.hbs +3 -3
  44. package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +5 -5
  45. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +34 -32
  46. package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +46 -48
  47. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +35 -31
  48. package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +41 -38
  49. package/templates/skills/AGENTS.md +11 -1
  50. package/templates/skills/CLAUDE.md +2 -1
  51. package/templates/skills/copilot-instructions.md +22 -6
  52. package/templates/skills/hai-ai/SKILL.md +115 -648
  53. package/templates/skills/hai-ai/reference.md +197 -0
  54. package/templates/skills/hai-api-client/SKILL.md +3 -1
  55. package/templates/skills/hai-app-create/SKILL.md +1 -1
  56. package/templates/skills/hai-app-review/SKILL.md +1 -1
  57. package/templates/skills/hai-deploy/SKILL.md +12 -9
  58. package/templates/skills/hai-kit/SKILL.md +5 -3
  59. package/templates/skills/hai-serv/SKILL.md +2 -0
  60. package/templates/skills/hai-ui/SKILL.md +25 -11
  61. package/dist/chunk-BPO34NRL.js.map +0 -1
  62. package/dist/cli-config-templates-JCS7T3NU.js +0 -5
  63. package/dist/src-N4NPS7AG.js.map +0 -1
@@ -3,735 +3,202 @@ name: hai-ai
3
3
  description: 使用 @h-ai/ai 进行 LLM 调用(OpenAI 兼容)、MCP 服务器创建、工具定义与注册、流式处理、记忆管理(objectId 隔离)、上下文压缩与会话持久化;当需求涉及 AI 对话、工具调用、MCP 协议、流式响应、记忆提取注入、上下文摘要压缩、会话管理或 AI 客户端时使用。
4
4
  ---
5
5
 
6
- # hai-ai
6
+ # hai-ai — @h-ai/ai 快速指南
7
7
 
8
- > `@h-ai/ai` 提供统一的 AI 能力,支持 LLM 调用(OpenAI 兼容 API)、MCP 服务器、工具定义与注册、流式处理、记忆管理(objectId 隔离)、上下文压缩与会话持久化。支持 Node.js 与浏览器双端。
8
+ `@h-ai/ai` 统一提供 LLM、工具、MCP、Embedding、Memory、Retrieval/RAG、Knowledge、Context、File、Rerank 与 A2A 能力。
9
9
 
10
- ---
11
-
12
- ## 运行环境
13
-
14
- | 能力 | Node.js | 浏览器 |
15
- |------|---------|--------|
16
- | `ai.llm` LLM 调用 | ✅ | ❌(通过 API 端点代理) |
17
- | `ai.mcp` MCP 服务器 | ✅ | ❌ |
18
- | `ai.tools` 工具定义 | ✅ | ✅(纯函数,无需 init) |
19
- | `ai.stream` 流处理 | ✅ | ✅(纯函数,无需 init) |
20
- | `ai.memory` / `ai.conversation` | ✅ | ❌(通过 API 查询) |
21
- | `ai.knowledge` 知识库 | ✅ | ❌(通过 API 查询) |
10
+ > 详细 API 表、错误码与长示例见同目录 `reference.md`。只有需要完整契约或边界用例时再读取,避免把长参考塞进上下文。
22
11
 
23
- 浏览器端消费 AI 能力的标准模式:通过 `@h-ai/api-client` 的 typed contract 调用 `apiClient.ai.chats.*`;如需 SSE 流式输出,由应用显式暴露自定义流式 endpoint,再用 `fetch` / `apiFetch` 消费。
12
+ ## 使用边界
24
13
 
25
- ---
14
+ - `ai.tools`、`ai.stream` 是纯函数能力,无需 `ai.init()`。
15
+ - `ai.llm`、`ai.embedding`、`ai.memory`、`ai.retrieval`、`ai.rag`、`ai.knowledge`、`ai.context`、`ai.a2a` 需要先 `await ai.init(...)`。
16
+ - 默认 DB Provider 需要在 `ai.init()` 前完成 `reldb.init()` 与 `vecdb.init()`;自定义 `AIStoreProvider` 可跳过这两个依赖。
17
+ - 浏览器端不要直接调用 Node-only 能力;通过 `@h-ai/api-client` 或应用自定义 API/SSE endpoint 代理。
18
+ - 公共 API 返回 `HaiResult<T>` 时按 `if (!result.success) return result` 处理;不要用 `try/catch` 包裹正常业务错误。
26
19
 
27
- ## 依赖
20
+ ## 初始化与关闭
28
21
 
29
- | 模块 | 用途 | 是否必需 | 初始化要求 |
30
- | --- | --- | --- | --- |
31
- | `@h-ai/reldb` | 数据库(对话记录/会话/记忆/知识库持久化) | **必需** | 需在 `ai.init()` 前初始化 |
32
- | `@h-ai/vecdb` | 向量数据库(记忆向量检索/知识库检索) | **必需** | 需在 `ai.init()` 前初始化 |
33
- | `@h-ai/datapipe` | 数据管道(知识库文档切分) | 内部使用 | 无需手动初始化 |
22
+ ```ts
23
+ import { ai } from '@h-ai/ai'
24
+ import { reldb } from '@h-ai/reldb'
25
+ import { vecdb } from '@h-ai/vecdb'
34
26
 
35
- > `ai.tools` 和 `ai.stream` 为纯函数子系统,无需 init 即可使用。
27
+ await reldb.init(core.config.get('db'))
28
+ await vecdb.init(core.config.get('vecdb'))
36
29
 
37
- ---
30
+ const init = await ai.init(core.config.get('ai'))
31
+ if (!init.success) return init
38
32
 
39
- ## 适用场景
33
+ // ... 使用 ai.llm / ai.memory / ai.knowledge 等
40
34
 
41
- - 调用 LLM 进行对话(非流式/流式)
42
- - 定义和执行工具(Function Calling)
43
- - 创建 MCP 服务器(Streamable HTTP/SSE/Stdio)
44
- - 注册 MCP 资源与提示词
45
- - 自动提取和管理对话记忆
46
- - 上下文压缩与 Token 控制
47
- - 会话持久化与恢复(配合 reldb/vecdb)
48
- - 前端 AI 客户端集成(记忆/会话查询)
35
+ await ai.close()
36
+ ```
49
37
 
50
- ---
38
+ 自定义存储:
51
39
 
52
- ## 使用步骤
40
+ ```ts
41
+ await ai.init(core.config.get('ai'), { storeProvider: myProvider })
42
+ await ai.close()
43
+ ```
53
44
 
54
- ### 1. 配置
45
+ ## 配置要点
55
46
 
56
47
  ```yaml
57
- # config/_ai.yml
58
48
  llm:
59
49
  apiKey: ${HAI_AI_LLM_API_KEY:}
60
50
  baseUrl: ${HAI_AI_LLM_BASE_URL:https://api.openai.com/v1}
61
51
  model: ${HAI_AI_LLM_MODEL:gpt-4o-mini}
62
- maxTokens: ${HAI_AI_LLM_MAX_TOKENS:4096}
63
- temperature: 0.7
64
52
  timeout: 60000
53
+ scenarios:
54
+ chat: fast
55
+ reasoning: strong
56
+ embedding: embed
57
+
58
+ embedding:
59
+ dimensions: 1536
60
+ batchSize: 100
65
61
 
66
- # 存储配置(可选)
67
- store:
68
- mode: memory # memory | persistent(需 reldb + vecdb 已初始化)
69
-
70
- # 记忆管理(可选)
71
- memory:
72
- maxEntries: 1000 # 最大记忆条数
73
- extractModel: gpt-4o-mini # 提取用模型(默认用 LLM 配置模型)
74
- apiKey: # 可选,覆盖 LLM apiKey
75
- baseUrl: # 可选,覆盖 LLM baseUrl
76
- recencyDecay: 0.95 # 时间衰减系数
77
- embeddingEnabled: true # 启用向量检索
78
- defaultTopK: 10 # 默认检索数量
79
-
80
- # Token 估算(可选)
81
- token:
82
- tokenRatio: 0.25 # Token 估算系数(4 字符 ≈ 1 token)
83
-
84
- # 摘要生成(可选)
85
- summary:
86
- systemPrompt: # 自定义摘要系统提示词
87
-
88
- # 上下文压缩(可选)
89
- compress:
90
- defaultStrategy: hybrid # 压缩策略:summary | sliding-window | hybrid
91
- defaultMaxTokens: 0 # 0 = 取模型 maxTokens 的 80%
92
- preserveLastN: 4 # 保留最近 N 条消息
93
-
94
- # 知识库(可选)
95
62
  knowledge:
96
63
  collection: hai_ai_knowledge
97
64
  dimension: 1536
98
- enableEntityExtraction: true
99
- cleanOptions:
100
- removeHtml: true
101
- normalizeWhitespace: true
102
65
  chunkOptions:
103
- mode: markdown # sentence | paragraph | markdown | page | custom
66
+ mode: markdown
104
67
  maxSize: 1500
105
68
  overlap: 200
106
69
  ```
107
70
 
108
- ### 2. 初始化与关闭
109
-
110
- ```typescript
111
- import { ai } from '@h-ai/ai'
112
- import { reldb } from '@h-ai/reldb'
113
- import { vecdb } from '@h-ai/vecdb'
114
-
115
- // 先初始化依赖
116
- await reldb.init(core.config.get('db'))
117
- await vecdb.init(core.config.get('vecdb'))
118
-
119
- // 再初始化 AI 模块(自动使用已初始化的 reldb 和 vecdb 单例)
120
- ai.init(core.config.get('ai'))
121
- // 使用后
122
- ai.close()
123
- ```
71
+ `ai.config` 是脱敏快照;`apiKey`、`privateKey`、URL 内嵌凭证会被隐藏。
124
72
 
125
- `ai.config` 返回的是脱敏后的配置快照;`apiKey`、`privateKey`、`baseUrl` 等敏感字段会自动隐藏,URL 类字段只掩码内嵌凭证。
73
+ ## 常用 API
126
74
 
127
- **特殊说明**:`ai.tools` 和 `ai.stream` 无需 init 即可使用;`ai.llm` 和 `ai.mcp` 操作需要先 init。
75
+ | 场景 | 首选 API | 备注 |
76
+ | --- | --- | --- |
77
+ | 非流式对话 | `ai.llm.chat({ messages })` | OpenAI 兼容 Chat Completion |
78
+ | 流式对话 | `ai.llm.chatStream({ messages })` | 返回 `AsyncIterable` |
79
+ | 工具定义 | `ai.tools.define(...)` | Zod schema 转 JSON Schema |
80
+ | 工具执行 | `registry.executeAll(toolCalls)` | 支持并行执行 |
81
+ | MCP 服务 | `createMcpServer(...)` | 按需连接 HTTP/SSE/Stdio transport |
82
+ | Embedding | `ai.embedding.embedText(text)` | 批量用 `embedBatch` |
83
+ | 记忆 | `ai.memory.extract/recall/injectMemories` | 用 `objectId` 做主体隔离 |
84
+ | Retrieval/RAG | `ai.retrieval.retrieve` / `ai.rag.query` | Retrieval source 先注册或由配置预置 |
85
+ | Knowledge | `ai.knowledge.setup/ingest/ask` | 入库前会调用 datapipe 清洗分块 |
86
+ | Context | `ai.context.createManager` | 编排 LLM + Memory + RAG + 压缩 |
87
+ | A2A | `ai.a2a.registerExecutor/handleRequest` | 延迟初始化 SDK handler |
128
88
 
129
- ---
89
+ ## LLM + 工具调用
130
90
 
131
- ## 核心 API
91
+ ```ts
92
+ const registry = ai.tools.createRegistry()
93
+ registry.register(ai.tools.define({
94
+ name: 'get_weather',
95
+ description: '获取天气',
96
+ parameters: z.object({ city: z.string() }),
97
+ handler: async ({ city }) => ({ city, temperature: 20 }),
98
+ }))
132
99
 
133
- ### LLM 操作 — `ai.llm`
100
+ const messages: ChatMessage[] = [{ role: 'user', content: '北京天气?' }]
101
+ let result = await ai.llm.chat({ messages, tools: registry.getDefinitions() })
134
102
 
135
- | 方法 | 签名 | 说明 |
136
- | ------------ | ------------------------------------------------------ | ---------------- |
137
- | `chat` | `(options) => Promise<HaiResult<ChatCompletionResponse>>` | 对话(非流式) |
138
- | `chatStream` | `(options) => AsyncIterable<ChatCompletionChunk>` | 对话(流式) |
139
- | `listModels` | `() => Promise<HaiResult<string[]>>` | 获取可用模型列表 |
103
+ while (result.success && result.data.choices[0]?.finish_reason === 'tool_calls') {
104
+ const assistant = result.data.choices[0].message
105
+ const toolCalls = assistant.tool_calls ?? []
106
+ messages.push(assistant)
140
107
 
141
- ```typescript
142
- // 非流式
143
- const result = await ai.llm.chat({
144
- messages: [
145
- { role: 'system', content: '你是一个有帮助的助手' },
146
- { role: 'user', content: '你好' },
147
- ],
148
- tools: registry.getDefinitions(), // 可选
149
- })
150
- if (result.success) {
151
- const message = result.data.choices[0].message
152
- }
108
+ const toolMessages = await registry.executeAll(toolCalls, { parallel: true })
109
+ if (!toolMessages.success) return toolMessages
110
+ messages.push(...toolMessages.data)
153
111
 
154
- // 流式
155
- for await (const chunk of ai.llm.chatStream({ messages })) {
156
- const delta = chunk.choices[0]?.delta?.content
157
- if (delta)
158
- process.stdout.write(delta)
112
+ result = await ai.llm.chat({ messages, tools: registry.getDefinitions() })
159
113
  }
160
114
  ```
161
115
 
162
- ### 工具定义 — `ai.tools`
163
-
164
- ```typescript
165
- import { ai } from '@h-ai/ai'
166
- import { z } from 'zod'
167
-
168
- // 定义工具(Zod Schema 自动转 JSON Schema)
169
- const weatherTool = ai.tools.define({
170
- name: 'get_weather',
171
- description: '获取天气信息',
172
- parameters: z.object({
173
- city: z.string().describe('城市名称'),
174
- unit: z.enum(['celsius', 'fahrenheit']).optional(),
175
- }),
176
- handler: async ({ city, unit }) => {
177
- return { temperature: 20, city, unit: unit ?? 'celsius' }
178
- },
179
- })
180
-
181
- // 注册表管理
182
- const registry = ai.tools.createRegistry()
183
- registry.register(weatherTool)
184
-
185
- // 获取 OpenAI 工具定义(传递给 LLM)
186
- const definitions = registry.getDefinitions()
187
-
188
- // 执行 LLM 返回的工具调用
189
- const toolMessage = await registry.execute(toolCall)
190
- const toolMessages = await registry.executeAll(toolCalls, { parallel: true })
191
- ```
192
-
193
- > `ai.tools.define` 和 `ai.tools.createRegistry` 也可通过 `defineTool` / `createToolRegistry` 直接导入,两者等价。
194
-
195
- ### 流处理 — `ai.stream`
196
-
197
- ```typescript
198
- import { ai } from '@h-ai/ai'
116
+ ## 流式处理
199
117
 
118
+ ```ts
200
119
  const processor = ai.stream.createProcessor()
201
120
  for await (const chunk of ai.llm.chatStream({ messages })) {
202
121
  const delta = processor.process(chunk)
203
- if (delta?.content) { /* 实时显示 */ }
122
+ if (delta?.content) process.stdout.write(delta.content)
204
123
  }
205
-
206
- const result = processor.getResult()
207
- // { content: string, toolCalls: ToolCall[], finishReason: string | null }
208
124
  const message = processor.toAssistantMessage()
209
- // { role: 'assistant', content: '...', tool_calls?: [...] }
210
-
211
- // 快捷收集全部内容
212
- const collected = await ai.stream.collect(stream)
213
125
  ```
214
126
 
215
- > `ai.stream.createProcessor` / `ai.stream.collect` 也可通过 `createStreamProcessor` / `collectStream` 直接导入。
127
+ ## MCP 服务器
216
128
 
217
- ### MCP 服务器 — `createMcpServer`
218
-
219
- ```typescript
129
+ ```ts
220
130
  import { createMcpServer, StreamableHTTPServerTransport } from '@h-ai/ai'
221
- import { z } from 'zod'
222
-
223
- const mcp = createMcpServer({ name: 'my-server', version: '1.0.0' })
224
131
 
225
- // 注册工具
226
- mcp.registerTool('search', {
132
+ const server = createMcpServer({ name: 'my-server', version: '1.0.0' })
133
+ server.registerTool('search', {
227
134
  description: '搜索',
228
135
  inputSchema: { query: z.string() },
229
136
  }, async ({ query }) => ({
230
137
  content: [{ type: 'text', text: `Results for ${query}` }],
231
138
  }))
232
139
 
233
- // 注册资源
234
- mcp.registerResource('config', 'config://app', {
235
- description: '应用配置',
236
- }, async uri => ({
237
- contents: [{ uri: uri.href, text: '{}' }],
238
- }))
239
-
240
- // 注册提示词
241
- mcp.registerPrompt('summarize', {
242
- description: '总结文本',
243
- argsSchema: { text: z.string() },
244
- }, async ({ text }) => ({
245
- messages: [{ role: 'user', content: { type: 'text', text } }],
246
- }))
247
-
248
- // 连接传输层
249
- const transport = new StreamableHTTPServerTransport({
250
- sessionIdGenerator: () => randomUUID(),
251
- })
252
- await mcp.connect(transport)
140
+ await server.connect(new StreamableHTTPServerTransport({ sessionIdGenerator: crypto.randomUUID }))
253
141
  ```
254
142
 
255
- **传输层选项**:`StreamableHTTPServerTransport`(推荐,Web 场景)、`SSEServerTransport`、`StdioServerTransport`
256
-
257
- ### MCP 操作 — `ai.mcp`
143
+ ## 记忆、RAG 与知识库
258
144
 
259
- | 方法 | 签名 | 说明 |
260
- | ------------------ | ---------------------------------- | ---------- |
261
- | `registerTool` | `(def, handler) => void` | 注册工具 |
262
- | `callTool` | `(name, input) => Promise<HaiResult>` | 调用工具 |
263
- | `registerResource` | `(def, reader) => void` | 注册资源 |
264
- | `readResource` | `(uri) => Promise<HaiResult>` | 读取资源 |
265
- | `registerPrompt` | `(def, generator) => void` | 注册提示词 |
266
- | `getPrompt` | `(name, args) => Promise<HaiResult>` | 获取提示词 |
145
+ ```ts
146
+ const enriched = await ai.memory.injectMemories(messages, { objectId: 'user-001', topK: 5 })
147
+ if (!enriched.success) return enriched
267
148
 
268
- ### 前端客户端
149
+ const compressed = await ai.compress.tryCompress(enriched.data, { strategy: 'hybrid', maxTokens: 4000 })
150
+ if (!compressed.success) return compressed
269
151
 
270
- ```typescript
271
- import { apiClient } from '@h-ai/api-client'
272
-
273
- await apiClient.init({ baseUrl: '/api/v1', auth: {} })
274
-
275
- // 非流式
276
- const response = await apiClient.ai.chats.createCompletion({ messages })
277
-
278
- // 便捷方法
279
- const reply = await apiClient.ai.chats.sendMessage({ message: '你好', systemPrompt: '系统提示' })
280
-
281
- // 记忆与会话查询
282
- const memories = await apiClient.ai.memories.recall({ query: '用户偏好', topK: 5, objectId: 'user-001' })
283
- const page = await apiClient.ai.memories.list({ objectId: 'user-001', limit: 20 })
284
- const sessions = await apiClient.ai.sessions.list({ objectId: 'user-001' })
152
+ const answer = await ai.rag.query('核心架构是什么?', { sources: ['docs'], topK: 5 })
285
153
  ```
286
154
 
287
- ### 记忆管理 — `ai.memory`
288
-
289
- | 方法 | 签名 | 说明 |
290
- | ----------------- | ------------------------------------------------------------- | ---------------------------------- |
291
- | `extract` | `(messages, options?) => Promise<HaiResult<MemoryEntry[]>>` | 从对话中自动提取记忆(LLM 驱动) |
292
- | `add` | `(entry) => Promise<HaiResult<MemoryEntry>>` | 手动添加一条记忆 |
293
- | `get` | `(memoryId) => Promise<HaiResult<MemoryEntry>>` | 获取指定记忆 |
294
- | `recall` | `(query, options?) => Promise<HaiResult<MemoryEntry[]>>` | 按查询检索最相关的记忆 |
295
- | `injectMemories` | `(messages, options?) => Promise<HaiResult<ChatMessage[]>>` | 将相关记忆自动注入消息列表 |
296
- | `remove` | `(memoryId) => Promise<HaiResult<void>>` | 删除指定记忆 |
297
- | `list` | `(options?) => Promise<HaiResult<MemoryEntry[]>>` | 列出记忆(支持类型/objectId 过滤) |
298
- | `listPage` | `(options?) => Promise<HaiResult<StorePage<MemoryEntry>>>` | 分页列出记忆 |
299
- | `clear` | `(options?) => Promise<HaiResult<void>>` | 清空记忆(支持按类型/objectId 清空)|
300
-
301
- ```typescript
302
- // 从对话中自动提取记忆
303
- const extracted = await ai.memory.extract(messages, { objectId: 'user-001' })
304
- if (extracted.success) {
305
- // extracted.data: MemoryEntry[](已自动存储)
306
- }
307
-
308
- // 手动添加记忆
309
- await ai.memory.add({
310
- content: '用户偏好使用中文',
311
- type: 'preference', // fact | preference | event | entity | instruction
312
- importance: 0.8,
313
- objectId: 'user-001', // 可选,不传为全局
314
- })
315
-
316
- // 检索相关记忆
317
- const memories = await ai.memory.recall('语言偏好', { topK: 5, objectId: 'user-001' })
318
-
319
- // 将记忆注入消息列表(自动检索 + 格式化 + 插入 system prompt)
320
- const enriched = await ai.memory.injectMemories(messages, { topK: 5, position: 'system' })
321
- if (enriched.success) {
322
- const response = await ai.llm.chat({ messages: enriched.data })
323
- }
324
- ```
155
+ 知识库流程:
325
156
 
326
- ### Token 估算 — `ai.token`
157
+ ```ts
158
+ const setup = await ai.knowledge.setup()
159
+ if (!setup.success) return setup
327
160
 
328
- | 方法 | 签名 | 说明 |
329
- | ------------------ | ----------------------------------- | ------------------------ |
330
- | `estimateMessages` | `(messages) => HaiResult<number>` | 估算消息列表的 Token 数 |
331
- | `estimateText` | `(text) => number` | 估算纯文本的 Token 数 |
332
-
333
- ```typescript
334
- // 估算消息列表 Token 数
335
- const tokens = ai.token.estimateMessages(messages)
336
- if (tokens.success) {
337
- // tokens.data — 估算的 Token 数
338
- }
339
-
340
- // 估算纯文本 Token 数
341
- const count = ai.token.estimateText('Hello world')
342
- ```
343
-
344
- ### 消息摘要 — `ai.summary`
345
-
346
- | 方法 | 签名 | 说明 |
347
- | ----------- | ----------------------------------------------------------------- | ----------------------------- |
348
- | `summarize` | `(messages, options?) => Promise<HaiResult<SummaryResult>>` | 对消息生成摘要(支持增量) |
349
- | `generate` | `(text, options?) => Promise<HaiResult<SummaryResult>>` | 对纯文本生成摘要 |
350
-
351
- ### 上下文压缩 — `ai.compress`
352
-
353
- | 方法 | 签名 | 说明 |
354
- | ------------- | ----------------------------------------------------------------- | ------------------------------------- |
355
- | `tryCompress` | `(messages, options?) => Promise<HaiResult<CompressResult>>` | 压缩消息列表至指定 Token 预算 |
356
-
357
- ```typescript
358
- // 压缩超长对话
359
- const result = await ai.compress.tryCompress(messages, {
360
- strategy: 'hybrid', // summary | sliding-window | hybrid
361
- maxTokens: 4000,
362
- preserveLastN: 4,
161
+ const ingest = await ai.knowledge.ingest({
162
+ documentId: 'doc-001',
163
+ content: markdownText,
164
+ title: '产品手册',
165
+ enableEntityExtraction: true,
363
166
  })
364
- if (result.success) {
365
- // result.data.messages — 压缩后的消息列表
366
- // result.data.originalTokens / compressedTokens — 压缩前后 Token 数
367
- // result.data.summary — 生成的摘要文本
368
- }
369
- ```
370
-
371
- ### 上下文管理 — `ai.context`
372
-
373
- | 方法 | 签名 | 说明 |
374
- | ---------------- | ----------------------------------------------------------------- | ------------------------------------------------------- |
375
- | `createManager` | `(options?) => HaiResult<ContextManager>` | 创建有状态上下文管理器(压缩 + LLM/Memory/RAG 编排) |
376
- | `restoreManager` | `(scope, options?) => Promise<HaiResult<ContextManager>>` | 从持久化存储恢复上下文管理器 |
377
- | `listSessions` | `(objectId) => Promise<HaiResult<SessionInfo[]>>` | 列出指定对象的所有会话 |
378
- | `renameSession` | `(sessionId, title) => Promise<HaiResult<void>>` | 重命名会话 |
379
- | `removeSession` | `(sessionId) => Promise<HaiResult<void>>` | 删除会话 |
380
-
381
- **ContextManager 方法**:
382
- - `addMessage(msg)` — 追加消息(自动压缩)
383
- - `getMessages()` / `getTokenUsage()` / `getSummaries()` — 查询状态
384
- - `chat(message, options?)` — 发送消息并获取回复(编排 LLM + Memory + RAG + Reasoning)
385
- - `chatStream(message, options?)` — 流式对话(产出 delta*/done 事件)
386
- - `save()` — 持久化 / `reset()` — 重置
387
-
388
- ```typescript
389
- // 创建管理器并直接对话(推荐)
390
- const managerResult = ai.context.createManager({
391
- scope: { objectId: 'user-001', sessionId: 'sess-001' },
392
- systemPrompt: '你是一个友好的助手。',
393
- compress: { maxTokens: 8000, strategy: 'hybrid', auto: true },
394
- memory: { enable: true, enableExtract: true },
395
- })
396
- if (managerResult.success) {
397
- const manager = managerResult.data
398
- const result = await manager.chat('你好')
399
- // result.data.reply / result.data.model / result.data.usage
400
- await manager.save()
401
- }
402
-
403
- // 流式对话
404
- for await (const event of manager.chatStream('讲个故事')) {
405
- if (event.type === 'delta') process.stdout.write(event.text)
406
- }
407
-
408
- // 恢复已有会话
409
- const restored = await ai.context.restoreManager(
410
- { objectId: 'user-001', sessionId: 'sess-001' },
411
- { compress: { maxTokens: 8000 }, memory: { enable: true } },
412
- )
167
+ if (!ingest.success) return ingest
413
168
 
414
- // 列出对象的所有会话
415
- const sessions = await ai.context.listSessions('user-001')
169
+ const answer = await ai.knowledge.ask('核心架构是什么?')
416
170
  ```
417
171
 
418
- ---
419
-
420
- ## 错误码 — `HaiAIError`
421
-
422
- | 错误码 | code | 说明 |
423
- | --------------------------------------------------- | -------------- | -------------------- |
424
- | `HaiAIError.INTERNAL_ERROR` | `hai:ai:000` | 内部错误 |
425
- | `HaiAIError.NOT_INITIALIZED` | `hai:ai:010` | 服务未初始化 |
426
- | `HaiAIError.CONFIGURATION_ERROR` | `hai:ai:011` | 配置错误 |
427
- | `HaiAIError.INIT_IN_PROGRESS` | `hai:ai:012` | 初始化进行中 |
428
- | `HaiAIError.RERANK_API_ERROR` | `hai:ai:020` | Rerank API 调用错误 |
429
- | `HaiAIError.RERANK_INVALID_REQUEST` | `hai:ai:021` | Rerank 请求参数无效 |
430
- | `HaiAIError.FILE_PARSE_FAILED` | `hai:ai:030` | 文件解析失败 |
431
- | `HaiAIError.FILE_UNSUPPORTED_FORMAT` | `hai:ai:031` | 不支持的文件格式 |
432
- | `HaiAIError.FILE_OCR_FAILED` | `hai:ai:032` | OCR 识别失败 |
433
- | `HaiAIError.FILE_INVALID_CONTENT` | `hai:ai:033` | 文件内容无效 |
434
- | `HaiAIError.API_ERROR` | `hai:ai:100` | API 调用错误 |
435
- | `HaiAIError.INVALID_REQUEST` | `hai:ai:101` | 无效请求 |
436
- | `HaiAIError.RATE_LIMITED` | `hai:ai:102` | 速率限制 |
437
- | `HaiAIError.TIMEOUT` | `hai:ai:103` | 请求超时 |
438
- | `HaiAIError.MODEL_NOT_FOUND` | `hai:ai:104` | 模型未找到 |
439
- | `HaiAIError.CONTEXT_LENGTH_EXCEEDED` | `hai:ai:105` | 上下文长度超限 |
440
- | `HaiAIError.LLM_RECORD_FAILED` | `hai:ai:106` | 对话记录保存失败 |
441
- | `HaiAIError.LLM_HISTORY_FAILED` | `hai:ai:107` | 对话历史查询失败 |
442
- | `HaiAIError.MCP_CONNECTION_ERROR` | `hai:ai:200` | MCP 连接错误 |
443
- | `HaiAIError.MCP_PROTOCOL_ERROR` | `hai:ai:201` | MCP 协议错误 |
444
- | `HaiAIError.MCP_TOOL_ERROR` | `hai:ai:202` | MCP 工具错误 |
445
- | `HaiAIError.MCP_RESOURCE_ERROR` | `hai:ai:203` | MCP 资源错误 |
446
- | `HaiAIError.MCP_SERVER_ERROR` | `hai:ai:204` | MCP 服务器错误 |
447
- | `HaiAIError.EMBEDDING_API_ERROR` | `hai:ai:300` | Embedding 调用错误 |
448
- | `HaiAIError.EMBEDDING_MODEL_NOT_FOUND` | `hai:ai:301` | Embedding 模型未找到 |
449
- | `HaiAIError.EMBEDDING_INPUT_TOO_LONG` | `hai:ai:302` | Embedding 输入过长 |
450
- | `HaiAIError.TOOL_NOT_FOUND` | `hai:ai:400` | 工具未找到 |
451
- | `HaiAIError.TOOL_VALIDATION_FAILED` | `hai:ai:401` | 工具验证失败 |
452
- | `HaiAIError.TOOL_EXECUTION_FAILED` | `hai:ai:402` | 工具执行失败 |
453
- | `HaiAIError.TOOL_TIMEOUT` | `hai:ai:403` | 工具执行超时 |
454
- | `HaiAIError.REASONING_FAILED` | `hai:ai:500` | 推理执行失败 |
455
- | `HaiAIError.REASONING_MAX_ROUNDS` | `hai:ai:501` | 推理轮次超限 |
456
- | `HaiAIError.REASONING_STRATEGY_NOT_FOUND` | `hai:ai:502` | 推理策略未找到 |
457
- | `HaiAIError.RETRIEVAL_FAILED` | `hai:ai:600` | 检索执行失败 |
458
- | `HaiAIError.RETRIEVAL_SOURCE_NOT_FOUND` | `hai:ai:601` | 检索源未配置 |
459
- | `HaiAIError.RAG_FAILED` | `hai:ai:700` | RAG 执行失败 |
460
- | `HaiAIError.RAG_CONTEXT_BUILD_FAILED` | `hai:ai:701` | RAG 上下文构建失败 |
461
- | `HaiAIError.KNOWLEDGE_SETUP_FAILED` | `hai:ai:800` | 知识库初始化失败 |
462
- | `HaiAIError.KNOWLEDGE_INGEST_FAILED` | `hai:ai:801` | 知识入库失败 |
463
- | `HaiAIError.KNOWLEDGE_RETRIEVE_FAILED` | `hai:ai:802` | 知识检索失败 |
464
- | `HaiAIError.KNOWLEDGE_ENTITY_EXTRACT_FAILED` | `hai:ai:803` | 实体提取失败 |
465
- | `HaiAIError.KNOWLEDGE_NOT_SETUP` | `hai:ai:804` | 知识库未初始化 |
466
- | `HaiAIError.KNOWLEDGE_COLLECTION_NOT_FOUND` | `hai:ai:805` | 集合不存在 |
467
- | `HaiAIError.MEMORY_EXTRACT_FAILED` | `hai:ai:900` | 记忆提取失败 |
468
- | `HaiAIError.MEMORY_STORE_FAILED` | `hai:ai:901` | 记忆存储失败 |
469
- | `HaiAIError.MEMORY_RECALL_FAILED` | `hai:ai:902` | 记忆召回失败 |
470
- | `HaiAIError.MEMORY_NOT_FOUND` | `hai:ai:903` | 记忆条目不存在 |
471
- | `HaiAIError.MEMORY_ENRICH_FAILED` | `hai:ai:904` | 记忆注入失败 |
472
- | `HaiAIError.CONTEXT_COMPRESS_FAILED` | `hai:ai:950` | 上下文压缩失败 |
473
- | `HaiAIError.CONTEXT_SUMMARIZE_FAILED` | `hai:ai:951` | 上下文摘要失败 |
474
- | `HaiAIError.CONTEXT_TOKEN_ESTIMATE_FAILED` | `hai:ai:952` | Token 估算失败 |
475
- | `HaiAIError.CONTEXT_BUDGET_EXCEEDED` | `hai:ai:953` | Token 预算超限 |
476
- | `HaiAIError.STORE_FAILED` | `hai:ai:960` | 存储操作失败 |
477
- | `HaiAIError.STORE_NOT_AVAILABLE` | `hai:ai:961` | 存储后端不可用 |
478
- | `HaiAIError.SESSION_NOT_FOUND` | `hai:ai:970` | 会话不存在 |
479
- | `HaiAIError.SESSION_FAILED` | `hai:ai:971` | 会话操作失败 |
480
- | `HaiAIError.A2A_NOT_CONFIGURED` | `hai:ai:980` | A2A 服务未配置 |
481
- | `HaiAIError.A2A_HANDLE_FAILED` | `hai:ai:981` | A2A 请求处理失败 |
482
- | `HaiAIError.A2A_REMOTE_CALL_FAILED` | `hai:ai:982` | A2A 远端调用失败 |
483
- | `HaiAIError.A2A_AUTH_FAILED` | `hai:ai:983` | A2A 认证失败 |
484
- | `HaiAIError.A2A_LIST_MESSAGES_FAILED` | `hai:ai:984` | A2A 消息查询失败 |
485
-
486
- ---
487
-
488
- ## 常见模式
489
-
490
- ### 带工具调用的对话循环
491
-
492
- ```typescript
493
- const registry = ai.tools.createRegistry()
494
- registry.register(weatherTool)
495
-
496
- const messages: ChatMessage[] = [
497
- { role: 'user', content: '北京天气怎么样?' },
498
- ]
499
-
500
- let result = await ai.llm.chat({
501
- messages,
502
- tools: registry.getDefinitions(),
503
- })
504
-
505
- while (result.success && result.data.choices[0].finish_reason === 'tool_calls') {
506
- const toolCalls = result.data.choices[0].message.tool_calls!
507
- messages.push(result.data.choices[0].message)
508
-
509
- const toolMessages = await registry.executeAll(toolCalls)
510
- if (toolMessages.success)
511
- messages.push(...toolMessages.data)
512
-
513
- result = await ai.llm.chat({ messages, tools: registry.getDefinitions() })
514
- }
515
- ```
516
-
517
- ### 知识库完整流程(文档入库 → 问答)
518
-
519
- ```typescript
520
- import { ai } from '@h-ai/ai'
521
-
522
- // 初始化
523
- await ai.knowledge.setup()
524
-
525
- // 导入多篇文档
526
- for (const doc of documents) {
527
- const result = await ai.knowledge.ingest({
528
- documentId: doc.id,
529
- content: doc.content,
530
- title: doc.title,
531
- enableEntityExtraction: true,
532
- })
533
- if (!result.success) {
534
- // 按错误码处理
535
- switch (result.error.code) {
536
- case HaiAIError.KNOWLEDGE_INGEST_FAILED:
537
- // 入库失败
538
- break
539
- case HaiAIError.EMBEDDING_API_ERROR:
540
- // 嵌入调用失败
541
- break
542
- }
543
- }
544
- }
545
-
546
- // 问答
547
- const answer = await ai.knowledge.ask('项目核心架构是什么?')
548
- if (answer.success) {
549
- // answer.data.answer — LLM 生成的回答
550
- // answer.data.citations — 信源引用列表
551
- }
552
- ```
553
-
554
- ### SvelteKit API 端点
555
-
556
- ```typescript
557
- // src/routes/api/ai/+server.ts
558
- export async function POST(event) {
559
- const { valid, data } = await kit.validate.form(event.request, ChatSchema)
560
- if (!valid)
561
- return kit.response.badRequest('Invalid request')
562
-
563
- const result = await ai.llm.chat({ messages: data!.messages })
564
- if (!result.success)
565
- return kit.response.error(result.error.code, result.error.message)
566
-
567
- return kit.response.ok(result.data)
568
- }
569
- ```
570
-
571
- ### 记忆增强的多轮对话
572
-
573
- ```typescript
574
- import { ai } from '@h-ai/ai'
575
-
576
- const messages: ChatMessage[] = [
577
- { role: 'system', content: '你是一个有帮助的助手' },
578
- ]
579
-
580
- async function chat(userInput: string) {
581
- messages.push({ role: 'user', content: userInput })
582
-
583
- // 1. 注入相关记忆到消息列表
584
- const enriched = await ai.memory.injectMemories(messages, {
585
- topK: 5,
586
- position: 'system',
587
- })
588
- if (!enriched.success) return
589
-
590
- // 2. 压缩上下文以适应 Token 限制
591
- const compressed = await ai.compress.tryCompress(enriched.data, {
592
- maxTokens: 4000,
593
- strategy: 'hybrid',
594
- })
595
- if (!compressed.success) return
596
-
597
- // 3. 调用 LLM
598
- const result = await ai.llm.chat({ messages: compressed.data.messages })
599
- if (!result.success) return
600
-
601
- const reply = result.data.choices[0].message
602
- messages.push(reply)
603
-
604
- // 4. 从新对话中提取记忆(异步,不阻塞响应)
605
- ai.memory.extract(messages.slice(-2))
606
-
607
- return reply.content
608
- }
609
- ```
610
-
611
- ### 有状态上下文管理器
172
+ ## Context 管理器
612
173
 
613
- ```typescript
614
- import { ai } from '@h-ai/ai'
615
-
616
- // 创建管理器(嵌套配置 + 对话编排)
174
+ ```ts
617
175
  const managerResult = ai.context.createManager({
618
176
  scope: { objectId: 'user-001', sessionId: 'sess-001' },
619
177
  systemPrompt: '你是一个友好的助手。',
620
- compress: { maxTokens: 8000, strategy: 'hybrid', preserveLastN: 4, auto: true },
178
+ compress: { auto: true, strategy: 'hybrid', maxTokens: 8000 },
621
179
  memory: { enable: true, enableExtract: true },
622
180
  })
623
- if (!managerResult.success) throw new Error(managerResult.error.message)
624
- const manager = managerResult.data
625
-
626
- // 方式一:直接对话(推荐,自动编排 LLM + Memory + 压缩)
627
- const result = await manager.chat('你好')
628
- if (result.success) {
629
- // result.data.reply / result.data.model / result.data.usage
630
- }
631
-
632
- // 流式对话
633
- for await (const event of manager.chatStream('讲个故事')) {
634
- if (event.type === 'delta') process.stdout.write(event.text)
635
- }
636
-
637
- // 方式二:手动管理消息
638
- async function manualChat(userInput: string) {
639
- await manager.addMessage({ role: 'user', content: userInput })
640
- const msgs = manager.getMessages()
641
- if (!msgs.success) return
642
- const chatResult = await ai.llm.chat({ messages: msgs.data })
643
- if (!chatResult.success) return
644
- await manager.addMessage(chatResult.data.choices[0].message)
645
- await manager.save()
646
- return chatResult.data.choices[0].message.content
647
- }
648
- ```
649
-
650
- ---
651
-
652
- ## 浏览器端使用
653
-
654
- ### typed 对话调用
655
-
656
- 浏览器 / App 通过 `@h-ai/api-client` 调用由 `@h-ai/api-contract` 定义、`@h-ai/serv` 挂载的 AI contract:
657
-
658
- ```typescript
659
- import { apiClient } from '@h-ai/api-client'
660
-
661
- const result = await apiClient.ai.chats.createCompletion({
662
- messages: [{ role: 'user', content: '你好' }],
663
- })
181
+ if (!managerResult.success) return managerResult
664
182
 
665
- if (result.success) {
666
- appendToUI(result.data.choices[0]?.message?.content ?? '')
667
- }
183
+ const reply = await managerResult.data.chat('你好')
184
+ await managerResult.data.save()
668
185
  ```
669
186
 
670
- ### 流式对话(SSE,自定义 endpoint)
671
-
672
- 标准 `api-client` 不再提供旧的通用流式方法。若产品需要 SSE 流式输出,请在应用服务端显式定义流式 endpoint(例如 SvelteKit `+server.ts` 或 `@h-ai/serv` 自定义 route),浏览器端再用 `fetch` / `apiFetch` 消费。
187
+ ## SvelteKit API 端点模式
673
188
 
674
- ```typescript
675
- import { apiFetch } from '$lib/utils/api'
676
-
677
- const controller = new AbortController()
678
-
679
- const res = await apiFetch('/api/ai/chat-stream', {
680
- method: 'POST',
681
- headers: { 'Content-Type': 'application/json' },
682
- body: JSON.stringify({ conversationId: 'conv-1', content: '你好' }),
683
- signal: controller.signal,
189
+ ```ts
190
+ export const POST = kit.handler(async ({ request }) => {
191
+ const data = await kit.validate.body(request, ChatSchema)
192
+ const result = await ai.llm.chat({ messages: data.messages })
193
+ if (!result.success) return kit.response.error(result.error.code, result.error.message)
194
+ return kit.response.ok(result.data)
684
195
  })
685
-
686
- const reader = res.body!.getReader()
687
- const decoder = new TextDecoder()
688
- while (true) {
689
- const { done, value } = await reader.read()
690
- if (done)
691
- break
692
-
693
- appendToUI(decoder.decode(value))
694
- }
695
196
  ```
696
197
 
697
- 或直接使用原生 `fetch`(跨域公共 API 场景):
698
-
699
- ```typescript
700
- const res = await fetch(`${apiBase}/ai/chat-stream`, {
701
- method: 'POST',
702
- headers: { 'Content-Type': 'application/json' },
703
- body: JSON.stringify({ conversationId, content: userInput }),
704
- signal: controller.signal,
705
- })
706
-
707
- // 手动消费 SSE 流
708
- const reader = res.body!.getReader()
709
- const decoder = new TextDecoder()
710
- while (true) {
711
- const { done, value } = await reader.read()
712
- if (done) break
713
- appendToUI(decoder.decode(value))
714
- }
715
- ```
716
-
717
- ### 浏览器端使用 ai.stream(处理流式 chunk)
718
-
719
- `ai.stream` 是纯函数,浏览器端可直接使用:
720
-
721
- ```typescript
722
- import { ai } from '@h-ai/ai'
723
-
724
- const processor = ai.stream.createProcessor()
725
- // 将服务端返回的 SSE chunk 逐条 process,提取 delta
726
- ```
727
-
728
- ---
729
-
730
- ## 相关 Skills
198
+ ## 质量与安全检查
731
199
 
732
- - `hai-build`:模块初始化顺序
733
- - `hai-core`:配置与 HaiResult 模型
734
- - `hai-kit`:SvelteKit API 端点集成
735
- - `hai-vecdb`:向量数据库(Retrieval / RAG / Knowledge 的存储层)
736
- - `hai-reldb`:关系数据库(Knowledge 实体索引的存储层)
737
- - `hai-datapipe`:数据清洗与分块(Knowledge 入库前的数据处理)
200
+ - API Key 只从配置 / 环境变量读取,禁止写死。
201
+ - 工具 handler 必须校验输入;用户可见错误走 i18n/错误码。
202
+ - 批量导入、批量 embedding 用批量 API,避免 await-in-loop。
203
+ - 文件解析/OCR 失败按 `HaiAIError.FILE_*` 返回,不抛业务异常。
204
+ - A2A 认证失败返回 `A2A_AUTH_FAILED`;不要记录 token、apiKey、私钥等敏感字段。