feat: 升级至 v0.3.4 — 接入 Xiaomi MiMo Provider + 文档全量校准

新增 MiMo (小米) LLM Provider 适配器,支持 mimo-v2.5-pro 和 mimo-v2.5 两个文本模型,复用 OpenAI 兼容 SSE 流式解析,支持 Thinking 模式和 Function Calling。同步校准全量 docs 文档与 README 使其与实际代码一致。

主要变更:
- 新增 mimo.adapter.ts 适配器(SSE + thinking.type + max_completion_tokens)
- 修复 thinking 逻辑 bug:禁用思考时未传 temperature/top_p
- 补全 sse-stream.ts 的 MiMo 缓存字段映射(prompt_tokens_details.cached_tokens)
- 补全 sse-stream.ts 的 finish_reason 映射(repetition_truncation)
- 注册 MiMo 适配器到 adapters/index.ts、main.ts 工厂
- handlers.ts 添加 mimo.contextWindow 热重载触发
- database.service.ts seed 添加 mimo 默认配置
- SettingsModal/OnboardingWizard/Header 添加 MiMo Provider UI
- constants.ts PROVIDER_LABELS 添加 mimo
- .env.example 添加 MIMO_API_KEY/MIMO_BASE_URL
- 反向修改 4 个 docs HTML 设计文档(工具数量/版本日期/适配器列表/数据库表)
- 反向修改 Agent网络工具通用设计-v2.md 附录 B 文件索引
- 完全重写 README.md(v0.3.4、27 工具、4 适配器、9 表)
This commit is contained in:
thzxx
2026-07-15 22:28:09 +08:00
parent 4554177db0
commit 656c6b7af1
22 changed files with 1677 additions and 115 deletions
+4
View File
@@ -9,6 +9,10 @@ DEEPSEEK_BASE_URL=https://api.deepseek.com
AGNES_API_KEY=your_agnes_api_key_here AGNES_API_KEY=your_agnes_api_key_here
AGNES_BASE_URL=https://apihub.agnes-ai.com/v1 AGNES_BASE_URL=https://apihub.agnes-ai.com/v1
# Xiaomi MiMo API
MIMO_API_KEY=your_mimo_api_key_here
MIMO_BASE_URL=https://api.xiaomimimo.com/v1
# Ollama API (local) # Ollama API (local)
OLLAMA_BASE_URL=http://localhost:11434 OLLAMA_BASE_URL=http://localhost:11434
+548 -46
View File
@@ -1,80 +1,582 @@
# MetonaAI Desktop # MetonaAI Desktop
生产级通用 AI Agent 智能体桌面应用 > 生产级通用 AI Agent 智能体桌面应用
[![Version](https://img.shields.io/badge/version-0.3.4-blue)](./package.json)
[![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
[![Electron](https://img.shields.io/badge/Electron-35-47848F)](https://www.electronjs.org/)
[![React](https://img.shields.io/badge/React-19-61DAFB)](https://react.dev/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.8-3178C6)](https://www.typescriptlang.org/)
Metona 是一款基于 Electron 的生产级 AI Agent 桌面应用,内置 ReAct 状态机驱动的智能体循环、27 个内置工具、三层记忆系统、四层安全防线与完整的可观测性链路。支持 DeepSeek、Agnes AI、MiMo(小米)、Ollama 四种 LLM Provider,兼容 MCP 协议扩展。
---
## 目录
- [技术栈](#技术栈)
- [快速开始](#快速开始)
- [核心特性](#核心特性)
- [项目架构](#项目架构)
- [内置工具](#内置工具)
- [LLM 适配器](#llm-适配器)
- [记忆系统](#记忆系统)
- [安全机制](#安全机制)
- [配置说明](#配置说明)
- [项目结构](#项目结构)
- [开发命令](#开发命令)
- [许可证](#许可证)
---
## 技术栈 ## 技术栈
- **运行时**: Electron 35 + React 19 + TypeScript 5.8 | 层级 | 技术 | 版本 |
- **状态管理**: Zustand 5 |------|------|------|
- **数据库**: better-sqlite3 | 运行时 | Electron | 35 |
- **LLM**: DeepSeek / Agnes AI / Ollama | 前端框架 | React | 19 |
- **协议**: MCP (Model Context Protocol) | 类型系统 | TypeScript | 5.8 |
- **构建**: electron-vite + Vite 6 | UI 组件库 | Material UI (MUI) | 9 |
| 状态管理 | Zustand | 5 |
| 数据库 | better-sqlite3 | 11 |
| 构建工具 | electron-vite + Vite | 3 / 6 |
| LLM 协议 | MCP SDK | 1.12 |
| 样式辅助 | Tailwind CSS | 4 |
| Markdown | react-markdown + remark-gfm | 10 / 4 |
| UUID | nanoid | 5 |
| 校验 | Zod | 3 |
| 缓存 | lru-cache | 11 |
| 日志 | electron-log | 5 |
| 配置存储 | electron-store | 10 |
---
## 快速开始 ## 快速开始
### 环境要求
- Node.js >= 18
- npm >= 9
- Windows / macOS / Linux
### 安装与运行
```bash ```bash
# 安装依赖 # 安装依赖
npm install npm install
# 开发模式 # 开发模式(启动 Electron + Vite 热重载)
npm run dev npm run dev
# 构建 # 类型检查
npm run typecheck
# 构建生产包(Windows 输出 NSIS 安装包 + 便携版)
npm run build npm run build
``` ```
## 配置 ### 配置 LLM Provider
复制 `.env.example``.env`,填入 API Key 1. 复制 `.env.example``.env`,填入 API Key
```bash ```bash
cp .env.example .env cp .env.example .env
``` ```
或在应用设置界面中配置 LLM Provider。 ```env
# DeepSeek API
DEEPSEEK_API_KEY=your_key
DEEPSEEK_BASE_URL=https://api.deepseek.com
# Agnes AI API
AGNES_API_KEY=your_key
AGNES_BASE_URL=https://apihub.agnes-ai.com/v1
# Ollama(本地运行,无需 Key
OLLAMA_BASE_URL=http://localhost:11434
```
2. 或在应用启动后,通过 **设置 → LLM 配置** 界面可视化配置 Provider、API Key、模型、上下文窗口等参数。
---
## 核心特性
### 智能体引擎
- **ReAct 状态机**8 状态闭环(INIT → THINKING → PARSING → EXECUTING → OBSERVING → REFLECTING → COMPRESSING → TERMINATED
- **流式对话**SSEDeepSeek/Agnes+ NDJSONOllama)双协议流式响应
- **Thinking 模式**:支持 deepseek-v4-pro / agnes-2.0-flash / qwen3 等模型的推理模式
- **死循环检测**:连续 3 轮相同工具调用签名自动终止
- **上下文压缩**:80% 阈值触发 LLM 摘要压缩,保留最近 10 条消息
- **错误重试**:指数退避(1s/2s/4s,上限 30s+ ±20% jitter
- **可配置迭代**:最大迭代次数(默认 20)、总超时(默认 600s)、工具执行超时(默认 120s)
### 工具与执行
- **27 个内置工具**:覆盖文件系统、代码搜索、网络搜索、浏览器自动化、Git、开发工具、记忆、任务管理等
- **MCP 协议支持**:动态加载外部 MCP Server 工具
- **子任务委派**TaskOrchestrator 支持最大 3 层深度的 SubAgent 编排
- **策略引擎**:三级权限(READ/WRITE/EXTERNAL_ACTION+ 滑动窗口频率限制
- **确认机制**HIGH/CRITICAL 风险工具需用户确认,支持会话内同类免确认与持久化自动执行
### 记忆与上下文
- **三层记忆系统**:情节记忆(episodic)、语义记忆(semantic)、工作记忆(working
- **TF-IDF 语义检索**:中英文分词 + 时间衰减(30 天半衰期)+ IDF 缓存
- **自动记忆固化**:会话结束时 LLM 提取重要信息写入 MEMORY.md
- **System Prompt 分区构建**SOUL.md + AGENTS.md + USERS.md + MEMORY.md + 安全准则
### 安全防线(四层纵深防御)
1. **路径安全**:工作空间边界校验 + realpathSync 符号链接逃逸检测 + 根目录 MEMORY.md 保护
2. **命令安全**SandboxManager 28 模式扫描 + shell-quote 双层防御 + 受保护文件检查
3. **权限控制**PolicyEngine 三级权限 + 频率限制(20 次/分钟)+ 用户确认钩子
4. **内容安全**PromptInjectionDefender30+ 正则 + 语义检测)+ OutputValidator(幻觉检测)
### 可观测性
- **全链路追踪**TraceViewer 可视化 ReAct 每轮迭代
- **会话录制**:9 种事件录制到 JSONL 文件
- **审计日志**SQLite 链式哈希防篡改(INSERT-ONLY 触发器)
- **Token 统计**:输入/输出/推理 Token 用量追踪
### 桌面体验
- **三栏布局**Sidebar + Chat + DetailPanel,支持专注模式
- **系统托盘**4 状态指示(idle/thinking/executing/error
- **全局快捷键**Cmd/Ctrl+Shift+M 唤起应用
- **14 个应用内快捷键**:会话管理、布局切换、主题切换等
- **暗色/亮色主题**:跟随系统 + 手动切换
- **首次使用引导**5 步 Onboarding 向导
- **命令面板**Cmd/Ctrl+K 快速搜索
- **多平台构建**WindowsNSIS + 便携版)、macOSDMG + ZIP)、LinuxAppImage + DEB
---
## 项目架构
### 四层 Harness 架构
```
┌─────────────────────────────────────────────────────────────┐
│ L1 推理与编排层 │
│ AgentLoopEngine (ReAct 状态机) · TaskOrchestrator (子任务) │
│ MetonaRequest / MetonaResponse / MetonaStreamEvent │
├─────────────────────────────────────────────────────────────┤
│ L2 上下文与记忆层 │
│ ContextBuilder · MemoryManager · MemoryConsolidator │
│ MetonaContext / MetonaMemoryItem │
├─────────────────────────────────────────────────────────────┤
│ L3 工具与安全执行层 │
│ ToolRegistry · SandboxManager · PolicyEngine · MCPAdapter │
│ PromptInjectionDefender · OutputValidator · ConfirmationHook │
│ MetonaToolDef / MetonaToolCall / MetonaToolResult │
├─────────────────────────────────────────────────────────────┤
│ L4 支撑与基础架构层 │
│ ConfigService · DatabaseService · AuditService │
│ SessionService · SessionRecorder · WindowManager │
│ TrayManager · MCPManager · WorkspaceService │
└─────────────────────────────────────────────────────────────┘
```
### 进程模型
| 进程 | 运行时 | 职责 |
|------|--------|------|
| Main Process | Node.js | Agent 引擎、工具调度、数据库、MCP 管理、配置 |
| Preload Script | 沙箱 | contextBridge 安全暴露 14 个 API 命名空间 |
| Renderer | Chromium | React 19 + MUI 9 界面渲染 |
### 数据流
```
用户输入 → ChatInput → agent-store.sendMessage
→ IPC: agent:sendMessage → handlers.ts
→ reloadAdapter (热重载,配置签名比对)
→ 保存用户消息到 SQLite
→ 加载历史消息 + 注入相关记忆
→ ContextBuilder.buildSystemPrompt (SOUL+AGENTS+USERS+MEMORY+安全准则)
→ PromptInjectionDefender.detect (riskScore>=7 阻断)
→ AgentLoopEngine.runStream
→ Adapter.sendStream (SSE/NDJSON 流式)
→ 流式事件 → webContents.send('agent:streamEvent')
→ 工具调用 → PreToolHooks (权限+频率+确认)
→ ToolRegistry.execute → PostToolHooks (审计+记忆触发)
→ 80% 阈值触发上下文压缩
→ 死循环检测 (3 轮相同签名)
→ OutputValidator.validate (幻觉检测)
→ 保存 assistant 消息 + tool 结果消息
→ MemoryConsolidator.consolidate (异步提取记忆)
→ AuditService.logSessionEnd
← useAgentStream Hook 监听事件 → Zustand Store → React 重渲染
```
---
## 内置工具
Metona 内置 27 个工具,按功能分类如下:
### 文件系统(5 个)
| 工具 | 风险 | 需确认 | 功能 |
|------|------|--------|------|
| `read_file` | SAFE | 否 | 读取文本文件,二进制检测,offset/limit 分页 |
| `write_file` | MEDIUM | 是 | 原子写入(tmp+rename),支持 overwrite/append |
| `list_directory` | SAFE | 否 | 列出目录,depthmax 5/glob/include_hidden |
| `search_files` | SAFE | 否 | 按模式搜索,content/files 模式,context_lines |
| `delete_file` | HIGH | 是 | 删除文件,禁止删除工作空间根目录和 MEMORY.md |
### 编辑与搜索(3 个)
| 工具 | 风险 | 需确认 | 功能 |
|------|------|--------|------|
| `file_editor` | MEDIUM | 是 | 精准编辑,replace/insert/delete/regex + dry_run |
| `code_search` | SAFE | 否 | 基于 ripgrep 高速搜索,回退 JS |
| `diff_viewer` | SAFE | 否 | unified diff 格式,LCS 算法 |
### 网络与浏览器(4 个)
| 工具 | 风险 | 需确认 | 功能 |
|------|------|--------|------|
| `web_search` | LOW | 否 | 双模式(SearXNG / 内置四引擎),智能排序,自动抓取 |
| `web_fetch` | LOW | 否 | 三阶段回退(HTTP + 反爬 + 浏览器渲染),10MB 限制 |
| `web_browser` | HIGH | 是 | 浏览器自动化,9 个 actionopen/screenshot/extract 等) |
| `http_request` | LOW | 否 | HTTP/REST API 请求,6 种 method |
### 记忆(2 个)
| 工具 | 风险 | 需确认 | 功能 |
|------|------|--------|------|
| `memory_store` | MEDIUM | 否 | 存储记忆到三层(episodic/semantic/working |
| `memory_search` | SAFE | 否 | TF-IDF 检索记忆,时间衰减 |
### 命令与开发(5 个)
| 工具 | 风险 | 需确认 | 功能 |
|------|------|--------|------|
| `run_command` | HIGH | 是 | 沙箱执行 Shell 命令,双重校验 + shell-quote 防御 |
| `lint_code` | SAFE | 否 | TypeScript tsc 或 ESLint 检查 |
| `run_tests` | LOW | 否 | 运行测试套件,filter 字符白名单防注入 |
| `project_info` | SAFE | 否 | 项目结构分析,4 种 detail |
| `delegate_task` | MEDIUM | 否 | 子任务委派给独立 SubAgent |
### Git4 个)
| 工具 | 风险 | 需确认 | 功能 |
|------|------|--------|------|
| `git_status` | SAFE | 否 | 工作树状态,porcelain v1 解析 |
| `git_diff` | SAFE | 否 | diff 输出,50KB 截断,5MB maxBuffer |
| `git_log` | SAFE | 否 | 提交历史,jest/vitest/mocha 格式解析 |
| `git_commit` | MEDIUM | 是 | 暂存+提交,校验 file 在 workspace 内 |
### 任务与辅助(4 个)
| 工具 | 风险 | 需确认 | 功能 |
|------|------|--------|------|
| `task_manager` | LOW | 否 | 持久化任务 CRUD,支持父子关系 |
| `todo_write` | SAFE | 否 | 会话级 TODOLRU 淘汰(max 50 sessions |
| `think` | SAFE | 否 | 结构化思考空间,无副作用 |
| `view_image` | SAFE | 否 | 读取图片返回 base64,5MB 限制,7 种格式 |
---
## LLM 适配器
| 适配器 | Provider ID | 模型 | 上下文窗口 | 流式格式 | Thinking | 多模态 |
|--------|-------------|------|-----------|----------|----------|--------|
| DeepSeekAdapter | deepseek | deepseek-v4-pro / deepseek-v4-flash | 1M(可配) | SSE | thinking + reasoning_effort | 否 |
| AgnesAdapter | agnes | agnes-2.0-flash | 1M(可配) | SSE | chat_template_kwargs | 是(URL |
| MimoAdapter | mimo | mimo-v2.5-pro / mimo-v2.5 | 131072(可配) | SSE | thinking.type | 是(URL |
| OllamaAdapter | ollama | qwen3 / gemma3 / deepseek-r1 | 4096(可配 num_ctx | NDJSON | think 参数 | 是(Base64 |
### 上下文窗口配置
- **DeepSeek / Agnes / MiMo**`contextWindow` 配置项影响本地压缩判断和 UI 显示(最小 4096)
- **Ollama**`num_ctx` 配置项直接影响 API 请求参数
- 切换 Provider 时自动清空 API Key,防止使用不兼容的密钥
- Engine 上下文窗口与 Adapter 配置值自动同步
---
## 记忆系统
### 三层记忆架构
| 层级 | 类型 | 存储表 | 用途 | 检索方式 |
|------|------|--------|------|----------|
| L2 | 情节记忆 (episodic) | episodic_memories | 会话事件、用户交互 | TF-IDF + 时间衰减 |
| L3 | 语义记忆 (semantic) | semantic_memories | 知识、偏好、事实 | 精确 + 模糊匹配 |
| L1 | 工作记忆 (working) | working_memories | 当前任务临时状态 | 精确键值查找 |
### 记忆生命周期
```
新记忆写入 → 重要性评分 (0-1)
├── 高 (>0.8) → 永久保存
├── 中 (0.4-0.8) → 定期回顾,逐渐衰减
└── 低 (<0.4) → 短期保留,自然遗忘
会话结束 → MemoryConsolidator 提取重要信息 → 写入 MEMORY.md
```
### 磁盘文件(工作空间)
| 文件 | 用途 | 是否必需 |
|------|------|----------|
| SOUL.md | AI 角色定义(灵魂) | 是 |
| AGENTS.md | AI 行为规则 | 是 |
| MEMORY.md | 动态记忆存储 | 是(仅根目录受保护) |
| USERS.md | 用户信息画像 | 是 |
---
## 安全机制
### 四层纵深防御
```
第 1 层:路径安全
└─ isPathWithinWorkspace + realpathSync + MEMORY.md 保护
第 2 层:命令安全
└─ SandboxManager (28 模式扫描) + shell-quote 双层防御
第 3 层:权限控制
└─ PolicyEngine (三级权限) + 频率限制 (20/min) + ConfirmationHook
第 4 层:内容安全
└─ PromptInjectionDefender (30+ 正则 + 语义检测) + OutputValidator (幻觉检测)
```
### 风险分级
| 风险等级 | 示例工具 | 确认要求 |
|----------|----------|----------|
| SAFE | read_file, list_directory, code_search | 无需确认 |
| LOW | web_search, web_fetch, http_request | 无需确认 |
| MEDIUM | write_file, file_editor, memory_store | 可配置自动执行 |
| HIGH | delete_file, run_command, web_browser, git_commit | 强制确认 |
| CRITICAL | (预留) | 强制确认 + 双人复核 |
---
## 配置说明
### 环境变量(.env
```env
# DeepSeek API
DEEPSEEK_API_KEY=your_deepseek_api_key_here
DEEPSEEK_BASE_URL=https://api.deepseek.com
# Agnes AI API
AGNES_API_KEY=your_agnes_api_key_here
AGNES_BASE_URL=https://apihub.agnes-ai.com/v1
# Xiaomi MiMo API
MIMO_API_KEY=your_mimo_api_key_here
MIMO_BASE_URL=https://api.xiaomimimo.com/v1
# Ollama API (local)
OLLAMA_BASE_URL=http://localhost:11434
# App
VITE_APP_TITLE=MetonaAI Desktop
```
### 应用配置(app_config 表)
关键配置项及默认值:
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `llm.provider` | deepseek | LLM Provider |
| `llm.model` | deepseek-v4-pro | 模型名称 |
| `agent.maxIterations` | 20 | 最大迭代次数 |
| `agent.totalTimeoutMs` | 600000 | 总超时(ms |
| `agent.thinkingEnabled` | true | Thinking 模式 |
| `agent.thinkingEffort` | high | 推理强度 |
| `agent.confirmationTimeoutMs` | 120000 | 确认超时(30s~600s |
| `deepseek.contextWindow` | 1000000 | DeepSeek 上下文窗口 |
| `agnes.contextWindow` | 1000000 | Agnes 上下文窗口 |
| `mimo.contextWindow` | 131072 | MiMo 上下文窗口 |
| `ollama.numCtx` | 4096 | Ollama 上下文窗口 |
### SearXNG 配置(可选)
启用 SearXNG 元搜索引擎替代内置四引擎搜索,支持 12 项配置(URL、引擎列表、认证方式等),详见设置界面。
---
## 项目结构 ## 项目结构
``` ```
MetonaAI-Desktop/ MetonaAI-Desktop/
├── electron/ # Electron 主进程 ├── electron/ # Electron 主进程
│ ├── main.ts # 应用入口 │ ├── main.ts # 应用入口(538 行)
│ ├── preload.ts # 安全桥接脚本 │ ├── preload.ts # 安全桥接(14 个 API 命名空间)
│ ├── ipc/ # IPC 通道处理 │ ├── ipc/
├── services/ # 业务服务(数据库、会话、记忆等 │ └── handlers.ts # IPC 通道处理(50+ 通道
── harness/ # Agent 核心引擎 ── services/ # 业务服务层
├── agent-loop/ # ReAct 状态机 ├── audit.service.ts # 审计日志(链式哈希防篡改)
├── adapters/ # LLM Provider 适配器 ├── config.service.ts # 配置管理
├── tools/ # 工具注册与内置工具 ├── database.service.ts # SQLite 数据库(9 张表)
├── memory/ # 记忆系统 ├── mcp-manager.service.ts # MCP Server 管理
├── prompts/ # System Prompt 构建 ├── session-recorder.service.ts # 会话录制
── types/ # Metona IR 类型定义 ── session.service.ts # 会话 CRUD
├── src/ # React 渲染进程 │ │ ├── tray-manager.service.ts # 系统托盘
│ ├── components/ # UI 组件 │ ├── update.service.ts # 自动更新
│ ├── hooks/ # React Hooks │ ├── window-manager.service.ts # 窗口管理
├── stores/ # Zustand 状态管理 │ └── workspace.service.ts # 工作空间管理
── styles/ # 全局样式 ── harness/ # Agent 核心引擎
└── types/ # 类型声明 ├── agent-loop/ # ReAct 状态机
├── docs/ # 设计文档 ├── engine.ts # 循环引擎(8 状态)
├── standard/ # 开发规范 │ │ └── types.ts # 状态枚举
└── apis/ # API 文档 │ ├── adapters/ # LLM Provider 适配器
│ │ ├── base-adapter.ts # 抽象基类
│ │ ├── deepseek.adapter.ts # DeepSeekSSE
│ │ ├── agnes-ai.adapter.ts # Agnes AISSE
│ │ ├── mimo.adapter.ts # MiMo 小米(SSE
│ │ ├── ollama.adapter.ts # OllamaNDJSON
│ │ └── shared/ # 共享模块
│ │ ├── openai-format.ts # OpenAI 兼容格式
│ │ └── sse-stream.ts # SSE 流解析
│ ├── tools/ # 工具系统
│ │ ├── registry.ts # 工具注册 + PolicyEngine
│ │ └── built-in/ # 27 个内置工具
│ │ ├── filesystem.ts # 文件系统(5 工具)
│ │ ├── file-editor.ts # 编辑器
│ │ ├── code-search.ts # 代码搜索
│ │ ├── diff-viewer.ts # 差异查看
│ │ ├── web-search.ts # 网络搜索
│ │ ├── web-fetch.ts # 网页抓取
│ │ ├── browser.ts # 浏览器工具
│ │ ├── browser-window-manager.ts # 浏览器窗口管理
│ │ ├── network.ts # 网络工具入口
│ │ ├── network-utils.ts # 网络工具函数
│ │ ├── http-request.ts # HTTP 请求
│ │ ├── memory.ts # 记忆工具
│ │ ├── command.ts # 命令执行
│ │ ├── git.ts # Git 工具(4 个)
│ │ ├── dev-tools.ts # 开发工具(3 个)
│ │ ├── task-manager.ts # 任务管理
│ │ ├── delegate-task.ts # 子任务委派
│ │ ├── todo.ts # TODO 工具
│ │ ├── think.ts # 思考工具
│ │ ├── view-image.ts # 图片查看
│ │ └── file-guard.ts # 文件保护
│ ├── types/ # Metona IR 类型定义
│ │ ├── metona-request.ts # 请求类型
│ │ ├── metona-response.ts # 响应类型
│ │ ├── metona-tool.ts # 工具类型
│ │ ├── metona-context.ts # 上下文类型
│ │ └── metona-adapter.ts # 适配器接口
│ ├── sandbox/ # 沙箱安全
│ │ ├── sandbox.ts # SandboxManager
│ │ └── permissions.ts # PolicyEngine
│ ├── security/ # 安全防御
│ │ └── prompt-injection-defense.ts
│ ├── memory/ # 记忆系统
│ │ ├── manager.ts # MemoryManager
│ │ └── consolidator.ts # MemoryConsolidator
│ ├── orchestration/ # 编排
│ │ └── orchestrator.ts # TaskOrchestrator
│ ├── prompts/ # 提示词构建
│ │ └── context-builder.ts # ContextBuilder
│ ├── hooks/ # 钩子
│ │ ├── pre-tool.ts # 工具前钩子
│ │ ├── post-tool.ts # 工具后钩子
│ │ └── confirmation-hook.ts # 确认钩子
│ ├── verification/ # 验证
│ │ └── output-validator.ts # OutputValidator
│ └── utils/ # 工具函数
│ └── token-estimator.ts # Token 估算
├── src/ # React 渲染进程
│ ├── main.tsx # React 入口
│ ├── App.tsx # 应用根组件
│ ├── components/ # UI 组件(25 个)
│ │ ├── chat/ # 聊天组件(11 个)
│ │ ├── layout/ # 布局组件(5 个)
│ │ ├── settings/ # 设置面板
│ │ ├── memory/ # 记忆查看器
│ │ ├── tasks/ # 任务列表
│ │ ├── trace/ # 追踪查看器(3 个)
│ │ ├── workspace/ # 工作空间查看器
│ │ ├── onboarding/ # 引导向导
│ │ └── common/ # 通用组件
│ ├── hooks/ # React Hooks
│ │ ├── useAgentStream.ts # Agent 流式事件
│ │ ├── useKeyboardShortcuts.ts # 键盘快捷键
│ │ └── useTheme.ts # 主题管理
│ ├── stores/ # Zustand 状态管理
│ │ ├── agent-store.ts # Agent 状态
│ │ ├── session-store.ts # 会话状态
│ │ └── ui-store.ts # UI 状态
│ ├── lib/ # 工具库
│ │ ├── constants.ts # 常量定义
│ │ ├── formatters.ts # 格式化函数
│ │ ├── theme.ts # MUI 主题
│ │ └── cn.ts # className 合并
│ ├── styles/
│ │ └── globals.css # 全局样式
│ └── types/
│ └── global.d.ts # window.metona 类型声明
├── docs/ # 设计文档
├── standard/ # 开发规范
├── apis/ # LLM API 文档
├── assets/ # 应用图标
├── .env.example # 环境变量示例
├── electron-builder.yml # 构建配置
├── electron.vite.config.ts # Vite 配置
├── package.json # 依赖与脚本
├── tsconfig.json # TypeScript 配置
├── tsconfig.node.json # Node 端配置
├── tsconfig.web.json # Web 端配置
└── LICENSE # MIT 许可证
``` ```
## 功能 ---
- ✅ 三栏布局(Sidebar + Chat + DetailPanel ## 开发命令
- ✅ 流式对话(DeepSeek / Agnes / Ollama
- ✅ 9 个内置工具(文件系统、网络、记忆、命令行) ```bash
- ✅ MCP 协议支持 # 开发
- ✅ 会话持久化(SQLite npm run dev # 启动开发模式
- ✅ 三层记忆系统(情节/语义/工作) npm run typecheck # 全量类型检查
- ✅ 全链路追踪(Trace Viewer npm run typecheck:node # Node 端类型检查
- ✅ 审计日志(防篡改) npm run lint # ESLint 检查
- ✅ 系统托盘 + 全局快捷键 npm run lint:fix # ESLint 自动修复
- ✅ 暗色/亮色主题 npm run format # Prettier 格式化
- ✅ 14 个键盘快捷键
# 测试
npm test # 运行单元测试 (Vitest)
npm run test:watch # 测试监听模式
npm run test:e2e # E2E 测试 (Playwright)
# 构建
npm run build # 构建生产包
npm run build:renderer # 仅构建渲染进程
npm run build:electron # 仅构建主进程
npm run preview # 预览构建产物
```
### 数据库 Schema9 张表)
| 表名 | 用途 |
|------|------|
| sessions | 会话管理 |
| messages | 消息持久化 |
| app_config | 应用配置(键值对) |
| audit_logs | 审计日志(链式哈希防篡改) |
| mcp_servers | MCP Server 配置 |
| episodic_memories | 情节记忆 |
| semantic_memories | 语义记忆 |
| working_memories | 工作记忆 |
| tasks | 持久化任务 |
---
## 许可证 ## 许可证
MIT [MIT](./LICENSE)
+793
View File
@@ -0,0 +1,793 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MiMo Chat Completions API 接口文档</title>
<style>
:root {
--primary: #ff6900;
--primary-light: #fff4eb;
--text: #1a1a2e;
--text-secondary: #555;
--border: #e0e0e0;
--bg: #ffffff;
--code-bg: #f6f8fa;
--accent-blue: #2563eb;
--accent-green: #16a34a;
--accent-red: #dc2626;
--accent-purple: #7c3aed;
--shadow: 0 2px 8px rgba(0,0,0,0.08);
--radius: 8px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Noto Sans SC', sans-serif; color: var(--text); line-height: 1.7; background: var(--bg); }
/* Header */
.hero { background: linear-gradient(135deg, #ff6900 0%, #ff8533 50%, #ffa366 100%); color: #fff; padding: 48px 24px 40px; text-align: center; }
.hero h1 { font-size: 2.2rem; font-weight: 800; letter-spacing: -0.5px; margin-bottom: 12px; }
.hero p { opacity: 0.92; font-size: 1.05rem; max-width: 700px; margin: 0 auto; }
.hero .badge { display: inline-block; background: rgba(255,255,255,0.22); padding: 4px 14px; border-radius: 20px; font-size: 0.82rem; margin-top: 16px; backdrop-filter: blur(4px); }
/* Container */
.container { max-width: 960px; margin: 0 auto; padding: 0 24px; }
/* TOC */
.toc { background: var(--primary-light); border: 1px solid #ffd9b3; border-radius: var(--radius); padding: 28px 32px; margin: -30px auto 36px; position: relative; z-index: 10; box-shadow: var(--shadow); }
.toc h2 { font-size: 1.15rem; color: var(--primary); margin-bottom: 14px; display: flex; align-items: center; gap: 8px; }
.toc-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 6px 20px; }
.toc a { color: var(--text); text-decoration: none; font-size: 0.92rem; padding: 3px 0; display: flex; align-items: center; gap: 6px; transition: color .15s; }
.toc a:hover { color: var(--primary); }
/* Sections */
section { margin-bottom: 48px; scroll-margin-top: 24px; }
section h2 { font-size: 1.55rem; font-weight: 700; margin-bottom: 18px; padding-bottom: 10px; border-bottom: 2px solid var(--primary); display: flex; align-items: center; gap: 10px; }
section h3 { font-size: 1.2rem; font-weight: 600; margin: 26px 0 12px; color: var(--text); }
section h4 { font-size: 1.02rem; font-weight: 600; margin: 18px 0 8px; color: var(--text-secondary); }
/* Method badge */
.method-badge { display: inline-flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.method-tag { padding: 4px 12px; border-radius: 5px; font-size: 0.82rem; font-weight: 700; letter-spacing: 0.5px; color: #fff; }
.method-post { background: var(--accent-green); }
.url-box { font-family: 'SF Mono', 'Fira Code', monospace; font-size: 0.88rem; background: var(--code-bg); padding: 10px 16px; border-radius: 6px; border: 1px solid var(--border); word-break: break-all; color: var(--accent-blue); }
/* Tables */
table { width: 100%; border-collapse: collapse; margin: 14px 0 20px; font-size: 0.9rem; }
th { background: #f0f0f0; text-align: left; padding: 10px 14px; font-weight: 600; border-bottom: 2px solid var(--border); white-space: nowrap; }
td { padding: 9px 14px; border-bottom: 1px solid #eee; vertical-align: top; }
tr:hover td { background: #fafafa; }
.type-tag { font-family: 'SF Mono', 'Fira Code', monospace; font-size: 0.83rem; color: var(--accent-purple); background: #f3effa; padding: 1px 7px; border-radius: 4px; }
.required-tag { font-size: 0.78rem; font-weight: 600; padding: 1px 7px; border-radius: 4px; }
.req-yes { background: #fef2f2; color: var(--accent-red); }
.req-no { background: #f0fdf4; color: var(--accent-green); }
.req-default { background: #eff6ff; color: var(--accent-blue); }
/* Code blocks */
pre { background: #1e1e2e; color: #cdd6f4; padding: 20px 24px; border-radius: var(--radius); overflow-x: auto; font-size: 0.85rem; line-height: 1.6; margin: 14px 0 20px; position: relative; }
pre code { font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace; background: none; padding: 0; }
.code-lang { position: absolute; top: 0; right: 12px; font-size: 0.72rem; color: rgba(255,255,255,0.35); text-transform: uppercase; letter-spacing: 1px; }
/* Inline code */
code:not(pre code) { font-family: 'SF Mono', 'Fira Code', monospace; font-size: 0.84em; background: var(--code-bg); padding: 2px 7px; border-radius: 4px; color: #d63384; }
/* Info / Warning boxes */
.info-box { background: #eff6ff; border-left: 4px solid var(--accent-blue); padding: 14px 18px; border-radius: 0 var(--radius) var(--radius) 0; margin: 16px 0; font-size: 0.92rem; }
.info-box strong { color: var(--accent-blue); }
.warn-box { background: #fffbeb; border-left: 4px solid #d97706; padding: 14px 18px; border-radius: 0 var(--radius) var(--radius) 0; margin: 16px 0; font-size: 0.92rem; }
.warn-box strong { color: #b45309; }
.tip-box { background: #f0fdf4; border-left: 4px solid var(--accent-green); padding: 14px 18px; border-radius: 0 var(--radius) var(--radius) 0; margin: 16px 0; font-size: 0.92rem; }
.tip-box strong { color: var(--accent-green); }
/* Tabs */
.tabs { display: flex; gap: 0; margin: 16px 0 0; border-bottom: 2px solid var(--border); }
.tab-btn { padding: 8px 18px; cursor: pointer; border: none; background: none; font-size: 0.88rem; font-weight: 500; color: var(--text-secondary); border-bottom: 2px solid transparent; margin-bottom: -2px; transition: all .15s; }
.tab-btn.active { color: var(--primary); border-bottom-color: var(--primary); font-weight: 600; }
.tab-content { display: none; }
.tab-content.active { display: block; }
/* Feature cards */
.feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; margin: 18px 0; }
.feature-card { background: #fafafa; border: 1px solid var(--border); border-radius: var(--radius); padding: 18px; transition: box-shadow .15s; }
.feature-card:hover { box-shadow: var(--shadow); }
.feature-card .icon { font-size: 1.5rem; margin-bottom: 8px; }
.feature-card h4 { margin: 0 0 6px; font-size: 0.95rem; }
.feature-card p { font-size: 0.84rem; color: var(--text-secondary); margin: 0; }
/* Footer */
footer { background: #1a1a2e; color: rgba(255,255,255,0.65); text-align: center; padding: 32px 24px; margin-top: 56px; font-size: 0.86rem; }
footer a { color: var(--primary); text-decoration: none; }
/* Responsive */
@media (max-width: 640px) {
.hero h1 { font-size: 1.6rem; }
.toc { padding: 20px; margin-top: -20px; }
table { font-size: 0.82rem; }
th, td { padding: 7px 10px; }
}
</style>
</head>
<body>
<!-- ===== HERO ===== -->
<div class="hero">
<h1>🔮 MiMo Chat Completions API</h1>
<p>Xiaomi MiMo V2.5 系列 OpenAI 兼容对话补全接口完整文档。支持多轮对话、深度思考、工具调用、联网搜索、语音合成、图像/视频输入等全功能。</p>
<div class="badge">支持模型:mimo-v2.5-pro · mimo-v2.5 · mimo-v2.5-tts · mimo-v2.5-tts-voicedesign · mimo-v2.5-tts-voiceclone</div>
</div>
<div class="container">
<!-- ===== TOC ===== -->
<nav class="toc">
<h2>📋 目录导航</h2>
<div class="toc-grid">
<a href="#overview">📌 概述 &amp; 基本信息</a>
<a href="#quickstart">⚡ 快速开始</a>
<a href="#models">💰 可用模型</a>
<a href="#request-auth">🔐 认证方式</a>
<a href="#request-body">📥 请求参数详解</a>
<a href="#response">📤 响应对象(非流式)</a>
<a href="#response-stream">📤 响应 Chunk(流式)</a>
<a href="#thinking">🧠 深度思考模式</a>
<a href="#tools">🔧 工具调用 (Function Calling)</a>
<a href="#websearch">🌐 联网搜索</a>
<a href="#tts">🎙️ 语音合成 (TTS)</a>
<a href="#examples">💻 代码示例</a>
<a href="#errors">⚠️ 错误码说明</a>
</div>
</nav>
<!-- ===== OVERVIEW ===== -->
<section id="overview">
<h2>📌 概述 &amp; 基本信息</h2>
<p>MiMo Chat Completions API 是小米大语言模型提供的 OpenAI 兼容对话补全接口,支持 RESTful HTTP 协议,所有请求与响应均使用 JSON 格式。流式响应采用 SSEServer-Sent Events)。</p>
<div style="margin-top:18px;">
<table>
<tr><th width="140">项目</th><th></th></tr>
<tr><td>Base URL</td><td><code>https://api.xiaomimimo.com/v1/chat/completions</code></td></tr>
<tr><td>协议</td><td>HTTPS (POST)</td></tr>
<tr><td>认证方式</td><td>api-key Header 或 Authorization Bearer</td></tr>
<tr><td>内容类型</td><td>application/json</td></tr>
<tr><td>SDK 兼容</td><td>OpenAI Python / Node.js SDK(修改 base_url 即可)</td></tr>
<tr><td>文档日期</td><td>2026-07-15</td></tr>
<tr><td>更新时间</td><td>2026-06-29(官方)</td></tr>
</table>
</div>
<div class="info-box" style="margin-top:18px;">
<strong>⚠️ 版本提醒:</strong>MiMo-V2 系列模型已于 <strong>2026.6.30 00:00</strong> 正式下线,原模型名称已失效。请使用 V2.5 系列模型。
</div>
<h3>核心能力一览</h3>
<div class="feature-grid">
<div class="feature-card"><div class="icon">💬</div><h4>多轮对话</h4><p>支持 system/user/assistant/developer/tool 多角色消息</p></div>
<div class="feature-card"><div class="icon">🧠</div><h4>深度思考</h4><p>思维链推理,返回 reasoning_content</p></div>
<div class="feature-card"><div class="icon">🔧</div><h4>工具调用</h4><p>Function Calling + Web Search 工具</p></div>
<div class="feature-card"><div class="icon">🌐</div><h4>联网搜索</h4><p>自动联网检索并返回引用注释</p></div>
<div class="feature-card"><div class="icon">🖼️</div><h4>多模态输入</h4><p>支持图像、音频、视频输入</p></div>
<div class="feature-card"><div class="icon">🎙️</div><h4>语音合成</h4><p>TTS / Voice Design / Voice Clone</p></div>
<div class="feature-card"><div class="icon">📡</div><h4>SSE 流式</h4><p>流式输出,降低首字延迟</p></div>
<div class="feature-card"><div class="icon">📊</div><h4>用量明细</h4><td>缓存命中/推理 token 等详细统计</td></div>
</div>
</section>
<!-- ===== QUICK START ===== -->
<section id="quickstart">
<h2>⚡ 快速开始</h2>
<p>获取 API Key 后即可开始调用。支持 OpenAI SDK 兼容格式,也可直接使用 HTTP 请求。</p>
<div class="tabs">
<button class="tab-btn active" onclick="switchTab(event,'tab-q-curl')">curl</button>
<button class="tab-btn" onclick="switchTab(event,'tab-q-python')">Python (OpenAI SDK)</button>
</div>
<div id="tab-q-curl" class="tab-content active">
<pre><span class="code-lang">bash</span>curl --location --request POST 'https://api.xiaomimimo.com/v1/chat/completions' \
--header "api-key: $MIMO_API_KEY" \
--header "Content-Type: application/json" \
--data-raw '{
"model": "mimo-v2.5-pro",
"messages": [
{
"role": "system",
"content": "You are MiMo, an AI assistant developed by Xiaomi."
},
{
"role": "user",
"content": "你好,请介绍一下自己"
}
],
"max_completion_tokens": 1024,
"temperature": 1.0,
"top_p": 0.95,
"stream": false,
"thinking": {
"type": "disabled"
}
}'</pre>
</div>
<div id="tab-q-python" class="tab-content">
<pre><span class="code-lang">python</span>from openai import OpenAI
client = OpenAI(
api_key="$MIMO_API_KEY",
base_url="https://api.xiaomimimo.com/v1"
)
response = client.chat.completions.create(
model="mimo-v2.5-pro",
messages=[
{"role": "system", "content": "You are MiMo, an AI assistant developed by Xiaomi."},
{"role": "user", "content": "你好,请介绍一下自己"}
],
max_completion_tokens=1024,
temperature=1.0,
thinking={"type": "disabled"}
)
print(response.choices[0].message.content)</pre>
</div>
</section>
<!-- ===== MODELS ===== -->
<section id="models">
<h2>💰 可用模型</h2>
<table>
<tr><th>模型 ID</th><th>类型</th><th>默认 max_tokens</th><th>深度思考</th><th>工具调用</th><th>语音合成</th><th>联网搜索</th></tr>
<tr><td><code>mimo-v2.5-pro</code></td><td>旗舰文本</td><td>131072</td><td>✅ 支持</td><td>✅ 支持</td><td></td><td>✅ 支持</td></tr>
<tr><td><code>mimo-v2.5</code></td><td>标准文本</td><td>32768</td><td>✅ 支持</td><td>✅ 支持</td><td></td><td>✅ 支持</td></tr>
<tr><td><code>mimo-v2.5-tts</code></td><td>语音合成</td><td>8192</td><td></td><td></td><td>✅ 预置音色</td><td></td></tr>
<tr><td><code>mimo-v2.5-tts-voicedesign</code></td><td>音色设计</td><td>8192</td><td></td><td></td><td>✅ 音色设计</td><td></td></tr>
<tr><td><code>mimo-v2.5-tts-voiceclone</code></td><td>声音克隆</td><td>8192</td><td></td><td></td><td>✅ 声音克隆</td><td></td></tr>
</table>
<div class="info-box">
<strong>💡 temperature 默认值:</strong>mimo-v2.5-pro / mimo-v2.5 默认 <code>1.0</code>TTS 系列默认 <code>0.6</code>
</div>
</section>
<!-- ===== AUTH ===== -->
<section id="request-auth">
<h2>🔐 认证方式</h2>
<p>接口支持以下两种认证方式,<strong>选择其中一种</strong>添加到请求头中:</p>
<h3>方式一:api-key 字段认证</h3>
<pre><span class="code-lang">http</span>api-key: $MIMO_API_KEY
Content-Type: application/json</pre>
<h3>方式二:Authorization Bearer 认证</h3>
<pre><span class="code-lang">http</span>Authorization: Bearer $MIMO_API_KEY
Content-Type: application/json</pre>
</section>
<!-- ===== REQUEST BODY ===== -->
<section id="request-body">
<h2>📥 请求参数详解</h2>
<div class="method-badge">
<span class="method-tag method-post">POST</span>
<span class="url-box">https://api.xiaomimimo.com/v1/chat/completions</span>
</div>
<h3>核心参数</h3>
<table>
<tr><th width="160">参数名</th><th width="90">类型</th><th width="70">必填</th><th>描述</th></tr>
<tr><td><code>model</code></td><td><span class="type-tag">string</span></td><td><span class="required-tag req-yes">必选</span></td><td>模型 ID。可选值:<code>mimo-v2.5-pro</code>, <code>mimo-v2.5</code>, <code>mimo-v2.5-tts</code>, <code>mimo-v2.5-tts-voicedesign</code>, <code>mimo-v2.5-tts-voiceclone</code></td></tr>
<tr><td><code>messages</code></td><td><span class="type-tag">array</span></td><td><span class="required-tag req-yes">必选</span></td><td>对话消息列表。支持 role: system / user / assistant / developer / tool</td></tr>
<tr><td><code>messages[].role</code></td><td><span class="type-tag">string</span></td><td><span class="required-tag req-yes">必选</span></td><td>角色:<code>system</code> / <code>user</code> / <code>assistant</code> / <code>developer</code> / <code>tool</code></td></tr>
<tr><td><code>messages[].content</code></td><td><span class="type-tag">string | array</span></td><td><span class="required-tag req-yes">必选</span></td><td>消息内容。支持纯文本或多模态 content parts 数组</td></tr>
<tr><td><code>messages[].name</code></td><td><span class="type-tag">string</span></td><td><span class="required-tag req-no">可选</span></td><td>参与者名称,用于区分相同角色的不同参与者</td></tr>
</table>
<h3>生成控制参数</h3>
<table>
<tr><th width="160">参数名</th><th width="90">类型</th><th width="70">必填</th><th>描述</th></tr>
<tr><td><code>max_completion_tokens</code></td><td><span class="type-tag">integer | null</span></td><td><span class="required-tag req-default">可选</span></td><td>生成 token 上限(含推理 token)。pro 默认 131072;标准版 32768TTS 系列 8192。范围 [1, 131072]</td></tr>
<tr><td><code>temperature</code></td><td><span class="type-tag">number</span></td><td><span class="required-tag req-default">可选</span></td><td>采样温度 [0, 1.5]。pro/标准默认 1.0TTS 默认 0.6。思考模式下不可自定义</td></tr>
<tr><td><code>top_p</code></td><td><span class="type-tag">number</span></td><td><span class="required-tag req-default">可选</span></td><td>核采样概率 [0.01, 1.0],默认 0.95。建议仅与 temperature 二选一。思考模式下不可自定义</td></tr>
<tr><td><code>frequency_penalty</code></td><td><span class="type-tag">number | null</span></td><td><span class="required-tag req-default">可选</span></td><td>频率惩罚 [-2.0, 2.0],默认 0</td></tr>
<tr><td><code>presence_penalty</code></td><td><span class="type-tag">number | null</span></td><td><span class="required-tag req-default">可选</span></td><td>存在惩罚 [-2.0, 2.0],默认 0</td></tr>
<tr><td><code>stop</code></td><td><span class="type-tag">string | array | null</span></td><td><span class="required-tag req-default">可选</span></td><td>停止序列(最多 4 个)。TTS 系列不支持</td></tr>
<tr><td><code>stream</code></td><td><span class="type-tag">boolean | null</span></td><td><span class="required-tag req-default">可选</span></td><td>是否 SSE 流式传输,默认 false</td></tr>
<tr><td><code>response_format</code></td><td><span class="type-tag">object</span></td><td><span class="required-tag req-default">可选</span></td><td>指定输出格式。TTS 系列不支持</td></tr>
</table>
<h3>深度思考参数</h3>
<table>
<tr><th width="160">参数名</th><th width="90">类型</th><th width="70">必填</th><th>描述</th></tr>
<tr><td><code>thinking</code></td><td><span class="type-tag">object</span></td><td><span class="required-tag req-default">可选</span></td><td>思维链控制。TTS 系列不支持</td></tr>
<tr><td><code>thinking.type</code></td><td><span class="type-tag">string</span></td><td><span class="required-tag req-default">可选</span></td><td><code>"enabled"</code>(默认)或 <code>"disabled"</code></td></tr>
</table>
<div class="warn-box">
<strong>⚠️ 思考模式限制:</strong>在思考模式下,mimo-v2.5-pro / mimo-v2.5 不支持自定义 temperature 和 top_p,强制使用推荐默认值 1.0 和 0.95。多轮工具调用中建议保留历史 reasoning_content。
</div>
<h3>工具调用参数</h3>
<table>
<tr><th width="160">参数名</th><th width="90">类型</th><th width="70">必填</th><th>描述</th></tr>
<tr><td><code>tools</code></td><td><span class="type-tag">array</span></td><td><span class="required-tag req-default">可选</span></td><td>工具列表。支持 function 和 web_search 两种类型。TTS 系列不支持</td></tr>
<tr><td><code>tools[].type</code></td><td><span class="type-tag">string</span></td><td><span class="required-tag req-yes">必选</span></td><td>工具类型:<code>"function"</code><code>"web_search"</code></td></tr>
<tr><td><code>tools[].function.name</code></td><td><span class="type-tag">string</span></td><td><span class="required-tag req-yes">必选</span></td><td>函数名(a-z, A-Z, 0-9, _, -),最大 64 字符</td></tr>
<tr><td><code>tools[].function.description</code></td><td><span class="type-tag">string</span></td><td><span class="required-tag req-default">可选</span></td><td>功能描述</td></tr>
<tr><td><code>tools[].function.parameters</code></td><td><span class="type-tag">object</span></td><td><span class="required-tag req-default">可选</span></td><td>JSON Schema 格式的参数定义</td></tr>
<tr><td><code>tools[].function.strict</code></td><td><span class="type-tag">boolean</span></td><td><span class="required-tag req-default">可选</span></td><td>是否严格遵循 schema,默认 false</td></tr>
<tr><td><code>tool_choice</code></td><td><span class="type-tag">string</span></td><td><span class="required-tag req-default">可选</span></td><td>仅支持 <code>"auto"</code>。传入其他值会被后端移除。TTS 系列不支持</td></tr>
</table>
<h3>语音合成参数 (audio)</h3>
<table>
<tr><th width="160">参数名</th><th width="90">类型</th><th width="70">必填</th><th>描述</th></tr>
<tr><td><code>audio</code></td><td><span class="type-tag">object</span></td><td><span class="required-tag req-default">可选</span></td><td>音频输出参数。仅 TTS 系列模型支持</td></tr>
<tr><td><code>audio.format</code></td><td><span class="type-tag">string</span></td><td><span class="required-tag req-default">可选</span></td><td>输出格式:<code>wav</code>(默认) / <code>mp3</code> / <code>pcm</code> / <code>pcm16</code>。stream:true 时默认 pcm</td></tr>
<tr><td><code>audio.voice</code></td><td><span class="type-tag">string</span></td><td><span class="required-tag req-default">可选</span></td><td>预置音色 ID 或 base64 音频样本。TTS 预置:<code>mimo_default</code>, <code>冰糖</code>, <code>茉莉</code>, <code>苏打</code>, <code>白桦</code>, <code>Mia</code>, <code>Chloe</code>, <code>Milo</code>, <code>Dean</code></td></tr>
<tr><td><code>audio.optimize_text_preview</code></td><td><span class="type-tag">boolean</span></td><td><span class="required-tag req-default">可选</span></td><td>智能润色播报文本,默认 false。仅 voicedesign 模型支持</td></tr>
</table>
<div class="tip-box">
<strong>💡 TTS 提示:</strong>要生成音频时,必须添加一条 <code>role: "assistant"</code> 的消息指定合成文本。使用 voicedesign + optimize_text_preview=true 时可省略 assistant 消息。
</div>
</section>
<!-- ===== RESPONSE ===== -->
<section id="response">
<h2>📤 响应对象(非流式输出)</h2>
<p><code>stream: false</code> 时,API 返回完整的 <code>chat.completion</code> 对象。</p>
<h3>顶层字段</h3>
<table>
<tr><th width="180">字段名</th><th width="90">类型</th><th>描述</th></tr>
<tr><td><code>id</code></td><td><span class="type-tag">string</span></td><td>响应的唯一标识符</td></tr>
<tr><td><code>object</code></td><td><span class="type-tag">string</span></td><td>固定值 <code>"chat.completion"</code></td></tr>
<tr><td><code>created</code></td><td><span class="type-tag">integer</span></td><td>Unix 时间戳(秒)</td></tr>
<tr><td><code>model</code></td><td><span class="type-tag">string</span></td><td>实际使用的模型 ID</td></tr>
</table>
<h3>choices[] 字段</h3>
<table>
<tr><th width="200">字段名</th><th width="80">类型</th><th>描述</th></tr>
<tr><td><code>choices[].index</code></td><td><span class="type-tag">integer</span></td><td>选项索引</td></tr>
<tr><td><code>choices[].finish_reason</code></td><td><span class="type-tag">string</span></td><td>停止原因:<code>stop</code> / <code>length</code> / <code>tool_calls</code> / <code>content_filter</code> / <code>repetition_truncation</code></td></tr>
<tr><td><code>choices[].message.content</code></td><td><span class="type-tag">string</span></td><td>回复内容</td></tr>
<tr><td><code>choices[].message.reasoning_content</code></td><td><span class="type-tag">string</span></td><td>思维链推理内容(思考模式)</td></tr>
<tr><td><code>choices[].message.role</code></td><td><span class="type-tag">string</span></td><td>固定为 <code>"assistant"</code></td></tr>
<tr><td><code>choices[].message.tool_calls</code></td><td><span class="type-tag">array</span></td><td>工具调用列表(如有)</td></tr>
<tr><td><code>choices[].message.tool_calls[].id</code></td><td><span class="type-tag">string</span></td><td>工具调用 ID</td></tr>
<tr><td><code>choices[].message.tool_calls[].type</code></td><td><span class="type-tag">string</span></td><td>固定为 <code>"function"</code></td></tr>
<tr><td><code>choices[].message.tool_calls[].function.name</code></td><td><span class="type-tag">string</span></td><td>被调用的函数名</td></tr>
<tr><td><code>choices[].message.tool_calls[].function.arguments</code></td><td><span class="type-tag">string</span></td><td>JSON 格式的调用参数</td></tr>
<tr><td><code>choices[].message.annotations</code></td><td><span class="type-tag">array</span></td><td>联网搜索引用注释(如有)</td></tr>
<tr><td><code>choices[].message.audio</code></td><td><span class="type-tag">object</span></td><td>音频响应数据(TTS 请求时)</td></tr>
<tr><td><code>choices[].message.audio.id</code></td><td><span class="type-tag">string</span></td><td>音频唯一标识</td></tr>
<tr><td><code>choices[].message.audio.data</code></td><td><span class="type-tag">string</span></td><td>Base64 编码的音频数据</td></tr>
<tr><td><code>choices[].final_text_preview</code></td><td><span class="type-tag">string</span></td><td>优化后的播报文本(optimize_text_preview 时返回)</td></tr>
</table>
<h3>usage 用量信息</h3>
<table>
<tr><th width="220">字段名</th><th width="80">类型</th><th>描述</th></tr>
<tr><td><code>usage.prompt_tokens</code></td><td><span class="type-tag">integer</span></td><td>提示词 token 数</td></tr>
<tr><td><code>usage.completion_tokens</code></td><td><span class="type-tag">integer</span></td><td>输出 token 数</td></tr>
<tr><td><code>usage.total_tokens</code></td><td><span class="type-tag">integer</span></td><td>总 token 数</td></tr>
<tr><td><code>usage.completion_tokens_details.reasoning_tokens</code></td><td><span class="type-tag">integer</span></td><td>推理 token 数</td></tr>
<tr><td><code>usage.prompt_tokens_details.cached_tokens</code></td><td><span class="type-tag">integer</span></td><td>缓存命中的 token 数</td></tr>
<tr><td><code>usage.prompt_tokens_details.audio_tokens</code></td><td><span class="type-tag">integer</span></td><td>音频输入 token 数</td></tr>
<tr><td><code>usage.prompt_tokens_details.image_tokens</code></td><td><span class="type-tag">integer</span></td><td>图像输入 token 数</td></tr>
<tr><td><code>usage.prompt_tokens_details.video_tokens</code></td><td><span class="type-tag">integer</span></td><td>视频输入 token 数</td></tr>
<tr><td><code>usage.web_search_usage.tool_usage</code></td><td><span class="type-tag">integer</span></td><td>联网搜索 API 调用次数</td></tr>
<tr><td><code>usage.web_search_usage.page_usage</code></td><td><span class="type-tag">integer</span></td><td>联网搜索返回网页数</td></tr>
</table>
<h3>响应示例</h3>
<pre><span class="code-lang">json</span>{
"id": "8b51f9e0515949cb8207fbd35ea6ea5c",
"object": "chat.completion",
"created": 1776848906,
"model": "mimo-v2.5-pro",
"choices": [
{
"finish_reason": "stop",
"index": 0,
"message": {
"content": "Hello! I'm MiMo, Xiaomi's AI assistant created by the Xiaomi LLM-Core team...",
"role": "assistant",
"tool_calls": null
}
}
],
"usage": {
"completion_tokens": 72,
"prompt_tokens": 57,
"total_tokens": 129,
"completion_tokens_details": {
"reasoning_tokens": 0
},
"prompt_tokens_details": null
}
}</pre>
</section>
<!-- ===== STREAM RESPONSE ===== -->
<section id="response-stream">
<h2>📤 响应 Chunk 对象(流式输出)</h2>
<p><code>stream: true</code> 时,API 通过 SSE 以 <code>chat.completion.chunk</code> 格式增量返回数据。</p>
<div class="info-box">
<strong>SSE 格式说明:</strong>每个 chunk 以 <code>data: {...}</code> 行发送,结束标记为 <code>data: [DONE]</code>
</div>
<h3>Chunk 特有字段(vs 非流式差异)</h3>
<table>
<tr><th width="220">字段名</th><th width="80">类型</th><th>描述</th></tr>
<tr><td><code>object</code></td><td><span class="type-tag">string</span></td><td>固定值 <code>"chat.completion.chunk"</code></td></tr>
<tr><td><code>choices[].delta</code></td><td><span class="type-tag">object</span></td><td>增量数据(替代 message</td></tr>
<tr><td><code>choices[].delta.content</code></td><td><span class="type-tag">string</span></td><td>本 chunk 的文本增量</td></tr>
<tr><td><code>choices[].delta.reasoning_content</code></td><td><span class="type-tag">string</span></td><td>本 chunk 的推理增量</td></tr>
<tr><td><code>choices[].delta.role</code></td><td><span class="type-tag">string</span></td><td>首个 chunk 中的角色(通常为 "assistant"</td></tr>
<tr><td><code>choices[].delta.tool_calls</code></td><td><span class="type-tag">array</span></td><td>增量工具调用(含 index 定位)</td></tr>
<tr><td><code>choices[].delta.tool_calls[].index</code></td><td><span class="type-tag">integer</span></td><td>工具调用在列表中的索引(从 0 开始)</td></tr>
<tr><td><code>choices[].delta.audio</code></td><td><span class="type-tag">object | null</span></td><td>音频增量数据</td></tr>
<tr><td><code>choices[].finish_reason</code></td><td><span class="type-tag">string | null</span></td><td>最后一个 chunk 的停止原因</td></tr>
</table>
<p>其余字段(id, created, model, usage, annotations 等)与非流式相同,通常仅在最后一个 chunk 中返回 usage。</p>
</section>
<!-- ===== THINKING ===== -->
<section id="thinking">
<h2>🧠 深度思考模式</h2>
<p>MiMo V2.5 Pro 和标准版支持思维链(Chain-of-Thought)推理,让模型在回答前进行深度推理。适用于数学、逻辑、编程、复杂分析等场景。</p>
<h3>核心参数</h3>
<table>
<tr><th width="160">参数名</th><th width="90">类型</th><th>描述</th></tr>
<tr><td><code>thinking.type</code></td><td><span class="type-tag">string</span></td><td><code>"enabled"</code>(默认)启用 / <code>"disabled"</code> 关闭</td></tr>
</table>
<h3>行为说明</h3>
<ul style="padding-left:20px;margin:12px 0;font-size:0.93rem;">
<li>启用后,响应中 <code>reasoning_content</code> 字段包含模型的推理过程,<code>content</code> 为最终回答</li>
<li>思考模式下 <code>temperature</code> 强制为 <code>1.0</code><code>top_p</code> 强制为 <code>0.95</code>,自定义无效</li>
<li>多轮工具调用中,模型同时返回 <code>tool_calls</code><code>reasoning_content</code></li>
<li><strong>建议</strong>:后续每次请求的 messages 中保留所有历史 reasoning_content,以获得最佳表现</li>
</ul>
<div class="warn-box">
<strong>不支持范围:</strong>mimo-v2.5-tts / mimo-v2.5-tts-voicedesign / mimo-v2.5-tts-voiceclone 不支持思考模式。
</div>
</section>
<!-- ===== TOOLS ===== -->
<section id="tools">
<h2>🔧 工具调用 (Function Calling)</h2>
<p>MiMo 支持 Function Calling,允许模型调用外部函数获取信息或执行操作。同时内置 <strong>Web Search</strong> 联网搜索工具。</p>
<h3>核心要点</h3>
<table>
<tr><th width="180">特性</th><th>描述</th></tr>
<tr><td>工具类型</td><td><code>function</code>(函数工具)+ <code>web_search</code>(联网搜索)</td></tr>
<tr><td>tool_choice</td><td>仅支持 <code>"auto"</code>。传入其他值会被后端移除</td></tr>
<tr><td>strict 模式</td><td>支持 <code>strict: true</code>,严格遵循 JSON Schema(子集)</td></tr>
<tr><td>思考模式兼容</td><td>V2.5 Pro/标准版支持思考模式下的工具调用</td></tr>
</table>
<h3>Function Tool 结构</h3>
<pre><span class="code-lang">json</span>{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的当前天气信息",
"parameters": {
"type": "object",
"properties": {
"location": { "type": "string", "description": "城市名称" }
},
"required": ["location"]
},
"strict": false
}
}</pre>
<div class="tip-box">
<strong>💡 多轮工具调用提示:</strong>思考模式下工具调用会同时返回 reasoning_content,务必在后续轮次中保留以维持上下文连贯性。
</div>
</section>
<!-- ===== WEB SEARCH ===== -->
<section id="websearch">
<h2>🌐 联网搜索</h2>
<p>MiMo 内置 Web Search 工具,模型可自动联网检索最新信息并在回答中引用来源。</p>
<h3>启用方式</h3>
<p><code>tools</code> 数组中添加 web search 类型的工具:</p>
<pre><span class="code-lang">json</span>{
"tools": [
{ "type": "web_search" }
]
}</pre>
<h3>返回的引用注释 (annotations)</h3>
<table>
<tr><th width="200">字段名</th><th width="80">类型</th><th>描述</th></tr>
<tr><td><code>annotations[].title</code></td><td><span class="type-tag">string</span></td><td>引用页面标题</td></tr>
<tr><td><code>annotations[].url</code></td><td><span class="type-tag">string</span></td><td>引用网址</td></tr>
<tr><td><code>annotations[].site_name</code></td><td><span class="type-tag">string</span></td><td>网站名称</td></tr>
<tr><td><code>annotations[].summary</code></td><td><span class="type-tag">string</span></td><td>内容摘要</td></tr>
<tr><td><code>annotations[].publish_time</code></td><td><span class="type-tag">string</span></td><td>发布时间</td></tr>
<tr><td><code>annotations[].logo_url</code></td><td><span class="type-tag">string</span></td><td>网站 Logo 地址</td></tr>
<tr><td><code>annotations[].type</code></td><td><span class="type-tag">string</span></td><td>类型</td></tr>
<tr><td><code>error_message</code></td><td><span class="type-tag">string</span></td><td>联网搜索错误信息(如有)</td></tr>
</table>
</section>
<!-- ===== TTS ===== -->
<section id="tts">
<h2>🎙️ 语音合成 (TTS)</h2>
<p>MiMo 提供三种 TTS 能力,通过不同的模型和 audio 参数组合实现。</p>
<h3>三种模式对比</h3>
<table>
<tr><th>能力</th><th>模型</th><th>audio.voice</th><th>特点</th></tr>
<tr><td>预置音色 TTS</td><td><code>mimo-v2.5-tts</code></td><td>可选,预置音色名</td><td>9 种预置音色,默认 mimo_default</td></tr>
<tr><td>音色设计</td><td><code>mimo-v2.5-tts-voicedesign</code></td><td>不支持</td><td>通过文字描述设计音色</td></tr>
<tr><td>声音克隆</td><td><code>mimo-v2.5-tts-voiceclone</code></td><td>必填,base64 音频</td><td>上传 3~10 秒音频样本克隆声音</td></tr>
</table>
<h3>预置音色列表 (mimo-v2.5-tts)</h3>
<table>
<tr><th>音色 ID</th><th>说明</th></tr>
<tr><td><code>mimo_default</code></td><td>默认音色</td></tr>
<tr><td><code>冰糖</code></td><td>甜美女声</td></tr>
<tr><td><code>茉莉</code></td><td>温柔女声</td></tr>
<tr><td><code>苏打</code></td><td>清爽男声</td></tr>
<tr><td><code>白桦</code></td><td>沉稳男声</td></tr>
<tr><td><code>Mia</code></td><td>英文女声</td></tr>
<tr><td><code>Chloe</code></td><td>英文女声</td></tr>
<tr><td><code>Milo</code></td><td>英文男声</td></tr>
<tr><td><code>Dean</code></td><td>英文男声</td></tr>
</table>
<h3>音频格式</h3>
<table>
<tr><th>format 值</th><th>说明</th></tr>
<tr><td><code>wav</code></td><td>WAV 格式(默认)</td></tr>
<tr><td><code>mp3</code></td><td>MP3 格式</td></tr>
<tr><td><code>pcm</code> / <code>pcm16</code></td><td>PCM16 格式(stream 模式下默认)</td></tr>
</table>
<div class="warn-box">
<strong>⚠️ TTS 限制:</strong>TTS 系列模型 max_completion_tokens 范围为 [1, 8192];不支持 thinking、tools、response_format、stop 参数。
</div>
</section>
<!-- ===== EXAMPLES ===== -->
<section id="examples">
<h2>💻 代码示例</h2>
<h3>基础调用(非流式)</h3>
<div class="tabs">
<button class="tab-btn active" onclick="switchTab(event,'tab-ex1-py')">Python SDK</button>
<button class="tab-btn" onclick="switchTab(event,'tab-ex1-curl')">curl</button>
</div>
<div id="tab-ex1-py" class="tab-content active">
<pre><span class="code-lang">python</span>from openai import OpenAI
client = OpenAI(api_key="$MIMO_API_KEY", base_url="https://api.xiaomimimo.com/v1")
response = client.chat.completions.create(
model="mimo-v2.5-pro",
messages=[
{"role": "system", "content": "You are MiMo, an AI assistant developed by Xiaomi."},
{"role": "user", "content": "你好,请介绍一下自己"}
],
max_completion_tokens=1024,
temperature=1.0,
thinking={"type": "disabled"}
)
print(response.choices[0].message.content)
print(f"输入: {response.usage.prompt_tokens}, 输出: {response.usage.completion_tokens}")</pre>
</div>
<div id="tab-ex1-curl" class="tab-content">
<pre><span class="code-lang">bash</span>curl --location --request POST 'https://api.xiaomimimo.com/v1/chat/completions' \
--header "api-key: $MIMO_API_KEY" \
--header "Content-Type: application/json" \
--data-raw '{"model":"mimo-v2.5-pro","messages":[{"role":"system","content":"You are MiMo."},{"role":"user","content":"你好"}],"max_completion_tokens":1024,"thinking":{"type":"disabled"}}'</pre>
</div>
<h3>流式响应</h3>
<div class="tabs">
<button class="tab-btn active" onclick="switchTab(event,'tab-ex2-py')">Python SDK</button>
<button class="tab-btn" onclick="switchTab(event,'tab-ex2-js')">JavaScript</button>
</div>
<div id="tab-ex2-py" class="tab-content active">
<pre><span class="code-lang">python</span>from openai import OpenAI
client = OpenAI(api_key="$MIMO_API_KEY", base_url="https://api.xiaomimimo.com/v1")
response = client.chat.completions.create(
model="mimo-v2.5-pro",
messages=[
{"role": "system", "content": "你是一个专业的编程助手。"},
{"role": "user", "content": "解释 JavaScript 的事件循环机制"}
],
stream=True
)
full_reply = ""
for chunk in response:
content = chunk.choices[0].delta.content
if content:
full_reply += content
print(content, end="", flush=True)
# 继续对话
messages.append({"role": "assistant", "content": full_reply})
messages.append({"role": "user", "content": "能给一个 async/await 的代码示例吗?"})</pre>
</div>
<div id="tab-ex2-js" class="tab-content">
<pre><span class="code-lang">javascript</span>const response = await fetch('https://api.xiaomimimo.com/v1/chat/completions', {
method: 'POST',
headers: {
'api-key': MIMO_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'mimo-v2.5-pro',
messages: [
{ role: 'system', content: '你是一个专业的编程助手。' },
{ role: 'user', content: '解释 JavaScript 的事件循环机制' }
],
stream: true
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop();
for (const line of lines) {
if (!line.startsWith('data: ') || line === 'data: [DONE]') continue;
const chunk = JSON.parse(line.slice(6));
const content = chunk.choices[0]?.delta?.content;
if (content) process.stdout.write(content);
}
}</pre>
</div>
<h3>深度思考模式</h3>
<pre><span class="code-lang">python</span>from openai import OpenAI
client = OpenAI(api_key="$MIMO_API_KEY", base_url="https://api.xiaomimimo.com/v1")
response = client.chat.completions.create(
model="mimo-v2.5-pro",
messages=[{"role": "user", "content": "9.11 和 9.8 哪个更大?"}],
extra_body={"thinking": {"type": "enabled"}}
)
msg = response.choices[0].message
print(f"[思考过程]\n{msg.reasoning_content}")
print(f"\n[最终回答]\n{msg.content}")</pre>
<h3>函数调用 (Function Calling)</h3>
<pre><span class="code-lang">python</span>from openai import OpenAI
client = OpenAI(api_key="$MIMO_API_KEY", base_url="https://api.xiaomimimo.com/v1")
response = client.chat.completions.create(
model="mimo-v2.5-pro",
messages=[{"role": "user", "content": "杭州今天天气怎么样?"}],
tools=[{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的当前天气信息",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "城市名称"}
},
"required": ["location"]
}
}
}]
)
tool_calls = response.choices[0].message.tool_calls
print("工具调用:", tool_calls)</pre>
<h3>联网搜索</h3>
<pre><span class="code-lang">python</span>from openai import OpenAI
client = OpenAI(api_key="$MIMO_API_KEY", base_url="https://api.xiaomimimo.com/v1")
response = client.chat.completions.create(
model="mimo-v2.5-pro",
messages=[{"role": "user", "content": "今天有什么科技新闻?"}],
tools=[{"type": "web_search"}]
)
msg = response.choices[0].message
print("回答:", msg.content)
if msg.annotations:
print("\n引用来源:")
for ann in msg.annotations:
print(f" - [{ann.title}]({ann.url})")</pre>
<h3>语音合成 (TTS)</h3>
<pre><span class="code-lang">python</span>from openai import OpenAI
import base64
client = OpenAI(api_key="$MIMO_API_KEY", base_url="https://api.xiaomimimo.com/v1")
response = client.chat.completions.create(
model="mimo-v2.5-tts",
messages=[
{"role": "user", "content": "用甜美的声音为大家念一首诗"},
{"role": "assistant", "content": "床前明月光,疑是地上霜。举头望明月,低头思故乡。"}
],
audio={
"format": "wav",
"voice": "茉莉"
}
)
audio_data = response.choices[0].message.audio.data
audio_bytes = base64.b64decode(audio_data)
with output("output.wav", "wb") as f:
f.write(audio_bytes)
print("音频已保存到 output.wav")</pre>
</section>
<!-- ===== ERRORS ===== -->
<section id="errors">
<h2>⚠️ 错误码说明</h2>
<table>
<tr><th width="120">错误码</th><th>HTTP 状态</th><th>描述</th></tr>
<tr><td><code>invalid_api_key</code></td><td>401</td><td>API Key 无效或未提供</td></tr>
<tr><td><code>rate_limit_exceeded</code></td><td>429</td><td>请求频率超限,请稍后重试</td></tr>
<tr><td><code>invalid_request</code></td><td>400</td><td>请求参数错误(如缺少必填字段、模型不存在等)</td></tr>
<tr><td><code>context_length_exceeded</code></td><td>400</td><td>输入内容超出模型上下文长度限制</td></tr>
<tr><td><code>content_filter</code></td><td>400</td><td>内容触发安全过滤策略</td></tr>
<tr><td><code>server_error</code></td><td>500</td><td>服务器内部错误</td></tr>
<tr><td><code>model_not_found</code></td><td>404</td><td>请求的模型不存在或已下线</td></tr>
</table>
<div class="info-box">
<strong>📌 finish_reason 含义参考:</strong><br>
<code>stop</code> — 自然结束 · <code>length</code> — 达到最大 token · <code>tool_calls</code> — 模型调用了工具<br>
<code>content_filter</code> — 内容被过滤 · <code>repetition_truncation</code> — 检测到复读截断
</div>
</section>
</div><!-- /.container -->
<!-- ===== FOOTER ===== -->
<footer>
<p>本文档基于 <a href="https://mimo.mi.com/docs/zh-CN/api/chat/openai-api" target="_blank">MiMo 官方 API 文档</a> 整理</p>
<p style="margin-top:8px;">支持模型:<strong>mimo-v2.5-pro · mimo-v2.5 · mimo-v2.5-tts · mimo-v2.5-tts-voicedesign · mimo-v2.5-tts-voiceclone</strong></p>
<p style="margin-top:8px;opacity:0.5;">Generated with ❤️ · 文档日期 2026-07-15 · 参考 DeepSeek API 文档风格</p>
</footer>
<script>
function switchTab(evt, tabId) {
const tabBtns = evt.target.parentElement.querySelectorAll('.tab-btn');
tabBtns.forEach(b => b.classList.remove('active'));
evt.target.classList.add('active');
const parent = evt.target.parentElement.parentElement;
parent.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
document.getElementById(tabId).classList.add('active');
}
</script>
</body>
</html>
+15 -9
View File
@@ -920,15 +920,21 @@ web_search(query) → [SearXNG模式] → JSON结果 → 相关性过滤
| 文件 | 说明 | | 文件 | 说明 |
|------|------| |------|------|
| `src/main/browser.ts` | 浏览器控制核心(9 个函数 | | `electron/harness/tools/built-in/browser.ts` | 浏览器控制核心(9 个 action 路由 |
| `src/main/tool-handlers-system.ts` | web_fetch + web_search + SearXNG(核心实现 | | `electron/harness/tools/built-in/browser-window-manager.ts` | 浏览器窗口状态管理 + 页面操作(单例 + 隔离会话 |
| `src/main/ipc.ts` | IPC 工具调度 | | `electron/harness/tools/built-in/web-search.ts` | web_search 工具(SearXNG / 内置四引擎双模式) |
| `src/main/main.ts` | 应用生命周期(browserClose on quit | | `electron/harness/tools/built-in/web-fetch.ts` | web_fetch 工具(三阶段回退抓取 |
| `src/renderer/components/searxng-modal.ts` | SearXNG 配置模态框 | | `electron/harness/tools/built-in/network-utils.ts` | 网络工具函数(LRU 缓存、UA 轮换、反爬请求头、拦截检测、HTML 转文本、SearXNG 认证) |
| `src/renderer/index.html` | SearXNG 模态框 HTML | | `electron/harness/tools/built-in/http-request.ts` | HTTP/REST API 请求工具 |
| `src/renderer/styles/style.css` | SearXNG 样式 | | `electron/harness/tools/built-in/file-guard.ts` | 文件保护(工作空间边界 + MEMORY.md 保护) |
| `src/renderer/services/tool-registry.ts` | 工具定义和参数声明 | | `electron/harness/tools/registry.ts` | 工具注册表 + PolicyEngine 策略引擎 + truncateResult |
| `src/renderer/services/agent-engine.ts` | 工具超时配置、并行/串行调度 | | `electron/harness/sandbox/permissions.ts` | PolicyEngine(三级权限 + 频率限制 + 通配符策略) |
| `electron/harness/agent-loop/engine.ts` | Agent Loop 引擎(ReAct 状态机 + 工具超时配置) |
| `electron/ipc/handlers.ts` | IPC 通道处理(50+ 通道,含 `searxng:testConnection` |
| `electron/main.ts` | 应用生命周期(启动流程 + browserClose on quit |
| `src/components/settings/SettingsModal.tsx` | 设置面板(含 SearXNG 配置 Tab |
| `src/stores/agent-store.ts` | Agent 状态管理(Zustand |
| `src/hooks/useAgentStream.ts` | 流式事件监听 Hook |
## 附录 C:第三方库速查表 ## 附录 C:第三方库速查表
@@ -480,7 +480,7 @@ MeToast.<span class="hl-fn">warning</span>(<span class="hl-str">'操作已取消
<tr><th>Tab</th><th>内容</th></tr> <tr><th>Tab</th><th>内容</th></tr>
<tr><td class="f1">LLM 配置</td><td class="f2">Provider 选择、API Key、模型名称、参数滑块(temperature/maxTokens/contextWindow</td></tr> <tr><td class="f1">LLM 配置</td><td class="f2">Provider 选择、API Key、模型名称、参数滑块(temperature/maxTokens/contextWindow</td></tr>
<tr><td class="f1">Agent 配置</td><td class="f2">最大迭代次数、超时、Thinking 开关、反思模式、压缩阈值</td></tr> <tr><td class="f1">Agent 配置</td><td class="f2">最大迭代次数、超时、Thinking 开关、反思模式、压缩阈值</td></tr>
<tr><td class="f1">工具管理</td><td class="f2">9 个基础工具开关、风险级别配置、路径白名单、命令黑名单</td></tr> <tr><td class="f1">工具管理</td><td class="f2">27 个内置工具开关、风险级别配置、路径白名单、命令黑名单</td></tr>
<tr><td class="f1">MCP 服务</td><td class="f2">Server 列表、添加/删除/启停、连接状态指示灯</td></tr> <tr><td class="f1">MCP 服务</td><td class="f2">Server 列表、添加/删除/启停、连接状态指示灯</td></tr>
<tr><td class="f1">外观</td><td class="f2">主题(light/dark/auto)、字体大小、消息密度、动画开关</td></tr> <tr><td class="f1">外观</td><td class="f2">主题(light/dark/auto)、字体大小、消息密度、动画开关</td></tr>
<tr><td class="f1">日志与数据</td><td class="f2">日志级别、数据库位置、数据导出/清理、使用统计</td></tr> <tr><td class="f1">日志与数据</td><td class="f2">日志级别、数据库位置、数据导出/清理、使用统计</td></tr>
@@ -718,7 +718,7 @@ MeToast.<span class="hl-fn">warning</span>(<span class="hl-str">'操作已取消
<div style="text-align:center;padding:40px 0 20px;color:var(--text-dim);font-size:13px;border-top:1px solid var(--border)"> <div style="text-align:center;padding:40px 0 20px;color:var(--text-dim);font-size:13px;border-top:1px solid var(--border)">
<p>&#x1F3A8; MetonaAI Desktop UI/UX 设计集成方案</p> <p>&#x1F3A8; MetonaAI Desktop UI/UX 设计集成方案</p>
<p>研究来源: <strong style="color:var(--accent)">Fuselab Creative · Ant Design X · Google A2UI · Hermes Agent · Siyu's Newsletter</strong></p> <p>研究来源: <strong style="color:var(--accent)">Fuselab Creative · Ant Design X · Google A2UI · Hermes Agent · Siyu's Newsletter</strong></p>
<p style="margin-top:6px">集成组件: <strong style="color:var(--purple)">MetonaToast v2.0.0</strong> · 版本 <strong style="color:var(--accent2)">v1.0.0</strong> · 2026-06-26</p> <p style="margin-top:6px">集成组件: <strong style="color:var(--purple)">MetonaToast v2.0.0</strong> · 版本 <strong style="color:var(--accent2)">v1.0.0</strong> · 2026-07-15</p>
</div> </div>
<!-- Back to Top Button --> <!-- Back to Top Button -->
@@ -280,7 +280,7 @@
<div class="hero-meta"> <div class="hero-meta">
<span><span class="dot dot-accent"></span> 版本: <strong>v1.0.0</strong></span> <span><span class="dot dot-accent"></span> 版本: <strong>v1.0.0</strong></span>
<span><span class="dot dot-green"></span> 适用范围: <strong>Electron 主进程 / IPC / 渲染进程</strong></span> <span><span class="dot dot-green"></span> 适用范围: <strong>Electron 主进程 / IPC / 渲染进程</strong></span>
<span><span class="dot dot-orange"></span> 更新日期: <strong>2026-06-26</strong></span> <span><span class="dot dot-orange"></span> 更新日期: <strong>2026-07-15</strong></span>
</div> </div>
<div class="note-box" style="margin-top:16px"> <div class="note-box" style="margin-top:16px">
<strong>&#x1F4CB; 文档层级:</strong>本文档是 <strong>类型系统与数据格式的权威定义</strong>,与《构建指南》第四章(ReAct)、第五章(Harness)对应。冲突时以本文档为准。 <strong>&#x1F4CB; 文档层级:</strong>本文档是 <strong>类型系统与数据格式的权威定义</strong>,与《构建指南》第四章(ReAct)、第五章(Harness)对应。冲突时以本文档为准。
@@ -309,11 +309,11 @@
<span class="hl-box">│ Metona IR Standard │</span> ← 项目内唯一标准 <span class="hl-box">│ Metona IR Standard │</span> ← 项目内唯一标准
<span class="hl-box">└────────────┬─────────────┘</span> <span class="hl-box">└────────────┬─────────────┘</span>
┌────────┼────────┬──────── ┌────────┼────────┐
│ │ │ │ │ │
<span class="hl-box">┌───▼──┐ ┌──▼───┐ ┌─▼───┐ ┌─▼─────┐</span> <span class="hl-box">┌───▼──┐ ┌──▼───┐ ┌─▼───┐</span>
<span class="hl-box">│DeepSeek│ │Agnes│ │Ollama│ │Anthropic│</span> ← Adapter 层 <span class="hl-box">│DeepSeek│ │Agnes│ │Ollama│</span> ← Adapter 层
<span class="hl-box">└───────┘ └─────┘ └──────┘ └────────┘</span> <span class="hl-box">└───────┘ └─────┘ └──────┘</span>
</pre> </pre>
</div> </div>
<p class="desc"> <p class="desc">
@@ -1000,13 +1000,13 @@
<td>Ollama 原生 JSON / NDJSON</td> <td>Ollama 原生 JSON / NDJSON</td>
</tr> </tr>
<tr> <tr>
<td class="field-name">AnthropicAdapter</td> <td class="field-name">AnthropicAdapter(未来计划,未实现)</td>
<td class="field-type">anthropic</td> <td class="field-type">anthropic</td>
<td>https://api.anthropic.com/v1/messages</td> <td>https://api.anthropic.com/v1/messages</td>
<td>Anthropic 原生 JSON</td> <td>Anthropic 原生 JSON</td>
</tr> </tr>
<tr> <tr>
<td class="field-name">OpenAIAdapter</td> <td class="field-name">OpenAIAdapter(未来计划,未实现)</td>
<td class="field-type">openai</td> <td class="field-type">openai</td>
<td>https://api.openai.com/v1/chat/completions</td> <td>https://api.openai.com/v1/chat/completions</td>
<td>OpenAI 原生 JSON</td> <td>OpenAI 原生 JSON</td>
@@ -1177,8 +1177,8 @@ electron/harness/adapters/
├── deepseek.adapter.ts ├── deepseek.adapter.ts
├── agnes-ai.adapter.ts ├── agnes-ai.adapter.ts
├── ollama.adapter.ts ├── ollama.adapter.ts
├── anthropic.adapter.ts # ├── anthropic.adapter.ts # 未来计划
└── openai.adapter.ts</pre> # └── openai.adapter.ts # 未来计划</pre>
</div> </div>
<h3>迁移检查清单</h3> <h3>迁移检查清单</h3>
@@ -1204,7 +1204,7 @@ electron/harness/adapters/
<!-- FOOTER --> <!-- FOOTER -->
<div style="text-align:center; padding: 40px 0 20px; color: var(--text-dim); font-size: 13px; border-top: 1px solid var(--border);"> <div style="text-align:center; padding: 40px 0 20px; color: var(--text-dim); font-size: 13px; border-top: 1px solid var(--border);">
<p>&#x1F4DC; Metona 内部 API 请求与响应标准 —— 项目端到端类型安全的基础</p> <p>&#x1F4DC; Metona 内部 API 请求与响应标准 —— 项目端到端类型安全的基础</p>
<p>版本: <strong style="color:var(--accent)">v1.0.0</strong> · 生效日期: <strong style="color:var(--accent)">2026-06-25</strong></p> <p>版本: <strong style="color:var(--accent)">v1.0.0</strong> · 生效日期: <strong style="color:var(--accent)">2026-07-15</strong></p>
<p style="margin-top:8px;">所属项目: Metona (AI Agent Desktop) · 技术栈: TypeScript + React + SQLite + Electron</p> <p style="margin-top:8px;">所属项目: Metona (AI Agent Desktop) · 技术栈: TypeScript + React + SQLite + Electron</p>
</div> </div>
@@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MetonaAI-Desktop 架构与交互设计文档 | v1.0</title> <title>MetonaAI-Desktop 架构与交互设计文档 | v1.1</title>
<style> <style>
:root { :root {
--bg: #0f1117; --bg: #0f1117;
@@ -141,7 +141,7 @@
<a href="#workspace">&#x1F4C1; 工作空间</a> <a href="#workspace">&#x1F4C1; 工作空间</a>
<a href="#db-config">&#x1F4BE; 数据库配置</a> <a href="#db-config">&#x1F4BE; 数据库配置</a>
<div class="sidebar-section">9 个基础工具</div> <div class="sidebar-section">27 个内置工具</div>
<a href="#tools-overview">&#x1F4CA; 工具总表</a> <a href="#tools-overview">&#x1F4CA; 工具总表</a>
<a href="#tool-filesystem">&#x1F4C4; 文件系统工具</a> <a href="#tool-filesystem">&#x1F4C4; 文件系统工具</a>
<a href="#tool-web">&#x1F310; 网络搜索与抓取</a> <a href="#tool-web">&#x1F310; 网络搜索与抓取</a>
@@ -165,14 +165,14 @@
<div class="hero"> <div class="hero">
<h1>MetonaAI-Desktop 架构与交互设计</h1> <h1>MetonaAI-Desktop 架构与交互设计</h1>
<p>基于「生产级通用 AI Agent 桌面应用构建指南」+「Metona 内部 IR 标准」,定义完整的系统架构、9 个基础工具、4 个用户级磁盘文件、工作空间机制、数据库配置规范及全链路可追踪日志体系。</p> <p>基于「生产级通用 AI Agent 桌面应用构建指南」+「Metona 内部 IR 标准」,定义完整的系统架构、27 个内置工具、4 个用户级磁盘文件、工作空间机制、数据库配置规范及全链路可追踪日志体系。</p>
<div class="hero-meta"> <div class="hero-meta">
<span><span class="dot dot-cyan"></span> 版本: <strong>v1.0.0</strong></span> <span><span class="dot dot-cyan"></span> 版本: <strong>v1.1.0</strong></span>
<span><span class="dot dot-green"></span> 技术栈: <strong>React + Material UI (MUI) + Electron + SQLite</strong></span> <span><span class="dot dot-green"></span> 技术栈: <strong>React + Material UI (MUI) + Electron + SQLite</strong></span>
<span><span class="dot dot-amber"></span> 日期: <strong>2026-06-26</strong></span> <span><span class="dot dot-amber"></span> 日期: <strong>2026-07-15</strong></span>
</div> </div>
<div class="note-box" style="margin-top:16px"> <div class="note-box" style="margin-top:16px">
<strong>&#x1F4CB; 文档层级:</strong>本文档是 <strong>工作空间、9 个基础工具、4 个磁盘文件、数据库配置的权威定义</strong>,与《构建指南》第三、五、六章对应。冲突时以本文档为准。 <strong>&#x1F4CB; 文档层级:</strong>本文档是 <strong>工作空间、27 个内置工具、4 个磁盘文件、数据库配置的权威定义</strong>,与《构建指南》第三、五、六章对应。冲突时以本文档为准。
</div> </div>
</div> </div>
@@ -184,7 +184,7 @@
<p class="desc"> <p class="desc">
MetonaAI-Desktop 是一个运行在用户本地桌面上的通用 AI Agent 应用。它以<strong>工作空间(Workspace</strong>为基本组织单元, MetonaAI-Desktop 是一个运行在用户本地桌面上的通用 AI Agent 应用。它以<strong>工作空间(Workspace</strong>为基本组织单元,
通过 <strong>4 个 Markdown 磁盘文件</strong> 定义 Agent 的灵魂、行为、记忆和用户画像, 通过 <strong>4 个 Markdown 磁盘文件</strong> 定义 Agent 的灵魂、行为、记忆和用户画像,
提供 <strong>9 个基础工具</strong> 赋予 Agent 操作文件系统、网络、记忆和命令行的能力。 提供 <strong>27 个内置工具</strong> 赋予 Agent 操作文件系统、网络、记忆和命令行的能力。
全链路操作<strong>透明可追踪</strong>,所有决策过程、工具调用、LLM 推理记录在本地 SQLite 日志中。 全链路操作<strong>透明可追踪</strong>,所有决策过程、工具调用、LLM 推理记录在本地 SQLite 日志中。
</p> </p>
@@ -203,11 +203,11 @@
<span class="hl-box">│ └────┬──────────────────────────────────────────────┘ │</span> <span class="hl-box">│ └────┬──────────────────────────────────────────────┘ │</span>
<span class="hl-box">│ │ │</span> <span class="hl-box">│ │ │</span>
<span class="hl-box">│ ┌────▼──────────────────────────────────────────────┐ │</span> <span class="hl-box">│ ┌────▼──────────────────────────────────────────────┐ │</span>
<span class="hl-box">│ │ 9 Base Tools (统一 IR) │ │</span> <span class="hl-box">│ │ 27 Base Tools (统一 IR) │ │</span>
<span class="hl-box">│ │ read_file | write_file | list_dir | search_files │ │</span> <span class="hl-box">│ │ filesystem(5) | editor | code_search | diff │ │</span>
<span class="hl-box">│ │ web_search | web_extract │ │</span> <span class="hl-box">│ │ web_search | web_fetch | web_browser | http │ │</span>
<span class="hl-box">│ │ memory_store | memory_search │ │</span> <span class="hl-box">│ │ memory(2) | run_command | delegate_task │ │</span>
<span class="hl-box">│ │ run_command │ │</span> <span class="hl-box">│ │ git(4) | dev_tools(3) | task_mgr | todo | think │ │</span>
<span class="hl-box">│ └────┬──────────────────────────────────────────────┘ │</span> <span class="hl-box">│ └────┬──────────────────────────────────────────────┘ │</span>
<span class="hl-box">│ │ │</span> <span class="hl-box">│ │ │</span>
<span class="hl-box">│ ┌────▼──────────────────────────────────────────────┐ │</span> <span class="hl-box">│ ┌────▼──────────────────────────────────────────────┐ │</span>
@@ -388,9 +388,9 @@
<hr class="section-divider"> <hr class="section-divider">
<!-- ====== 9 个基础工具 ====== --> <!-- ====== 27 个内置工具 ====== -->
<section class="api-section" id="tools-overview"> <section class="api-section" id="tools-overview">
<h2>&#x1F4CA; 9 个基础工具 — 总表</h2> <h2>&#x1F4CA; 27 个内置工具 — 总表</h2>
<p class="desc">所有工具使用 <strong>Metona IR 的 MetonaToolDef / MetonaToolCall / MetonaToolResult</strong> 结构。内置在 Tool Registry 中,Adaper 为 LLM 生成 JSON Schema 格式的描述。</p> <p class="desc">所有工具使用 <strong>Metona IR 的 MetonaToolDef / MetonaToolCall / MetonaToolResult</strong> 结构。内置在 Tool Registry 中,Adaper 为 LLM 生成 JSON Schema 格式的描述。</p>
<table class="spec"> <table class="spec">
@@ -400,16 +400,17 @@
<tr><td class="f-name">3</td><td class="f-name">list_directory</td><td class="f-type">filesystem</td><td class="f-risk risk-safe">SAFE</td><td></td><td>列出目录内容</td></tr> <tr><td class="f-name">3</td><td class="f-name">list_directory</td><td class="f-type">filesystem</td><td class="f-risk risk-safe">SAFE</td><td></td><td>列出目录内容</td></tr>
<tr><td class="f-name">4</td><td class="f-name">search_files</td><td class="f-type">filesystem</td><td class="f-risk risk-safe">SAFE</td><td></td><td>按模式搜索文件(名称/内容)</td></tr> <tr><td class="f-name">4</td><td class="f-name">search_files</td><td class="f-type">filesystem</td><td class="f-risk risk-safe">SAFE</td><td></td><td>按模式搜索文件(名称/内容)</td></tr>
<tr><td class="f-name">5</td><td class="f-name">web_search</td><td class="f-type">network</td><td class="f-risk risk-medium">LOW</td><td></td><td>网络搜索,返回结果列表</td></tr> <tr><td class="f-name">5</td><td class="f-name">web_search</td><td class="f-type">network</td><td class="f-risk risk-medium">LOW</td><td></td><td>网络搜索,返回结果列表</td></tr>
<tr><td class="f-name">6</td><td class="f-name">web_extract</td><td class="f-type">network</td><td class="f-risk risk-medium">LOW</td><td></td><td>抓取网页内容转 Markdown</td></tr> <tr><td class="f-name">6</td><td class="f-name">web_fetch</td><td class="f-type">network</td><td class="f-risk risk-medium">LOW</td><td></td><td>抓取网页内容转 Markdown</td></tr>
<tr><td class="f-name">7</td><td class="f-name">memory_store</td><td class="f-type">database</td><td class="f-risk risk-medium">MEDIUM</td><td></td><td>存储一条记忆到 SQLite</td></tr> <tr><td class="f-name">7</td><td class="f-name">memory_store</td><td class="f-type">database</td><td class="f-risk risk-medium">MEDIUM</td><td></td><td>存储一条记忆到 SQLite</td></tr>
<tr><td class="f-name">8</td><td class="f-name">memory_search</td><td class="f-type">database</td><td class="f-risk risk-safe">SAFE</td><td></td><td>检索记忆(关键词匹配)</td></tr> <tr><td class="f-name">8</td><td class="f-name">memory_search</td><td class="f-type">database</td><td class="f-risk risk-safe">SAFE</td><td></td><td>检索记忆(关键词匹配)</td></tr>
<tr><td class="f-name">9</td><td class="f-name">run_command</td><td class="f-type">code_execution</td><td class="f-risk risk-high">HIGH</td><td></td><td>执行 Shell 命令,沙箱限制</td></tr> <tr><td class="f-name">9</td><td class="f-name">run_command</td><td class="f-type">code_execution</td><td class="f-risk risk-high">HIGH</td><td></td><td>执行 Shell 命令,沙箱限制</td></tr>
<tr><td colspan="6" style="padding:14px;color:var(--text-dim);font-size:13px;">完整 27 个工具列表详见 README.md「内置工具」章节。新增工具涵盖:file_editor、code_search、diff_viewer、web_browser、http_request、git_status/git_diff/git_log/git_commit、lint_code、run_tests、project_info、delegate_task、task_manager、todo_write、think、view_image。</td></tr>
</table> </table>
</section> </section>
<!-- --- 文件系统工具 --- --> <!-- --- 文件系统工具 --- -->
<section class="api-section" id="tool-filesystem"> <section class="api-section" id="tool-filesystem">
<h2>&#x1F4C4; 类别一:文件系统工具(4个)</h2> <h2>&#x1F4C4; 类别一:文件系统工具(5个)</h2>
<h3>1. read_file</h3> <h3>1. read_file</h3>
<p class="desc">读取文件完整内容。支持行偏移和行数限制,自动检测二进制文件。文件超过 100K 字符时返回截断提示。</p> <p class="desc">读取文件完整内容。支持行偏移和行数限制,自动检测二进制文件。文件超过 100K 字符时返回截断提示。</p>
@@ -453,7 +454,7 @@
<!-- --- 网络工具 --- --> <!-- --- 网络工具 --- -->
<section class="api-section" id="tool-web"> <section class="api-section" id="tool-web">
<h2>&#x1F310; 类别二:网络搜索与抓取(2个)</h2> <h2>&#x1F310; 类别二:网络搜索与抓取(2个)(另有 18 个工具见 README.md</h2>
<h3>5. web_search</h3> <h3>5. web_search</h3>
<p class="desc">执行网络搜索,返回标题、摘要和 URL。支持搜索运算符(site:、filetype: 等)。</p> <p class="desc">执行网络搜索,返回标题、摘要和 URL。支持搜索运算符(site:、filetype: 等)。</p>
@@ -463,8 +464,8 @@
<tr><td class="f-name">limit</td><td class="f-type">number</td><td class="f-opt">可选</td><td>结果数(默认 5,最大 100</td></tr> <tr><td class="f-name">limit</td><td class="f-type">number</td><td class="f-opt">可选</td><td>结果数(默认 5,最大 100</td></tr>
</table> </table>
<h3>6. web_extract</h3> <h3>6. web_fetch</h3>
<p class="desc">抓取网页内容并转换为 Markdown。支持 HTML 页面和 PDF 链接。超过 5000 字符自动摘要。</p> <p class="desc">抓取网页内容,三阶段回退(HTTP + 反爬 + 浏览器渲染),10MB 限制</p>
<table class="spec"> <table class="spec">
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr> <tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
<tr><td class="f-name">urls</td><td class="f-type">string[]</td><td class="f-req">必填</td><td>待抓取的 URL 列表(最多 5 个)</td></tr> <tr><td class="f-name">urls</td><td class="f-type">string[]</td><td class="f-req">必填</td><td>待抓取的 URL 列表(最多 5 个)</td></tr>
@@ -473,7 +474,7 @@
<!-- --- 记忆工具 --- --> <!-- --- 记忆工具 --- -->
<section class="api-section" id="tool-memory"> <section class="api-section" id="tool-memory">
<h2>&#x1F9E0; 类别三:记忆工具(2个)</h2> <h2>&#x1F9E0; 类别三:记忆工具(2个)(另有 18 个工具见 README.md</h2>
<h3>7. memory_store</h3> <h3>7. memory_store</h3>
<p class="desc">将一条内容存入持久记忆。写入 SQLite,支持关键词检索。Agent 可在对话中保存重要信息。</p> <p class="desc">将一条内容存入持久记忆。写入 SQLite,支持关键词检索。Agent 可在对话中保存重要信息。</p>
@@ -499,7 +500,7 @@
<!-- --- 命令工具 --- --> <!-- --- 命令工具 --- -->
<section class="api-section" id="tool-command"> <section class="api-section" id="tool-command">
<h2>&#x2692;&#xFE0F; 类别四:命令工具(1个)</h2> <h2>&#x2692;&#xFE0F; 类别四:命令工具(1个)(另有 18 个工具见 README.md</h2>
<h3>9. run_command</h3> <h3>9. run_command</h3>
<p class="desc">在沙箱环境中执行 Shell 命令。命令在工作空间目录下运行,有超时限制和输出截断。高危命令需用户确认。</p> <p class="desc">在沙箱环境中执行 Shell 命令。命令在工作空间目录下运行,有超时限制和输出截断。高危命令需用户确认。</p>
@@ -962,7 +963,7 @@
<div style="text-align:center; padding: 40px 0 20px; color: var(--text-dim); font-size: 13px; border-top: 1px solid var(--border);"> <div style="text-align:center; padding: 40px 0 20px; color: var(--text-dim); font-size: 13px; border-top: 1px solid var(--border);">
<p>&#x1F3D7;&#xFE0F; MetonaAI-Desktop 架构与交互设计文档</p> <p>&#x1F3D7;&#xFE0F; MetonaAI-Desktop 架构与交互设计文档</p>
<p>基于: <strong style="color:var(--accent)">生产级通用 AI Agent 构建指南</strong> + <strong style="color:var(--accent)">Metona 内部 IR 标准</strong></p> <p>基于: <strong style="color:var(--accent)">生产级通用 AI Agent 构建指南</strong> + <strong style="color:var(--accent)">Metona 内部 IR 标准</strong></p>
<p style="margin-top:6px;">版本 <strong style="color:var(--accent)">v1.1.0</strong> · 2026-06-26</p> <p style="margin-top:6px;">版本 <strong style="color:var(--accent)">v1.1.0</strong> · 2026-07-15</p>
</div> </div>
<!-- Back to Top Button --> <!-- Back to Top Button -->
@@ -591,7 +591,7 @@
<div class="hero-meta"> <div class="hero-meta">
<span><span class="dot dot-blue"></span> 技术栈: <strong>TypeScript + React + SQLite + Electron</strong></span> <span><span class="dot dot-blue"></span> 技术栈: <strong>TypeScript + React + SQLite + Electron</strong></span>
<span><span class="dot dot-green"></span> 版本: <strong>v1.0.0</strong></span> <span><span class="dot dot-green"></span> 版本: <strong>v1.0.0</strong></span>
<span><span class="dot dot-amber"></span> 更新日期: <strong>2026-06-26</strong></span> <span><span class="dot dot-amber"></span> 更新日期: <strong>2026-07-15</strong></span>
</div> </div>
<div class="note-box" style="margin-top:16px"> <div class="note-box" style="margin-top:16px">
<strong>&#x1F4CB; 文档层级:</strong>本文档是 <strong>理论基础与全景概述</strong>。各子领域的权威定义见以下文档(冲突时以子文档为准):<br>• 类型系统与数据格式 → <strong>《Metona 内部 API 请求与响应标准》</strong><br>• 工作空间/工具/磁盘文件 → <strong>《MetonaAI-Desktop 架构与交互设计》</strong><br>• 用户界面与交互 → <strong>《MetonaAI-Desktop UI/UX 设计集成方案》</strong> <strong>&#x1F4CB; 文档层级:</strong>本文档是 <strong>理论基础与全景概述</strong>。各子领域的权威定义见以下文档(冲突时以子文档为准):<br>• 类型系统与数据格式 → <strong>《Metona 内部 API 请求与响应标准》</strong><br>• 工作空间/工具/磁盘文件 → <strong>《MetonaAI-Desktop 架构与交互设计》</strong><br>• 用户界面与交互 → <strong>《MetonaAI-Desktop UI/UX 设计集成方案》</strong>
@@ -601,7 +601,7 @@
<div class="content"> <div class="content">
<h1>生产级通用 AI Agent 智能体桌面应用:完整设计与构建指南</h1> <h1>生产级通用 AI Agent 智能体桌面应用:完整设计与构建指南</h1>
<blockquote> <blockquote>
<p><strong>技术栈</strong>TypeScript + React + SQLite + Electron<br><strong>版本</strong>v1.0.0 | <strong>更新日期</strong>2026-06-24<br><strong>定位</strong>:从架构设计到代码实现的全链路生产级工程指南</p> <p><strong>技术栈</strong>TypeScript + React + SQLite + Electron<br><strong>版本</strong>v1.0.0 | <strong>更新日期</strong>2026-07-15<br><strong>定位</strong>:从架构设计到代码实现的全链路生产级工程指南</p>
</blockquote> </blockquote>
<hr> <hr>
<h2>目录</h2> <h2>目录</h2>
@@ -1092,12 +1092,28 @@
│ │ ├── tools/ # 工具注册与管理 │ │ ├── tools/ # 工具注册与管理
│ │ │ ├── registry.ts # 工具注册表 │ │ │ ├── registry.ts # 工具注册表
│ │ │ ├── base-tool.ts # 工具基类/接口 │ │ │ ├── base-tool.ts # 工具基类/接口
│ │ │ └── built-in/ # 内置工具集 │ │ │ └── built-in/ # 内置工具集21 个文件,27 个工具)
│ │ │ ├── filesystem.ts # 文件系统工具 │ │ │ ├── filesystem.ts # 文件系统5 工具
│ │ │ ├── web-search.ts # 网络搜索工具 │ │ │ ├── file-editor.ts # 编辑器
│ │ │ ├── calculator.ts # 计算器工具 │ │ │ ├── code-search.ts # 代码搜索
│ │ │ ├── code-executor.ts # 代码执行工具 │ │ │ ├── diff-viewer.ts # 差异查看
│ │ │ ── index.ts │ │ │ ── web-search.ts # 网络搜索
│ │ │ ├── web-fetch.ts # 网页抓取
│ │ │ ├── browser.ts # 浏览器工具
│ │ │ ├── browser-window-manager.ts
│ │ │ ├── network.ts # 网络工具入口
│ │ │ ├── network-utils.ts # 网络工具函数
│ │ │ ├── http-request.ts # HTTP 请求
│ │ │ ├── memory.ts # 记忆工具
│ │ │ ├── command.ts # 命令执行
│ │ │ ├── git.ts # Git 工具(4 个)
│ │ │ ├── dev-tools.ts # 开发工具(3 个)
│ │ │ ├── task-manager.ts # 任务管理
│ │ │ ├── delegate-task.ts # 子任务委派
│ │ │ ├── todo.ts # TODO 工具
│ │ │ ├── think.ts # 思考工具
│ │ │ ├── view-image.ts # 图片查看
│ │ │ └── file-guard.ts # 文件保护
│ │ ├── prompts/ # Prompt 模板管理 │ │ ├── prompts/ # Prompt 模板管理
│ │ │ ├── system-prompt.ts # 系统 Prompt 构建 │ │ │ ├── system-prompt.ts # 系统 Prompt 构建
│ │ │ ├── templates/ # Prompt 模板文件 │ │ │ ├── templates/ # Prompt 模板文件
@@ -2661,6 +2677,7 @@ export class ToolRegistry {
} }
} }
</code></pre> </code></pre>
<p><strong>&#x1F527; 备注</strong>v0.3.3 当前已实现 <strong>27 个内置工具</strong>,完整列表详见 <code>README.md</code>「内置工具」章节。下方仅以文件系统工具为示例展示实现规范。</p>
<p><strong>内置工具示例——文件系统工具</strong></p> <p><strong>内置工具示例——文件系统工具</strong></p>
<pre><code class="language-typescript">// ====== electron/harness/tools/built-in/filesystem.ts ====== <pre><code class="language-typescript">// ====== electron/harness/tools/built-in/filesystem.ts ======
@@ -3298,6 +3315,7 @@ export interface ValidationIssue {
</tbody> </tbody>
</table> </table>
<h3>6.2 SQLite 存储方案设计</h3> <h3>6.2 SQLite 存储方案设计</h3>
<p><strong>&#x1F4C1; 实际表结构</strong>v0.3.3 实际共 <strong>9 张表</strong>,包含:<code>sessions</code><code>messages</code><code>app_config</code><code>audit_logs</code><code>mcp_servers</code><code>episodic_memories</code><code>semantic_memories</code><code>working_memories</code><code>tasks</code>(新增任务表,用于 Agent 子任务管理)。</p>
<pre><code class="language-sql">-- ====== database/schema.sql ====== <pre><code class="language-sql">-- ====== database/schema.sql ======
-- ============================================ -- ============================================
@@ -4345,6 +4363,7 @@ declare global {
} }
</code></pre> </code></pre>
<h3>8.3 IPC 通道设计规范</h3> <h3>8.3 IPC 通道设计规范</h3>
<p><strong>&#x1F517; 实际规模</strong>v0.3.3 实际已注册 <strong>50+ 个 IPC 通道</strong>,覆盖 agent、sessions、db、mcp、config、app、tasks 等多个 domain。下方代码为注册规范的代表性示例。</p>
<pre><code class="language-typescript">// ====== electron/ipc/index.ts ====== <pre><code class="language-typescript">// ====== electron/ipc/index.ts ======
/** /**
@@ -6548,7 +6567,7 @@ ENABLE_TELEMETRY=true
<!-- FOOTER --> <!-- FOOTER -->
<div style="text-align:center;padding:40px 0 20px;color:var(--text-dim);font-size:13px;border-top:1px solid var(--border)"> <div style="text-align:center;padding:40px 0 20px;color:var(--text-dim);font-size:13px;border-top:1px solid var(--border)">
<p>&#x1F916; 生产级通用 AI Agent 智能体桌面应用:完整设计与构建指南</p> <p>&#x1F916; 生产级通用 AI Agent 智能体桌面应用:完整设计与构建指南</p>
<p>版本: <strong style="color:var(--accent)">v1.0.0</strong> · 最后更新: <strong style="color:var(--accent)">2026-06-24</strong></p> <p>版本: <strong style="color:var(--accent)">v1.0.0</strong> · 最后更新: <strong style="color:var(--accent)">2026-07-15</strong></p>
<p style="margin-top:8px">作者: <strong style="color:var(--accent2)">AI Agent Engineering Team</strong> · 许可协议: <strong style="color:var(--green)">CC BY-SA 4.0</strong></p> <p style="margin-top:8px">作者: <strong style="color:var(--accent2)">AI Agent Engineering Team</strong> · 许可协议: <strong style="color:var(--green)">CC BY-SA 4.0</strong></p>
</div> </div>
+3 -1
View File
@@ -1,9 +1,10 @@
/** /**
* Provider Adapter 导出 * Provider Adapter 导出
* *
* 种 Provider 各自独立继承 BaseAdapter,无耦合关系: * 种 Provider 各自独立继承 BaseAdapter,无耦合关系:
* - DeepSeekAdapter — OpenAI 兼容 + DeepSeek 特有参数 * - DeepSeekAdapter — OpenAI 兼容 + DeepSeek 特有参数
* - AgnesAdapter — OpenAI 兼容 + Agnes 特有参数 * - AgnesAdapter — OpenAI 兼容 + Agnes 特有参数
* - MimoAdapter — OpenAI 兼容 + MiMo 特有参数
* - OllamaAdapter — Ollama 原生 API * - OllamaAdapter — Ollama 原生 API
* *
* 共享工具(仅供 OpenAI 兼容 Adapter 使用): * 共享工具(仅供 OpenAI 兼容 Adapter 使用):
@@ -14,4 +15,5 @@
export { BaseAdapter } from './base-adapter'; export { BaseAdapter } from './base-adapter';
export { DeepSeekAdapter } from './deepseek.adapter'; export { DeepSeekAdapter } from './deepseek.adapter';
export { AgnesAdapter } from './agnes-ai.adapter'; export { AgnesAdapter } from './agnes-ai.adapter';
export { MimoAdapter } from './mimo.adapter';
export { OllamaAdapter } from './ollama.adapter'; export { OllamaAdapter } from './ollama.adapter';
+204
View File
@@ -0,0 +1,204 @@
/**
* MiMo (Xiaomi) Provider Adapter
*
* 基于 OpenAI 兼容 API。支持 Tool Calling、Thinking 模式、流式输出。
* 模型: mimo-v2.5-pro131072 max_tokens/ mimo-v2.532768 max_tokens
*
* 独立继承 BaseAdapter,通过 shared/openai-format 和 shared/sse-stream 复用
* OpenAI 兼容格式构建和 SSE 流式解析逻辑。不与其他 Provider Adapter 耦合。
*
* 与 DeepSeek 适配器的关键差异:
* - 使用 max_completion_tokens(非 max_tokens
* - thinking 参数结构与 DeepSeek 一致(thinking.type: "enabled"/"disabled"
* - 不提供 /models 端点(listModels 回退到本地元数据)
* - 不提供 /user/balance 端点
* - tool_choice 仅支持 "auto"
*
* @see apis/mimo-api-docs-20260715.html
*/
import { BaseAdapter } from './base-adapter';
import type { MetonaRequest, MetonaResponse, MetonaStreamEvent } from '../types';
import { MetonaFinishReason } from '../types';
import type { MetonaModelInfo } from '../types/metona-adapter';
import { buildOpenAICompatibleMessages, buildOpenAICompatibleTools } from './shared/openai-format';
import { parseSSEStream, parseOpenAICompatibleResponse } from './shared/sse-stream';
export class MimoAdapter extends BaseAdapter {
override readonly providerId: string = 'mimo';
readonly supportedModels = ['mimo-v2.5-pro', 'mimo-v2.5'];
readonly supportsToolCalling = true;
readonly supportsThinking = true;
// MiMo 模型元信息
// mimo-v2.5-pro: 131072 max_completion_tokensmimo-v2.5: 32768
// 官方未公布上下文窗口大小,保守设为 131072(与 pro 的 max_output 一致)
private static readonly MODEL_INFO: Record<string, MetonaModelInfo> = {
'mimo-v2.5-pro': {
id: 'mimo-v2.5-pro',
name: 'MiMo V2.5 Pro',
contextWindow: 131_072,
maxOutputTokens: 131_072,
supportsToolCalling: true,
supportsThinking: true,
description: '小米 MiMo 旗舰模型,支持深度思考与工具调用',
},
'mimo-v2.5': {
id: 'mimo-v2.5',
name: 'MiMo V2.5',
contextWindow: 131_072,
maxOutputTokens: 32_768,
supportsToolCalling: true,
supportsThinking: true,
description: '小米 MiMo 标准模型,低延迟推理',
},
};
// ===== POST /chat/completions (非流式) =====
async send(request: MetonaRequest): Promise<MetonaResponse> {
const body = this.toNativeRequest(request, false);
const response = await fetch(`${this.config.baseURL}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.config.apiKey}`,
...this.config.headers,
},
body: JSON.stringify(body),
signal: this.getFetchSignal(this.config.timeoutMs ?? 120_000),
});
if (!response.ok) {
const errorBody = await response.text().catch(() => '');
throw new Error(`MiMo API error: ${response.status} ${response.statusText} - ${errorBody}`);
}
const data = await response.json() as Record<string, unknown>;
const parsed = parseOpenAICompatibleResponse(data, request.meta.requestId, this.providerId, this.config.defaultModel);
return {
meta: {
requestId: request.meta.requestId,
provider: this.providerId,
model: (data.model as string) ?? this.config.defaultModel,
latencyMs: 0,
timestamp: Date.now(),
},
content: parsed.content,
reasoningContent: parsed.reasoningContent,
toolCalls: parsed.toolCalls,
usage: parsed.usage,
finishReason: parsed.finishReason as MetonaFinishReason,
};
}
// ===== POST /chat/completions (流式) =====
async *sendStream(request: MetonaRequest): AsyncIterable<MetonaStreamEvent> {
const body = this.toNativeRequest(request, true);
const response = await fetch(`${this.config.baseURL}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.config.apiKey}`,
...this.config.headers,
},
body: JSON.stringify(body),
signal: this.getFetchSignal(this.config.timeoutMs ?? 300_000),
});
if (!response.ok || !response.body) {
throw new Error(`MiMo stream error: ${response.status}`);
}
yield* parseSSEStream(
response.body,
request.meta.requestId,
request.meta.sessionId,
request.meta.iteration,
);
}
// ===== 模型列表 =====
/**
* MiMo 官方未提供 /models 端点,直接返回本地元数据。
*/
override async listModels(): Promise<MetonaModelInfo[]> {
return this.supportedModels.map((id) => MimoAdapter.MODEL_INFO[id] ?? { id });
}
/**
* 获取上下文窗口大小
*
* v0.3.1: 优先使用配置注入的 contextWindow,回退到 MODEL_INFO 默认值。
* MiMo OpenAI 兼容 API 不支持 context_window 参数,此值仅用于
* Engine 压缩判断和前端 UI 显示。
*/
override getContextWindow(): number {
if (typeof this.config.contextWindow === 'number' && this.config.contextWindow > 0) {
return this.config.contextWindow;
}
const modelInfo = MimoAdapter.MODEL_INFO[this.config.defaultModel];
return modelInfo?.contextWindow ?? 131_072;
}
// ========== 私有方法 ==========
/**
* 构建 MiMo 原生请求体
*
* MiMo 特有参数:
* - thinking: { type: "enabled" / "disabled" } — 与 DeepSeek 一致
* - max_completion_tokens — 非 max_tokensMiMo 使用新字段名)
* - stream_options: { include_usage: true } — 流式返回 usage
* - tool_choice: "auto" — MiMo 仅支持 auto
*
* 思考模式下 temperature/top_p 会被 API 强制覆盖,因此不传这两个参数。
*/
private toNativeRequest(request: MetonaRequest, stream: boolean): Record<string, unknown> {
const messages = buildOpenAICompatibleMessages(request);
const tools = buildOpenAICompatibleTools(request.tools);
const body: Record<string, unknown> = {
model: this.config.defaultModel,
messages,
// MiMo 使用 max_completion_tokens(非 max_tokens
max_completion_tokens: request.params.maxTokens,
stream,
};
if (stream) {
body.stream_options = { include_usage: true };
}
if (tools) {
body.tools = tools;
// MiMo 仅支持 tool_choice: "auto"
body.tool_choice = 'auto';
}
// Thinking 模式(与 DeepSeek 参数结构一致)
// MiMo API 默认 thinking.type = "enabled",必须显式发送 disabled 才能关闭
if (request.params.thinkingEnabled === false) {
// 显式禁用思考:传 disabled + temperature/top_p(非思考模式下这两个参数有效)
body.thinking = { type: 'disabled' };
body.temperature = request.params.temperature;
body.top_p = request.params.topP;
} else {
// 启用思考(包括 undefined,因为 MiMo 默认 enabled
// 思考模式下 temperature/top_p 被 API 强制覆盖为 1.0/0.95,不传
body.thinking = { type: 'enabled' };
}
// 停止序列
if (request.params.stopSequences?.length) {
body.stop = request.params.stopSequences;
}
return body;
}
}
@@ -2,11 +2,12 @@
* OpenAI 兼容 API 格式构建工具 * OpenAI 兼容 API 格式构建工具
* *
* 将 MetonaRequest 转换为 OpenAI /chat/completions 兼容的原生请求格式。 * 将 MetonaRequest 转换为 OpenAI /chat/completions 兼容的原生请求格式。
* DeepSeekAgnes AI 共享此工具,各自 Adapter 只需处理 Provider 特有的差异参数。 * DeepSeekAgnes AI 和 MiMo 共享此工具,各自 Adapter 只需处理 Provider 特有的差异参数。
* *
* @see electron/harness/types/metona-request.ts — MetonaRequest 定义 * @see electron/harness/types/metona-request.ts — MetonaRequest 定义
* @see apis/deepseek-api-docs-20260518.html * @see apis/deepseek-api-docs-20260518.html
* @see apis/agnes-ai-api-docs-20260625.html * @see apis/agnes-ai-api-docs-20260625.html
* @see apis/mimo-api-docs-20260715.html
*/ */
import type { MetonaRequest, MetonaToolDef } from '../../types'; import type { MetonaRequest, MetonaToolDef } from '../../types';
+10 -3
View File
@@ -2,7 +2,7 @@
* SSE 流式解析工具 * SSE 流式解析工具
* *
* 解析 OpenAI 兼容的 Server-Sent Events (SSE) 流式响应, * 解析 OpenAI 兼容的 Server-Sent Events (SSE) 流式响应,
* 产出 MetonaStreamEvent。DeepSeekAgnes AI 共享此工具。 * 产出 MetonaStreamEvent。DeepSeekAgnes AI 和 MiMo 共享此工具。
* *
* SSE 格式:data: {json}\n\n * SSE 格式:data: {json}\n\n
* 结束标记:data: [DONE] * 结束标记:data: [DONE]
@@ -174,7 +174,10 @@ export async function* parseSSEStream(
outputTokens: chunk.usage.completion_tokens ?? 0, outputTokens: chunk.usage.completion_tokens ?? 0,
totalTokens: chunk.usage.total_tokens ?? 0, totalTokens: chunk.usage.total_tokens ?? 0,
reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens, reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens,
cacheHitTokens: chunk.usage.prompt_cache_hit_tokens, // DeepSeek: prompt_cache_hit_tokens / prompt_cache_miss_tokens
// MiMo: prompt_tokens_details.cached_tokens
cacheHitTokens: chunk.usage.prompt_cache_hit_tokens
?? chunk.usage.prompt_tokens_details?.cached_tokens,
cacheMissTokens: chunk.usage.prompt_cache_miss_tokens, cacheMissTokens: chunk.usage.prompt_cache_miss_tokens,
}; };
@@ -252,7 +255,9 @@ export function parseOpenAICompatibleResponse(
outputTokens: (usage?.completion_tokens as number) ?? 0, outputTokens: (usage?.completion_tokens as number) ?? 0,
totalTokens: (usage?.total_tokens as number) ?? 0, totalTokens: (usage?.total_tokens as number) ?? 0,
reasoningTokens: (usage?.completion_tokens_details as Record<string, unknown>)?.reasoning_tokens as number | undefined, reasoningTokens: (usage?.completion_tokens_details as Record<string, unknown>)?.reasoning_tokens as number | undefined,
cacheHitTokens: usage?.prompt_cache_hit_tokens as number | undefined, // DeepSeek: prompt_cache_hit_tokens / MiMo: prompt_tokens_details.cached_tokens
cacheHitTokens: (usage?.prompt_cache_hit_tokens as number | undefined)
?? (usage?.prompt_tokens_details as Record<string, unknown> | undefined)?.cached_tokens as number | undefined,
cacheMissTokens: usage?.prompt_cache_miss_tokens as number | undefined, cacheMissTokens: usage?.prompt_cache_miss_tokens as number | undefined,
}, },
}; };
@@ -264,6 +269,8 @@ function mapOpenAIFinishReason(reason: string): string {
case 'length': return 'length'; case 'length': return 'length';
case 'tool_calls': return 'tool_calls'; case 'tool_calls': return 'tool_calls';
case 'content_filter': return 'content_filter'; case 'content_filter': return 'content_filter';
// MiMo 特有:检测到复读截断
case 'repetition_truncation': return 'stop';
default: return 'stop'; default: return 'stop';
} }
} }
+1 -1
View File
@@ -38,7 +38,7 @@ export interface MetonaModelInfo {
* Provider 适配器配置 * Provider 适配器配置
*/ */
export interface AdapterConfig { export interface AdapterConfig {
/** Provider 标识(如 'deepseek'、'agnes'、'ollama' */ /** Provider 标识(如 'deepseek'、'agnes'、'mimo'、'ollama' */
provider: string; provider: string;
/** API 基础 URL */ /** API 基础 URL */
baseURL: string; baseURL: string;
+4 -4
View File
@@ -667,9 +667,9 @@ export function registerAllIPCHandlers(
}); });
// LLM 相关配置变更时热重载 Adapter // LLM 相关配置变更时热重载 Adapter
// v0.3.1: 加入 deepseek.contextWindow / agnes.contextWindow,使上下文窗口配置变化也触发热重载 // v0.3.1: 加入 deepseek.contextWindow / agnes.contextWindow / mimo.contextWindow,使上下文窗口配置变化也触发热重载
if (['llm.provider', 'llm.model', 'llm.apiKey', 'llm.baseURL', 'ollama.numCtx', if (['llm.provider', 'llm.model', 'llm.apiKey', 'llm.baseURL', 'ollama.numCtx',
'deepseek.contextWindow', 'agnes.contextWindow'].includes(key)) { 'deepseek.contextWindow', 'agnes.contextWindow', 'mimo.contextWindow'].includes(key)) {
// C-1 修复: reloadAdapter 返回 false 时表示加载失败,需要通知前端 // C-1 修复: reloadAdapter 返回 false 时表示加载失败,需要通知前端
const reloadSuccess = reloadAdapter(); const reloadSuccess = reloadAdapter();
if (!reloadSuccess) { if (!reloadSuccess) {
@@ -700,8 +700,8 @@ export function registerAllIPCHandlers(
agentLoop.updateConfig({ contextLength: (value as number) || undefined }); agentLoop.updateConfig({ contextLength: (value as number) || undefined });
orchestrator.updateDefaultConfig({ contextLength: (value as number) || undefined }); orchestrator.updateDefaultConfig({ contextLength: (value as number) || undefined });
log.info(`[CONFIG] Agent contextLength updated to ${value}`); log.info(`[CONFIG] Agent contextLength updated to ${value}`);
} else if (key === 'deepseek.contextWindow' || key === 'agnes.contextWindow') { } else if (key === 'deepseek.contextWindow' || key === 'agnes.contextWindow' || key === 'mimo.contextWindow') {
// v0.3.1: DeepSeek/Agnes contextWindow 变更,同步到 Engine 和 Orchestrator // v0.3.1: DeepSeek/Agnes/MiMo contextWindow 变更,同步到 Engine 和 Orchestrator
// reloadAdapter 已重建 adapter,此处确保 Engine contextWindow 同步(兜底) // reloadAdapter 已重建 adapter,此处确保 Engine contextWindow 同步(兜底)
const ctxWindow = (value as number) || undefined; const ctxWindow = (value as number) || undefined;
agentLoop.updateConfig({ contextWindow: ctxWindow }); agentLoop.updateConfig({ contextWindow: ctxWindow });
+2
View File
@@ -36,6 +36,7 @@ import { AgentLoopEngine } from './harness/agent-loop';
import { ToolRegistry } from './harness/tools/registry'; import { ToolRegistry } from './harness/tools/registry';
import { DeepSeekAdapter } from './harness/adapters/deepseek.adapter'; import { DeepSeekAdapter } from './harness/adapters/deepseek.adapter';
import { AgnesAdapter } from './harness/adapters/agnes-ai.adapter'; import { AgnesAdapter } from './harness/adapters/agnes-ai.adapter';
import { MimoAdapter } from './harness/adapters/mimo.adapter';
import { OllamaAdapter } from './harness/adapters/ollama.adapter'; import { OllamaAdapter } from './harness/adapters/ollama.adapter';
import { import {
ReadFileTool, WriteFileTool, ListDirectoryTool, SearchFilesTool, ReadFileTool, WriteFileTool, ListDirectoryTool, SearchFilesTool,
@@ -161,6 +162,7 @@ async function initialize(): Promise<void> {
const adapterConfig = { provider, baseURL, apiKey, defaultModel: model, contextWindow }; const adapterConfig = { provider, baseURL, apiKey, defaultModel: model, contextWindow };
switch (provider) { switch (provider) {
case 'agnes': return new AgnesAdapter(adapterConfig); case 'agnes': return new AgnesAdapter(adapterConfig);
case 'mimo': return new MimoAdapter(adapterConfig);
case 'ollama': return new OllamaAdapter(adapterConfig); case 'ollama': return new OllamaAdapter(adapterConfig);
default: return new DeepSeekAdapter(adapterConfig); default: return new DeepSeekAdapter(adapterConfig);
} }
+2
View File
@@ -378,6 +378,8 @@ export class DatabaseService {
// v0.3.1: DeepSeek/Agnes 改为可配置,不再写死 1M // v0.3.1: DeepSeek/Agnes 改为可配置,不再写死 1M
{ key: 'deepseek.contextWindow', value: '1000000', category: 'deepseek' }, { key: 'deepseek.contextWindow', value: '1000000', category: 'deepseek' },
{ key: 'agnes.contextWindow', value: '1000000', category: 'agnes' }, { key: 'agnes.contextWindow', value: '1000000', category: 'agnes' },
// v0.3.4: MiMo 上下文窗口(官方未公布,保守设为 131072)
{ key: 'mimo.contextWindow', value: '131072', category: 'mimo' },
// Onboarding // Onboarding
{ key: 'onboarding.completed', value: 'false', category: 'general' }, { key: 'onboarding.completed', value: 'false', category: 'general' },
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "metona-ai-desktop", "name": "metona-ai-desktop",
"version": "0.3.3", "version": "0.3.4",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "metona-ai-desktop", "name": "metona-ai-desktop",
"version": "0.3.3", "version": "0.3.4",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "metona-ai-desktop", "name": "metona-ai-desktop",
"version": "0.3.3", "version": "0.3.4",
"description": "MetonaAI Desktop — 生产级通用 AI Agent 智能体桌面应用", "description": "MetonaAI Desktop — 生产级通用 AI Agent 智能体桌面应用",
"main": "dist-electron/main/main.js", "main": "dist-electron/main/main.js",
"author": "Metona Team", "author": "Metona Team",
+1
View File
@@ -30,6 +30,7 @@ import { useAgentStore } from '@renderer/stores/agent-store';
const PROVIDER_LABELS: Record<string, string> = { const PROVIDER_LABELS: Record<string, string> = {
deepseek: 'DeepSeek', deepseek: 'DeepSeek',
agnes: 'Agnes', agnes: 'Agnes',
mimo: 'MiMo',
ollama: 'Ollama', ollama: 'Ollama',
}; };
@@ -76,6 +76,7 @@ export function OnboardingWizard(): React.JSX.Element | null {
<Select value={provider} label="Provider" onChange={(e) => setProvider(e.target.value)}> <Select value={provider} label="Provider" onChange={(e) => setProvider(e.target.value)}>
<MenuItem value="deepseek">DeepSeek</MenuItem> <MenuItem value="deepseek">DeepSeek</MenuItem>
<MenuItem value="agnes">Agnes AI</MenuItem> <MenuItem value="agnes">Agnes AI</MenuItem>
<MenuItem value="mimo">MiMo ()</MenuItem>
<MenuItem value="ollama">Ollama ()</MenuItem> <MenuItem value="ollama">Ollama ()</MenuItem>
</Select> </Select>
</FormControl> </FormControl>
+19 -3
View File
@@ -86,7 +86,7 @@ function useConfig<T>(key: string, defaultValue: T): [T, (v: T) => void] {
return [value, set]; return [value, set];
} }
const PROVIDER_URLS: Record<string, string> = { deepseek: 'https://api.deepseek.com', agnes: 'https://apihub.agnes-ai.com/v1', ollama: 'http://localhost:11434' }; const PROVIDER_URLS: Record<string, string> = { deepseek: 'https://api.deepseek.com', agnes: 'https://apihub.agnes-ai.com/v1', mimo: 'https://api.xiaomimimo.com/v1', ollama: 'http://localhost:11434' };
function WorkspaceSettings() { function WorkspaceSettings() {
const [workspacePath, setWorkspacePath] = useConfig('workspace.path', ''); const [workspacePath, setWorkspacePath] = useConfig('workspace.path', '');
@@ -290,9 +290,10 @@ function LLMSettings() {
const [apiKey, setApiKey] = useConfig('llm.apiKey', ''); const [apiKey, setApiKey] = useConfig('llm.apiKey', '');
const [baseURL, setBaseURL] = useConfig('llm.baseURL', ''); const [baseURL, setBaseURL] = useConfig('llm.baseURL', '');
const [numCtx, setNumCtx] = useConfig('ollama.numCtx', null as number | null); const [numCtx, setNumCtx] = useConfig('ollama.numCtx', null as number | null);
// v0.3.1: DeepSeek/Agnes contextWindow 可配置(不再写死 1M // v0.3.1: DeepSeek/Agnes/MiMo contextWindow 可配置(不再写死)
const [dsCtxWindow, setDsCtxWindow] = useConfig('deepseek.contextWindow', 1000000); const [dsCtxWindow, setDsCtxWindow] = useConfig('deepseek.contextWindow', 1000000);
const [agnesCtxWindow, setAgnesCtxWindow] = useConfig('agnes.contextWindow', 1000000); const [agnesCtxWindow, setAgnesCtxWindow] = useConfig('agnes.contextWindow', 1000000);
const [mimoCtxWindow, setMimoCtxWindow] = useConfig('mimo.contextWindow', 131072);
const [showKey, setShowKey] = useState(false); const [showKey, setShowKey] = useState(false);
// 同步 Provider/Model 到 Agent Store(含 contextWindow // 同步 Provider/Model 到 Agent Store(含 contextWindow
@@ -308,8 +309,10 @@ function LLMSettings() {
if (dsCtxWindow != null && dsCtxWindow > 0) useAgentStore.setState({ contextWindow: dsCtxWindow }); if (dsCtxWindow != null && dsCtxWindow > 0) useAgentStore.setState({ contextWindow: dsCtxWindow });
} else if (provider === 'agnes') { } else if (provider === 'agnes') {
if (agnesCtxWindow != null && agnesCtxWindow > 0) useAgentStore.setState({ contextWindow: agnesCtxWindow }); if (agnesCtxWindow != null && agnesCtxWindow > 0) useAgentStore.setState({ contextWindow: agnesCtxWindow });
} else if (provider === 'mimo') {
if (mimoCtxWindow != null && mimoCtxWindow > 0) useAgentStore.setState({ contextWindow: mimoCtxWindow });
} }
}, [provider, numCtx, dsCtxWindow, agnesCtxWindow]); }, [provider, numCtx, dsCtxWindow, agnesCtxWindow, mimoCtxWindow]);
// 切换 Provider 时自动填充默认 URL + 清空 apiKey(不同 Provider 的 key 不通用) // 切换 Provider 时自动填充默认 URL + 清空 apiKey(不同 Provider 的 key 不通用)
const handleProviderChange = (newProvider: string) => { const handleProviderChange = (newProvider: string) => {
@@ -335,6 +338,7 @@ function LLMSettings() {
<Select value={provider} label="Provider" onChange={(e) => handleProviderChange(e.target.value)}> <Select value={provider} label="Provider" onChange={(e) => handleProviderChange(e.target.value)}>
<MenuItem value="deepseek">DeepSeek</MenuItem> <MenuItem value="deepseek">DeepSeek</MenuItem>
<MenuItem value="agnes">Agnes AI</MenuItem> <MenuItem value="agnes">Agnes AI</MenuItem>
<MenuItem value="mimo">MiMo ()</MenuItem>
<MenuItem value="ollama">Ollama ()</MenuItem> <MenuItem value="ollama">Ollama ()</MenuItem>
</Select> </Select>
</FormControl> </FormControl>
@@ -391,6 +395,18 @@ function LLMSettings() {
helperText="用于上下文压缩判断,不传给 API" helperText="用于上下文压缩判断,不传给 API"
/> />
)} )}
{provider === 'mimo' && (
<TextField
size="small"
label="上下文窗口 (contextWindow)"
type="number"
value={mimoCtxWindow ?? 131072}
onChange={(e) => setMimoCtxWindow(Number(e.target.value) || 131072)}
placeholder="如 32768、65536、131072"
slotProps={{ htmlInput: { min: 4096, step: 4096 } }}
helperText="官方未公布具体值,默认 131072,用于压缩判断"
/>
)}
</Stack> </Stack>
); );
} }
+1
View File
@@ -90,5 +90,6 @@ export const SHORTCUTS = {
export const PROVIDER_LABELS: Record<string, string> = { export const PROVIDER_LABELS: Record<string, string> = {
deepseek: 'DeepSeek', deepseek: 'DeepSeek',
agnes: 'Agnes AI', agnes: 'Agnes AI',
mimo: 'MiMo (小米)',
ollama: 'Ollama', ollama: 'Ollama',
}; };