功能配置

MCP 服务器、A2A 集成、备用提供商、用户管理、浏览器自动化、热重载、执行策略、审批流程、预算控制、思考模式、文本转语音、Docker 沙箱、画布、自动回复、广播、收件箱、绑定、配对、扩展、密钥库、Webhook 触发器、代理、会话管理和队列管理的配置。


[[mcp_servers]]

MCP(模型上下文协议)服务器连接提供外部工具集成。每个条目是 [[mcp_servers]] 数组中的一个独立元素。

[[mcp_servers]]
name = "filesystem"
timeout_secs = 30
env = []

[mcp_servers.transport]
type = "stdio"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/docs"]
[[mcp_servers]]
name = "remote-api"
timeout_secs = 60
env = ["GITHUB_PERSONAL_ACCESS_TOKEN"]

[mcp_servers.transport]
type = "sse"
url = "https://mcp.example.com/sse"
[[mcp_servers]]
name = "my-http-backend"
timeout_secs = 30

[mcp_servers.transport]
type = "http_compat"
base_url = "https://tools.example.com"
headers = [{name = "Authorization", value_env = "MY_API_KEY"}]

[[mcp_servers.transport.tools]]
name = "search"
description = "Search documents"
path = "/search"
method = "post"

Streamable HTTP

[[mcp_servers]]
name = "remote-tools"

[mcp_servers.transport]
type = "http"
url = "https://mcp.example.com/v1"

使用 Streamable HTTP 传输方式(MCP 规范 2025-03-26+)。与 SSE 不同,这是基于标准 HTTP POST 的更简单的请求/响应模式。

字段类型默认值说明
namestring必填MCP 服务器的显示名称。工具以 mcp_{name}_{tool} 的命名空间注册。
timeout_secsu6430请求超时时间(秒)。
envlist of strings[]传递给子进程的环境变量名称(仅 stdio 传输方式)。

传输方式变体(基于 type 的标记联合体):

type字段说明
stdiocommand(string)、args(list of strings,默认 [])启动子进程,通过 stdin/stdout 上的 JSON-RPC 通信。
sseurl(string)连接到 HTTP Server-Sent Events 端点。
Httpurl(string)Streamable HTTP 传输方式(MCP 规范 2025-03-26+)。基于 HTTP POST 的简单请求/响应。
http_compatbase_url(string)、headers(header 配置列表)、tools(工具配置列表)内置兼容适配器,用于没有原生 MCP 服务器的纯 HTTP/JSON 工具后端。每个工具映射到一个 HTTP 端点。

http_compat header 配置:

字段类型说明
namestringHTTP 请求头名称(例如 "Authorization")。
valuestring 或 null静态请求头值。
value_envstring 或 null用作请求头值的环境变量名称(密钥推荐使用此方式)。

http_compat 工具配置:

字段类型默认值说明
namestring必填暴露给 LLM 的工具名称。
descriptionstring""展示给 LLM 的工具描述。
pathstring必填HTTP 路径(例如 "/search")。
methodstring"post"HTTP 方法:get、post、put、patch、delete。
request_modestring"json_body"参数发送方式:json_body、query、none。
response_modestring"json"响应解析方式:json、text。
input_schemaobject{"type":"object"}工具输入参数的 JSON Schema。

[a2a]

Agent 间协议(A2A)配置,实现跨 LibreFang 实例的 Agent 间通信。

[a2a]
enabled = true
name = "LibreFang Agent OS"
description = "My production agent OS"
listen_path = "/a2a"

[[a2a.external_agents]]
name = "research-agent"
url = "https://agent.example.com/.well-known/agent.json"

[[a2a.external_agents]]
name = "code-reviewer"
url = "https://reviewer.example.com/.well-known/agent.json"
字段类型默认值说明
enabledboolfalse是否启用 A2A 协议。
namestring"LibreFang Agent OS"在 well-known Agent 名片中展示的服务级显示名称。
descriptionstring""在 well-known Agent 名片中展示的服务级描述。
listen_pathstring"/a2a"A2A 端点的 URL 路径前缀。
external_agentslist of objects[]要发现和交互的外部 A2A Agent 列表。

external_agents 条目:

字段类型说明
namestring外部 Agent 的显示名称。
urlstringAgent 名片端点 URL(通常为 /.well-known/agent.json)。

[[fallback_providers]]

后备提供商链。当主 LLM 提供商([default_model])失败时,按顺序尝试这些后备提供商。

[[fallback_providers]]
provider = "ollama"
model = "llama3.2:latest"
api_key_env = ""
# base_url = "http://localhost:11434"

[[fallback_providers]]
provider = "groq"
model = "llama-3.3-70b-versatile"
api_key_env = "GROQ_API_KEY"
字段类型默认值说明
providerstring""提供商名称(例如 "ollama"、"groq"、"openai")。
modelstring""该提供商的模型标识符。
api_key_envstring""API Key 的环境变量名称。本地提供商(ollama、vllm、lmstudio)留空。
base_urlstring 或 nullnull基础 URL 覆盖。为 null 时使用模型目录默认值。

[[users]]

RBAC 多用户配置。用户可以被分配角色,并绑定到各通道平台的身份标识。

[[users]]
name = "Alice"
role = "owner"
api_key_hash = "sha256_hash_of_api_key"

[users.channel_bindings]
telegram = "123456"
discord = "987654321"
slack = "U0ABCDEFG"
字段类型默认值说明
namestring必填用户显示名称。
rolestring"user"用户在 RBAC 层级中的角色。
channel_bindingsmap of string to string{}将通道平台名称映射到平台特定用户 ID,实现跨通道的用户身份绑定。
api_key_hashstring 或 nullnull用户个人 API Key 的 SHA256 哈希值,用于认证 API 访问。

角色层级(从最高到最低权限):

