> For the complete documentation index, see [llms.txt](https://tinyhumans.gitbook.io/openhuman/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tinyhumans.gitbook.io/openhuman/zh/kai-fa/components.md).

# 组件

【components】配置表：每个微小依赖对应一个开关，它如何缩小宿主范围，以及旧标志如何迁移。

`config.toml` 有一个 `[components]` 一个表，每个 tiny 依赖对应一个布尔值。默认都为 `true`.

```toml
[components]
flows = true       # tinyflows：已保存的工作流
tinyflows = true   # tinyflows 模块：计划任务（cron）的运行时、存储和调度计算
memes = true       # tinymemes
documents = true   # tinydocs
search = true      # tinysearch
mcp = true         # tinymcp
connectors = true  # tinyconnectors（Composio）
hosting = true     # tinyhosts
juice = true       # tinyjuice
memory = true      # tinymemory
wallet = true      # tinywallet（web3，x402）
computer = true    # tinycomputer
sandbox = true     # tinybox
channels = true    # tinychannels
skills = true      # tinyskills
voice = true       # tinyvoice
eventqueues = true # tinyeventqueues：入站事件队列
```

每个开关还会读取 `OPENHUMAN_COMPONENT_<NAME>=0|1` （例如 `OPENHUMAN_COMPONENT_VOICE=0`）；环境变量优先于文件。

## 开关的作用

这些开关只会缩小范围。核心会将它们解析为一个组件集合，并计算 `effective = host choice ∩ components` 用于领域家族、后台服务和工具组。一个主机预设（`harness`, `kernel`, `saas`，……）决定哪些可以存在；开关可以从中移除某些内容，但绝不能把它加回来。

某个组件关闭的方法会返回 `COMPONENT_DISABLED:<name>` （JSON-RPC `error.message`，并带有 `error.data = {"kind": "component_disabled", "component": "<name>"}`），而不是 `未知方法`，以便 UI 和 CLI 能解释原因。主机只是省略掉的方法仍会显示为未注册。

可加载模块遵循同样的开关。其组件关闭的模块绝不会被下载、映射或注册： `modules.list` 将其状态报告为 `disabled`，启动时会跳过它（包括模块搜索路径上的匹配构件），加载它会返回 `COMPONENT_DISABLED:<name>`. `about_app` 会用状态标记由已禁用组件支持的能力 `disabled`.

标志更改会在下一次核心启动时生效。没有热切换。

每个组件的领域都会在各自的变更中把自己接到对应开关上（工具、服务和 RPC）；在此之前，开关只会移除核心组件集合中列出的家族、服务和工具组。

## Tinyflows

`tinyflows = false` 停止 cron 调度器服务，并使每个 `cron.*` RPC 回复 `COMPONENT_DISABLED:tinyflows`； `tinyflows` 模块绝不会被下载或加载，因此不会运行任何任务，也不会存储任何任务。它与 `flows`不同，后者只会停止由调度触发的流程，而代理、shell 和系统任务仍会继续运行。

## 不可切换

内核依赖（tinyagents、tinytools、tinyinference、tinybus）始终开启。 `[modules] 已启用` 仍然是可加载原生模块的全局总开关。

## 旧版标志

首次加载旧配置时，会将这些一次性迁移到 `[components]`，并在下次保存时写入结果：

| 旧版设置                                                                          | 变为                                                                                  |
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `[hosting] enabled = false`                                                   | `components.hosting = false`                                                        |
| `[search] enabled = false`                                                    | `components.search = false`                                                         |
| `[mcp_client] enabled = false`                                                | `components.mcp = false`                                                            |
| `[memory] engine = "none"`                                                    | `components.memory = false` (`引擎` 会保留）                                              |
| `OPENHUMAN_TINYMEMES` 设置为关闭值                                                  | `components.memes = false`                                                          |
| `OPENHUMAN_DISABLE_CHANNEL_LISTENERS=1`                                       | `components.channels = false` 在加载时（环境覆盖项，保留一个版本； `OPENHUMAN_COMPONENT_CHANNELS` 优先） |
| `[dictation] 已启用`, `[voice_server] 自动启动` 以及 `always_on_enabled` 全部显式地 `false` | `components.voice = false` （任何其他组合都会保持语音开启）；这些旧版键仅作为迁移输入                            |
| `[sandbox] enabled = false` （或 `[security.sandbox] enabled = false`)          | `components.sandbox = false` (`enabled = true` 或未设置会保持开启；旧键会保留）                     |
| `[composio] 已启用`                                                              | 已弃用；它从未被读取                                                                          |

一个 `components.*` 文件中已存在的键始终优先于其旧标志，因此迁移是幂等的，把组件重新打开也会生效并保持。

## 梗图

`components.memes` 是主开关：关闭意味着不进行重混，也不加载模块，A/B 分支会被忽略。开启时， `OPENHUMAN_TINYMEMES` (`关闭`, `开启`, `ab`, `ab:NN`；构建时默认值 `OPENHUMAN_TINYMEMES_DEFAULT`）会选择被重混的线程。 `[memes]` 部分会对其进行调优： `timeout_ms` （默认 20000）以及 `meme_cooldown` （默认 3）。 `OPENHUMAN_TINYMEMES_TIMEOUT_MS` 以及 `OPENHUMAN_TINYMEMES_MEME_COOLDOWN` 在设置时覆盖它们。

模块会读取其自己的 `TINYMEMES_*` 来自进程环境变量的覆盖项，因此凭据绝不会经过总线： `TINYMEMES_OPENROUTER_KEY` （以及 `TINYMEMES_MODEL`）会通过 OpenRouter 路由改写模型和俚语研究， `TINYMEMES_JEV` (`auto`, `typesafe`, `openrouter`, `openjev`, `llm`）配合 `TINYMEMES_TYPESAFE_KEY` / `TINYMEMES_OPENJEV_KEY` 选择 Jev 路径。若没有任何覆盖项，模块会向 OpenHuman 请求 Jev；当 OpenHuman 没有可用内容时（已登出、离线会话），则由聊天模型代替回答 Jev 的问题。回复送达后，模块可以通过 OpenHuman 网页搜索在后台执行俚语和梗图研究。学到的俚语、梗图以及重混消息环都会作为存储端口上的文档（较旧的 `<workspace>/tinymemes/*.json` 文件会被接管一次并重命名为 `*.migrated`).

## 事件队列

`eventqueues` 是入站事件队列（ `tinyeventqueues` 模块），由 `[inbound]` 部分进行调优。当它开启且 `[inbound] enabled` 为 `true` （默认值）时，Composio 触发器和代理 webhook 不再在到达处进行分诊。它们会被摄入模块，模块在答复之前会将其写入主机的存储。模块会拒绝同一触发器的重复投递（Composio 触发器 UUID，或 webhook 的关联 ID），过滤并节流洪泛（速率限制会把溢出折叠成一条摘要），并向主机回调，带上 `投递` 在每个事件到期时。届时执行分诊。返回 `延期` 的分诊会在其 `defer_until_ms` 时重试，而不是被丢弃。失败或缓慢的分诊会进行退避，而耗尽尝试次数，或被分诊标记为终态的事件，会被停放到死信队列中。 `inbound.stats`, `inbound.dead` 以及 `inbound.redrive` 检查并重放队列。它们会返回 `COMPONENT_DISABLED:eventqueues` 当开关关闭时。

在开关关闭时， `[inbound] enabled = false`，或者该主机上模块不可用时，事件会像以前一样走直接分诊路径。过大而无法入队的事件（编码后超过 192 KiB）也会走直接路径。

一旦分诊作出决定，投递就会被确认，而该决定会在自己的任务中应用。因此，长时间运行的代理绝不会持有队列租约，但应用失败会被报告而不是重试。延期也算一次投递尝试：如果某个触发器在该车道的 `max_attempts` 最大尝试次数（5）之后仍在延期，就会进入死信并可以重驱。Webhook 按请求依据关联 ID 去重。队列激活时，模块会在启动时于后台加载，因此上一次运行留下的待处理工作会在不等待下一个事件的情况下被投递。

| `[inbound]` key     | 默认值     | 含义                                                                                |
| ------------------- | ------- | --------------------------------------------------------------------------------- |
| `已启用`               | `true`  | 通过队列路由入站事件                                                                        |
| `queues_path`       | 未设置     | 一个 `queues.json` 替换模块随附的那个；主机在模块的存储作用域上精确定义这些队列                                   |
| `pipeline_path`     | 未设置     | 一个 `pipeline.json` 替换随附的那个（闸门、合并、显著性、车道、节奏、重试退避）                                  |
| `queue_url`         | `存储`    | 队列所在的位置：模块的存储作用域，或 `amqp://` / `amqps://` 在带有存储驱动的构建中用于 RabbitMQ 代理 `rabbitmq` 驱动 |
| `triage_timeout_ms` | `25000` | 一次已投递事件的分诊在重新投递前可花费的最长时间；最多 25 秒，低于模块 30 秒的回调截止时间；在启动时读取                          |

### 外部来源和固定代理

核心之外的集成（X 机器人、订阅阅读器）可以将自己的事件推送到同一组队列中，并让每个批次由它指定的一个代理处理，而无需分诊。请在以下位置配置该来源： `[inbound.sources."<name>"]`；名称为 `<kind>:<name>` (`^[a-z0-9_-]+:[a-z0-9_.*-]+$`），且前缀 `composio:`, `webhook:`, `web:` 以及 `channel:` 保留给核心自身的路径。

```toml
[inbound.sources."x:mentions"]
agent_id = "teeny-mentions"   # 必需：内置或工作区 agents/*.toml 代理
debounce_ms = 30000
max_wait_ms = 300000
max_batch = 20
prompt = "为每条提及起草回复。\n{{events}}"
result_ttl_days = 7
```

| `[inbound.sources.<name>]` key | 默认值     | 含义                                                                                                     |
| ------------------------------ | ------- | ------------------------------------------------------------------------------------------------------ |
| `agent_id`                     | 必需      | 每个批次运行所用的代理                                                                                            |
| `debounce_ms`                  | `5000`  | 在批次的最后一个事件之后如此长时间刷新该批次                                                                                 |
| `max_wait_ms`                  | `60000` | 最迟在批次的第一个事件之后如此长时间刷新该批次                                                                                |
| `max_batch`                    | `25`    | 一次轮次承载的最大事件数；完整批次会一次性刷新                                                                                |
| `prompt`                       | 一条简短指令  | 该轮次的消息； `{{events}}` 会被批次替换，作为一个 JSON 数组，其中包含 `{id, text, payload, received_at_ms}` （如果没有占位符，则附加 JSON） |
| `result_ttl_days`              | `7`     | 轮次结果保留多长时间                                                                                             |

来源会在启动时读取，就像其余的 `[inbound]`。`sources[].coalesce` 中 `tinyeventqueues`，这需要一个包含它的模块版本）。 `inference_agent_chat` 适用：被阻止的事件会从提示中排除，并在结果中列为 `blocked: [{event_id, reason}]` （原因是守卫的裁决和规则代码，绝不是文本），因此一个注入事件不可能让其批次失败或拖住批次。若批次中的每个事件都被阻止，则会以空的 `文本`，而无需调用模型，并且仍会发布 `InboundTurnCompleted`。守卫仍会将渲染后的提示作为整体检查；如果触发，轮次就会失败并像其他失败一样重试。失败的轮次会按照该车道的退避进行重试，并在最后一次尝试后存为失败；在投递预算耗尽时仍在运行的轮次会被确认，并在结束时存储其结果；核心停止时仍在运行的轮次会在下次启动时重新运行。

| RPC                            | 参数                                                                                         | 结果                                                                                                                                     |
| ------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| `openhuman.inbound_offer`      | `source`, `key`, `event_id`, `文本`, `payload?`, `received_at_ms?`                           | `{disposition}`: `accepted`，或 `duplicate` 用于重复投递的 `event_id` （成功）；未配置的来源则是错误                                                           |
| `openhuman.inbound_results`    | `source?`, `since_ms?`, `limit?` （50，最多 500）， `status?` (`running`, `completed`, `failed`) | `{results: [{turn_id, source, key, agent_id, status, text?, error?, event_ids, blocked, created_ms, completed_ms?}]}`，按最早优先，已确认的结果会被排除 |
| `openhuman.inbound_result_ack` | `turn_id`                                                                                  | `{acked: true}`；未知轮次是错误                                                                                                                |

| 事件（领域 `inbound`)       | 字段                                                  | 当                 |
| ---------------------- | --------------------------------------------------- | ----------------- |
| `InboundTurnCompleted` | `source`, `key`, `turn_id`, `event_ids`, `agent_id` | 某个批次的代理已回复且其文本已存储 |
| `InboundTurnFailed`    | `source`, `key`, `turn_id`, `详情`                    | 某个批次永久失败          |

**SSE 流会唤醒你；RPC 会返回结果。** 事件不携带代理文本，并且 `/events/domain` 只携带简短详情（`x:mentions/mentions 轮次 <id>：3 个事件`）。消费者订阅该流（或轮询），调用 `inbound_results` 在被唤醒时处理每个结果，并使用 `inbound_result_ack`.

队列的闸门仍适用于外部来源：其发送者就是来源本身，因此超过 `gate.rate_limits.per_sender` 每分钟事件（在出货的流水线中为 30 个）会涌入摘要，而近乎重复的文本会因习惯化而失去显著性。某个已固定来源事件的摘要轮次仍会在其固定代理上运行。

## 钱包

`wallet = false` 移除 `web3` 域系列和工具组，因此 `wallet.*`, `web3.*` 以及 `x402.*` 回答 `COMPONENT_DISABLED:wallet`。位于 `http_request` 会在任何报价或签名之前拒绝（返回的 402 响应为未付款），并且 `tinywallet` 绝不会被下载、验证或加载。钥匙串材料、支出限额（`OPENHUMAN_X402_*_MAX`）以及 RPC 端点（`OPENHUMAN_WALLET_RPC_*`）都会保持原样，因此设置 `wallet = true` 再次将于下次核心启动时恢复钱包。

## 语音

`voice = false` 移除 `语音` 域系列，因此 `voice.*` 回答 `COMPONENT_DISABLED:voice`，并且 `tinyvoice` 绝不会被下载、验证或加载。此前用于听写和语音服务器设置的配置控制器已被弃用；其旧键仅作为迁移输入保留，不再控制语音启动。 `voice_server.stt_engine` 当未选择明确的 STT 提供方时，仍作为回退引擎。 `about_app` 以及 `modules.list` 将语音报告为已禁用。Cargo `语音` 功能保持不变。

## 沙盒

`sandbox = false` 表示 TinyBox 模块绝不会被下载、验证、加载或初始化，并且 `sandbox.*` 回答 `COMPONENT_DISABLED:sandbox`。沙盒执行采用文档所述的回退方式，与 `[sandbox] enabled = false` 或 `OPENHUMAN_SANDBOX=off`：命令在未加笼的情况下运行，Rust 路径检查、始终禁止的凭据底线以及安全策略仍然适用。TinyBox 不注册任何工具，也不启动任何后台服务。 `modules.list` 报告 `tinybox` 为 `disabled`, `about_app` 将沙盒能力标记为已禁用，而宿主 `DomainSet` 仍然生效。SaaS 核心仍然需要每用户容器路径，因此在模块禁用时，它会安全失败，而不是在无沙盒状态下运行。
