> ## 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.

# 自部署 Mirobody

> 在你自己的机器或服务器上运行开源引擎：三种运行方式、各阶段所在的位置，以及一套部署对外提供什么。

export const OssVersion = ({lang = "en"}) => <p className="text-sm text-gray-500 dark:text-gray-400">
    {lang === "zh" ? "对应 mirobody " : "Written for mirobody "}
    <a href="https://github.com/thetahealth/mirobody/tree/c1aae297b4f3fad5cb87c5fc5bffc5163f7758c9">
      <code>1.5.3</code>
    </a>
  </p>;

export const OssLink = ({path = "", children}) => {
  const base = "https://github.com/thetahealth/mirobody";
  const commit = "c1aae297b4f3fad5cb87c5fc5bffc5163f7758c9";
  const href = !path ? base + "/tree/" + commit : base + (path.endsWith("/") ? "/tree/" : "/blob/") + commit + "/" + path.replace(/\/$/, "");
  return <a href={href}>{children ?? <code>{path}</code>}</a>;
};

export const Fact = ({k, code = false, sep = ", "}) => {
  const facts = {
    "version": "1.5.3",
    "commit": "c1aae297b4f3fad5cb87c5fc5bffc5163f7758c9",
    "commitShort": "c1aae29",
    "repo": "https://github.com/thetahealth/mirobody",
    "python": "3.12",
    "port": 18060,
    "url": "http://localhost:18060",
    "pgPort": 18062,
    "redisPort": null,
    "account": "you@mirobody.ai",
    "accounts": ["you@mirobody.ai", "mom@mirobody.ai"],
    "code": "111111",
    "mcpUrlTtlDays": 10,
    "llmKeys": ["OPENROUTER_API_KEY", "DASHSCOPE_API_KEY", "GOOGLE_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "DEEPSEEK_API_KEY"],
    "cli": ["serve", "dev", "worker", "doctor", "fetch", "parse", "import", "resolve", "device-bundle", "mcp", "migrate-observations", "migrate-genotypes", "recode"],
    "tools": {
      "mcp": ["query_genetic_data", "query_health_indicators", "query_medications", "query_pharmacogenomics", "resolve_indicator", "convert_unit", "normalize_unit"],
      "gated": ["query_genetic_data", "query_pharmacogenomics", "query_health_indicators", "query_medications"]
    },
    "readme": {
      "en": {
        "tagline": "Self-hosted AI health data engine: every source, one standard, answers that cite their source.",
        "stages": [{
          "mark": "①",
          "name": "Collect",
          "what": "Lab reports, wearables, phone photos, genetic files, all pulled in. The source file is kept as it was, so every indicator points back to the page it was read from."
        }, {
          "mark": "②",
          "name": "Translate",
          "what": "One name to one code, one unit to UCUM, offline and deterministic. `A1c`, `HbA1c` and `Glycated Hemoglobin` become the same test here, and `头疼` and `headache` the same complaint (ICPC-3)."
        }, {
          "mark": "③",
          "name": "Agent",
          "what": "Ask over the coded record. Trend a value by minute, hour, day, week or month; get count, min, max, avg or change over any window in one call; compare across labs and devices, because they share one code. It charts the result in its reply, reads medications and genetic variants too, and names the file every number came from."
        }]
      },
      "zh": {
        "tagline": "自托管的 AI 原生健康数据引擎：任何来源，一套标准，每个答案都有出处。",
        "stages": [{
          "mark": "①",
          "name": "收集 Collect",
          "what": "化验单、穿戴设备、手机照片、基因文件，都收进来。源文件原样留下，每一项指标都能指回它被读出来的那一页。"
        }, {
          "mark": "②",
          "name": "转译 Translate",
          "what": "一个名字解析成一个码，一个单位统一到 UCUM，全程离线、结果确定。`A1c`、`HbA1c`、`糖化血红蛋白` 在这一层变成同一项检查，`头疼` 和 `headache` 也成了同一条主诉（ICPC-3）。"
        }, {
          "mark": "③",
          "name": "智能体 Agent",
          "what": "在编码后的记录上提问。按分钟、小时、天、周、月给出趋势，一次调用就能算出计数、最小值、最大值、均值和变化量；同一个码，跨化验所、跨设备直接比较。图表画在回复里，用药记录和基因型数据也读得了，每个数字都说明出自哪份文件。"
        }]
      }
    },
    "source": {
      "cli": "mirobody/cli.py",
      "tools": "mirobody/agent/tools"
    }
  };
  const value = k.split(".").reduce((o, p) => o == null ? undefined : o[p], facts);
  if (value === undefined) return <span>{"[unknown fact " + k + "]"}</span>;
  const items = Array.isArray(value) ? value : [value];
  return <>
      {items.map((item, i) => <span key={i}>
          {i > 0 ? sep : null}
          {code ? <code>{String(item)}</code> : String(item)}
        </span>)}
    </>;
};

<OssVersion lang="zh" />

凡是能运行 Python <Fact k="python" /> 或 Docker 的地方都能运行 Mirobody：笔记本、本地服务器或云主机。引擎以 Apache 2.0 许可开源，仓库是 [thetahealth/mirobody](https://github.com/thetahealth/mirobody)，它保存的数据留在你自己运维的 Postgres 里。Mirobody 是什么，以及自部署与 Mirobody Cloud 的区别，见[概览](/zh)。

## 运行方式

按你的目的选一行即可，三者是互相替代的选择，不是先后步骤。

| 你想要 | 方式 | 需要 | 模型密钥 |
| - | - | - | - |
| 在自己的代码里解析指标名称与单位 | [作为库使用](/zh/quickstart#a--the-library) | Python <Fact k="python" /> | 不需要 |
| 运行完整产品，并带有演示数据 | [Docker 栈](/zh/quickstart#b--the-stack) | Git、带 Compose 的 Docker | 使用模型功能时需要一个 |
| 修改代码并查看效果 | [源码检出](/zh/quickstart#c--a-checkout) | Python 与带 pgvector 的 Postgres | 使用模型功能时需要一个 |

Docker 栈由三个容器组成：带 pgvector 的 Postgres、服务端与后台 worker。自带的 Web 客户端运行在 <Fact k="url" code />。

<h2 id="first-local-result">
  第一次在本地运行
</h2>

安装 Git 和 Docker Compose 后，启动本地演示环境并检查预置读数。模型功能需要在 `.env` 中设置受支持的密钥；查看样例读数无需密钥。

<Steps>
  <Step title="启动演示环境">
    检出引擎并启动服务。脚本会拉取应用镜像、创建演示账号、写入样例读数，随后持续显示服务日志。

    ```bash theme={null}
    git clone --depth 1 https://github.com/thetahealth/mirobody.git && cd mirobody
    ./deploy.sh
    ```
  </Step>

  <Step title="检查服务状态">
    脚本显示 `Up` 后，让该终端保持运行。在引擎仓库目录另开一个终端，执行：

    ```bash theme={null}
    docker compose ps
    curl -fsS http://localhost:18060/api/health
    docker compose exec mirobody mirobody doctor
    ```

    `mirobody` 服务应变为 `healthy`，健康检查会返回 `version` 和 `agent`。最后一条命令在**容器内**检查模型功能。启动后才在 `.env` 中添加密钥时，请运行 `docker compose up -d`；`docker compose restart` 不会重新读取 `.env`。
  </Step>

  <Step title="查看样例读数">
    打开 <Fact k="url" code />，使用 `you@mirobody.ai` 和验证码 `111111` 登录，再打开 **Data**。页面应显示预置读数。[快速开始](/zh/quickstart#b--the-stack)也介绍了演示上传文件和其他运行方式。
  </Step>
</Steps>

保存真实记录前，请在**首次启动前**按照[在服务器上部署](/zh/deployment/production)设置。已经运行过演示环境时，请按[已有演示环境路径](/zh/deployment/production#after-a-local-demo)使用全新数据卷。

自部署的记录保存在你运维的 Postgres 和文件存储中。文件提取与智能体回答会把数据发送给你配置的模型服务商；启用这些功能前，请确认该服务商对健康数据的处理方式。

## 三个阶段所在的位置

代码包与本文档按同样的三个阶段组织。

<Frame caption="一条读数依次经过收集、转译，并供智能体使用。">
  <img alt="收集、转译、智能体：从左到右的三个阶段" src="https://mintcdn.com/thetahealth/ypMLkjCdwPelODUf/images/oss/docs/images/collect-translate-agent.zh-CN.svg?fit=max&auto=format&n=ypMLkjCdwPelODUf&q=85&s=7abb042afdd5a8dbcd4fe08661948cb2" className="block dark:hidden" width="1120" height="380" data-path="images/oss/docs/images/collect-translate-agent.zh-CN.svg" />

  <img alt="收集、转译、智能体：从左到右的三个阶段" src="https://mintcdn.com/thetahealth/ypMLkjCdwPelODUf/images/oss/docs/images/collect-translate-agent-dark.zh-CN.svg?fit=max&auto=format&n=ypMLkjCdwPelODUf&q=85&s=efc995f25db85a4fafda178c394fd29f" className="hidden dark:block" width="1120" height="380" data-path="images/oss/docs/images/collect-translate-agent-dark.zh-CN.svg" />
</Frame>

| 阶段 | 代码包 | 相关页面 |
| - | - | - |
| ① 收集 | `mirobody/collect/` | [设备 Provider](/zh/providers/using-providers)、[Apple Health](/zh/providers/apple-health)、[文件处理](/zh/concepts/file-processing) |
| ② 转译 | `mirobody/engine/`、`mirobody/translate/` | [标准化](/zh/concepts/indicators)、[设备字段对照](/zh/concepts/device-crosswalk) |
| ③ 智能体 | `mirobody/agent/` | [内置 Agent](/zh/tools/agents)、[健康数据工具](/zh/tools/overview)、[MCP 接入](/zh/tools/mcp-integration) |

一条读数如何依次经过这三个阶段，见[数据管线](/zh/concepts/architecture)；目录结构见[仓库结构](/zh/concepts/repository-layout)。

## 部署对外提供的入口

| 入口 | 地址 | 使用者 |
| - | - | - |
| Web 客户端 | `/` | 终端用户：在 `/data` 管理文档与读数，在 `/ask` 向 agent 提问，以及关爱圈共享 |
| HTTP API | `/api/*` | 你自己的应用；从[本地 HTTP API 指南](/zh/http-api)开始 |
| MCP 端点 | `/mcp` | Claude Desktop、Cursor 或你自己的 agent 循环，见 [MCP 接入](/zh/tools/mcp-integration) |

自部署的引擎不提供 Cloud 的 `/v1` 接口。那一套接口在云端标签页中说明，上表列出的是引擎自身的路由。

## 下一步

<CardGroup cols={2}>
  <Card title="快速开始" icon="rocket" href="/zh/quickstart">
    安装库、启动整套服务，或运行源码检出。
  </Card>

  <Card title="配置" icon="sliders" href="/zh/configuration">
    模型密钥、配置文件，以及部署时需要修改的设置。
  </Card>

  <Card title="HTTP API" icon="code" href="/zh/http-api">
    获取本地令牌、写入一条读数并读取确认。
  </Card>

  <Card title="完整演示" icon="play" href="/zh/walkthrough">
    用四个场景走完运行中的整套系统，使用演示数据。
  </Card>

  <Card title="在服务器上部署" icon="server" href="/zh/deployment/production">
    生产模式、密钥、HTTPS 与部署验证。
  </Card>

  <Card title="升级部署" icon="arrow-up" href="/zh/deployment/upgrade">
    备份、核对版本、升级并验证运行中的服务。
  </Card>

  <Card title="核验与恢复备份" icon="database" href="/zh/deployment/restore">
    演练恢复，或恢复 Postgres 与本地上传文件。
  </Card>
</CardGroup>

部署无法正常启动时，[排错](/zh/troubleshooting)列出了常见原因与处理方法。

<h2 id="contributing">
  参与贡献
</h2>

最有价值的贡献是修正解析器解析错的术语。运行 `mirobody resolve "<术语>"`，如果结果错误或为空，可以提交 issue，或者在 <OssLink path="mirobody/res/loinc/resolver_overrides.tsv" /> 中加一行，并在 <OssLink path="mirobody/tests/test_engine_coverage.py" /> 中加一个用例。覆盖率得分就是评审标准。

开发环境安装测试依赖后，运行测试套件与导入约束检查：

```bash theme={null}
pip install -e '.[test]' && pytest -q && lint-imports
```

完整的贡献流程见 <OssLink path="CONTRIBUTING.md" />，测试的组织方式见 <OssLink path="docs/testing.md" />。

`benchmarks/` 目录下的两个公开基准可以在克隆下来的仓库里直接运行，不需要任何私有数据：`python -m unittest benchmarks.health_records.test_cases`（13 条合成读数与 26 句主诉短语，覆盖五种语言，核对各自的 LOINC/UCUM 与 ICPC-3 结果）以及 `python -m unittest discover -s benchmarks/genomics -p 'test_*.py'`（13 个公开的 1000 Genomes 位点调用，覆盖十种文件形态）。参见 <OssLink path="benchmarks/health_records/README.md" /> 与 <OssLink path="benchmarks/genomics/README.md" />。

<CardGroup cols={2}>
  <Card title="GitHub 仓库" icon="github" href="https://github.com/thetahealth/mirobody">
    源码、issue 与 pull request。
  </Card>

  <Card title="报告问题" icon="bug" href="https://github.com/thetahealth/mirobody/issues">
    解析出错的化验单是很有价值的 issue，请附上去除个人信息后的样本。
  </Card>
</CardGroup>

安全问题请通过私密安全通告提交，不要公开提 issue，见 <OssLink path="SECURITY.md" />。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.