> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mirobody.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 配置

> 通过 config.yml 配置 Mirobody：LLM 提供商、数据库、缓存、存储、鉴权、健康圈以及 MCP 暴露。

Mirobody 通过单个 **`config.yml`** 文件配置 —— 一组扁平的键，由 C++ 内核解析。把提交在仓库里的模板复制成 `config.yml` 再编辑；每个键都有可用的默认值，所以你只需设置真正要改的那些。

```bash theme={null}
cp config.example.yml config.yml
```

<Info>
  配置更改在重启后生效。`config.yml` 已被 git 忽略 —— 只提交 `config.example.yml`。
</Info>

## 解析顺序

配置按键从分层来源合并，高优先级覆盖低优先级：

| 优先级   | 来源                       | 说明                                                |
| ----- | ------------------------ | ------------------------------------------------- |
| 1（最高） | **环境变量**                 | 任何键都可被同名环境变量覆盖。                                   |
| 2     | **`config.yml`**         | 你的本地文件（已被 git 忽略）。                                |
| 3     | **远程配置**                 | 仅当 `CONFIG_SERVER`、`CONFIG_TOKEN`、`ENV` 三者都设置时拉取。 |
| 4（最低） | **`config.example.yml`** | 提交在仓库里的默认值模板。                                     |

### 加密的密钥

任何以 `gAAAA` 开头的字符串值都会被当作 Fernet token，在设置了 `CONFIG_ENCRYPTION_KEY` 时于加载时解密（`config.yml` 和远程拉取都适用）。反过来，**键名**包含 `_KEY`、`_PASSWORD`、`_PASS`、`_PWD`、`_SECRET`、`_SK`、`_TOKEN` 的值，会用同一个 `CONFIG_ENCRYPTION_KEY` 自动加密 —— 于是你填进去的明文密钥会被改写成静态的 `gAAAA…`。

```bash theme={null}
# 生成一个密钥
openssl rand -hex 32

# 经环境提供（不存进 config.yml）
export CONFIG_ENCRYPTION_KEY="<your-key>"
```

<Note>
  `PG_ENCRYPTION_KEY` 有意**不**自动加密（不做 `_KEY` 后缀处理）—— 它是用来加密数据列的密钥，因此必须保持可读，以便引导数据库启动。生产环境把它放在环境变量或密钥管理器里。
</Note>

## HTTP 服务器

```yaml config.yml theme={null}
HTTP_HOST: '0.0.0.0'      # 监听所有接口
HTTP_PORT: 8080           # 默认端口
# HTTP_URI_PREFIX: ''     # 把整个应用挂到子路径下，例如 '/mirobody'
HTTP_ROOT: 'res/htdoc'    # 为非 API 的 GET 提供的静态 Web 客户端

# CORS（取消注释以为浏览器客户端启用）
# HTTP_HEADERS:
#   Access-Control-Allow-Origin: '*'
#   Access-Control-Allow-Methods: 'GET, POST, PUT, DELETE, OPTIONS'
#   Access-Control-Allow-Headers: 'Authorization, Content-Type, X-Timezone'
#   Access-Control-Max-Age: '86400'
```

<Tip>
  鉴权用的是 `Authorization` 头里的 bearer token（不是 cookie），所以本地使用时 `Access-Control-Allow-Origin: '*'` 没问题。不要把 `'*'` 和 `Access-Control-Allow-Credentials: 'true'` 一起用 —— 浏览器会拒绝；需要携带凭据时改用固定 origin。生产环境把 origin 限制到你的域名。
</Tip>

## 日志

```yaml config.yml theme={null}
LOG_LEVEL: 'INFO'     # debug | info | warning | error | critical（大小写不敏感）
LOG_NAME: 'mirobody'
LOG_DIR: ''           # 留空 => stdout；设一个路径则写日志文件
```

## LLM 提供商

**至少**设置一个提供商密钥以启用对话。Agent 在请求时按**提供商**名挑客户端；默认提供商回退是 `gemini-2.5-flash`。工具执行方式因提供商而异：OpenAI 和 Gemini 在**进程内本地**运行内置的 MCP 工具，而 MiroThinker 在**提供商侧**执行工具（因此需要一个公网可达的 `MCP_PUBLIC_URL`）。

### Google Gemini

```yaml config.yml theme={null}
GOOGLE_API_KEY: 'AIza...'    # AI Studio 密钥（也会读取为 GEMINI_API_KEY）
```

要覆盖 HIPAA/BAA，把 Gemini 经 **Vertex AI** 而非 AI Studio 路由：