角色说明
owner完全管理权限。可以管理所有 Agent、用户和配置。
admin可以管理 Agent 和大多数设置。无法修改 owner 账户。
user可以与 Agent 交互。管理能力有限。
viewer只读访问。可以查看 Agent 回复,但无法发送消息。

[browser]

配置 Agent 工具 browser_* 使用的无头浏览器自动化引擎。

[browser]
enabled = true
headless = true
viewport_width = 1280
viewport_height = 720
timeout_secs = 30
idle_timeout_secs = 300
max_sessions = 5
# chromium_path = "/usr/bin/chromium"
字段类型默认值说明
enabledbooltrue启用内置 CDP 浏览器工具。设为 false 关闭所有 browser_* 工具。
headlessbooltrue以无头模式运行浏览器(不显示窗口)。
viewport_widthu321280浏览器视口宽度(像素)。
viewport_heightu32720浏览器视口高度(像素)。
timeout_secsu6430每个操作的超时时间(秒)。
idle_timeout_secsu64300浏览器会话闲置超过此秒数后自动关闭。
max_sessionsusize5最大并发浏览器会话数。
chromium_pathstring 或 nullnullChromium/Chrome 二进制文件路径。为 null 时自动检测。

[reload]

控制配置文件的自动监控和热重载。

[reload]
mode = "hybrid"
debounce_ms = 500
字段类型默认值说明
modestring"hybrid"重载模式。见下表。
debounce_msu64500检测到文件变更后,重载前的防抖窗口时长(毫秒)。

mode 值说明:

值说明
off不自动重载。更改需手动重启。
restart任何配置变更都进行完整守护进程重启。
hot仅对安全的部分进行热重载(通道、技能、心跳)。
hybrid尽可能热重载;对需要重启的部分标记提示(默认)。

可热重载的配置段(无需重启):

以下配置段在 daemon 运行时修改 config.toml 后会自动生效(每 30 秒轮询检测):

  • [channels] — 添加、删除或重新配置渠道适配器
  • [[skills]] — 技能注册表
  • [proxy] — HTTP/HTTPS/SOCKS5 代理设置
  • [browser] — 浏览器自动化设置
  • [web] — 网页搜索/抓取配置
  • [approval] — 审批策略
  • [cron] — 定时任务设置
  • [webhook_triggers] — Webhook 触发器
  • [extensions] — 扩展配置
  • [[mcp_servers]] — MCP 服务器连接
  • [a2a] — Agent-to-Agent 协议配置
  • [fallback_providers] — 备用提供商链
  • provider_urls — 提供商 URL 覆盖
  • default_model — 默认模型选择
  • tool_policy — 工具过滤规则
  • proactive_memory — 主动记忆阈值
  • provider_api_keys — 提供商 API key(会刷新驱动缓存)
  • usage_footer — 使用量页脚模式
  • sanitize — 输入清理规则

需要重启的配置段: home_dir、data_dir、api_listen、tls、telemetry。这些字段的变更会记录警告日志,但只在重启后生效。


[exec_policy]

控制 Agent 通过 exec 和 shell 工具允许执行的 Shell 命令。

[exec_policy]
mode = "allowlist"
allowed_commands = ["git", "python3", "node"]
timeout_secs = 30
max_output_bytes = 102400
no_output_timeout_secs = 30
字段类型默认值说明
modestring"allowlist"安全模式。见下表。
safe_binslist of strings["sleep","true","false","cat","sort","uniq","cut","tr","head","tail","wc","date","echo","printf","basename","dirname","pwd","env"]始终绕过白名单检查的命令(仅标准输入的 POSIX 工具)。
safe_bins_skip_approvalboolfalse可选开启:在 allowlist 模式下,若 shell_exec 的每个基础命令都在 safe_bins 中,则同时跳过人工审批提示。链式命令中只要有一个基础命令不在其中,仍走审批流程。
full_mode_skips_approvalbooltruemode = "full" 是否同时对 shell_exec 豁免全局 approval.require_approval 列表。设为 false 即可实现"命令不受限但仍需审批"。
allowed_commandslist of strings[]当 mode = "allowlist" 时额外允许的命令。
timeout_secsu6430每条命令的最大挂钟执行时间(秒)。
max_output_bytesusize102400stdout+stderr 合并输出的最大大小(字节)。默认 100 KB。
no_output_timeout_secsu6430进程在此秒数内无输出则被终止。0 = 禁用。

mode 值说明:

值别名说明
denynone、disabled阻止所有 Shell 执行。
allowlistrestricted仅允许 safe_bins 或 allowed_commands 中的命令(默认)。
fullallow、all、unrestricted允许所有命令。不安全——仅限开发使用。

mode 与审批是两个独立的决定。 历史上 full 同时意味着"跳过白名单校验"和"跳过审批提示";full_mode_skips_approval = false 将两者解耦,使 require_approval 在 full 下与在 allowlist 下被同样对待。 默认值保持 true,因为 approval.require_approval 默认就包含 shell_exec,而声明了 shell_exec 的独立 Agent 在 spawn 时会被提升为 full,翻转默认值会让标准安装在每条命令上都弹出审批。

与 exec_policy 的其他字段一样,该字段在 Agent spawn 或恢复时被复制进该 Agent 的 manifest,因此 POST /api/config/reload 不会把它追加到已经在运行的 Agent 上——需要杀掉该 Agent 让其重新 spawn,或者重启守护进程。 少了这一步,把它设为 false 看起来就像静默失效:没有任何新的审批提示出现,而最自然的解读会是"没有命令需要审批"。

危险命令黑名单与 mode 无关,在 deny、allowlist、full 下都会扫描每条命令——full 关闭的是白名单校验,而不是这份黑名单。 其覆盖范围包括 LibreFang 守护进程生命周期命令(librefang stop / start / restart,含 gateway 别名与带路径的调用形式),它们会连带重启同一守护进程上的所有 Agent 与频道适配器;以及针对守护进程自身 librefang.db 的写入型 SQL 与输出重定向。 对该数据库的只读检查(select、.schema、.dump)以及 librefang status 被有意排除在外。


