AI 子系统设计
爱生活 的 AI 子系统采用双引擎架构,同时支持端侧推理和云端 Agent,确保在离线/弱网环境下也能提供智能助理体验。
1. 整体架构
┌─────────────────────────────────────────────────────────────┐
│ 表示层 (UI) │
│ AI 对话界面 │ 快捷指令 │ 智能补全 │ 每日摘要 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ AI Agent 服务层 │
│ cloudAgentChat() │ edgeAgentChat() │ parseActions() │
└───────────────────────────┬─────────────────────────────────┘
│
┌─────────────┴─────────────┐
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ 云端 AI 引擎 │ │ 边缘 AI 引擎 │
│ │ │ │
│ SSE 流式对话 │ │ Llama.cpp (App) │
│ + Agent Pipeline │ │ Transformers.js(H5) │
└──────────────────────┘ └──────────────────────┘
│ │
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ Action 注册中心 │ │ 模型管理器 │
│ 40+ 原子操作 │ │ 8 个预置模型 │
└──────────────────────┘ └──────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ 数据层 (Repository) │
└─────────────────────────────────────────────────────────────┘设计原则:
- 离线优先:端侧 AI 可在完全离线环境下运行,覆盖核心操作
- 能力对等:云端与端侧的 Action 注册表共享同一套 Schema 定义
- 优雅降级:云端不可用时自动切换端侧;端侧不支持的操作提示用户联网
- 流式体验:两端均支持流式输出,首字延迟控制在 500ms 以内
2. 边缘 AI 引擎
边缘 AI 让核心智能能力不依赖网络,始终可用。根据平台选择不同的推理方案:
| 维度 | App (iOS / Android) | H5 (Web) |
|---|---|---|
| 推理引擎 | Llama.cpp (Native) | Transformers.js (WASM) |
| 模型格式 | GGUF | ONNX / safetensors |
| 硬件加速 | Metal (iOS) / Vulkan (Android) | WebGPU (可选) |
| 流式支持 | 原生异步流式 | 浏览器端迭代 |
| 存储位置 | _doc/models/ | IndexedDB |
2.1 推理管线 (H5)
/src/ai/pipeline.ts — H5 端使用 Transformers.js 加载和运行模型,基于 WebAssembly 执行推理:
用户输入 → Tokenizer 编码 → 模型推理 → Tokenizer 解码 → 流式输出管线的核心职责:
- 初始化 Transformers.js 环境(WASM 运行时、模型配置)
- 提供
downloadOnly()接口,供模型管理器单独下载模型文件 - 封装推理调用,屏蔽底层 API 差异
2.2 流式引擎
/src/ai/edge-stream.ts — 统一封装不同平台的流式推理接口,对上层提供一致的流式 API。
iOS 异步流式方案:
端侧模型通过异步 Native 桥接实现真正的逐 token 输出:
chatStreamStart(modelId, messages) → 启动推理会话
↓
chatStreamPoll(sessionId) → 每 120ms 轮询一次
↓
chatStreamStop(sessionId) → 停止推理轮询间隔固定为 120ms,在延迟与性能之间取得平衡。返回协议格式:
STATUS | tokensUsed | promptTokens | delta| 字段 | 说明 |
|---|---|
STATUS | 状态码:0=生成中, 1=正常结束, 2=错误, 3=已停止 |
tokensUsed | 累计已消耗 token 数 |
promptTokens | 提示词 token 数 |
delta | 本次增量文本内容 |
Android 同步回退方案:
当异步流式不可用时,回退到同步 chatStream() 调用,一次性获取完整结果。Android 上也计划支持异步轮询方案,当前以同步方式保障兼容性。
错误码映射:
| 错误码 | 含义 |
|---|---|
| 1 | 会话未找到 |
| 2 | 模型未加载 |
| 3 | 推理引擎内部错误 |
| 4 | 超时 |
| 5 | 内存不足 |
| 6 | token 超限 |
| 7 | 未知错误 |
对外暴露的函数:
edgeChatStream(modelId, messages, onChunk)— 自动选择平台最优方案edgeStopChatStream(sessionId)— 停止当前流式推理- 内部
chatStreamStart()/chatStreamPoll()/chatStreamStop()— iOS 三阶段原语
3. 模型管理
/src/ai/model-manager.ts — 管理端侧模型的完整生命周期:发现、下载、加载、切换、卸载。
3.1 模型目录
系统预置 8 个模型,按功能分为推理和嵌入两类:
| 模型名称 | 参数量 | 用途 | GGUF 大小 | 适用平台 |
|---|---|---|---|---|
| TinyLlama 1.1B | 1.1B | 通用对话 | ~650MB | App + H5 |
| Qwen2.5-0.5B | 0.5B | 轻量对话 | ~350MB | App + H5 |
| Qwen3-0.6B | 0.6B | 对话(新一代) | ~380MB | App + H5 |
| Qwen2.5-1.5B | 1.5B | 高质量对话 | ~900MB | App |
| Phi-3 Mini | 3.8B | 高性能推理 | ~2.2GB | App |
| Gemma 2B | 2B | 通用推理 | ~1.2GB | App |
| MiniLM | 22M | 文本嵌入 | ~40MB | App + H5 |
| BGE Small | 24M | 中文嵌入 | ~45MB | App + H5 |
嵌入模型(MiniLM、BGE Small)用于语义搜索、知识库检索等非对话场景,参数量小,可同时常驻内存。
3.2 模型下载
H5 端(IndexedDB 存储):
模型管理器.downloadModel(modelId)
→ pipeline.downloadOnly(modelId, onProgress)
→ Transformers.js 下载模型到浏览器 IndexedDB模型文件由 Transformers.js 内部管理,存储在浏览器 IndexedDB 中。前端通过 onProgress 回调获取下载进度。
App 端(GGUF 文件存储):
模型管理器.downloadModel(modelId)
→ uni.downloadFile({ url: modelUrl, filePath: `_doc/models/${filename}` })
→ 写入应用私有目录
→ 校验文件完整性 (MD5)GGUF 文件存储在 {APP_DOC_DIR}/models/ 目录下。下载完成后执行 MD5 校验,确保文件完整。
3.3 模型加载
加载模型到内存中用于推理:
// App 端:Llama.cpp 加载 GGUF,全量 GPU 卸载
loadModel(modelId, { gpuLayers: -1 })
// H5 端:Transformers.js 从 IndexedDB 加载
loadModel(modelId)App 端使用 gpuLayers: -1 将所有层卸载到 GPU(Metal / Vulkan),最大化推理速度。H5 端通过 Transformers.js 从 IndexedDB 读取模型文件到 WASM 内存。
ModelManager 维护一个已加载模型列表,仅允许同时加载 1 个对话模型(内存限制),嵌入模型可常驻。
3.4 ModelInfo 结构
每个模型对应一个 ModelInfo 对象:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | string | 模型唯一标识,如 qwen2.5-0.5b |
| name | string | 显示名称,如 Qwen2.5-0.5B |
| description | string | 中文描述 |
| url | string | GGUF 下载地址(App) |
| size | string | 模型大小估算,如 ~350MB |
| parameters | string | 参数量,如 0.5B |
| type | enum | chat / embedding |
| quantization | string | 量化方式,如 Q4_K_M |
| hfModelId | string | HuggingFace 模型 ID(H5) |
4. 云端 AI Agent
/src/services/ai-agent.service.ts — 云端 AI 的核心服务,负责对话管理和 Agent 指令执行。
4.1 流式对话
云端使用 SSE (Server-Sent Events) 实现流式对话:
用户输入 → 构建上下文 → POST /api/chat (SSE) → 逐 token 流式返回 → UI 实时渲染SSE 连接持有对话历史,后端将完整上下文发送给大语言模型,模型以 data: {"delta":"..."} 格式逐 token 返回。前端每收到一个 chunk 立即追加到 UI,提供打字机效果。
4.2 Agent 指令解析
AI 回复中包含 [ACTION]...[/ACTION] 标记块,Agent 解析这些块并执行对应的数据操作:
[ACTION]
{ "name": "create_task", "content": "完成季度报告", "dueDate": "2026-08-08" }
[/ACTION]解析流程:
AI 完整回复
↓ 正则匹配 [ACTION]...[/ACTION]
提取 JSON 块
↓ JSON 修复(见下文)
解析为 ActionCommand
↓ 查找 Action 注册表
执行 execute()
↓
返回执行结果 → 追加到对话上下文4.3 JSON 修复策略
边缘 AI 模型输出的 JSON 往往不完美,系统内置多层修复逻辑:
- 标点修复:将中文标点(
"、"、,)替换为英文标点 - 引号补全:缺失的属性名引号自动添加
- 括号平衡:缺失的
{/}自动补齐 - 容错解析:优先使用
JSON.parse(),失败后回退到宽松解析器
此设计让端侧小模型也能可靠执行结构化操作,降低对云端模型的依赖。
4.4 Agent 对话函数
| 函数 | 说明 |
|---|---|
cloudAgentChat(messages, onChunk) | 云端 SSE 流式对话 + Agent 指令解析 |
edgeAgentChat(messages, onChunk) | 端侧流式对话 + Agent 指令解析 |
parseActions(content) | 从文本中提取并执行所有 [ACTION] 块 |
两个对话函数接口一致,上层调用方无需关心使用的是云端还是端侧引擎——这也是"双引擎对等"的体现。
5. Action 注册中心
/src/ai/actions.ts — 定义 Agent 可执行的所有原子操作。云端和端侧共享同一份 Action 注册表。
5.1 Action Schema 结构
每个 Action 遵循统一的结构:
interface ActionDefinition {
name: string // 操作名,对应 JSON 中的 name 字段
description: string // 操作描述,写入 System Prompt 供 AI 理解
schema: ZodSchema // 参数校验 Schema(基于 Zod)
execute: (params) => Promise<ActionResult> // 执行函数
}
interface ActionResult {
success: boolean
message: string // 人类可读的结果描述,反馈给 AI
data?: any
}5.2 已注册 Action 清单
系统共注册 40+ 个原子操作,覆盖全部 8 个模块分类:
任务管理(Task):
| Action | 说明 |
|---|---|
create_task | 创建任务 |
complete_task | 完成任务 |
list_tasks | 查询任务列表 |
update_task | 更新任务 |
delete_task | 删除任务 |
日程与习惯(Event & Habit):
| Action | 说明 |
|---|---|
create_event | 创建日程 |
list_events | 查询日程列表 |
update_event | 更新日程 |
delete_event | 删除日程 |
check_in_habit | 习惯打卡 |
create_habit | 创建习惯 |
list_habits | 查询习惯列表 |
update_habit | 更新习惯 |
delete_habit | 删除习惯 |
笔记与日记(Note & Diary):
| Action | 说明 |
|---|---|
create_note | 创建笔记 |
list_notes | 查询笔记列表 |
update_note | 更新笔记 |
delete_note | 删除笔记 |
write_diary | 写日记 |
财务管理(Finance):
| Action | 说明 |
|---|---|
record_transaction | 记录收支 |
list_transactions | 查询交易列表 |
健康管理(Health):
| Action | 说明 |
|---|---|
record_mood | 记录心情 |
list_moods | 查询心情记录 |
record_diet | 记录饮食 |
list_diets | 查询饮食记录 |
record_water | 记录饮水 |
list_water | 查询饮水记录 |
record_sleep | 记录睡眠 |
record_body | 记录身体数据 |
record_medicine | 记录用药 |
record_period | 记录经期 |
record_health | 记录健康指标 |
list_health | 查询健康数据 |
get_focus_today | 获取当日专注数据 |
目标与成就(Goal & Achievement):
| Action | 说明 |
|---|---|
create_goal | 创建目标 |
list_goals | 查询目标列表 |
update_goal_progress | 更新目标进度 |
list_achievements | 查询成就列表 |
生活工具(Life Tools):
| Action | 说明 |
|---|---|
add_shopping_item | 添加购物项 |
list_shopping | 查询购物清单 |
mark_shopping_bought | 标记已购 |
add_wishlist_item | 添加心愿单 |
list_wishlist | 查询心愿单 |
list_countdowns | 查询倒计时 |
create_countdown | 创建倒计时 |
delete_countdown | 删除倒计时 |
create_memo | 创建便签 |
list_memos | 查询便签列表 |
list_channels | 查询频道列表 |
record_trajectory | 记录轨迹 |
list_trajectories | 查询轨迹列表 |
get_today_summary | 获取今日摘要 |
web_search | 联网搜索 |
资产管理(Inventory & Lend):
| Action | 说明 |
|---|---|
list_inventory | 查询物品清单 |
create_inventory_item | 添加物品 |
delete_inventory_item | 删除物品 |
record_item_lend | 记录物品借出 |
record_money_lend | 记录借还款 |
模式与扩展(Mode & Extras):
| Action | 说明 |
|---|---|
switch_mode | 切换模式 |
get_current_mode | 获取当前模式 |
record_dream | 记录梦境 |
record_movie | 记录影视 |
add_birthday | 添加生日 |
list_birthdays | 查询生日列表 |
list_subscriptions | 查询订阅列表 |
add_book | 添加书籍 |
create_study_plan | 创建学习计划 |
create_idea | 记录灵感 |
record_pet_activity | 记录宠物活动 |
record_plant_watering | 记录植物浇水 |
add_recipe | 添加食谱 |
record_medical | 记录就诊 |
add_person | 添加联系人 |
add_address | 添加地址 |
add_coupon | 添加优惠券 |
5.3 Action 示例
以 create_task 为例,展示一个完整的 Action 定义:
{
name: 'create_task',
description: '创建一个新的待办任务',
schema: z.object({
content: z.string().describe('任务内容'),
priority: z.enum(['high', 'medium', 'low']).optional(),
dueDate: z.string().optional().describe('截止日期 YYYY-MM-DD'),
categoryId: z.string().optional().describe('所属分类 ID'),
tags: z.array(z.string()).optional().describe('标签列表'),
}),
execute: async (params) => {
const task = await taskRepo.create({
content: params.content,
priority: params.priority || 'medium',
dueDate: params.dueDate,
categoryId: params.categoryId,
tags: params.tags || [],
})
return {
success: true,
message: `已创建任务:${task.content},截止日期 ${task.dueDate || '无'}`,
data: task,
}
}
}每个 Action 执行完毕后,返回的 message 作为 AI 上下文反馈,让模型了解操作结果并做出后续响应。
6. Agent 提示词设计
/src/ai/agent-prompt.ts — 构建 Agent 的 System Prompt,让模型理解可用操作和输出格式。
6.1 提示词结构
System Prompt 由以下部分组装而成:
你是一个 AI 生活助理,当前时间是 {当前日期}。
可用操作(必须用 [ACTION] 格式调用):
{所有 Action 的 name + description + schema}
输出格式:
用自然语言回复用户。
如需执行操作,在回复末单独放置 [ACTION] 块:
[ACTION]
{{ "name": "操作名", "参数": "值" }}
[/ACTION]
每个 [ACTION] 块只能包含一个操作。
示例:
{12 个示例对话}6.2 示例对话
提示词中包含 12 个示例对话,覆盖常见场景:
| 场景 | 用户输入 | 预期 Action |
|---|---|---|
| 创建任务 | "帮我记一个任务,明天下班前提交周报" | create_task |
| 完成任务 | "把提交周报这个任务标记完成" | complete_task |
| 查看任务 | "我还有哪些没完成的任务" | list_tasks |
| 创建日程 | "帮我安排周五下午3点开会" | create_event |
| 习惯打卡 | "我今天冥想打卡" | check_in_habit |
| 记录心情 | "今天心情不错" | record_mood |
| 记笔记 | "帮我记个笔记:数据库连接字符串" | create_note |
| 记录收支 | "午饭花了35块" | record_transaction |
| 添加购物项 | "我要买牛奶" | add_shopping_item |
| 喝水记录 | "刚喝了一杯水" | record_water |
| 睡眠记录 | "昨晚睡了7个半小时" | record_sleep |
| 写日记 | "写个日记:今天效率很高" | write_diary |
示例对话教给模型两件事:
- 意图识别:从自然语言中提取操作类型和参数
- 格式规范:严格按照
[ACTION]+ JSON 的格式输出
6.3 设计考量
- 注入当前日期:让模型能正确理解"今天"、"明天"、"下周"等相对时间
- 仅 JSON 格式:Action 只接受 JSON,不接受自然语言参数描述,减少解析错误
- 一个 Action 一个块:多个操作需要多个
[ACTION]块,避免嵌套 JSON 的解析复杂度 - schema 直译:将 Zod Schema 的描述信息直接写入提示词,无需手动维护两份定义
7. 错误处理与降级
7.1 降级策略
用户发起对话
↓
检测网络状态 & 模型状态
↓
┌─ 在线 + 云端可用 ──→ 使用云端 Agent
├─ 在线 + 端侧已加载 → 提示用户选择
├─ 离线 + 端侧已加载 → 自动切换端侧
├─ 离线 + 端侧未下载 → 提示下载模型
└─ 离线 + 端侧不支持 → 提示联网后重试7.2 异常处理
| 场景 | 处理方式 |
|---|---|
| 云端 SSE 连接中断 | 自动重连(最多 3 次),失败后降级到端侧 |
| 端侧推理超时 | 30 秒超时,提示用户简化问题或切换云端 |
| 模型加载失败 | 重试一次,失败后提示重新下载 |
| JSON 解析失败 | 三层修复 → 若仍失败,跳过该 Action,回复自然语言部分 |
| Action 执行异常 | 捕获错误,返回 { success: false, message: 错误描述 } 反馈给 AI |
| 存储空间不足 | 下载前检查可用空间,不足时提示清理 |
7.3 端侧不支持的 Action
部分 Action(如 web_search)在端侧无法执行。当端侧模型尝试调用这些 Action 时,系统返回明确的错误反馈,由 AI 模型在对话中告知用户需要联网。
8. 扩展点
8.1 新增 Action
在 actions.ts 中按以下步骤添加:
- 定义 Zod Schema
- 实现
execute函数 - 注册到
allActions数组
注册后自动出现在 System Prompt 中,无需修改提示词模板。
8.2 新增模型
在 model-manager.ts 的 MODEL_CATALOG 数组中追加 ModelInfo 对象。需提供:
- GGUF 下载地址(App 端)
- HuggingFace 模型 ID(H5 端)
- 量化方式、参数量等元信息
8.3 自定义 Agent 策略
ai-agent.service.ts 的 parseActions() 和对话函数可被替换或装饰,以支持自定义的 Agent 行为(如多轮确认、人机协作审批等)。