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

# 排错

> 如何阅读 doctor 报告，以及自部署无法正常启动的常见原因。

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/83362582a3f8add278456a81eefe2f87ba5898d2">
      <code>1.5.1</code>
    </a>
  </p>;

export const OssLink = ({path = "", children}) => {
  const base = "https://github.com/thetahealth/mirobody";
  const commit = "83362582a3f8add278456a81eefe2f87ba5898d2";
  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.1",
    "commit": "83362582a3f8add278456a81eefe2f87ba5898d2",
    "commitShort": "8336258",
    "repo": "https://github.com/thetahealth/mirobody",
    "python": "3.12",
    "port": 18060,
    "url": "http://localhost:18060",
    "pgPort": 18062,
    "redisPort": 18069,
    "account": "you@mirobody.ai",
    "accounts": ["you@mirobody.ai", "mom@mirobody.ai"],
    "code": "111111",
    "mcpUrlTtlDays": 30,
    "llmKeys": ["OPENROUTER_API_KEY", "DASHSCOPE_API_KEY", "GOOGLE_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "DEEPSEEK_API_KEY"],
    "cli": ["serve", "dev", "worker", "doctor", "parse", "import", "resolve", "mcp", "migrate-observations", "recode"],
    "tools": {
      "mcp": ["query_genetic_data", "query_health_indicators", "query_medications", "resolve_indicator", "convert_unit", "normalize_unit"],
      "gated": ["query_genetic_data", "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" />

先看 doctor 报告和容器日志。大多数故障会在这两处之一直接给出修复方法。

```bash theme={null}
docker compose exec mirobody python -m mirobody doctor   # 每个用途选中了哪个模型
docker compose logs --tail 80 mirobody                  # 服务端自身的日志
```

## doctor 报告

`mirobody doctor` 先打印一行找到的密钥，再为每个用途各打印一行：

| 输出                    | 含义                                                                                      |
| --------------------- | --------------------------------------------------------------------------------------- |
| `keys present : none` | 没有配置模型密钥。① 收集与 ② 转译照常工作，文件抽取和 agent 不可用。在 `.env` 中写入一个密钥，然后执行 `docker compose restart`。 |
| 标记为 `OK` 的用途          | 该用途选中的条目与模型。                                                                            |
| 标记为 `--` 的用途          | 该用途没有可用模型，下一行写明缺少的密钥或条目。                                                                |
| `retired keys`        | 早期版本的配置键，已不再读取。请把这项设置移到 `config.llm.yaml` 的 `MODELS` 条目或 `UTILS_*` 路由中。                 |
| `unread fields`       | `MODELS` 条目里没有任何代码读取的字段，通常是拼写错误。                                                        |

## 启动失败

| 现象                                             | 原因与处理                                                                                                                |
| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `deploy.sh` 中止：端口被其他容器占用                       | 另一个项目占用了 <Fact k="port" />、<Fact k="pgPort" /> 或 <Fact k="redisPort" />。`deploy.sh` 不会停止不属于本项目的容器：请释放端口，或修改本项目的端口。   |
| `deploy.sh` 中止：已有 Docker 网络占用了该子网              | `compose.yaml` 固定了一个子网，所以同一时间只能运行一份检出。删除提示中那个未使用的网络，或把本项目移到空闲子网，并在覆盖文件中把 `PG_HOST` 与 `REDIS_HOST` 改成新地址。             |
| Compose 拒绝具名卷（`Host path binding is rejected`） | Docker 以 rootless 或加固模式运行。把 <OssLink path="compose.override.yaml.example" /> 复制为 `compose.override.yaml`，并创建其中列出的目录。 |
| 拉取镜像失败，或构建时无法访问 Docker Hub                     | Docker Hub 无响应时，`deploy.sh` 会改用镜像源。通过代理上网时，需要为 Docker 守护进程本身配置代理，只配置 shell 不够。                                       |
| 首次启动耗时很长                                       | 首次启动时会先把 Python 依赖安装到一个卷中，然后服务端才开始监听端口。可以在 `.env` 中设置 `PIP_INDEX_URL`，改用更近的包索引。                                      |
| 设置 `PRODUCTION: true` 后服务端拒绝启动                 | 仍有预置登录码或 `REPLACE_THIS_VALUE_IN_PRODUCTION` 占位值，日志会写明是哪些，见[在服务器上部署](/zh/deployment/production#production-posture)。   |
| `mirobody serve` 找不到配置                         | `config.yaml` 不在 PyPI 包中。请在源码检出中运行，或使用不需要配置文件的 `mirobody dev`。                                                       |
| `mirobody dev` 退出并要求提供 Postgres                | 传入 `--pg-url`，或设置 `PG_URL` 或 `DATABASE_URL`。数据库需要 pgvector 扩展。                                                       |

## 运行中的问题

| 现象                                    | 原因与处理                                                                                  |
| ------------------------------------- | -------------------------------------------------------------------------------------- |
| 新克隆的仓库在查询 LOINC 时报错                   | 术语包用 Git LFS 存储，克隆下来的只是指针文件。执行 `git lfs install && git lfs pull`。                      |
| 设备同步从不执行，其余功能正常                       | Redis 无法连接。其他功能都能平稳降级，唯独厂商数据拉取不能。                                                      |
| 通过代理上网时模型调用报 `Cannot connect to host` | 容器不会继承 shell 的代理设置。在 `.env` 中设置 `HTTP_PROXY` 与 `HTTPS_PROXY`，`compose.yaml` 会把它们传给服务端。 |
| 上传的文件已保存，但没有出现读数                      | 没有选中视觉或文本模型（见 doctor 报告），或者 `ENABLE_INDICATOR_EXTRACTION` 为 `0`。                       |
| 演示账号无法登录                              | 首次启动时 `SEED_DEMO_DATA` 为 `false`，或覆盖文件替换了 `EMAIL_PREDEFINE_CODES`（覆盖文件中的字典是替换，不是合并）。   |
| 某个设备 provider 没有出现                    | 它的凭据缺失，启动时自行跳过了，启动日志会写明是哪一个。见[设备 Provider](/zh/providers/using-providers#排查)。          |

以上没有覆盖的问题，欢迎在 [GitHub](https://github.com/thetahealth/mirobody/issues) 提交 issue：附上 doctor 报告和容器日志的最后几行，并去除其中的个人数据。