[approval]

配置哪些工具在执行前需要明确的人工批准。引用 ApprovalPolicy 类型。

[approval]
require_approval = ["shell_exec"]
timeout_secs = 60
auto_approve_autonomous = false
auto_approve = false
second_factor = "none"
totp_issuer = "LibreFang"
totp_grace_period_secs = 300
字段类型默认值说明
require_approvallist of strings["shell_exec"]需要暂停执行并等待人工批准的工具名称列表。
timeout_secsu6460审批请求的超时时间(秒)。
auto_approve_autonomousboolfalseAgent 处于自主模式时自动批准工具执行。
auto_approveboolfalse自动批准所有工具执行(不安全,仅限开发使用)。
second_factorstring"none"二次验证方式:"none" 或 "totp"。设为 "totp" 时,审批操作需要输入验证器应用的 6 位 TOTP 码。
totp_issuerstring"LibreFang"注册时显示在验证器应用中的发行者名称。
totp_grace_period_secsu64300成功验证 TOTP 后,在此时间窗口内跳过重复验证。设为 0 表示每次都要求输入验证码。最大值:3600。
totp_toolslist of strings[]需要 TOTP 验证的工具(支持 glob 模式)。为空时 require_approval 中的所有工具都需要 TOTP。示例:["shell_exec"]。

TOTP 设置

当 second_factor = "totp" 时,需要先注册验证器应用:

  1. 生成密钥: POST /api/approvals/totp/setup — 返回 base32 密钥、otpauth:// URI、二维码(base64 PNG)和 8 个恢复码。
  2. 添加到验证器: 扫描二维码或在 Google Authenticator、1Password、Authy 等应用中手动输入密钥。
  3. 保存恢复码: 妥善保存 8 个恢复码——丢失验证器设备时可用恢复码代替 TOTP 码,每个恢复码只能使用一次。
  4. 确认注册: POST /api/approvals/totp/confirm,body 为 {"code": "123456"} — 验证应用生成的一次性密码。
  5. 启用强制验证: 在配置中设置 second_factor = "totp"(支持热重载)。

也可以在 Dashboard 的 设置 > 安全 中完成 TOTP 设置。

启用后,审批流程变更:

  • Dashboard: 点击"批准"时会弹出 6 位验证码输入框。
  • 频道(Telegram/Slack): 使用 /approve <id> <6位验证码> 代替 /approve <id>。
  • API: 在 POST /api/approvals/{id}/approve 的 body 中发送 {"totp_code": "123456"}。
  • 恢复码: 可用恢复码(格式:xxxx-xxxx)代替 TOTP 码。每个恢复码使用后即失效。

撤销 TOTP:使用 DELETE /api/approvals/totp(body: {"code": "..."})或在 Dashboard 中操作。重置需要验证当前 TOTP 或恢复码。

频率限制: 连续 5 次 TOTP 验证失败后锁定 5 分钟。

TOTP 密钥存储在加密保管库(~/.librefang/vault.enc)中,使用 AES-256-GCM 加密。


[budget]

设置 LLM API 费用的全局支出限制。所有限制默认为 0.0(无限制)。

[budget]
max_hourly_usd = 1.00
max_daily_usd = 10.00
max_monthly_usd = 50.00
alert_threshold = 0.8
default_max_llm_tokens_per_hour = 0
字段类型默认值说明
max_hourly_usdf640.0所有 Agent 每小时的最大 LLM 费用(美元)。0.0 = 无限制。
max_daily_usdf640.0所有 Agent 每天的最大 LLM 费用(美元)。0.0 = 无限制。
max_monthly_usdf640.0所有 Agent 每月的最大 LLM 费用(美元)。0.0 = 无限制。
alert_thresholdf640.8各限额的告警阈值(0.0-1.0 的比例)。设为 0.8 时,达到限额 80% 即记录告警日志。
default_max_llm_tokens_per_houru640全局覆盖每个 Agent 的每小时 Token 预算。大于 0 时覆盖所有 Agent 自身的 Token 限制。0 = 保持各 Agent 自身的限制。

按提供商限额 ([budget.providers.<id>])

当你同时使用免费的本地提供商(如 litellm、ollama)与付费提供商(如 moonshot、openai)时,可以只给付费方设置花销上限,而不影响免费方。提供商 ID 必须与 Agent [model] 中的 provider 字段一致。字段缺省或为 0 表示无限制。

[budget.providers.moonshot]
max_cost_per_day_usd = 2.0
max_tokens_per_hour = 500000

[budget.providers.openai]
max_cost_per_hour_usd = 1.0
max_cost_per_month_usd = 50.0

[budget.providers.litellm]
# 省略或全部为 0 = 无限制
字段类型默认值说明
max_cost_per_hour_usdf640.0该提供商每小时费用上限。0.0 = 无限制。
max_cost_per_day_usdf640.0该提供商每天费用上限。0.0 = 无限制。
max_cost_per_month_usdf640.0该提供商每月费用上限。0.0 = 无限制。
max_tokens_per_houru640该提供商每小时 Token 上限(输入+输出)。0 = 无限制。

命中按提供商的上限时,调用将以 QuotaExceeded 错误返回,错误信息中包含提供商名;其他提供商的用量不受影响。


[thinking]

配置支持扩展思维(思维链推理)的模型(例如启用了 thinking 模式的 Claude 3.7 Sonnet)。