```yaml config.yml theme={null}
GOOGLE_GENAI_USE_VERTEXAI: '1'
VERTEX_ACCESS_TOKEN: ''      # OAuth 访问令牌，例如 `gcloud auth print-access-token`（约每小时轮换）
GCP_PROJECT: ''              # Google Cloud 项目 id（也会读取为 GOOGLE_CLOUD_PROJECT）
VERTEX_LOCATION: 'us-central1'
# VERTEX_BASE_URL: ''        # 可选的 host 覆盖（主权云 / 区域）
```

### OpenAI

```yaml config.yml theme={null}
OPENAI_API_KEY: 'sk-...'
# OPENAI_BASE_URL: ''        # 指向任意 OpenAI 兼容端点（OpenRouter、DeepSeek、Groq…）
```

要覆盖 HIPAA/BAA，把 GPT 跑在你自己的 **Azure OpenAI** 资源里 —— 设置了 `AZURE_OPENAI_ENDPOINT` 时客户端会自动切到 Azure 模式：

```yaml config.yml theme={null}
AZURE_OPENAI_ENDPOINT: 'https://my-resource.openai.azure.com'
AZURE_OPENAI_DEPLOYMENT: 'gpt-4o-mini'
AZURE_OPENAI_API_VERSION: '2024-10-21'
AZURE_OPENAI_API_KEY: ''     # 或 AZURE_OPENAI_KEY
```

### MiroThinker

```yaml config.yml theme={null}
MIROTHINKER_API_KEY: '...'   # MiroThinker 提供商必需
# MIROTHINKER_BASE_URL: '...'   # 端点覆盖 *(待核实)*
# MIROTHINKER_MODEL: '...'      # 模型名 *(待核实)*
```

