服务器配置

🔵 平台由两个文件进行配置:config/server.yaml(核心、提供商、 日志、auth、widget)和 config/budgets.yaml(预算组和 模板绑定)。调度在 config/cron.yaml 中,变更监控在 config/monitors.yaml 中,工作流在 config/workflows/ 中。密钥在 .env 中 (机器本地),或以加密形式存放在 config/secrets.yaml 中 (参见 配置版本化)。


3.1 配置来源 — 本地文件夹或 git 仓库

整个配置树(server.yaml、agents/、web/、workflows/、 budgets.yaml、cron.yaml、monitors.yaml)从同一个根加载。默认情况下是本地 ./config;也可以改为克隆 git 仓库, 以便配置与二进制文件分开进行版本化和评审。

优先级:CLI 标志 → 环境变量 → 本地 ./config。

CLI 标志 变量 默认值 作用
--config-repo <url> AGENT_OS_CONFIG_REPO — 从 git 克隆配置(否则使用本地文件夹)
--config-ref <ref> AGENT_OS_CONFIG_REF main 配置固定到的分支 / 标签 / 提交
--config-dir <path> AGENT_OS_CONFIG_DIR ./config 配置的本地文件夹(在 git 模式下被忽略)
--config-poll <secs> AGENT_OS_CONFIG_POLL off 定期 git pull + 配置热重载
AGENT_OS_CONFIG_REPO=https://github.com/org/agent-config \
AGENT_OS_CONFIG_REF=v1.2.0 \
./agent-os serve

仓库被克隆到 ~/.agent-os/configs/<repo>-<ref> 的本地 分支(--config-ref),而不是 detached HEAD,以便主机可以提交和 推送;提交的 SHA 在启动时写入日志,并可在 /health 的 config 字段中看到。配置轮询器执行 merge --ff-only/rebase —— 本地提交 (配置版本)不会被覆盖。密钥可以加密形式存储在配置仓库中 (config/secrets.yaml,参见 配置版本化);.env 和 env.overrides.yaml 仍留在仓库之外。


3.2 config/server.yaml

provider — LLM

provider:
  base_url: "https://api.deepseek.com"   # любой OpenAI-совместимый endpoint
  model: "deepseek-v4-flash"
  api_key: ""                            # опционально; предпочтительно .env
  pricing:                               # оценка стоимости инференса
    prompt_per_million: 0.14
    cached_per_million: 0.014
    completion_per_million: 0.28
  fallbacks: ["strong"]                  # цепочка failover при сбое провайдера
  circuit_breaker:                       # пропускать упавший провайдер
    failure_threshold: 5                 # подряд ошибок до размыкания (0 = выкл.)
    cooldown_secs: 30                    # пауза до пробного запроса
    half_open_max: 1                     # успешных проб для замыкания
字段 默认值 描述
base_url https://api.deepseek.com OpenAI 兼容提供商的基础 URL
model deepseek-v4-flash 模型名称
api_key 空 密钥;空值 → 从 env 获取
pricing.prompt_per_million 0.14 每 1M prompt token 的 $
pricing.cached_per_million 0.014 每 1M 缓存 prompt token 的 $
pricing.completion_per_million 0.28 每 1M completion token 的 $
fallbacks [] 用于 failover 的 models: 名称(按顺序)
circuit_breaker.failure_threshold 5 断开前连续错误数;0 = 关闭
circuit_breaker.cooldown_secs 30 试探请求前的暂停(half-open)
circuit_breaker.half_open_max 1 闭合前连续成功试探数

Circuit breaker(原生)。 在连续 failure_threshold 次错误后, 提供商被标记为「open」,并在 failover 链中被跳过 —— 请求 会转到下一个提供商,而不是反复冲击已故障的提供商。经过 cooldown_secs 后, 用一个请求(half-open)试探该提供商:成功则闭合电路,错误 则再次断开。断路器的键是 model-config 名称(default、strong、……)。 该机制是核心的一部分(agent_os_core::circuit_breaker)。这里设置的是 全局默认值;具体 agent 可以通过钩子 kind: circuit_breaker 覆盖它(参见 06-hooks-routing §6.17)。由 操作员通过 Kernel::provider_breakers().reset(name) 重置。