[thinking]
budget_tokens = 10000
stream_thinking = false
reasoning_mode = "high"        # 可选:none | low | high | max
字段类型默认值说明
budget_tokensu3210000分配给思维/推理阶段的最大 Token 数。
stream_thinkingboolfalse是否将思维 Token 流式传输给客户端(在 API 响应流中可见)。
reasoning_modestring未设置显式推理强度:"none"、"low"、"high"、"max"。未设置时,线路上仍有预算的驱动按 budget_tokens 推导——详见下方各家映射。

reasoning_mode(#7946)

budget_tokens 无法表达"完全不推理"——低于 1024 只是省略推理开关,而默认开启推理的模型(所有 DeepSeek V4 型号)依然会推理。 它也无法达到某些提供商的最高档,因为推导出的档位最高只到 high。 reasoning_mode 是直接的开关,各驱动会把它翻译成自己线路上真正接受的形式。

可在三处设置,解析顺序为按调用 > 按 Agent > 全局 > 编译默认值:

  • 按调用——POST /api/agents/{id}/message(以及 /message/stream)请求体中的 reasoning_mode。同时传入旧的布尔 thinking 字段时,以 reasoning_mode 为准。
  • 按 Agent——该 Agent 的 agent.toml 中的 [thinking] reasoning_mode。按 Agent 的设置在 config.toml 中无效。
  • 全局——此处的 [thinking] reasoning_mode。

各提供商的线路映射:

模式DeepSeek V4(直连)经 OpenRouter其他 OpenAI 兼容端点
nonethinking: {"type":"disabled"}reasoning: {"effort":"none"}省略该字段
lowthinking: enabled + reasoning_effort: "low"reasoning: {"effort":"low"}reasoning_effort: "low"
highthinking: enabled + reasoning_effort: "high"reasoning: {"effort":"high"}reasoning_effort: "high"
maxthinking: enabled + reasoning_effort: "max"reasoning: {"effort":"xhigh"}reasoning_effort: "high"

请求体永远不会同时携带 reasoning_effort 和 reasoning.effort——OpenRouter 会对这种组合返回 HTTP 400。 Kimi/Moonshot 模型始终发送 thinking: disabled(这是多轮工具调用的正确性要求,而非偏好设置),DeepSeek R1 没有线路开关。 Ollama 驱动在分级档位上仍读取 budget_tokens——它的线路开关本就是 token 预算而非强度枚举——但它会遵循 none:该模式必须依附于一份实例化的 thinking 配置传递,若把这份配置当作纯预算读取,就会在明确要求不推理的回合上反而开启推理。 Anthropic 驱动按模型分流:Haiku 4.5 及更早的 Claude 模型与上述一致;而 Opus 4.6 及以后的模型已经没有 token 预算可发,模式是它们唯一的档位来源——low / high / max 直接映射到 output_config.effort;未设置模式时则省略该字段,交由 API 自身的默认值决定,而不从一个运维可能从未选择过的预算里推导档位。 Gemini 完全不读取 thinking 配置。 完整细节(包括为何 medium 不是可设置的模式)见 docs/architecture/reasoning-mode-resolution.md。


[tts]

配置语音合成(Text-to-Speech)功能。

[tts]
enabled = false
provider = "openai"          # openai | elevenlabs | google_tts
output_format = "mp3"        # mp3 | ogg_opus
max_text_length = 4096
timeout_secs = 30

[tts.openai]
voice = "alloy"
model = "tts-1"
format = "mp3"
speed = 1.0

[tts.elevenlabs]
voice_id = "21m00Tcm4TlvDq8ikWAM"
model_id = "eleven_monolingual_v1"
stability = 0.5
similarity_boost = 0.75

[tts.google]
voice = "en-US-Standard-F"
language_code = "en-US"
speaking_rate = 1.0
pitch = 0.0
format = "mp3"

[tts] 字段:

字段类型默认值说明
enabledboolfalse启用 TTS 合成。
providerstring 或 nullnull默认 TTS 提供商:"openai"、"elevenlabs" 或 "google_tts"。
output_formatstring 或 nullnull(→ "mp3")text_to_speech 工具 output_format 参数的默认值,对所有提供商生效:"mp3" 或 "ogg_opus"。"mp3" 不做任何转换——保留提供商返回的原始格式。工具调用中显式传入的值优先。
max_text_lengthusize4096单次 TTS 请求的最大文本长度(字符数)。
timeout_secsu6430每次 TTS 调用的请求超时时间(秒)。

当合成的音频要发送到消息平台时,请设置 output_format = "ogg_opus"。语音消息必须是 Ogg/Opus 格式,而多数提供商返回 MP3,平台会拒收——但 text_to_speech 本身仍报告成功,因为文件确实写入了。转换通过 ffmpeg 完成;若未安装 ffmpeg,则回退为提供商自身的格式。

这与下面的 [tts.elevenlabs] output_format 不同:后者是发给 ElevenLabs 的查询参数,有自己的取值集合,且已经直接请求 Opus,因此无需转换。

[tts.openai] 字段:

字段类型默认值说明
voicestring"alloy"语音名称。可选:alloy、echo、fable、onyx、nova、shimmer。
modelstring"tts-1"TTS 模型:"tts-1"(快速)或 "tts-1-hd"(高质量)。
formatstring"mp3"输出格式:mp3、opus、aac、flac。
speedf321.0语速倍率(0.25 到 4.0)。

[tts.elevenlabs] 字段:

字段类型默认值说明
voice_idstring"21m00Tcm4TlvDq8ikWAM"ElevenLabs 语音 ID(默认:Rachel)。
model_idstring"eleven_monolingual_v1"ElevenLabs 模型 ID。
stabilityf320.5语音稳定性(0.0-1.0)。越高越稳定,但表现力越低。
output_formatstring"opus_48000_32"发送给 ElevenLabs 的 ?output_format= 查询参数。其他取值:mp3_44100_128、mp3_22050_32、opus_24000_32、pcm_16000、pcm_44100、ulaw_8000。
similarity_boostf320.75语音相似度增强(0.0-1.0)。

[tts.google] 字段:

字段类型默认值说明
voicestring"en-US-Standard-F"Google TTS 语音名称(如 en-US-Standard-F、pl-PL-Wavenet-A)。
language_codestring"en-US"BCP-47 语言代码(如 en-US、pl-PL)。
speaking_ratef321.0语速倍率(0.25 到 4.0)。
pitchf320.0音高调整(半音,-20.0 到 20.0)。
formatstring"mp3"输出格式:mp3、opus、wav。

需要设置 GOOGLE_API_KEY 或 GOOGLE_CLOUD_API_KEY 环境变量。


[docker]

配置用于隔离代码执行的 Docker 容器沙箱。

[docker]
enabled = false
image = "python:3.12-slim"
container_prefix = "librefang-sandbox"
workdir = "/workspace"
network = "none"
memory_limit = "512m"
cpu_limit = 1.0
timeout_secs = 60
read_only_root = true
mode = "off"
scope = "session"
reuse_cool_secs = 300
idle_timeout_secs = 86400
max_age_secs = 604800
blocked_mounts = []
字段类型默认值说明
enabledboolfalse启用 Docker 沙箱用于代码执行。
imagestring"python:3.12-slim"沙箱容器使用的 Docker 镜像。
container_prefixstring"librefang-sandbox"容器名称前缀。完整名称为 {prefix}-{8 位 agent 摘要}-{8 位实例摘要},而 Docker 的容器名上限是 63 个字符,因此前缀超过 45 个字符会让每次沙箱创建都以 "Container name too long" 失败。
workdirstring"/workspace"容器内的工作目录。
networkstring"none"网络模式:"none"(隔离)、"bridge" 或自定义网络名称。
memory_limitstring"512m"内存限制(例如 "256m"、"1g")。
cpu_limitf641.0CPU 限制(例如 0.5、1.0、2.0)。
timeout_secsu6460每条命令的最大执行时间(秒)。
read_only_rootbooltrue将根文件系统挂载为只读。
modestring"off"激活模式。见下表。
scopestring"session"容器生命周期范围。见下表。
reuse_cool_secsu64300某个 Agent 释放的容器交给其他 Agent 之前需要静置的秒数。仅对 scope = "shared" 生效——session / agent 作用域把容器绑定在释放它的那个工作负载上;即便在 shared 下,Agent 重新取回自己刚释放的容器也不需要等待。
idle_timeout_secsu6486400池中容器闲置超过此秒数后被销毁(默认 24 小时)。设为 0 关闭闲置回收。它和 max_age_secs 是唯一会让池收缩的机制——参见下文关于容器堆积的说明。
max_age_secsu64604800池中容器最大存活时间,超时后强制销毁(默认 7 天);从 docker run 起算,而不是从最后一次使用起算。设为 0 关闭寿命回收。
blocked_mountslist of strings[]禁止绑定挂载到容器的宿主机路径。
cap_addlist of strings[]添加到容器的 Linux 能力(例如 ["NET_ADMIN"])。请谨慎使用。
tmpfslist of strings["/tmp:size=64m"]容器内的 tmpfs 挂载。每个条目格式为 "path:options"(例如 "/tmp:size=128m")。
pids_limitu32100容器内的最大进程数。防止 Fork 炸弹。

mode 值说明:

值说明
off禁用 Docker 沙箱(默认)。
non_main仅对非主(子)Agent 使用 Docker。
all对所有 Agent 使用 Docker。

scope 值说明:

值说明
session每个(Agent,会话)一个容器,该会话内的每次工具调用都重用它(默认)。
agent每个 Agent 一个容器,跨该 Agent 的各个会话重用。
shared每个(配置,工作区)一个容器,任何挂载相同工作区的 Agent 都可重用。

只有整个键完全匹配时容器才会被重用:上表的作用域归属、决定容器形态的 [docker] 字段(image、network、memory_limit、cpu_limit、workdir、read_only_root、cap_add、tmpfs、pids_limit、blocked_mounts),以及挂载进容器的宿主机工作区路径。工作区对所有作用域(包括 shared)都是键的一部分:容器带着创建时挂载的工作区,把它交给挂载了其他路径的调用方就等于把一个 Agent 的工作区暴露给另一个 Agent。

容器由两条路径销毁:enabled = true 时每 60 秒运行一次、按 idle_timeout_secs 与 max_age_secs 回收的清理循环,以及销毁全部池中容器的守护进程关闭流程。LibreFang 没有单独的“会话结束”信号,因此 session 作用域的容器会在最后一次工具调用之后继续存在,直到上述两者之一将其回收——把 idle_timeout_secs 设成你希望的存活窗口。

没有 LibreFang 会话的调用方(POST /api/tools/{name} 桥接以及其他带外调用)无法遵循 scope = "session",因此它会为该次调用创建容器并在调用后销毁,而不是把容器放宽到整个 Agent。

shared 不只是一种池化策略,更是一条信任边界。容器是整体交接的:上一次调用往可写路径和 /tmp tmpfs 里写下的一切都还在,它遗留的进程也还在跑。reuse_cool_secs 只是推迟两个 Agent 之间的交接,它不做任何清理,其他机制也不做。只有当挂载该工作区的所有 Agent 之间本来就彼此信任时才用 shared,否则请用 agent 或 session。

reuse_cool_secs 推迟交接,而不是把调用排队。如果一次调用只找到仍在静置的容器,它会再创建一个容器而不是等待,这个容器释放后同样进入池中。因此在 shared 下被多个 Agent 并发使用的工作区,以及任何作用域下被并发调用命中的键,都会持有不止一个容器。

除了回收循环之外没有任何东西会把容器还给宿主机,所以一个键持有的闲置容器数量会稳定在该键的并发峰值,并保持 idle_timeout_secs(默认 24 小时)那么久。启用沙箱时要按这个数量来规划容量:把 idle_timeout_secs 调小,而不是指望负载回落时容器会自己消失。


[canvas]

配置 Canvas(Agent 到 UI)工具,允许 Agent 在仪表盘中渲染 HTML。

[canvas]
enabled = false
max_html_bytes = 524288
allowed_tags = []
字段类型默认值说明
enabledboolfalse启用 Canvas 工具。
max_html_bytesusize524288HTML 负载的最大大小(字节)。默认 512 KB。
allowed_tagslist of strings[]用于净化的允许 HTML 标签名称。空列表 = 允许所有安全标签。

[auto_reply]

配置后台自动回复引擎,可以在无需人工交互的情况下自动回复传入消息。

[auto_reply]
enabled = false
max_concurrent = 3
timeout_secs = 120
suppress_patterns = ["/stop", "/pause"]
字段类型默认值说明
enabledboolfalse启用自动回复引擎。
max_concurrentusize3最大并发自动回复任务数。
timeout_secsu64120每个自动回复任务的默认超时时间(秒)。
suppress_patternslist of strings["/stop", "/pause"]抑制自动回复的传入消息匹配模式。

[broadcast]

配置消息广播,将单条传入消息同时路由到多个 Agent。

[broadcast]
strategy = "parallel"
routes = { "announcement-channel" = ["agent-a", "agent-b", "agent-c"] }
字段类型默认值说明
strategystring"parallel"投递策略。"parallel" = 同时发送给所有 Agent;"sequential" = 按顺序逐个发送。
routesmap of string to list of strings{}将对等方/通道标识符映射到接收消息的 Agent 名称列表。

[inbox]

基于文件的异步外部命令输入收件箱。将文本文件放入监控目录后,文件内容会作为消息分发给 Agent。已处理的文件会被移动到 processed/ 子目录以避免重复投递。

[inbox]
enabled = true
directory = "~/.librefang/inbox/"
poll_interval_secs = 5
default_agent = "assistant"
字段类型默认值说明
enabledboolfalse启用收件箱目录监控。
directorystring 或 nullnull监控的目录。默认为 $HOME_DIR/inbox/。支持 ~ 展开。
poll_interval_secsu645扫描目录新文件的间隔(秒)。最小值 1。
default_agentstring 或 nullnull文件中未找到 agent: 指令时路由到的 Agent 名称。

文件格式: 纯文本文件(.txt、.md、.json、.py 等)。第一行可以包含 agent:<name> 指令以指定目标 Agent;其余部分作为消息正文发送。没有指令的文件使用 default_agent。

安全限制: 大于 1 MB 的文件会被跳过。二进制文件(非文本扩展名)会被跳过。空文件会被移动到 processed/ 但不发送。

使用示例:

指定目标 Agent:

cat > ~/.librefang/inbox/task.txt << 'EOF'
agent:code-reviewer
Please review this code for security issues:

def login(user, password):
    query = f"SELECT * FROM users WHERE name='{user}' AND pass='{password}'"
    return db.execute(query)
EOF

发送到默认 Agent:

echo "Summarize today's system logs" > ~/.librefang/inbox/summarize.txt

定时任务:

# crontab -e
0 9 * * * grep ERROR /var/log/app.log > ~/.librefang/inbox/daily_errors.txt

CI/CD 构建后处理:

echo "agent:devops
Build failed, please analyze:
$(tail -100 build.log)" > ~/.librefang/inbox/build_$(date +%s).txt

批量处理:

for doc in ~/reports/*.md; do
  cp "$doc" ~/.librefang/inbox/
done

检查收件箱状态:

curl -s http://127.0.0.1:4545/api/inbox/status
# {"enabled":true,"pending_count":3,"processed_count":12,...}

[[bindings]]

Agent 绑定将特定的通道/账号/对等方组合路由到特定的 Agent。更具体的绑定(非空字段越多)优先级越高。

[[bindings]]
agent = "support-agent"
[bindings.match_rule]
channel = "telegram"
guild_id = "123456"

[[bindings]]
agent = "vip-agent"
[bindings.match_rule]
channel = "discord"
peer_id = "987654321"
roles = ["premium"]

顶层字段:

字段类型说明
agentstring匹配的消息路由到的目标 Agent 名称或 ID。
match_ruleobject匹配条件。所有指定的(非空)字段必须全部匹配。

match_rule 字段:

字段类型默认值说明
channelstring 或 nullnull要匹配的通道类型(例如 "discord"、"telegram"、"slack")。
account_idstring 或 nullnull通道内特定的 Bot 账号 ID(用于多 Bot 场景)。
peer_idstring 或 nullnull用于私聊路由的用户/对等方 ID。
guild_idstring 或 nullnull服务器/Guild ID(Discord/Slack)。
roleslist of strings[]基于角色的路由;用户必须拥有其中至少一个角色。

具体度评分(分值越高越先匹配):peer_id(+8)> guild_id(+4)> roles(+2)= account_id(+2)> channel(+1)。


[pairing]

配置 LibreFang 移动端伴侣应用的设备配对和推送通知。

[pairing]
enabled = false
max_devices = 10
token_expiry_secs = 300
push_provider = "ntfy"
ntfy_url = "https://ntfy.sh"
ntfy_topic = "my-librefang-notifications"
字段类型默认值说明
enabledboolfalse启用设备配对。
max_devicesusize10最大配对设备数。
token_expiry_secsu64300配对令牌有效期(秒)。默认 5 分钟。
push_providerstring"none"推送通知提供商:"none"、"ntfy" 或 "gotify"。
ntfy_urlstring 或 nullnullntfy 服务器 URL(当 push_provider = "ntfy" 时使用)。
ntfy_topicstring 或 nullnull推送通知的 ntfy 主题。

[extensions]

配置 MCP 服务器的重连行为和健康监控。

[extensions]
auto_reconnect = true
reconnect_max_attempts = 10
reconnect_max_backoff_secs = 300
health_check_interval_secs = 60
字段类型默认值说明
auto_reconnectbooltrueMCP 服务器断开连接时自动重连。
reconnect_max_attemptsu3210放弃前的最大重连尝试次数。
reconnect_max_backoff_secsu64300重连尝试之间的最大退避时间(秒)。
health_check_interval_secsu6460已连接扩展的健康检查间隔(秒)。

何时触发重连。 有两类失败会把服务器标记为错误状态并交给健康检查循环处理。 连接或重连握手失败时会在第一次失败就立即触发——此时并不存在需要保留的活动连接。 握手成功、之后才在传输层出问题的服务器(工具调用超时、传输通道关闭、与子进程之间的 stdio 管道断开),在观察到第一次这类失败时就会被标记为错误状态,但只有在连续 三次 传输层失败且期间没有任何一次调用成功之后,才会真正被拆除并重建。 单次超时与一个确实很慢的工具无法区分,而重连会连带丢弃服务器进程及其持有的状态,因此这里有意不让单次失败触发重连。 真正卡死的服务器会让每一次调用都失败,所以这三次会随着 agent 的重试很快累积。

工具返回的普通错误——参数不正确、文件不存在,以及任何由服务器以格式正确的 JSON-RPC 错误答复的情况——都不计入其中。 这类答复是由传输通道正常送达的,因此重连只会丢弃一个健康的服务器。

两类失败都会体现在 GET /api/mcp/health 中(status、last_error、consecutive_failures),而一次成功的工具调用会重置计数并刷新 last_ok。


[vault]

配置用于存储敏感密钥的加密凭据库。

[vault]
enabled = true
# path = "~/.librefang/vault.enc"
字段类型默认值说明
enabledbooltrue启用凭据库。如果 vault.enc 已存在则自动检测。
pathpath 或 nullnull自定义凭据库文件路径。默认为 ~/.librefang/vault.enc。

[webhook_triggers]

允许外部系统通过经过认证的 HTTP Webhook 在 /hooks/wake 和 /hooks/agent 端点触发 Agent 操作。

[webhook_triggers]
enabled = true
token_env = "LIBREFANG_WEBHOOK_TOKEN"
max_payload_bytes = 65536
rate_limit_per_minute = 30
字段类型默认值说明
enabledboolfalse启用 Webhook 触发器端点。
token_envstring"LIBREFANG_WEBHOOK_TOKEN"存放 Bearer 令牌的环境变量名称(不是令牌本身)。令牌长度必须大于等于 32 个字符。enabled = true 时必填。
max_payload_bytesusize65536入站负载的最大大小(字节)。默认 64 KB。
rate_limit_per_minuteu3230每个来源 IP 每分钟的最大 Webhook 请求数。

[proxy]

配置所有出站连接(LLM API、网页搜索、MCP 服务器等)使用的 HTTP 代理。环境变量 HTTP_PROXY、HTTPS_PROXY 和 NO_PROXY 也作为后备选项被识别。

[proxy]
http_proxy = "http://proxy.corp.example:8080"
https_proxy = "http://proxy.corp.example:8080"
no_proxy = "localhost,127.0.0.1,.internal.corp"
字段类型默认值说明
http_proxystring 或 nullnullHTTP 代理 URL。回退到 HTTP_PROXY / http_proxy 环境变量。URL 中的凭据在日志中会被脱敏。
https_proxystring 或 nullnullHTTPS 代理 URL。回退到 HTTPS_PROXY / https_proxy 环境变量。
no_proxystring 或 nullnull绕过代理的主机/域名逗号分隔列表。回退到 NO_PROXY / no_proxy 环境变量。

[session]

配置空闲或多余会话的自动清理。

[session]
retention_days = 30
max_sessions_per_agent = 100
cleanup_interval_hours = 24
字段类型默认值说明
retention_daysu320空闲会话在自动清理前的最大保留天数。0 = 无限制。
max_sessions_per_agentu320每个 Agent 的最大会话数(最旧的优先清理)。0 = 无限制。
cleanup_interval_hoursu3224后台清理任务的运行间隔(小时)。

[queue]

配置 Agent 命令队列,包括深度限制、TTL 和分通道并发度。

[queue]
max_depth_per_agent = 100
max_depth_global = 1000
task_ttl_secs = 3600

[queue.concurrency]
main_lane = 3
cron_lane = 2
subagent_lane = 3

[queue] 字段:

字段类型默认值说明
max_depth_per_agentu320每个 Agent 的最大排队任务数。队列满时新任务被拒绝。0 = 无限制。
max_depth_globalu320所有 Agent 的最大排队任务总数。0 = 无限制。
task_ttl_secsu643600未处理的任务在此秒数后过期。0 = 无限制。

[queue.concurrency] 字段:

字段类型默认值说明
main_laneusize3并发用户消息任务数。
cron_laneusize2并发定时任务数。
subagent_laneusize3并发子 Agent 调用任务数。

[tool_budget]

源码: librefang-runtime/src/tool_budget.rs

工具预算执行器限制单次 agent 轮次中工具调用返回的数据大小,防止意外的大体积工具输出填满上下文窗口并推高 token 成本。

工作原理

每个工具结果在放入上下文之前都要经过三层大小检查:

层级阈值行为
内联≤ 50 KB结果直接嵌入上下文。
溢出50 KB – 200 KB结果写入 /tmp/librefang-results/ 下的临时文件,并在上下文中用包含溢出路径和字节数的简短摘要替换。
截断> 200 KB仅保留前 200 KB,剩余内容静默丢弃,并输出包含工具名和原始大小的 WARN 日志。

溢出文件命名

溢出文件命名为 <agent_id>_<tool_call_id>_<timestamp>.txt,写入 /tmp/librefang-results/。轮次之间不自动清理;守护进程重启时清除该目录。

Agent 可在后续轮次中读取溢出文件——注入上下文的摘要中包含完整路径。

配置

当前版本不支持按 agent 配置工具预算。50 KB 和 200 KB 阈值在编译时固定。

为什么是这两个限制?

50 KB 内联文本约合 12 500 个 token——已是大多数模型上下文窗口的相当大一部分。超过此限制后,结果对 agent 的边际价值递减,而提示词成本线性增加。200 KB 硬上限防止单次失控工具调用耗尽整个上下文窗口。


[context_compression]

上下文压缩可在 Agent 上下文窗口的 token 用量增长过大时自动缩减,防止达到硬性上下文限制而出错,无需手动管理会话。

工作原理

在即将发送 LLM 调用时,ContextEngine 会估算已组装上下文(系统提示 + 会话历史 + 工具 schema)的 token 数。若用量超过模型上下文窗口的 80%,引擎会在发送请求前触发一次压缩。

压缩自动发生——无需任何配置即可启用。[context_compression] 章节为未来可调参数预留;当前所有阈值均在内部固定。

三层保护

层级触发条件机制
LLM 摘要压缩≥ 上下文窗口的 80%内部 LLM 调用将会话历史中最早的部分压缩为紧凑摘要消息,并注入回历史中替换原始轮次。
强制截断摘要后仍超出限制直接删除最旧的非系统消息,直到上下文适合窗口。仅在摘要本身体积过大无法解决问题时应用。
上下文守护最终安全兜底硬编码上限,确保构建的提示词永远不超过模型声明的最大值。超出的 token 被截断,并在 ERROR 级别记录警告。

摘要保留与迭代精炼

摘要消息以带有内部特殊标签的普通 assistant 轮次存入会话历史。后续压缩时,现有摘要会被纳入待重新摘要的内容,而非原样保留。这意味着长时间运行的会话会经历迭代精炼——较旧的摘要会被折叠进更新、更紧凑的摘要中。

压缩器 system prompt 现在指示 LLM 用对话中用户在用的语言写摘要。中文对话产出中文摘要;阿拉伯语对话产出阿语摘要。这条指令同时作用于单次的 summarize_messages 路径和 summarize_in_chunks 的合并步骤,分块压缩也覆盖到 — 长的非英文 session 不再夹带意外的英文摘要。

可插拔 ContextEngine

压缩算法实现于 ContextEngine trait 之后。当前实现使用基于 LLM 的摘要器。LibreFang 未来版本将支持配置替代压缩算法——包括基于 DAG 的压缩和 LCM(Latent Context Modeling)——等实现稳定后开放配置。

无需配置

上下文压缩对所有 Agent 始终启用,当前在 config.toml 中没有任何开关或可调参数。80% 阈值、强制截断回退和上下文守护上限均由运行时自动执行。


Cron 调度器

按 agent 的定时任务(interval、一次性、或 5 字段 cron 表达式)触发 agent 回合、system event 或 workflow 运行。调度器支持多目标分发带失败隔离、pre-script 把数据注入 LLM prompt,以及 silent marker 在运行时抑制分发。

完整 schema、扇出目标变体(channel / webhook / local file / email)、Webhook URL 的 SSRF 防护、pre-script 的 <home_dir>/scripts/ 路径白名单、以及 wake-gate vs pre-script 的取舍,见 Cron 调度器。


可观测性 — Tempo + 业务 span

LibreFang 自带可选的可观测性栈,一条 docker compose 起栈。捕获 HTTP 请求和 LibreFang 自己的工作 — agent 回合、tool 调用、channel 发送、cron 触发 — 作为可搜索的 Tempo trace。

栈组件(全部捆绑进二进制,不需要外部下载):

  • librefang-otel-collector — :4317 上的 OTLP/gRPC 收集器。daemon 把 span 导出到这。
  • librefang-tempo — 单二进制 Grafana Tempo,本地文件系统存储,24 小时保留。内部 :4317 接收,查询 API 在 :3200。
  • librefang-grafana — 预配置的 Grafana,Tempo 数据源已接好。

自动启动是 opt-in 的。 可观测性栈不会跟着 librefang start 自动起 —— 想让 daemon 帮你管理生命周期就在 config.toml 设 [telemetry] auto_start = true。否则用 librefang observability up / down 显式管理。捆绑的容器按 home_dir 加 Docker label,同一台机器上多个 LibreFang 安装不会抢同名容器;拆栈用 RAII 风格清理,daemon 异常退出时也能回收。

起栈:

librefang observability up      # 起捆绑的 compose 栈
librefang observability status  # 各容器健康检查
librefang observability down    # 拆栈

配置 daemon 导出到那里:

[telemetry]
otlp_endpoint = "http://localhost:4317"
service_name = "librefang"
sample_rate = 1.0           # 0.0–1.0;高量生产环境降低
prometheus_enabled = true   # 同时暴露 /api/metrics

Trace 搜索: 在 http://localhost:3000 打开 Grafana,把 Explore tab 切到 Tempo 数据源,按 trace ID、span 名、或属性搜。业务级 span 包括 agent.turn、tool.call、channel.send、cron.fire、provider.request,每个带 agent id、channel 名、model、结果作为可搜索属性。

cache_hit_ratio 指标: runtime 给每个 agent.turn span 计算

cache_hit_ratio = cache_read / (cache_read + cache_creation)   # 取值 [0.0, 1.0]

Some(1.0) 是完美前缀缓存命中;Some(0.0) 是 cold start,缓存激活但没命中;None 是 run 完全没启用缓存。出现在 trajectory 导出(metadata.cache_hit_ratio)和 Grafana per-agent 面板。