<Warning>
  MiroThinker 在提供商侧运行工具：MiroMind 的端点会经公网回调到**本**服务器的 `/mcp`，所以你还必须设置一个公网可达的 [`MCP_PUBLIC_URL`](#mcp-公开-url)。localhost 或私网地址不行。
</Warning>

### 设备本地 Gemma（可选）

原生应用可以完全在设备本地运行 **Gemma 4 E2B**（Android/iOS 上经 LiteRT-LM，Electron 上经 llama.cpp）—— 无密钥、无网络。如果你自托管了一个 Gemma 运行时（例如 Ollama），把独立服务器指向它：

```yaml config.yml theme={null}
# GEMMA_CHAT_BASE_URL: 'http://localhost:11434/v1'   # 回退到 OLLAMA_BASE_URL
# GEMMA_CHAT_MODEL: 'gemma-4-e2b'                     # 你的运行时提供的标签
# GEMMA_CHAT_API_KEY: ''                              # 本地服务器通常留空
```

### Embeddings

```yaml config.yml theme={null}
EMBEDDING_PROVIDER: 'gemini'   # embeddings 提供商（使用上面的 Gemini/Google 密钥）
```

## 数据库

二进制里只编译进一个 SQL 后端（在构建期经 `MIROBODY_DATABASE_BACKEND` 选定 —— 参见 [安装指南](/zh/installation#数据库后端)）。下面这些键配置的是 **PostgreSQL** 后端，即桌面默认：

```yaml config.yml theme={null}
PG_HOST: '127.0.0.1'
PG_PORT: 5432
PG_USER: 'postgres'
PG_DBNAME: 'mirobody'
PG_SCHEMA: 'public'
PG_PASSWORD: 'REPLACE_THIS_VALUE_IN_PRODUCTION'
PG_ENCRYPTION_KEY: 'REPLACE_THIS_VALUE_IN_PRODUCTION'   # 加密敏感数据列
```

SQLite 后端（移动端 / 自包含构建）无需连接设置。MySQL、DuckDB、ClickHouse 各有类似的键块。

## 缓存

缓存层是**可选**的。让 `REDIS_HOST` 留空，服务器就用进程内的内存 KV 存储 —— 单实例运行没问题。设置 `REDIS_HOST` 以在多实例间共享缓存状态：

```yaml config.yml theme={null}
# REDIS_HOST: ''
# REDIS_PORT: 6379
# REDIS_PASSWORD: ''
```

## 文件存储

上传的文件和生成的资源（例如图表）会写入一个存储后端，在运行时选定。默认是**本地文件系统**；其他后端（S3 / S3 兼容、阿里云 OSS、Azure Blob）通过填写各自的块来启用。

<Tabs>
  <Tab title="本地（默认）">
    ```yaml config.yml theme={null}
    LOCAL_STORAGE_DIR: '_local/storage'          # 对象写入位置
    LOCAL_STORAGE_URL_PREFIX: '/files'           # 本服务器提供对象的 URL 路径
    LOCAL_STORAGE_SECRET: 'REPLACE_THIS_VALUE_IN_PRODUCTION'  # 预签名 URL 的 HMAC 密钥（设置了 URL 前缀时需 >=16 字符）
    # LOCAL_STORAGE_PREFIX: ''                   # 对象键前缀（留空 => 'mirobody'）
    # LOCAL_STORAGE_BASE_URL: ''                 # 公开前缀 / CDN 基址
    ```
  </Tab>

  <Tab title="S3 / S3 兼容">
    ```yaml config.yml theme={null}
    S3_KEY: ''         # AWS access key id
    S3_TOKEN: ''       # AWS secret access key
    S3_REGION: ''      # 例如 us-east-1
    S3_BUCKET: ''
    # S3_ENDPOINT: ''  # S3 兼容存储的 host 覆盖（MinIO、R2、GCS interop）；留空 => AWS
    # S3_PREFIX: ''    # 键前缀（留空 => 'mirobody'）
    ```
  </Tab>

  <Tab title="阿里云 OSS">
    ```yaml config.yml theme={null}
    ALI_OSS_ACCESS_KEY: ''
    ALI_OSS_SECRET_KEY: ''
    ALI_OSS_ENDPOINT: 'oss-cn-hangzhou.aliyuncs.com'
    ALI_OSS_BUCKET_NAME: ''
    # ALI_OSS_PREFIX: ''
    ```
  </Tab>

  <Tab title="Azure Blob">
    ```yaml config.yml theme={null}
    AZURE_BLOB_ACCOUNT: ''
    AZURE_BLOB_KEY: ''                          # 账户密钥（base64）
    AZURE_BLOB_CONTAINER: ''
    # AZURE_BLOB_ENDPOINT_SUFFIX: 'core.windows.net'   # 主权云的覆盖
    ```
  </Tab>
</Tabs>

<Info>
  上传的可选静态加密：设置 `FILE_ENCRYPTION_KEY`（一个可轮换的列表 —— 最后一个密钥负责加密，所有密钥都可解密）外加一个稳定的 `FILE_KEY_SEED`。参见 `config.example.yml` 里的注释。
</Info>

## 长期记忆

`remember` / `recall_memory` 工具存储持久事实。每个后端都会编译进来；在运行时挑一个：

```yaml config.yml theme={null}
MEMORY_PROVIDER: 'local'   # local（默认）| mem0 | zep | everos
```

`local` 把事实连同 1024 维 embeddings 存进应用数据库，按进程内余弦相似度排序 —— 无需额外服务。其他后端经 HTTP 与远程记忆服务通信。

## 鉴权

### 邮箱验证码

Web 客户端的默认登录方式。本地测试用预置验证码：

```yaml config.yml theme={null}
EMAIL_PREDEFINE_CODES:
  demo1@mirobody.ai: '777777'
  demo2@mirobody.ai: '777777'
  demo3@mirobody.ai: '777777'
```

<Warning>
  生产环境切勿保留预置验证码。
</Warning>

### JWT

```yaml config.yml theme={null}
JWT_KEY: 'REPLACE_THIS_VALUE_IN_PRODUCTION'    # HS256 密钥（256 位）：`openssl rand -hex 32`
JWT_SALT: 'REPLACE_THIS_VALUE_IN_PRODUCTION'
# JWT_PRIVATE_KEY: |                            # 设置以启用 RS256 —— 优先于 JWT_KEY，并发布 JWKS
#   -----BEGIN PRIVATE KEY-----
#   ...
```

只设置一种签名模式：`JWT_KEY` 用于 HS256，或 `JWT_PRIVATE_KEY` 用于 RS256（经发布的 JWKS 启用第三方令牌校验）。`cli/jwt_keygen` 工具可生成一对 RSA 密钥。

### 社交登录（可选）

Google、Apple、X 登录经 **Firebase**；WeChat 和 GitHub 是直连；**Tanka 二维码登录**默认开启。

```yaml config.yml theme={null}
FIREBASE_PROJECT_ID: ''          # 校验 Google/Apple/X 的 ID token
# FIREBASE_WEB_API_KEY: ''       # Web 应用浏览器 apiKey
# FIREBASE_MESSAGING_SENDER_ID: ''
# APPLE_CLIENT_ID: ''
# WECHAT_APPID: ''
# WECHAT_SECRET: ''
# GITHUB_CLIENT_ID: ''
# GITHUB_CLIENT_SECRET: ''
# TANKA_LOGIN_ENABLED: true      # 二维码登录；设为 false 可隐藏按钮
```

## OAuth 2.0 授权服务器

Mirobody 内嵌一个 OAuth 2.0 授权服务器（授权码 + PKCE），让 MCP 客户端可以经浏览器获取令牌，而不必粘贴 bearer token。它配合上面的 JWT 密钥工作；所有 `OAUTH_*` 键都有可用默认值。

```yaml config.yml theme={null}
# JWT_ISS: ''          # 铸进自签名令牌的 iss/aud/client_id/scope 声明
# JWT_AUD: ''
# OAUTH_RESOURCE_PATH: '/mcp'   # 这些令牌保护的资源
```

发现文档在 `/.well-known/oauth-authorization-server`、`/.well-known/openid-configuration`、`/.well-known/jwks.json` 提供。

## MCP 公开 URL

```yaml config.yml theme={null}
# MCP_PUBLIC_URL: 'https://yourdomain.com'
```

`MCP_PUBLIC_URL` 是你 MCP 端点对外可达的基址（`<MCP_PUBLIC_URL>/mcp`）。它是 MiroThinker 提供商（工具在提供商侧运行）**必需**的，也是把 MCP 服务器暴露给 ChatGPT Apps、远程 Claude Desktop 及其他 MCP 客户端所必需的。本地开发没有公网域名时，用隧道打通它：

<Steps>
  <Step title="安装并运行 ngrok">
    ```bash theme={null}
    ngrok http 8080
    ```
  </Step>

  <Step title="设置隧道 URL">
    ```yaml config.yml theme={null}
    MCP_PUBLIC_URL: 'https://abc123.ngrok-free.app'
    ```
  </Step>
</Steps>

参见 [ChatGPT Apps](/zh/tools/chatgpt-apps)。

## 健康圈（Care circles）

在单用户内核之上的可选分享功能。规模由上限约束（`0` 禁用对应项）：

```yaml config.yml theme={null}
CIRCLE_MAX_PER_USER: 5    # 一个用户可拥有的最大圈子数
CIRCLE_MAX_MEMBERS: 5     # 每个圈子的最大成员数（待处理的邀请也计入）
```

分享默认关闭；AI 助手对另一位成员的数据是**只读**的。参见 [架构概览](/zh/concepts/architecture)。

## 对话限流

对每个用户的 Agent / 实时轮次设上限：

```yaml config.yml theme={null}
CHAT_RATE_MAX: 5           # 每个用户每个窗口的最大轮次（0 = 不限）
CHAT_RATE_WINDOW_SEC: 60   # 窗口长度（秒）
```

## 远程配置（可选）

从一个服务而非本地文件拉取配置 —— 仅当三者都设置时生效：

```yaml config.yml theme={null}
# 经环境变量：
# CONFIG_SERVER=https://config.example.com
# CONFIG_TOKEN=<作为 X-Config-Token 发送的 bearer>
# ENV=prod                  # 选择远程环境
```

## 配置最佳实践

<AccordionGroup>
  <Accordion title="版本控制" icon="git">
    * ✅ 提交 `config.example.yml`
    * ❌ 绝不提交带真实凭据的 `config.yml`（它默认已被 git 忽略）
    * ✅ 经环境变量提供 `CONFIG_ENCRYPTION_KEY`，而非写进文件
  </Accordion>

  <Accordion title="安全性" icon="shield">
    * 把每一个 `REPLACE_THIS_VALUE_IN_PRODUCTION` 占位符替换成强随机值
    * 当其他服务需要校验你的令牌时，使用 RS256（`JWT_PRIVATE_KEY`）
    * 生产环境限制 CORS origin
    * 让 PHI 留在设备本地，或把托管 LLM 调用经 BAA 覆盖的通道路由（Vertex AI / Azure OpenAI）
  </Accordion>

  <Accordion title="校验 YAML" icon="circle-check">
    ```bash theme={null}
    python3 -c "import yaml; yaml.safe_load(open('config.yml'))"
    ```

    没有输出表示语法有效。服务器也会在启动时报告配置错误，而不是静默降级。
  </Accordion>
</AccordionGroup>

## 下一步

<CardGroup cols={2}>
  <Card title="快速开始" icon="rocket" href="/zh/quickstart">
    用这份配置构建内核并运行
  </Card>

  <Card title="Provider 集成" icon="plug" href="/zh/development/provider-integration">
    接入新的健康数据源
  </Card>

  <Card title="API 参考" icon="code" href="/zh/api-reference/overview">
    HTTP、WebSocket、MCP 和 FHIR 端点
  </Card>

  <Card title="生产部署" icon="server" href="/zh/deployment/production">
    后端、密钥与加固
  </Card>
</CardGroup>