密钥选择顺序:provider.api_key → AGENT_OS_API_KEY → DEEPSEEK_API_KEY。全部为空 → agent 以模拟响应作答。

models — 命名模型

附加模型配置:每个都会成为单独的提供商,拥有自己的 base_url / api_key / model / pricing。Agent 模板通过其 agent.yaml 中的 model: 字段选择所需的模型。上面的 provider: 是默认值(它也会 注册为 "default")。

models:
  cheap:
    base_url: "https://api.deepseek.com"
    model: "deepseek-chat"
  strong:
    base_url: "https://api.openai.com/v1"
    model: "gpt-4o"
    api_key: ""

kernel — 调度器和会话

kernel:
  scheduler: round_robin
  concurrency_limit: 3
  time_slice_secs: 30
  live_reload: true
  session_ttl_secs: 300
  session_cleanup_interval_secs: 60
  max_output_tokens: 4096
字段 默认值 描述
scheduler round_robin 调度策略:round_robin / priority_preemptive / fair_share / deadline_driven
concurrency_limit 3 最大并发 LLM 流数
time_slice_secs 30 抢占量子(秒)—— 仅在 priority_preemptive 时;0 = 关闭
live_reload true 热重载 config/agents/** 和 config/web/**
session_ttl_secs 300 会话最大空闲时间(秒),超过后会被杀死
session_cleanup_interval_secs 60 会话清理器的运行频率(秒)
max_output_tokens 4096 单次 inference 的响应长度全局上限(主要杠杆:必要时会提高进程的 per-turn 预算;agent.yaml 中的 per-agent max_output_tokens 会覆盖它;0 → 默认值)

logging — 日志和存储

logging:
  filter: "agent_os=debug"
  jsonl_dir: "logs"
  duckdb_path: "logs/events.db"
  messages_db_path: "logs/messages.db"
  messages_retention_days: 30
  state_db_path: "logs/state.db"
  temp_dir: "logs/temp"
  access_db_path: "logs/access.db"
  cron_db_path: "logs/cron.db"
  user_db_path: "logs/users.db"
  team_db_path: "logs/teams.db"
  monitor_db_path: "logs/monitors.db"
  workflow_db_path: "logs/workflows.db"
  bench_db_path: "logs/bench.db"
  side_effect_db_path: ""            # пусто = идемпотентность выключена
  outbox_db_path: ""                 # пусто = outbox/саги выключены
  dead_letter_db_path: ""            # пусто = dead-letter только в памяти
  durable_db_path: ""                # пусто = durability (durable-агенты) выключена
  pending_db_path: ""                # пусто = pending (approvals/ввод/await_event) только в памяти
字段 默认值 描述
filter agent_os=debug 日志级别过滤器(tracing)
jsonl_dir logs JSONL 日志文件夹(process-*.jsonl、access.jsonl)
duckdb_path logs/events.db DuckDB 事件存储(空字符串 = 关闭)
messages_db_path logs/messages.db 共享 message store:转录 + FAQ 缓存(空 = 关闭)
messages_retention_days 30 转录在清理前保留多少天
state_db_path logs/state.db scoped state store(session / user / agent);空 = 关闭
temp_dir logs/temp 用于文件生成器的会话 scratch 存储;file_tool 从中读取(空 = 关闭)
access_db_path logs/access.db HTTP 请求的 DuckDB 日志(空 = 关闭)
cron_db_path logs/cron.db cron:动态任务 + 运行历史(空 = 动态任务无法在重启后保留)
user_db_path logs/users.db 规范用户注册表:渠道 id → 一个 user id(空 = 关闭)
team_db_path logs/teams.db 命令注册表:成员关系(多对多)和授权;空 = 命令被禁用(参见 §45)
monitor_db_path logs/monitors.db change 监控器游标(空 = 游标不持久化)
workflow_db_path logs/workflows.db 工作流运行历史(空 = 关闭)
bench_db_path logs/bench.db 基准测试历史和快照(空 = 不写入历史)
side_effect_db_path (空) 副作用幂等性存储(DuckDB);空 = 去重已关闭
outbox_db_path (空) 持久化 outbox(DuckDB):副作用的至少一次投递 + 补偿事务/补偿操作;未完成的意图在启动时补齐;空 = 已关闭
dead_letter_db_path (空) 持久化死信队列(DuckDB),用于被监督的毒丸轮次隔离的输入;空 = 仅内存
durable_db_path (空) 持久化会话存储(DuckDB),用于带有 durable: true 的代理;空 = 进程持久化已关闭
pending_db_path (空) 未完成请求的存储(审批 / 输入 / await_event);空 = 仅内存。持久化代理需要它来在重启后存活

teams — 命令访问

通过存在 logging.team_db_path 来启用(参见 §45「命令与访问」)。 该节选择哪些资源受团队授权管控。

teams:
  gated_tools: ["gdrive_upload", "notion_write"]   # только по гранту команды
字段 默认值 描述
gated_tools [] 用户在团队中仅通过 tool 授权才能使用的工具;空 = 工具管控已关闭

对代理和模块化路由的访问不在此处配置:代理 — 通过 template 授权 (加上每代理的 login),路由 — 通过声明 Access::Team。

sandbox — 主机上进程的限制

对工具、脚本(shell()/exec())和浏览器启动的进程的全局策略。 无根限制:Unix — setrlimit (CPU/AS/NPROC/NOFILE/FSIZE)+ 新会话/进程组;Windows — Job Object (内存/进程/CPU,关闭时终止)。文件系统限制(read_roots/write_roots)— 通过 Landlock(Linux)和路径检查;树级内存/CPU/pid — cgroup v2。 网络目前不受限制。 空/enabled: false = 已关闭。 每代理覆盖 — agent.yaml → sandbox:。

sandbox:
  enabled: true
  cpu_secs: 30          # лимит CPU-времени
  mem_mb: 512           # адресное пространство / память процесса
  max_procs: 64         # число процессов
  max_fds: 256          # открытых файловых дескрипторов
  max_file_mb: 64       # размер создаваемого файла (Unix RLIMIT_FSIZE)
  read_roots: []        # allowlist чтения (пусто = без ограничений); file_tool, fs_read/fs_list
  write_roots: []       # allowlist записи (пусто = без ограничений); file_tool, fs_write
  cgroup_parent: ""     # cgroup v2 для лимитов (Linux); пусто = cgroups выкл
  cpu_quota_percent: null  # доля одного ядра (cgroup cpu.max), напр. 50 = 0.5 ядра
  allow_net: true       # false = процесс в изолированном netns (Linux, без сети)
  clear_env: true       # очистить окружение
  env_allow: [PATH, LANG, HOME]   # что оставить при clear_env

后端(Linux): 文件系统限制(read_roots/write_roots)通过 Landlock(无根,内核 ≥5.13)在 file_tool 和脚本 fs_* 中应用;内存/CPU/pid 限制 — 通过 cgroup v2(树级,包括后代),当设置了 cgroup_parent 时;网络(allow_net: false)— 通过 unshare(CLONE_NEWUSER | CLONE_NEWNET)(无路由)。在 Windows 上,资源限制由 Job Object 提供。

可观测性。 沙箱拒绝会被计数并记录日志;GET /metrics 返回 agentos_sandbox_violations_total{kind="…"}(例如 fs_read_denied、 fs_write_denied)。

cgroup v2 — 部署要求。 cgroup_parent 必须 委托给服务(无根模型):目录 D 不含进程,启用 D/cgroup.subtree_control(库启用 +memory +pids +cpu 中可用的), 属于服务用户,且服务本身 在 D/<child> 内启动。systemd 示例:单元上的 Delegate=yes + sandbox.cgroup_parent: /sys/fs/cgroup/<slice>/<unit>。

auth — 管理 API 和 Web 登录

auth:
  admin_token_env: ""              # имя env-переменной с токенами (опционально)
  admin_tokens:                    # карта имя → токен (или { token, templates })
    admin: "<token>"
    drom-op:
      token: "<token>"
      templates: [drom-agent]
  users:                           # web-логин: логин → пароль (или { password | password_env, templates })
    admin:
      password_env: AGENT_OS_ADMIN_PASSWORD   # пароль из env, а не из git
    drom-viewer:
      password: "<password>"
      templates: [drom-agent]
  web_user_secret_env: ""          # env с HMAC-секретом для токенов конечных пользователей

web_user_secret_env — 包含 HMAC 密钥的环境变量名,用于签名 最终用户身份令牌(POST /v1/user-ticket)。如果为空,服务器 自行生成并将密钥存储在 data/web_user_secret — 身份 默认通过 HMAC 签名。详情见 08-http-api。

管理端点接受 Authorization: Bearer <токен> 或 cookie agentos_session。

相同的来源(auth.users 和 auth.oidc)也服务于最终 用户在小组件中的登录:代理可以通过 login.required: true 要求授权(providers — OIDC,allow_users — 来自 auth.users 的登录名/密码)。参见 04-building-an-agent 和 19-widget-and-channels。

auth.oidc — 通过 Google / Yandex / Tinkoff 登录

除了密码(auth.users)之外,管理 UI 还可以通过外部 身份提供商登录(OIDC / OAuth 2.0,Authorization Code + PKCE)。角色通过 外部身份的授权授予 — subject(精确 provider:sub)、email 或 email_domain(后缀)。第一个匹配获胜;无匹配 = 登录 被拒绝(故障关闭)。

auth:
  oidc:
    base_url: ""                         # пусто = авто-вывод из запроса (Host + X-Forwarded-Proto)
    providers:
      google:
        issuer: "https://accounts.google.com"   # discovery
        client_id: "<client id>"
        client_secret_env: "GOOGLE_OIDC_CLIENT_SECRET"
      yandex:                                   # discovery нет — явные endpoints
        authorization_endpoint: "https://oauth.yandex.ru/authorize"
        token_endpoint: "https://oauth.yandex.ru/token"
        userinfo_endpoint: "https://login.yandex.ru/info?format=json"
        client_id: "<client id>"
        client_secret_env: "YANDEX_OIDC_CLIENT_SECRET"
        scopes: ["login:email", "login:info"]
        userinfo_sub_field: "id"
        userinfo_email_field: "default_email"
      tinkoff:
        issuer: "https://id.tinkoff.ru/auth/realms/tinkoff"
        client_id: "<client id>"
        client_secret_env: "TINKOFF_OIDC_CLIENT_SECRET"
    grants:
      - email_domain: "example.com"
        role: "admin"                        # полный доступ
      - email: "ops@example.com"
        templates: [drom-agent]              # оператор: только эти шаблоны

connectors — 以用户身份访问外部 API

代理可以以最终用户的身份在第三方服务(Google Drive、Yandex.Disk 等)中操作。 连接器复用来自 auth.oidc.providers 的 OIDC 客户端,并请求自己的 scope。令牌按用户存储在 logging.connector_db_path 中,使用 AES-256-GCM 加密(密钥来自 auth.connector_encryption_key_env 或自动密钥 data/connector_secret)。

connectors:
  gdrive:
    provider: google
    scopes: ["https://www.googleapis.com/auth/drive.file"]
  yandex_disk:
    provider: yandex
    scopes: ["cloud_api:disk.app_folder"]

工具(kind: oauth_connect / kind: connector_request)通过 tool_files 接入 agent.yaml —— 参见 05-tools-reference。连接器的 Redirect URI: {base_url}/v1/connectors/callback。

widget — 可嵌入组件

widget:
  default_model: "drom-agent"   # агент по умолчанию

唯一的服务器端设置是 default_model:当未显式指定模型时,组件所调用的代理。 组件的完整解析(嵌入、模型选择顺序、演示、视觉块)——见 §19。

proxy — HTTP 代理限制

proxy:
  allow_hosts: []   # пусто = любой публичный хост

/proxy 和 /proxy/content 在每次重定向时始终阻止私有、loopback、link-local 和 reserved 范围(包括 169.254.169.254)。 allow_hosts 额外按主机限制(精确匹配或 子域)。

inbound — 传入 webhook

inbound:
  token_env: ""   # env с bearer-токеном для POST /v1/inbound/:template (пусто = открыто, dev)

POST /v1/inbound/:template —— 外部系统同步触发代理的一次运行。 参见 08-http-api。

approval — 操作审批(human-in-the-loop)

approval:
  required_tools: ["refund", "send_email", "delete_*"]  # glob-паттерны тулов, требующих одобрения
  operator_notify:              # доставка операторских запросов в мессенджер/webhook
    - type: telegram
      chat_id: -100123456789
      token_env: TG_OPERATOR_TOKEN
    - type: webhook
      url: https://ops.example.com/approvals
  context_template: |           # необязательный MiniJinja-шаблон контекста
    📝 {{ user_request }}
    {% for m in messages %}{{ m.role }}: {{ m.content }}{% endfor %}

modules — 模块和原生插件

平台是模块化的:功能(workflow、cron、频道、docs、widget、approvals 等) 和原生扩展作为模块接入——linked crate 或 DLL。

modules:
  enabled: [docs, widget, analytics]  # активировать на буте (+ requires транзитивно)
  auto_install: false                  # не ходить в сеть за артефактами на старте

参见 20-native-plugins 和 32-building-a-module。


3.3 config/budgets.yaml

budget_groups:
  - name: my-shared
    refill_per_sec: 50
    max_bucket: 50000
    max_total: 5000000        # null/отсутствует = без лимита
    warn_at: 0.8              # порог on_budget_threshold для группы
    per_user:                 # опционально
      refill_per_sec: 20
      max_bucket: 20000
      max_total: 500000
      warn_at: 0.8            # порог on_budget_threshold для per_user
    per_session:              # опционально
      refill_per_sec: 30
      max_bucket: 10000
      max_total: 200000

agents:
  - template: my-agent        # перекрывает budget_group / budget из agent.yaml
    group: my-shared
    budget:                   # опционально
      refill_per_sec: 10
      max_bucket: 200
      max_total: 1000

预算字段

字段 描述
refill_per_sec 令牌桶补充速率,令牌/秒
max_bucket 令牌桶容量(burst)
max_total 整个生命周期的硬限制(缺失 = 无限制)
warn_at 占 max_total 的比例(0..1),达到该比例时触发 on_budget_threshold 钩子

warn_at 可以在每一层设置(группа / per_user / per_session), 并且钩子会为阈值被跨越的那一层调用(level = group / user / session)。


3.4 计划任务(config/cron.yaml)

Cron 按计划运行代理模板。在两个位置设置,两者 都支持热重载:

jobs:
  - name: morning-digest
    template: rss
    schedule: "0 0 9 * * *"      # sec min hour dom month dow (UTC)
    prompt: "Резюмируй сегодняшние новости."
    notify: [{ type: log }]

每个任务恰好指定 schedule / interval_secs / oneshot_at 之一, 并将结果投递到 notify-sink 列表(log、webhook、telegram、 vk、yandex_messenger、messages_db、agent、email、workflow)。历史记录在 logs/cron.db, 端点 GET /v1/cron。完整参考——11-cron-jobs。


3.5 变更监视器(config/monitors.yaml)

监视器是一种拉取触发器:监视外部源,仅在出现新元素时 启动代理的一次运行(与按计划独立触发的 cron 不同)。完整描述——监视器。

monitors:
  - name: news-watch
    source:
      type: rss
      url: "https://lenta.ru/rss"
    template: rss-agent
    prompt: "Кратко резюмируй эти новые статьи:\n{{items}}"
    notify: [{ type: telegram, chat_id: 123456 }]
    poll_secs: 300

监视器将游标存储在 logs/monitors.db 中:首次轮询仅「追赶」 (保存游标,不启动任何内容),之后仅在比游标更新的元素上启动。


3.6 环境变量

变量 用途
DEEPSEEK_API_KEY / AGENT_OS_API_KEY LLM 提供商密钥
AGENT_OS_BIND 绑定地址(默认 127.0.0.1:3000)
AGENT_OS_URL 反向代理后服务器的公共 origin(例如 https://platform.vectorres.ru)。主机将其作为 server_url 的基础传递给服务代理,以免链接指向 127.0.0.1:3000。优先于 config/server.yaml 中的 public_url:。如果未设置——从 AGENT_OS_BIND 推导
AGENT_OS_TLS_CERT / AGENT_OS_TLS_KEY HTTPS 的 PEM 证书和密钥路径
AGENT_OS_ADMIN_TOKEN 管理员令牌:逗号分隔列表,имя=токен。仅当变量名在 config/server.yaml → auth.admin_token_env: AGENT_OS_ADMIN_TOKEN 中指定时才读取(默认该字段为空,变量被忽略)。替代方案——auth.admin_tokens 映射
AGENT_OS_BENCH_MODE 1 = 无限制预算(用于本地启动基准测试)
AGENT_OS_CONFIG_REPO / _REF / _DIR / _POLL 配置来源(见 §3.1)
AGENT_OS_DISTRIB_DIR 发行版文件夹路径(默认 distrib)
(按 agent)来自 telegram.token_env 的名称 在 agent.yaml 中命名的 Telegram 机器人令牌
(按 agent)来自 email.imap_password_env 的名称 email 通道的 IMAP 密码
(按 agent)来自 vk.token_env 的名称 在 agent.yaml 中命名的 VK 社区 group access token
(按 agent)来自 yandex_messenger.token_env 的名称 在 agent.yaml 中命名的 Yandex Messenger 机器人 OAuth 令牌

.env 在启动时自动加载(dotenv)。它位于 .gitignore, 模板为 .env.example。


3.7 实时重载

在 kernel.live_reload: true(默认)下,服务器监视 config/ 并 无需重启即可应用更改:

如果配置来自 git 仓库,在 --config-poll / AGENT_OS_CONFIG_POLL 时,同一路径会在每次新提交后应用; 未变化的 SHA 会被跳过。


3.8 HTTP 请求日志记录

每个 HTTP 请求(方法、路径、状态、持续时间、客户端 IP、user-agent、 X-User-Id、管理令牌名称)都会写入 logs/access.jsonl 和 logs/access.db (两者均可通过在相应 logging 字段中填入空字符串来禁用)。令牌的 值不会被记录 — 只会记录其名称或 invalid 标记。


3.9 ONNX 服务(security.onnx)

用于 NER / 分类 / 嵌入的本地 ONNX Runtime。需要以 onnx 特性构建 服务器。ONNX Runtime 核心不会被链接,也不会随附:它会动态加载 (load-dynamic),并在首次使用时由 runtime 块解析/下载。模型会延迟加载,并在首次使用时 下载(固定修订版本),除非 download: never / offline: true。

security:
  onnx:
    models_dir: data/models
    download: auto            # auto | eager | never
    offline: false
    allow_hosts: []           # пусто = встроенные хосты HF (+ GitHub для runtime)
    runtime:                  # динамически загружаемое ядро ONNX Runtime
      # lib_path: /opt/onnxruntime/lib/libonnxruntime.so  # явный путь (без скачивания)
      version: "1.28.0"       # должно соответствовать ort (rc.13 → 1.28)
      variant: cpu            # cpu | gpu | cuda12 | cuda13 | directml
      # base_url: https://github.com/microsoft/onnxruntime/releases/download/v1.28.0
      # sha256: "..."
    models:
      - id: langid            # используется хуком language_guard (detector: onnx)
        task: text_classification
        # templates: [ngu-agent]        # per-agent селектор (необязательно)
        source:
          hf: { repo: onnx-community/language_detection-ONNX, revision: <commit>, path: onnx/model_quantized.onnx }
        tokenizer: tokenizer.json
        config: config.json
字段 描述
models_dir 产物目录(缓存;data/models/ 位于 .gitignore);ort/<version>/<variant>/ 也在那里
download auto(首次使用时)、eager(启动时)、never(仅本地)
offline 禁止网络(仅限随附的模型/ORT)
allow_hosts 允许的下载主机;为空 = 内置 HF 主机(对于默认来源,ORT GitHub 主机会自动添加)
runtime.lib_path ORT 核心的显式路径(跳过下载)
runtime.version / variant ORT 发行版及其变体(cpu/gpu/cuda12/cuda13/directml)
runtime.base_url / sha256 发行版镜像和归档校验和
models[].id 用于 OnnxService 和钩子的 id(onnx_model/ner_model)
models[].task ner | text_classification | embeddings
models[].source hf / http(+ sha256)/ local
models[].tokenizer / config 来源内的相对路径(tokenizer.json、vocab.txt、spm.model、…)
models[].templates 将模型限制到 agent 模板(按 agent 选择器)
models[].labels / map 标签覆盖 / 类别映射
models[].execution_provider / device_id EP(cpu/cuda/…)和 GPU 索引;需要 agent_os_onnx 特性

管理端点:GET /v1/onnx/models、GET /v1/onnx/metrics、 GET /v1/onnx/runtime。

模型 langid(mini-BERT,约 25 MB int8,200 种语言)默认注册, 并由带有 detector: onnx 的 language_guard 钩子使用 (06 §6.18)。如果 服务或模型不可用,该钩子保持中立。