Alice

AI Local Interactive Cross-device Engine

同一个 AI 会话,随时随地。终端 ↔ 飞书,没有云锁定。

Alice 是一个飞书长连接连接器,把 CLI 型 LLM agent 变成飞书里的交互式 bot。支持 OpenCode(DeepSeek V4)、Codex、Claude、Gemini、Kimi。

核心思路

你的终端 agent 和飞书 bot 是同一个会话。在 IDE 里开始重构,手机上查进度,飞书里发下一条指令。Alice 把你的本地 CLI agent 桥接到飞书 WebSocket,让你不被任何设备束缚。

Alice 解决什么问题?

你已经装了 LLM agent CLI,终端里用得很好。但是:

  • 通过 WebSocket 连接飞书,实时接收消息
  • 把收到的消息路由到 chat(闲聊)或 work(任务执行)场景
  • 调用配置好的 LLM CLI,传入合适的 prompt、模型和权限设置
  • 把进度更新、最终回复、文件、图片发回飞书
  • 暴露本地 HTTP API 给 bundled skill 和自动化任务使用

主要特性

  • 多 bot:一个进程、一份 config.yaml、多个独立的 bot
  • 场景路由chatwork 两种模式,各自使用不同的 LLM profile
  • 六种后端:OpenCode、Codex、Claude、Gemini、Kimi — 可以按场景切换
  • 会话持久化:可恢复的 thread、session 别名、用量计数器
  • 实时状态卡片:显示后端活动和文件变更的心跳卡片
  • 自动化:类似 cron 的定时任务,支持 send_textrun_llmrun_workflow
  • Bundled Skill:可扩展的脚本 skill,通过 runtime API 交互
  • 子任务委托alice delegate 让 OpenCode agent 把子任务发给其他后端
  • 零云依赖:所有东西都在你的机器上运行

谁适合用 Alice?

  • 使用飞书的团队,想把 LLM agent 放进群聊而不必自己写集成
  • 开发者,已经在用 CLI agent,想在群里也能用
  • 运维人员,需要定时自动化 + 交互式 LLM 能力

导航

区域适合
教程新用户 — 5 分钟跑起来
操作指南针对具体目标的操作步骤
讲解深入理解概念和设计
参考完整的配置、API 和 CLI 文档
开发面向贡献者的架构和开发指南

快速开始

npm install -g @alice_space/alice
alice setup
# 编辑 ~/.alice/config.yaml 填入飞书凭据
alice --feishu-websocket

详见快速开始教程


English · GitHub

快速开始

5 分钟内让 Alice 运行起来并响应消息。

前置条件

  • Node.js(用于 npm install)或 Go 1.25+(用于源码编译)
  • 一个飞书应用,已启用 bot 能力和长连接
  • 至少安装并认证好一个 LLM CLI

如果还没搭建飞书应用,请先按照飞书开放平台配置教程操作。

第 1 步:安装

通过 npm(推荐):

npm install -g @alice_space/alice

通过安装脚本:

curl -fsSL https://cdn.jsdelivr.net/gh/Alice-space/alice@main/scripts/alice-installer.sh | bash -s -- install

从源码编译:

git clone https://github.com/Alice-space/alice.git
cd alice
go build -o bin/alice ./cmd/connector

第 2 步:初始化

alice setup

这会在 ~/.alice/ 下创建目录结构,写入默认的 config.yaml,同步内置 bundled skill,并在 Linux 上注册 systemd 用户单元。

第 3 步:配置

编辑 ~/.alice/config.yaml,至少填入以下内容:

bots:
  my_bot:
    name: "Alice"
    feishu_app_id: "cli_xxxxxxxx"      # 来自飞书开放平台
    feishu_app_secret: "your_secret"    # 来自飞书开放平台
    llm_profiles:
      chat:
        provider: "opencode"
        model: "deepseek/deepseek-v4-flash"
      work:
        provider: "opencode"
        model: "deepseek/deepseek-v4-pro"

默认配置包含指向 DeepSeek 模型的 OpenCode profile。如果使用其他 LLM CLI,请参见配置 LLM 后端

第 4 步:验证后端认证

确保你的 LLM CLI 能正常认证:

opencode --version    # 或 codex、claude 等

第 5 步:启动

alice --feishu-websocket

你应该能看到飞书 WebSocket 连接和每个 bot runtime 初始化的日志。

第 6 步:用 Work 模式测试

大多数人用 Alice 是冲着 work 模式来的 — 工程任务、调试、自动化。用法:

在飞书群聊里 @你的 bot:

@Alice #work 修复 auth.go 里的登录超时问题

发生了什么:

  1. Alice 为这个任务创建飞书 thread
  2. 启动配置好的 LLM 后端(如 DeepSeek V4)
  3. 进度和工具调用实时推送到 thread
  4. 会话被持久化 — 之后可以从终端恢复

试试内置命令:

/help       — 显示命令列表
/status     — 查看当前会话和后端信息
/stop       — 取消正在运行的任务

Chat 模式(闲聊)

Alice 也支持 casual chat 模式,bot 像群里的常住成员一样参与对话。直接 @ 即可:

@Alice 今天天气怎么样?

Chat 模式使用 chat LLM profile(轻量模型),整个群共享一个会话,不创建 thread。用 /clear 重置聊天会话。

提示:默认 config.example.yaml 同时启用了两种模式。大多数运维场景以 work 模式为主。如果只需要 work,设置 group_scenes.chat.enabled: false 即可。

接下来?

飞书开放平台配置

本教程将指导你创建一个可供 Alice 连接的飞书应用。预计耗时:15 分钟。

概览

Alice 需要一个具备以下条件的飞书应用:

  1. 已启用 bot 能力
  2. 已订阅 im.message.receive_v1 事件
  3. 已配置所需的消息权限
  4. 已启用长连接模式

第 1 步:登录飞书开放平台

访问飞书开放平台,使用你的组织账号登录。

Lark(国际版)用户:请访问 Lark Open Platform,并在 bot 配置中设置 feishu_base_url: "https://open.larksuite.com"

第 2 步:创建应用

  1. 点击创建应用
  2. 选择企业自建应用
  3. 为应用命名(例如 "Alice Bot")并上传图标
  4. 点击创建

第 3 步:启用 Bot 能力

  1. 在左侧边栏,进入功能机器人
  2. 开启启用机器人
  3. 根据需要配置 bot 的名称、头像和简介

第 4 步:添加事件订阅

  1. 进入事件订阅
  2. 点击添加事件
  3. 找到并选择接收消息im.message.receive_v1
  4. 点击确认

这样 Alice 就能收到 bot 可见的所有消息了。

第 5 步:配置权限

  1. 进入权限管理
  2. 搜索并开通以下权限:
权限用途
im:message读取发送给 bot 的消息
im:message:send_as_bot以 bot 身份发送消息
im:message:read读取消息内容
im:resource下载图片和文件
contact:user.id:readonly获取用户名称
contact:group.id:readonly获取群聊信息
  1. 点击保存

第 6 步:启用长连接

  1. 进入功能事件订阅
  2. 找到连接方式区域
  3. Request URL切换为长连接
  4. 保存更改

这一步至关重要。Alice 使用 WebSocket 长连接,而非 HTTP webhook。如果不启用长连接模式,Alice 将无法接收消息。

第 7 步:获取凭据

  1. 进入应用设置基础信息
  2. 复制你的 App ID(应用凭证 → App ID)
  3. 复制你的 App Secret(应用凭证 → App Secret)

将这些填入 config.yaml

bots:
  my_bot:
    feishu_app_id: "cli_xxxxxxxx"      # 你的 App ID
    feishu_app_secret: "your_secret"    # 你的 App Secret

第 8 步:发布并审批

  1. 进入版本管理与发布
  2. 点击创建版本,填写版本信息
  3. 创建后,点击申请发布
  4. 你的飞书组织管理员需要审批通过
  5. 审批通过后,组织内用户即可搜索并与 bot 互动

提示:开发期间,可以在应用设置中将个人用户添加为应用协作者,这样在发布前就可以测试 bot。

验证

使用 alice --feishu-websocket 启动 Alice 后,检查日志:

feishu-codex connector started (long connection mode)

如果看到 WebSocket 连接错误,请确认长连接模式已启用且凭据无误。

下一步

安装 Alice

三种安装方式。选择适合你的那一种。

npm(推荐)

npm install -g @alice_space/alice

安装后运行设置向导:

alice setup

此命令会创建 ~/.alice/、写入初始 config.yaml、同步内置 bundled skill、注册 systemd 用户单元(Linux),并安装 OpenCode delegate 插件。

要求: Node.js 18+

安装脚本

通过一行命令从 GitHub Releases 安装:

# 安装最新稳定版
curl -fsSL https://cdn.jsdelivr.net/gh/Alice-space/alice@main/scripts/alice-installer.sh | bash -s -- install

# 安装指定版本
curl -fsSL https://cdn.jsdelivr.net/gh/Alice-space/alice@main/scripts/alice-installer.sh | bash -s -- install --version v1.2.3

# 卸载
curl -fsSL https://cdn.jsdelivr.net/gh/Alice-space/alice@main/scripts/alice-installer.sh | bash -s -- uninstall

安装脚本会自动下载你平台的正确二进制文件(darwin-amd64、darwin-arm64、linux-amd64、linux-arm64、win32-x64)并校验 checksum。

安装后运行 alice setup 初始化配置和 skill 目录。

要求: curltar

从源码编译

git clone https://github.com/Alice-space/alice.git
cd alice
go build -o bin/alice ./cmd/connector

可选:安装到 PATH:

cp bin/alice /usr/local/bin/alice

要求: Go 1.25+

验证安装

alice --version

应输出版本号。如果已运行过 alice setup,还可以检查:

ls ~/.alice/
# config.yaml  skills/  log/  bots/

Runtime Home

Alice 根据构建渠道使用不同的默认 home 目录:

构建渠道默认 Home
Release(npm / 安装脚本)~/.alice
Dev(源码编译)~/.alice-dev

可通过 --alice-home 或环境变量 ALICE_HOME 覆盖。

配置 Chat 和 Work 场景

Alice 将收到的群消息路由到两个场景之一:chat 用于日常对话,work 用于明确的任务执行。

场景路由概览

收到消息
  ├─ 是内置命令?(/help、/status、/stop、/clear、/session)
  │   └─ 直接处理,不使用 LLM
  ├─ 匹配 work 触发词?(@Alice #work ...)
  │   └─ 路由到 work 场景
  └─ 其他情况
      └─ 路由到 chat 场景(如已启用)

两个场景都在 bots.<id>.group_scenes 下配置。

Chat 场景

Chat 场景适用于低门槛的持久化对话。每个群聊共用一个 session。

group_scenes:
  chat:
    enabled: true
    session_scope: "per_chat"
    llm_profile: "chat"
    no_reply_token: "[[NO_REPLY]]"
    create_feishu_thread: false
字段说明
enabled设为 true 启用 chat 场景
session_scope"per_chat" — 整个群共用一个 session。"per_thread" — 每个飞书话题一个 session
llm_profilellm_profiles 下的 LLM profile 名称
no_reply_token模型返回此字符串时,Alice 保持静默不回复
create_feishu_thread是否将回复包裹在飞书话题中

使用 /clear 重置 chat session 重新开始。

Work 场景

Work 场景适用于面向任务的执行。每个 work 任务拥有独立的话题和 session。

group_scenes:
  work:
    enabled: true
    trigger_tag: "#work"
    session_scope: "per_thread"
    llm_profile: "work"
    create_feishu_thread: true
字段说明
enabled设为 true 启用 work 场景
trigger_tag消息中必须(在 @bot 提及之后)包含的标签才能触发 work 模式
session_scope"per_thread" — 每个飞书话题独立的 session。"per_chat" — 共享 session
llm_profile要使用的 LLM profile 名称(通常使用更强大的模型)
create_feishu_thread自动为 work 回复创建飞书话题

Work 模式用法:

@Alice #work fix the login bug              → 启动 work,调用 LLM
@Alice #work                                 → 创建 work 话题但不调用 LLM
@Alice #work /session <backend-session-id>   → 将话题绑定到已有的后端 session

常见模式

仅 Chat 的 Bot

group_scenes:
  chat:
    enabled: true
    session_scope: "per_chat"
    llm_profile: "chat"
    no_reply_token: "[[NO_REPLY]]"
  work:
    enabled: false

Chat + Work 分离

Chat 使用轻量模型,work 使用更强模型:

llm_profiles:
  chat:
    provider: "opencode"
    model: "deepseek/deepseek-v4-flash"
  work:
    provider: "opencode"
    model: "deepseek/deepseek-v4-pro"
    variant: "max"
    permissions:
      sandbox: "danger-full-access"
      ask_for_approval: "never"

group_scenes:
  chat:
    enabled: true
    session_scope: "per_chat"
    llm_profile: "chat"
    no_reply_token: "[[NO_REPLY]]"
  work:
    enabled: true
    trigger_tag: "#work"
    session_scope: "per_thread"
    llm_profile: "work"
    create_feishu_thread: true

旧版触发模式

如果 chatwork 都被禁用,Alice 将回退到旧版触发系统:

bots:
  my_bot:
    trigger_mode: "at"       # at | prefix | all
    trigger_prefix: ""       # 仅当 trigger_mode 为 "prefix" 时使用
模式行为
at仅接受 @bot 的消息
prefix仅接受以 trigger_prefix 开头的消息
all接受所有消息(无过滤)

新部署建议优先使用显式的场景路由。

配置 LLM 后端

Alice 支持五种 LLM 后端。每个场景引用一个 llm_profile,指定使用哪个 provider、模型和设置。

支持的 Provider

ProviderCLI 工具备注
opencodeopencodeOpenCode CLI,用于 DeepSeek 及其他模型
codexcodexOpenAI Codex CLI。支持 reasoning_effortpersonalityprofile
claudeclaudeAnthropic Claude Code CLI。默认使用流式输出
geminigeminiGoogle Gemini CLI
kimikimiMoonshot Kimi CLI

每个 provider 需要单独安装和认证。Alice 不管理 provider 的认证。

Profile 配置

Profile 定义在 bots.<id>.llm_profiles 下:

bots:
  my_bot:
    llm_profiles:
      my_profile:
        provider: "opencode"
        model: "deepseek/deepseek-v4-pro"
        variant: "max"
        timeout_secs: 172800
        permissions:
          sandbox: "danger-full-access"
          ask_for_approval: "never"
          add_dirs: ["/data/corpus"]

通用字段

字段全部支持说明
provider后端名称:opencodecodexclaudegeminikimi
commandCLI 二进制路径。默认为 provider 名称(如 opencode
timeout_secs每次运行超时(秒)。默认:172800(48 小时)
model模型标识符(必填)
permissions.sandbox"read-only""workspace-write""danger-full-access"
permissions.ask_for_approval"untrusted""on-request""never"
permissions.add_dirsagent 可访问的额外目录
prompt_prefix每次 prompt 前添加的文本

Codex 专属字段

字段说明
reasoning_effort思考级别:"low""medium""high""xhigh"
personalityCodex CLI 配置中的命名人格预设
profileCodex CLI 配置中的命名子 profile

OpenCode 专属字段

字段说明
variantDeepSeek 变体:"max""high""minimal"

自定义二进制路径

如果你的 CLI 二进制不在 $PATH 中,请指定绝对路径:

llm_profiles:
  work:
    provider: "opencode"
    command: "/usr/local/bin/opencode"
    model: "deepseek/deepseek-v4-pro"

你也可以通过 env 扩展 $PATH

bots:
  my_bot:
    env:
      PATH: "/home/user/bin:/usr/local/bin:/usr/bin:/bin"

按 Profile 的覆盖

部分后端支持通过 profile_overrides 实现按 profile 的 runner 覆盖。这是一项高级功能,适用于同一个 provider 在不同场景下需要不同 CLI 配置的情况。

llm_profiles:
  executor:
    provider: "codex"
    model: "gpt-5.4-mini"
    profile: "executor"
    profile_overrides:
      executor:
        command: "/opt/bin/codex-executor"
        provider_profile: "executor-v2"
        timeout: 3600
        exec_policy:
          sandbox: "danger-full-access"
          ask_for_approval: "never"

后端进程的环境变量

bots.<id> 下的 env 字段将环境变量传递给每个 LLM 子进程:

bots:
  my_bot:
    env:
      HTTPS_PROXY: "http://127.0.0.1:8080"
      ALL_PROXY: "http://127.0.0.1:8080"

这对代理配置和 API 密钥管理特别有用。

示例

OpenCode + DeepSeek(chat)

llm_profiles:
  chat:
    provider: "opencode"
    model: "deepseek/deepseek-v4-flash"

Codex + reasoning

llm_profiles:
  work:
    provider: "codex"
    command: "codex"
    model: "gpt-5.4-mini"
    reasoning_effort: "high"
    permissions:
      sandbox: "danger-full-access"
      ask_for_approval: "never"

Claude

llm_profiles:
  work:
    provider: "claude"
    model: "claude-sonnet-4-6"
    prompt_prefix: "You are a senior software engineer. Be concise."
    permissions:
      sandbox: "danger-full-access"
      ask_for_approval: "never"

Gemini

llm_profiles:
  chat:
    provider: "gemini"
    model: "gemini-2.5-pro"

Kimi

llm_profiles:
  chat:
    provider: "kimi"
    model: "kimi-model-identifier"

自定义 SOUL.md 人格

每个 bot 可以拥有一个名为 SOUL.md 的人格文档,用于定义其行为、语气和回复偏好。

什么是 SOUL.md?

SOUL.md 是一个带 YAML frontmatter 的 Markdown 文件。它有两个用途:

  1. 人格设定:Markdown 正文被注入到 chat 场景的 LLM prompt 中,塑造 bot 的语气和行为
  2. 元数据:YAML frontmatter 控制机器可读的回复行为

文件位置

默认情况下,Alice 在 bot 的 alice_home 下查找 SOUL.md

~/.alice/bots/<bot_id>/SOUL.md

你可以通过 soul_path 自定义路径:

bots:
  my_bot:
    soul_path: "SOUL.md"            # 相对于 alice_home(默认)
    # soul_path: "/path/to/custom/SOUL.md"  # 绝对路径

如果启动时文件不存在,Alice 会从 prompts/SOUL.md.example 写入内嵌模板。

Frontmatter 字段

---
image_refs:
  - refs/avatar.png
  - refs/signature.jpg
output_contract:
  hidden_tags:
    - reply_will
    - motion
  reply_will_tag: reply_will
  reply_will_field: reply_will
  motion_tag: motion
  suppress_token: "[[NO_REPLY]]"
---
字段说明
image_refsbot 可以引用的本地图片路径列表。路径相对于 SOUL.md 所在目录
output_contract.hidden_tagsbot 回复中 Alice 在发送给飞书之前会剥离的标签
output_contract.reply_will_tag标记 bot 回复意图的标签
output_contract.reply_will_field标签内的字段名
output_contract.motion_tag动作/动画提示的标签
output_contract.suppress_tokenbot 输出此 token 时,Alice 完全抑制回复

完整示例

---
image_refs:
  - refs/avatar.png
output_contract:
  hidden_tags:
    - reply_will
    - motion
  reply_will_tag: reply_will
  reply_will_field: reply_will
  motion_tag: motion
  suppress_token: "[[NO_REPLY]]"
---

# 人格设定

你是 Alice,一个乐于助人的工程助手。你用简洁的中文夹杂英文技术术语进行交流。除非明确要求,否则不使用 emoji。

## 规则

- 代码片段保持在 30 行以内
- 优先解释思路再展示代码
- 永远不要道歉 — 直接解决问题

SOUL.md 何时生效?

  • Chat 场景:完整正文被添加到 prompt 前,Alice 解析 frontmatter 用于回复控制
  • Work 场景:SOUL.md 被刻意跳过。Work 模式用于任务执行,不需要人格角色扮演

测试你的人格

  1. 编辑 SOUL.md
  2. 重启 Alice(多 bot 模式需要重启;单 bot 模式支持热重载)
  3. 在 chat 场景中发消息 — bot 应体现更新后的人格
  4. 如需重置对话,使用 /clear

部署到服务器

将 Alice 作为持久化后台服务运行,确保它能在重启后存活并可靠运行。

systemd(Linux)

如果系统支持 systemd,alice setup 会自动创建 systemd 用户单元。

# 启动
systemctl --user start alice.service

# 设置开机自启
systemctl --user enable alice.service

# 查看状态
systemctl --user status alice.service

# 查看日志
journalctl --user-unit alice.service -n 100 --no-pager
journalctl --user-unit alice.service --since "30 min ago" --no-pager

# 重启
systemctl --user restart alice.service

如果安装时没有运行 alice setup,手动创建单元文件:

# ~/.config/systemd/user/alice.service
[Unit]
Description=Alice Feishu LLM Connector
After=network-online.target

[Service]
Type=simple
ExecStart=%h/.alice/bin/alice --feishu-websocket
Restart=on-failure
RestartSec=10
Environment=HOME=%h

[Install]
WantedBy=default.target

然后:

systemctl --user daemon-reload
systemctl --user start alice.service

macOS

在 macOS 上,使用 launchd 或手动运行。

launchd

<!-- ~/Library/LaunchAgents/com.alice.connector.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.alice.connector</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/you/.alice/bin/alice</string>
        <string>--feishu-websocket</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/Users/you/.alice/log/stdout.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/you/.alice/log/stderr.log</string>
</dict>
</plist>
launchctl load ~/Library/LaunchAgents/com.alice.connector.plist

手动运行

alice --feishu-websocket

使用 tmuxscreen 保持进程在登出后继续运行。

Runtime-Only 模式

对于只需要自动化和 Runtime API(无需飞书 WebSocket)的部署:

alice --runtime-only

在无头环境中:

alice-headless --runtime-only

重要alice-headless 无法启动飞书连接器。它被显式限制为仅 runtime-only 模式。

日志

Alice 使用 zerolog 输出结构化 JSON 日志,支持按天滚动。

log_level: "info"          # debug | info | warn | error
log_file: ""               # 空 = <ALICE_HOME>/log/YYYY-MM-DD.log
log_max_size_mb: 20        # 超过 20 MB 后滚动
log_max_backups: 5         # 保留 5 个滚动文件
log_max_age_days: 7        # 日志保留 7 天
log_compress: false        # 是否 gzip 压缩滚动日志

健康检查

Runtime API 暴露了一个健康检查端点:

curl http://127.0.0.1:7331/healthz
# {"status":"ok"}

监控

  • 飞书中的 /status 命令显示用量总计和活动自动化任务
  • journalctl(systemd)或日志文件用于结构化日志分析
  • Session 和 runtime 状态持久化到 JSON 文件供检查

多 Bot 部署

一个 alice 进程可以承载多个 bot。所有 bot 共享同一进程,但各自拥有独立的 runtime 目录、工作空间和队列。

bots:
  engineering_bot:
    feishu_app_id: "cli_11111"
    # ...
  support_bot:
    feishu_app_id: "cli_22222"
    # ...

多 bot 模式会禁用配置热重载。配置更改后需重启进程。

使用 alice delegate

alice delegate 子命令从命令行向任意已配置的 LLM 后端发送一次性 prompt。

基本用法

alice delegate --provider codex --prompt "Refactor the auth module to use JWT"
alice delegate --provider claude --prompt "Review this code for security issues"
alice delegate --provider opencode --prompt "Explain how DNS resolution works"

参数

参数说明
--providerLLM 后端:opencodecodexclaudegeminikimi
--promptprompt 文本(必填)
--model覆盖默认模型
--workspace覆盖工作目录

管道输入

通过 stdin 发送 diff 或文件内容:

cat diff.patch | alice delegate --provider claude --prompt "Review this PR diff"
alice delegate --provider codex --prompt "Summarize this log" < /var/log/app.log

OpenCode 插件集成

alice setup 会将插件写入 ~/.config/opencode/plugins/alice-delegate.js。一旦就位,OpenCode agent(包括 DeepSeek)会自动获得两个额外工具:

  • codex — 将子任务委托给 Codex
  • claude — 将子任务委托给 Claude

无需额外配置。OpenCode 会自动从该目录加载插件。

这是 alice delegate 的主要用途:让 OpenCode agent 可以将并行工作发散出去,或将专项任务委托给其他 LLM 后端。

连接方式

alice delegate 使用与 Alice 主运行时相同的 llm_profiles 配置。默认使用第一个 bot 下名为 delegate 的 profile。该 profile 决定了委托运行时的模型、权限和环境变量。

bots:
  my_bot:
    llm_profiles:
      delegate:
        provider: "claude"
        model: "claude-sonnet-4-6"
        permissions:
          sandbox: "workspace-write"
          ask_for_approval: "never"

示例

快速代码审查

alice delegate --provider claude --prompt "Check this function for bugs and suggest improvements" < src/auth.go

重构

alice delegate --provider codex --prompt "Extract the database logic into a separate package"

生成文档

alice delegate --provider opencode --prompt "Generate JSDoc comments for all exported functions"

使用内置命令

Alice 提供多个斜杠命令,这些命令绕过 LLM,由连接器直接处理。所有命令在群聊和私聊中均可使用。

/help

显示内置命令帮助卡片,列出所有可用命令。

/help

/status

显示状态卡片,包含:

  • 总计 session 和用量计数器
  • 活动的自动化任务
  • 当前 LLM 后端和 session 详情
/status

/clear

重置当前 chat 场景的 session。下一条消息将以全新对话开始,不带有之前的上下文。

/clear

仅影响 chat 场景。work 场景是基于话题的,话题结束时自然重置。

/stop

立即取消当前活跃 session 正在运行的 LLM 调用。

/stop

当 agent 陷入循环或运行时间过长时使用此命令。Bot 会确认停止,并恢复接受新消息。

/session

将飞书 work 话题绑定到已有的后端 session。适用于重启后恢复长时间运行的任务。

/session <backend-session-id>
/session <backend-session-id> Continue the review
  • 不带指令:绑定 session,不调用 LLM
  • 带指令:绑定 session 并立即用该指令调用 LLM

仅在 work 场景话题中有效。

/cd/ls/pwd

查看和更改当前 work session 的工作目录:

/pwd               # 显示当前目录
/ls                # 列出文件
/ls internal/      # 列出子目录中的文件
/cd /tmp/build     # 更改目录

这些命令仅影响 work session。目录更改在整个 session 期间持续有效。

命令优先级

当消息以 / 开头时,Alice 在路由到 LLM 之前先检查内置命令:

  1. 匹配内置命令 → 直接处理
  2. 不匹配 → 路由到场景(由 LLM 处理)

要强制将以 / 开头的消息发送给 LLM,请在前面加空格或使用 work 触发词:

 /some-custom-command     # 斜杠前的空格 → LLM 路径
@Alice #work /some-cmd    # Work 触发词 → LLM 路径

配置私聊场景

Alice 可以使用与群聊相同的场景路由来处理私聊消息。

私聊 vs 群聊场景

群聊使用 group_scenes。私聊使用 private_scenes。两者的配置方式相同,但位于不同的配置键下:

bots:
  my_bot:
    group_scenes:
      chat: { ... }
      work: { ... }

    private_scenes:
      chat: { ... }
      work: { ... }

私聊场景默认禁用。需要显式启用。

Chat 场景(私聊)

private_scenes:
  chat:
    enabled: true
    session_scope: "per_user"        # 每个 DM 用户共用一个 session
    llm_profile: "chat"
    no_reply_token: "[[NO_REPLY]]"
    create_feishu_thread: false
字段说明
session_scope"per_user" — 同一用户的所有 DM 共用一个 session。"per_message" — 每条 DM 创建新 session
llm_profile与群聊场景相同的 profile 引用
no_reply_token抑制回复的 token

典型用途:在 DM 中提供个人助手服务,按用户维护上下文。

Work 场景(私聊)

private_scenes:
  work:
    enabled: true
    trigger_tag: "#work"
    session_scope: "per_message"     # 每条 #work DM 都是全新 session
    llm_profile: "work"
    create_feishu_thread: true
字段说明
session_scopeDM 推荐 "per_message" — 每个任务隔离

完整示例

bots:
  my_bot:
    group_scenes:
      chat:
        enabled: true
        session_scope: "per_chat"
        llm_profile: "chat"
        no_reply_token: "[[NO_REPLY]]"
      work:
        enabled: true
        trigger_tag: "#work"
        session_scope: "per_thread"
        llm_profile: "work"
        create_feishu_thread: true

    private_scenes:
      chat:
        enabled: true
        session_scope: "per_user"
        llm_profile: "chat"
        no_reply_token: "[[NO_REPLY]]"
      work:
        enabled: true
        trigger_tag: "#work"
        session_scope: "per_message"
        llm_profile: "work"
        create_feishu_thread: true

与群聊的行为差异

  • 提及是隐式的 — DM 不需要 @bot。每条消息都直接面向 bot。
  • user_id 解析 — Alice 通过飞书 API 解析 DM 用户的名称
  • 话题创建 — 当 create_feishu_thread: true 时,work 回复会在 DM 内创建话题

编写 Bundled Skill

Bundled skill 扩展了 Alice,提供基于脚本的工具来调用 Runtime HTTP API。本指南将教你如何创建。

Skill 结构

一个 bundled skill 是 skills/ 下的一个目录:

skills/my-skill/
├── SKILL.md           # Skill 文档
├── scripts/
│   └── my-skill.sh    # 可执行脚本
└── agents/
    └── openai.yaml    # OpenAI agent 配置(可选)

第 1 步:创建目录

在 Alice 源码树的 skills/ 下,或在 ${ALICE_HOME}/skills/ 下(本地开发),创建你的 skill。

第 2 步:编写 SKILL.md

SKILL.md 为人类和 LLM agent 提供 skill 文档:

# my-skill

将活跃自动化任务的每日摘要发送到指定的飞书群聊。

## 用途

此 skill 由自动化系统触发。它从 runtime API 读取所有活跃任务,并发送格式化的摘要卡片。

## 环境

需要设置 `ALICE_RUNTIME_API_BASE_URL` 和 `ALICE_RUNTIME_API_TOKEN`。

第 3 步:编写脚本

脚本以子进程方式运行。Alice 注入以下环境变量:

变量说明
ALICE_RUNTIME_API_BASE_URLRuntime API 的 base URL(如 http://127.0.0.1:7331
ALICE_RUNTIME_API_TOKENAPI 认证的 Bearer token
ALICE_RUNTIME_BINalice 二进制路径
ALICE_RECEIVE_ID_TYPE接收目标的类型(如 chat_id
ALICE_RECEIVE_ID接收目标的 ID
ALICE_SOURCE_MESSAGE_ID触发消息的 ID(如适用)
ALICE_ACTOR_USER_ID交互者的飞书 user ID
ALICE_ACTOR_OPEN_ID交互者的飞书 open ID
ALICE_CHAT_TYPE对话类型:groupp2p
ALICE_SESSION_KEY当前对话的规范 session key

示例脚本

#!/usr/bin/env bash
set -euo pipefail

# 获取所有活跃任务
TASKS=$(curl -sS \
  -H "Authorization: Bearer ${ALICE_RUNTIME_API_TOKEN}" \
  "${ALICE_RUNTIME_API_BASE_URL}/api/v1/automation/tasks?status=active")

# 计数和格式化
COUNT=$(echo "$TASKS" | jq '. | length')
echo "Active tasks: $COUNT"

赋予执行权限:

chmod +x skills/my-skill/scripts/my-skill.sh

第 4 步:注册 Skill

将你的 skill 添加到 bot 的允许 skill 列表中:

bots:
  my_bot:
    permissions:
      allowed_skills: ["alice-message", "alice-scheduler", "my-skill"]

Skill 可用的 Runtime API 端点

Skill 主要使用以下端点:

端点方法用途
/api/v1/messages/imagePOST向对话发送图片
/api/v1/messages/filePOST向对话发送文件
/api/v1/automation/tasksGET列出自动化任务
/api/v1/automation/tasksPOST创建自动化任务
/api/v1/automation/tasks/:idGET/PATCH/DELETE管理特定任务

所有请求都需要 Authorization: Bearer <token> 头部。

权限

Skill 在 bot 的运行时权限下运行:

permissions:
  runtime_message: true       # 允许通过 API 发送消息
  runtime_automation: true    # 允许管理自动化任务

如果某权限被禁用,对应的 API 端点将返回 403 Forbidden

内置 Skill 参考

Alice 内置了两个 bundled skill:

  • alice-message:通过 runtime API 发送富文本消息和附件
  • alice-scheduler:从飞书对话中管理自动化任务

研究它们的源码(skills/alice-message/skills/alice-scheduler/),了解 skill 结构和 API 使用的实际示例。

排错指南

运行 Alice 的常见问题和解决方案。

Bot 在群聊中不响应

检查场景路由:

  • 确认 group_scenes.chat.enabledtrue
  • 如果两个场景都被禁用,检查 trigger_mode(应为 atprefix

检查 bot 身份:

  • Bot 的 open_id 现在在启动时自动获取 — 无需手动配置 feishu_bot_open_id
  • 确认 feishu_app_idfeishu_app_secret 是否正确

检查日志:

# 查找 WebSocket 连接状态
grep "long connection" ~/.alice/log/*.log
# 查找认证错误
grep "error" ~/.alice/log/*.log | head -20

Work 模式从不触发

  • 确认 group_scenes.work.enabledtrue
  • 确认 trigger_tag 已设置(如 "#work"
  • 消息必须包含 @BotName #work ... — @mention 和触发标签缺一不可
  • 触发标签必须出现在同一条消息中 @mention 之后

模型或推理级别不正确

  • 检查 llm_profilesprovidermodel 和专属字段是否正确
  • 确认场景指向正确的 profile key:
    group_scenes:
      work:
        llm_profile: "work"  # 必须与 llm_profiles 下的 key 匹配
    
  • 直接运行 provider CLI 验证认证:
    codex --version
    claude --version
    

Skill 无法发送附件或管理任务

检查权限:

permissions:
  runtime_message: true
  runtime_automation: true

检查 API 连通性:

# 在运行 Alice 的机器上执行
curl -s -H "Authorization: Bearer <token>" http://127.0.0.1:7331/healthz
# 应返回 {"status":"ok"}

Runtime HTTP API 绑定在 runtime_http_addr 指定的地址(默认 127.0.0.1:7331)。多 bot 设置会自动递增端口。

配置更改不生效

  • 多 bot 模式:配置热重载被禁用。需重启 Alice。
  • 单 bot 模式:支持部分热重载,但并非所有配置项都会被监听。
  • 配置更改后务必检查日志:
    grep "config" ~/.alice/log/*.log | tail -5
    

WebSocket 连接错误

如果日志中出现连接失败:

  1. 确认飞书开放平台中已启用长连接模式
  2. 确认应用已发布并通过审批
  3. 确认能访问 open.feishu.cn(或 Lark 用户的 open.larksuite.com)的网络
  4. 确认 Lark(国际版)用户已正确设置 feishu_base_url
    feishu_base_url: "https://open.larksuite.com"
    

Provider CLI 未找到

Alice 默认在 $PATH 中查找 CLI 二进制文件。如果未找到:

  1. 指定绝对路径:
    llm_profiles:
      chat:
        command: "/usr/local/bin/opencode"
    
  2. 或在 bot 的 env 中扩展 $PATH
    env:
      PATH: "/home/user/.local/bin:/usr/local/bin:/usr/bin:/bin"
    

LLM 运行无限挂起

  • 检查 LLM profile 中的 timeout_secs(默认:48 小时)
  • 在飞书中使用 /stop 取消正在运行的 session
  • 检查日志中 provider 专属的错误:
    grep -E "timeout|cancelled|killed" ~/.alice/log/*.log
    
  • 对于 Codex,检查 codex_idle_timeout_secs 设置

日志没有有用信息

将日志级别提高到 debug

log_level: "debug"

重启 Alice。Debug 模式包含:

  • 每次运行的 provider 和 agent 名称
  • Thread/session ID
  • 渲染后的输入 prompt
  • 观察到的工具调用活动
  • 最终输出或错误

警告:Debug 日志可能包含完整渲染后的 prompt,包括 SOUL.md 内容。

系统模型

本页讲解 Alice 背后的基本概念:多 bot 架构、场景路由、session 和启动模式。理解这些有助于你高效配置和排错。

Alice 是什么(以及不是什么)

Alice 是一个连接器,而非 bot 框架。它不直接实现聊天逻辑、NLU 或自定义集成。实际上,它:

  1. 从飞书接收消息
  2. 决定调用哪个 LLM 后端以及如何调用
  3. 以子进程方式调用 LLM CLI
  4. 将响应发回飞书

"智能"部分在 LLM 后端(Codex、Claude 等)。Alice 处理"管道"工作:路由、排队、session 管理、附件 I/O 和进度展示。

多 Bot 模型

一个 alice 进程可以通过一份 config.yaml 托管多个独立的 bot:

bots:
  engineering_bot:
    feishu_app_id: "cli_11111"
    # ...
  support_bot:
    feishu_app_id: "cli_22222"
    # ...

每个 bot 拥有自己独立的:

  • Runtime 目录~/.alice/bots/<bot_id>/
  • 工作空间、prompt 和 SOUL.md
  • 飞书凭据(App ID、App Secret)
  • LLM profile — 可以使用不同的 provider 和模型
  • 场景配置 — 独立的 chat/work 路由
  • Runtime API 端口 — 自动递增(7331、7332……)

Bot 之间共享:

  • 相同的进程和 worker 池
  • 默认的 CODEX_HOME(可按 bot 覆盖)

Bot 目录布局

~/.alice/bots/<bot_id>/
├── workspace/                        # Agent 工作空间
├── prompts/                          # Prompt 模板覆盖
├── SOUL.md                           # Bot 人格
└── run/connector/
    ├── automation.db                 # 持久化任务存储(bbolt)
    ├── campaigns.db                  # 活动索引(bbolt)
    ├── session_state.json            # Session 别名、用量计数器
    ├── runtime_state.json            # 可变运行时状态
    └── resources/scopes/             # 已下载的附件和产物

场景路由

每条收到的群消息经过一个决策树:

收到消息
  │
  ├─ 是内置命令?(/help、/status、/stop、/clear、/session)
  │   └─ 是 → 直接处理,不涉及 LLM
  │
  ├─ 匹配 work 触发词?(@Bot #work ...)
  │   └─ 是 → 路由到 work 场景
  │
  ├─ chat 场景已启用?
  │   └─ 是 → 路由到 chat 场景
  │
  └─ 两个场景都禁用?
      └─ 回退到旧版 trigger_mode(at / prefix / all)

场景 vs 旧版触发

旧版 trigger_mode(at/prefix/all)是一个简单的闸门:它决定是接受还是忽略消息。如果接受,只有一个 LLM 流水线。

场景更进一步:它们为每个场景分配不同的 LLM profile、session 作用域、话题行为和 SOUL.md 处理方式。新部署应始终使用场景。

Session 管理

Session 是 LLM 的上下文窗口。Alice 决定何时开始新 session,何时继续已有 session。

Session Key

Alice 使用规范 key 来标识 session:

格式示例
{receive_id_type}:{receive_id}chat_id:oc_123
`{key}scene:{scene}`
`{key}scene:{scene}

Session 作用域

session_scope 控制何时创建和复用 session:

作用域行为
per_chat整个群/DM 共用一个 session
per_thread每个飞书话题一个 session
per_user(仅 DM)每个用户一个 session
per_message(仅 DM)每条消息新建 session

Session 持久化

Alice 将 session 元数据持久化到 session_state.json

  • Provider thread ID(用于与后端恢复)
  • Session 别名
  • 用量计数器
  • 最后消息时间戳
  • Work-thread ID 别名

当收到一个新 job 时,Alice 检查是否存在活跃 session。如果存在:

  • Provider 原生注入:部分后端(Codex、Claude)允许向正在运行的 session 注入新输入。Alice 优先尝试此方式。
  • 排队:如果原生注入失败且 LLM 运行仍活跃,新 job 排队等待。较新的 job 会取代队列中较旧的 job。
  • 新运行:如果没有活跃的运行,则向 LLM 后端发送新的 RunRequest。

取消和中断

  • /stop 立即通过 context 取消取消活跃的 LLM 运行
  • 较新的用户消息会取代排队的 job,但不会中断活跃的运行
  • 自动化任务也可能被获取了 session 锁的用户消息中断

启动模式

Alice 支持两种显式的启动模式:

--feishu-websocket

完整模式。连接飞书 WebSocket,处理实时消息,运行自动化,并暴露 Runtime API。

--runtime-only

仅本地模式。Runtime API 和自动化引擎运行,但飞书连接器不启动。用于:

  • 调试和开发
  • 仅运行自动化调度器
  • 无头环境(使用 alice-headless --runtime-only

alice-headless 是一个专用二进制文件,不能启动飞书连接器。尝试 alice-headless --feishu-websocket 会报错。

配置热重载

  • 单 bot 模式:支持有限的局部热重载。部分配置项会被监听变化。
  • 多 bot 模式:热重载被刻意禁用。配置更改后务必重启 Alice。

Runtime Home

构建渠道默认 Home
Release(npm / 安装脚本)~/.alice
Dev(源码编译)~/.alice-dev

可通过 --alice-home 或环境变量 ALICE_HOME 覆盖。

消息处理流水线

本页带你走完一条飞书消息的完整生命周期 — 从 WebSocket 接收到最终回复。理解这条流水线有助于调试路由问题和调优行为。

概览

飞书 WebSocket
  └─ App(job 队列)
      └─ Processor(执行)
          └─ LLM Backend(子进程)
              └─ Reply Dispatcher(发回飞书)

1. WebSocket 接收

Alice 与飞书的 WebSocket 端点建立长连接。当用户发送 bot 可见的消息时,飞书通过此连接投递 im.message.receive_v1 事件。

事件包含:

  • 发送者身份(open_id、user_id、名称)
  • 消息内容(文本、附件、提及)
  • 对话上下文(chat_id、chat_type、如在线程中则为 thread_id)
  • Bot 身份(哪个 bot 收到了这条消息)

2. Job 创建

原始事件被标准化为一个 Job 结构体。此步骤:

  • 提取被提及的用户
  • 解析接收 ID 类型(chat_idopen_id 等)
  • 设置 bot 所配置的 LLM profile、场景和回复偏好
  • 生成 session key 和资源作用域 key
  • 附加单调递增的版本号

3. 路由

routeIncomingJob 决定对 job 做什么:

内置命令

如果消息以 /help/status/clear/stop/session/cd/ls/pwd 开头,由连接器直接处理 — 不调用 LLM。参见使用内置命令

Work 场景

如果 group_scenes.work.enabled 且消息在 @bot 提及后包含 trigger_tag(如 #work),job 被路由到 work 场景。Work job 使用 work 作用域的 session key 和 LLM profile。

Chat 场景

如果 group_scenes.chat.enabled,所有其他消息被路由到 chat。Chat job 使用 chat 作用域的 session key 和 LLM profile。

旧版回退

如果两个场景都被禁用,Alice 回退到匹配 trigger_modetrigger_prefix

4. 队列和序列化

每个 session 有一个互斥锁来序列化执行:

  • 存在活跃运行 → 首先尝试 provider 原生注入(向正在运行的 session 注入新输入)
  • 原生注入不可用 → 新 job 排队。较新的 job 取代队列中较旧的 job。
  • 无活跃运行 → 接受 job 并分派给 Processor。

Runtime store(runtime_store.go)维护内存中的协调状态:

  • 每个 session 的最新版本
  • 待处理的排队 job
  • 活跃运行的取消句柄
  • 每个 session 的互斥锁

5. LLM 前处理

在调用 LLM 之前,Processor 会:

  1. 加载并解析 SOUL.md(仅 chat)— 分离 YAML frontmatter 和 Markdown 正文
  2. 将收到的附件下载到作用域内的资源目录
  3. 为对话推导运行时环境变量
  4. 准备渲染后的 prompt 文本

Session 状态检查

Alice 检查 session_state.json

  • 如果存在 provider thread ID,后端调用将恢复该 thread
  • 如果 session 最近活跃,上一轮对话的上下文可用

6. LLM 执行

Processor 构建一个 RunRequest 并将其分派给 LLM 后端:

RunRequest {
    ThreadID       → 来自 session 状态(空 = 新 session)
    UserText       → 渲染后的 prompt
    Provider       → 来自 llm_profile
    Model          → 来自 llm_profile
    ReasoningEffort → 来自 llm_profile
    WorkspaceDir   → 每个 bot 的工作空间
    ExecPolicy     → 沙箱 + 批准设置
    Env            → 每个 bot + 进程环境变量
    OnProgress     → 将进度更新流式传输到飞书
}

后端将 provider CLI 作为子进程启动并流式输出。进度更新以状态卡片补丁的形式发送到飞书。

7. 回复分发

当 LLM 完成时,Alice 处理回复:

内容处理

  • 如果回复匹配 no_reply_token,保持静默
  • 如果在 SOUL.md 中配置了 output_contract,剥离隐藏标签
  • 应用飞书格式(富文本、@mention)

话题

  • Work 场景且 create_feishu_thread: true:回复发布在飞书话题中
  • Chat 场景且 create_feishu_thread: false:回复作为顶级消息发布
  • 话题回复:飞书支持时回复到话题。否则回退到直接回复。

即时反馈

在 LLM 开始之前,Alice 发送即时确认:

  • immediate_feedback_mode: "reaction" → 给源消息添加 reaction 表情
  • immediate_feedback_mode: "reply" → 发送显式 收到! 回复

8. 运行后

  • Session 状态持久化到 session_state.json(thread ID、用量计数器、时间戳)
  • 已下载的附件保留在作用域资源目录中
  • Runtime 状态定期刷新

关键不变量

  1. 每个 session 同时最多一个 LLM 运行 — 由每个 session 的互斥锁强制执行
  2. 较新的消息取代排队的,但不取代活跃的 — 只有 /stop 才能中断正在运行的 LLM
  3. Session 状态以磁盘为后盾 — 进程重启后仍存在
  4. 附件有作用域隔离 — 每个对话有自己的资源目录

Prompt 拼装

Alice 如何构建发送给 LLM 后端的 prompt 文本。

模板系统

Alice 使用 Go text/template 并结合 Sprig 函数进行 prompt 模板化。模板文件以 .md.tmpl 为后缀。

模板加载

1. 检查磁盘:<prompt_dir>/<template>.tmpl
2. 如果未找到,使用内嵌模板(编译进二进制文件)

磁盘文件会覆盖内嵌模板,支持按 bot 自定义。

模板文件

所有模板位于 prompts/ 下:

模板用途
connector/bot_soul.md.tmpl将 SOUL.md 正文注入 prompt
connector/current_user_input.md.tmpl格式化当前用户消息
connector/reply_context.md.tmpl添加来自被回复消息的上下文
connector/runtime_skill_hint.md.tmpl描述可用的 bundled skill
connector/synthetic_mention.md.tmpl格式化合成 @mention
connector/help.md.tmpl/help 命令响应
llm/initial_prompt.md.tmpl首轮系统指令
goals/goal_start.tmplGoal 初始化 prompt
goals/goal_continue.tmplGoal 继续 prompt
goals/goal_timeout.tmplGoal 超时通知

模板变量

模板可以访问完整的 Job 上下文和 session 元数据。关键变量包括:

变量说明
.UserText用户的消息文本
.BotName回复 bot 的显示名称
.SenderName发送消息的用户名称
.MentionedUsers消息中 @mention 的用户列表
.ReplyContext被回复消息的文本
.Attachments收到的附件元数据
.Scene"chat""work"
.SessionKey规范 session 标识符
.SoulBodySOUL.md 正文内容(仅 chat)
.SkillDescriptions已启用 bundled skill 的描述

首轮 vs 恢复

Prompt 拼装的关键区别:

首轮(无已有 Thread)

  • 拼装完整的初始 prompt
  • 包含系统指令(initial_prompt.md.tmpl
  • Chat 场景:前置 SOUL.md 正文
  • 身份提示(Name说:、@mention 规则),除非 disable_identity_hints: true

恢复(存在 Provider Thread)

  • 仅发送当前用户的消息文本
  • Alice 依赖 provider 端的 thread/session 保持之前的上下文
  • 无系统 prompt,无 SOUL.md,无身份提示
  • 这样更高效 — 后端模型已拥有完整的对话历史

SOUL.md 注入

SOUL.md 根据场景有两个用途:

Chat 场景

  1. Alice 读取文件,解析 YAML frontmatter
  2. Frontmatter 字段(image_refsoutput_contract)被 Alice 消耗用于回复控制
  3. 剩余 Markdown 正文通过 bot_soul.md.tmpl 前置到首轮 prompt

Work 场景

SOUL.md 被刻意跳过。Work 模式用于任务执行 — 注入人格会干扰工具使用和代码生成。

身份提示

disable_identity_hints: false(默认)时,Alice 为消息添加上下文:

张三说:fix the login timeout

disable_identity_hints: true,原始消息直接传递:

fix the login timeout

Prompt 前缀

每个 LLM profile 可以设置 prompt_prefix

llm_profiles:
  work:
    prompt_prefix: "You are a senior Go engineer. Be concise, use idiomatic patterns."

此文本会被前置到该 profile 的每次 prompt 中,包括恢复的 session。

Prompt 与调试

log_level: debug 时,Alice 会记录发送给每个后端的完整渲染 prompt。Debug 跟踪包括:

  • Provider 名称
  • 模型和 profile
  • Thread/session ID
  • 完整的渲染输入文本
  • 观察到的工具调用活动和最终输出

警告:渲染后的 prompt 可能包含 SOUL.md 内容和对话历史。避免公开发布 debug 日志。

自动化子系统

Alice 的自动化引擎调度并执行定期任务、工作流和系统维护。

架构

自动化子系统(internal/automation/)使用基于 tick 的执行模型,配合持久化存储。

Automation Engine
  ├─ Tick Scheduler(周期性循环)
  │   ├─ 认领到期任务
  │   ├─ 执行任务(send_text / run_llm / run_workflow)
  │   └─ 处理完成 / 失败
  ├─ System Task Scheduler
  │   ├─ Session 状态刷新
  │   └─ Campaign 调和
  ├─ Watchdog
  │   └─ 对超期或卡住的任务发出告警
  └─ Store(bbolt)
      └─ 任务持久化

任务模型

作用域

任务的作用域定义其执行位置:

作用域说明
user限定于特定用户(DM 上下文)
chat限定于特定群聊

操作

操作说明
send_text向作用域发送预设文本消息
run_llm在作用域中按指定 prompt 运行 LLM 调用
run_workflow运行结合 LLM 调用和操作的多步工作流

调度

任务可通过两种方式调度:

  • Cron 表达式"0 9 * * *" — 每天 9 点运行
  • 一次性时间戳:ISO 8601 — 在指定时间运行一次

任务生命周期

Created → Active → Claimed → Executing → Completed
                                  ↓
                              Failed → Active(重试)/ Cancelled
  • 到期任务在周期性 tick 中被认领(每次 tick 认领一个)
  • 被认领的任务在作用域的对话上下文中执行
  • 带 cron 表达式的已完成任务会被重新调度到下一次
  • 失败任务可能被重试或取消
  • 已取消任务被删除或标记为不活跃

执行模型

任务执行时:

  1. 引擎获取任务作用域的 session 锁
  2. 任务继承与交互式运行相同的对话上下文:
    • 相同的工作空间目录
    • 相同的 LLM profile 和权限
    • 相同的环境变量
  3. 对于 run_llmrun_workflow,任务的 prompt 被发送到 LLM 后端
  4. 回复发送到任务的作用域(群聊或用户 DM)

用户消息可以中断已获取 session 锁的自动化任务。

系统任务

Alice 在引导过程中注册内建的系统任务:

任务间隔用途
Session 状态刷新周期性将内存中的 session 状态持久化到 session_state.json
Campaign 调和周期性同步 campaign 仓库状态

Watchdog

Watchdog 监控自动化任务的异常:

  • 超期任务:已过调度时间但尚未被认领的任务
  • 卡住任务:执行时间过长的任务

当 Watchdog 检测到问题时,它可以:

  • 记录警告日志
  • 向配置的群聊发送告警消息
  • 强制取消卡住的任务

存储

任务持久化在本地 bbolt 数据库中:

~/.alice/bots/<bot_id>/run/connector/automation.db

进程重启后仍存在。存储支持:

  • 任务的 CRUD 操作
  • 按作用域、状态和到期时间查询
  • 原子性的认领并更新以防止重复执行

管理任务

通过 Runtime API

alice runtime automation create '{
  "scope_type": "chat",
  "scope_id": "oc_xxxxxxxxxxxxx",
  "action": "send_text",
  "text": "Daily standup reminder!",
  "cron": "0 10 * * 1-5"
}'

通过 Bundled Skill

alice-scheduler skill 让用户可以直接从飞书对话中创建和管理任务。

完整任务管理端点参见 Runtime API 参考

Runtime API 设计

Alice 本地 HTTP API 背后的理念和设计决策。

为什么需要一个本地 API?

Alice 将 bundled skill 作为子进程脚本运行。这些脚本需要与运行中的连接器交互 — 发送图片、管理任务、查询状态。从 shell 脚本中无法直接进行 Go 互操作,因此 Alice 暴露了一个本地 HTTP API。

设计原则

1. 默认仅本地访问

API 绑定到 127.0.0.1(可通过 runtime_http_addr 配置)。它并非设计为对外暴露。如果需要远程访问,请使用反向代理或 SSH 隧道 — 但这并非预期用例。

2. Bearer Token 认证

每个请求(/healthz 除外)都需要:

Authorization: Bearer <token>

Token 在启动时自动生成。环境变量 ALICE_RUNTIME_API_TOKEN 自动将其注入到 skill 脚本中。

3. 纵深防御

多层保护:

  • 认证速率限制:每个 token 每分钟 120 个请求
  • 请求体大小限制:每个请求 1 MB
  • 文件验证:上传的文件必须是可读、非空的常规文件
  • 飞书大小限制:上传仍受飞书文件大小和类型限制的约束

4. 无纯文本发送端点

Runtime API 没有纯文本发送端点。为什么?

文本回复通常走主回复流水线 — 它们需要 session 上下文、正确的线程和回复元数据。Runtime API 设计用于副作用(发送图片、文件、管理任务),而非绕过回复流水线。

如果 skill 需要以自动化任务的方式发送文本消息,它会通过 automation API 创建一个 send_text 任务。自动化引擎处理其余工作。

API 接口

消息

  • POST /api/v1/messages/image — 上传并发送图片
  • POST /api/v1/messages/file — 上传并发送文件

两者接受 multipart/form-data,可选 caption 字段。

自动化

  • 自动化任务的完整 CRUD:创建、列表、获取、更新、删除

Goal

  • Goal 生命周期管理:获取、创建、暂停、恢复、完成、删除

健康检查

  • GET /healthz — 无需认证,服务器运行时返回 200

环境变量注入

Skill 无需知道 API 地址或 token。Alice 注入它们:

ALICE_RUNTIME_API_BASE_URL="http://127.0.0.1:7331"
ALICE_RUNTIME_API_TOKEN="<auto-generated>"
ALICE_RUNTIME_BIN="/usr/local/bin/alice"

额外的上下文变量:

ALICE_RECEIVE_ID_TYPE="chat_id"
ALICE_RECEIVE_ID="oc_xxxxxxxxxxxxx"
ALICE_SOURCE_MESSAGE_ID="om_xxxxxxxxxxxxx"
ALICE_ACTOR_USER_ID="ou_xxxxxxxxxxxxx"
ALICE_ACTOR_OPEN_ID="ou_xxxxxxxxxxxxx"
ALICE_CHAT_TYPE="group"
ALICE_SESSION_KEY="chat_id:oc_xxx|scene:chat"

多 Bot API 端口

在多 bot 模式下,每个 bot 在自动递增的端口上获得自己的 Runtime API 服务器:

Bot 序号端口
第一个7331
第二个7332
第三个7333
…………

Skill 通过继承其被触发所在对话作用域的环境变量来指向正确的 bot。

优雅关闭

当 Alice 收到关闭信号时,Runtime API 服务器:

  1. 停止接受新连接
  2. 等待最多 runtime_api_shutdown_timeout_secs 秒(默认:5 秒)让正在处理的请求完成
  3. 超时后强制关闭剩余连接

扩展 API

新端点可在 internal/runtimeapi/ 中添加。开发者指南参见贡献指南架构概览

配置项手册

config.yaml 中所有配置项的完整参考。结构按文件布局组织。


顶级键

bots(必填)

Bot ID 到 bot 配置的映射。bots 下的每个 key 是作为运行时标识符的 bot ID。

bots:
  engineering_bot:
    # bot 配置...
  support_bot:
    # bot 配置...

log_level

字段
类型string
默认值"info"
可选值"debug""info""warn""error"

整个进程的结构化日志级别。

log_file

字段
类型string
默认值""(自动:<ALICE_HOME>/log/YYYY-MM-DD.log

日志文件路径,按日滚动。为空则使用默认值。

log_max_size_mb

字段
类型int
默认值20

日志文件滚动前的最大大小(MB)。

log_max_backups

字段
类型int
默认值5

保留的滚动日志文件最大数量。

log_max_age_days

字段
类型int
默认值7

保留滚动日志文件的最大天数。

log_compress

字段
类型bool
默认值false

是否 gzip 压缩滚动日志文件。


bots.<id>

每个 bot 由其 key 标识,并用以下字段配置。

name

字段
类型string
必填

在 prompt 和状态卡片中使用的显示名称。默认使用 bot ID。

feishu_app_id(必填)

字段
类型string
必填

飞书开放平台 App ID(cli_...)。

feishu_app_secret(必填)

字段
类型string
必填

飞书开放平台 App Secret。请安全保管此值。

feishu_base_url

字段
类型string
默认值"https://open.feishu.cn"

飞书 API base URL。Lark(国际版)用户使用 "https://open.larksuite.com"


运行时目录

alice_home

字段
类型string
默认值"<ALICE_HOME>/bots/<bot_id>"

Bot 专有的运行时根目录。所有按 bot 的状态都位于此路径下。

workspace_dir

字段
类型string
默认值"<alice_home>/workspace"

Agent 工作空间目录。这是 LLM 子进程的工作目录。

prompt_dir

字段
类型string
默认值"<alice_home>/prompts"

Bot 专有 prompt 模板覆盖目录。此处的文件会覆盖内嵌模板。

codex_home

字段
类型string
默认值"$CODEX_HOME""~/.codex"

Codex 配置和认证目录。默认在 bot 间共享,除非在此覆盖。

soul_path

字段
类型string
默认值"SOUL.md"(相对于 alice_home

SOUL.md 人格文档路径。相对路径相对于 alice_home 解析。如果启动时文件不存在,Alice 会写入内嵌模板。


消息触发(旧版)

trigger_mode

字段
类型string
默认值"at"
可选值"at""prefix""all"

旧版触发模式。仅在 group_scenes.chat.enabledgroup_scenes.work.enabled 均为 false 时使用。

行为
"at"仅接受 @bot 消息
"prefix"仅接受以 trigger_prefix 开头的消息
"all"接受所有消息

trigger_prefix

字段
类型string
默认值""

trigger_mode: "prefix" 时的前缀字符串。


llm_profiles

Profile 名称到 LLM profile 配置的映射。每个 profile 选择一个 provider、模型和设置。

Profile 字段

provider(必填)
字段
类型string
可选值"opencode""codex""claude""gemini""kimi"

LLM 后端 provider。

command
字段
类型string
默认值provider 相同(如 "opencode"

CLI 二进制路径或名称。对不在 $PATH 中的二进制使用绝对路径。

timeout_secs
字段
类型int
默认值172800(48 小时)

每次运行的超时时间(秒)。

model(必填)
字段
类型string

模型标识符。示例:"deepseek/deepseek-v4-pro""gpt-5.4-mini""claude-sonnet-4-6"

variant(仅 OpenCode)
字段
类型string
可选值"max""high""minimal"

OpenCode 的 DeepSeek 模型变体。

profile(仅 Codex)
字段
类型string

Codex CLI 配置中的命名子 profile。

reasoning_effort(仅 Codex)
字段
类型string
可选值"low""medium""high""xhigh"

推理强度级别。

personality(仅 Codex)
字段
类型string

Codex CLI 配置中的命名人格预设。

prompt_prefix
字段
类型string
默认值""

每次 prompt 发送给模型前添加的文本。

permissions
字段
类型object

沙箱和批准设置。

permissions.sandbox
字段
类型string
默认值"workspace-write"
可选值"read-only""workspace-write""danger-full-access"

LLM agent 的文件系统访问级别。

permissions.ask_for_approval
字段
类型string
默认值"never"
可选值"untrusted""on-request""never"

agent 执行工具调用前何时需要请求批准。

permissions.add_dirs
字段
类型string[]
默认值[]

agent 在工作空间之外可访问的额外目录。

profile_overrides
字段
类型map[string]ProfileRunnerConfig
默认值{}

高级:按 profile 的 runner 覆盖。key 为 profile 名称。每个覆盖可设置:

  • command — 二进制路径覆盖
  • timeout — 超时覆盖(秒)
  • provider_profile — provider 专属的 profile 名称
  • exec_policy — 按覆盖的沙箱和批准设置

env

字段
类型map[string]string
默认值{}

传递给所有 LLM 子进程的环境变量。适用于 PATH、代理设置(HTTPS_PROXYALL_PROXY)和 API key。


回复消息

failure_message

字段
类型string
默认值"暂时不可用,请稍后重试。"

LLM 后端失败时显示的消息。

thinking_message

字段
类型string
默认值"正在思考中..."

LLM 处理期间在进度卡片中显示的消息。


即时反馈

immediate_feedback_mode

字段
类型string
默认值"reaction"
可选值"reaction""reply"

Alice 在 LLM 响应之前确认收到消息的方式。

immediate_feedback_reaction

字段
类型string
默认值"OK"

Reaction 反馈的飞书表情名称(如 "OK""WINK""THUMBSUP")。


group_scenes

group_scenes.chat

字段
类型object

群聊 / 话题群的 chat 场景配置。

enabled
字段
类型bool
默认值true
session_scope
字段
类型string
默认值"per_chat"
可选值"per_chat""per_thread"
llm_profile
字段
类型string

使用的 llm_profiles 下的 LLM profile 名称。

no_reply_token
字段
类型string
默认值""

模型返回此精确字符串时,Alice 保持静默。

create_feishu_thread
字段
类型bool
默认值false

是否为回复创建飞书话题。

group_scenes.work

字段
类型object

群聊 / 话题群的 work 场景配置。

enabled
字段
类型bool
默认值true
trigger_tag
字段
类型string
默认值"#work"

消息中(在 @bot 提及后)触发 work 模式所需的标签。

session_scope
字段
类型string
默认值"per_thread"
可选值"per_thread""per_chat"
llm_profile
字段
类型string
create_feishu_thread
字段
类型bool
默认值true
no_reply_token
字段
类型string
默认值""

private_scenes

group_scenes 结构相同。chatwork 子部分默认均禁用

private_scenes.chat

额外 session 作用域:"per_user" — 同一用户的所有 DM 消息共用一个 session。

private_scenes.work

额外 session 作用域:"per_message" — 每条带 #work 的 DM 创建全新 session。


Runtime HTTP API

runtime_http_addr

字段
类型string
默认值"127.0.0.1:7331"

Runtime HTTP API 的监听地址。多 bot 设置会自动递增端口(73327333……)。

runtime_http_token

字段
类型string
默认值自动生成

API 认证的 Bearer token。为空则自动生成。显式设置用于跨进程调用。


permissions

runtime_message

字段
类型bool
默认值true

允许 bundled skill 通过 runtime API 发送消息。

runtime_automation

字段
类型bool
默认值true

允许 bundled skill 通过 runtime API 管理自动化任务。

allowed_skills

字段
类型string[]
默认值["alice-message", "alice-scheduler"]

此 bot 启用的 bundled skill。内置 skill:alice-messagealice-scheduleralice-goal


Worker 池

queue_capacity

字段
类型int
默认值256

最大待处理 job 数。超过后新消息被丢弃。

worker_concurrency

字段
类型int
默认值3

处理 job 的并发 worker 数量。


超时

所有值单位为秒。

automation_task_timeout_secs

字段
类型int
默认值6000

定时自动化和工作流运行的外部超时。

auth_status_timeout_secs

字段
类型int
默认值15

启动时 provider 认证状态检查的超时。

runtime_api_shutdown_timeout_secs

字段
类型int
默认值5

关闭 Runtime HTTP API 服务器时的宽限期。

local_runtime_store_open_timeout_secs

字段
类型int
默认值10

启动时打开本地 BoltDB Runtime Store 的超时。

codex_idle_timeout_secs

字段
类型int
默认值900

Codex 默认/中等推理强度的空闲超时。

codex_high_idle_timeout_secs

字段
类型int
默认值1800

Codex 高推理强度的空闲超时。

codex_xhigh_idle_timeout_secs

字段
类型int
默认值3600

Codex 极高推理强度的空闲超时。


显示选项

show_shell_commands

字段
类型bool
默认值true

是否在心跳状态卡片中显示最近执行的 shell 命令。

disable_identity_hints

字段
类型bool
默认值false

true 时,消息以原始文本发送给 LLM,不带身份上下文(Name说:、@mention 规则)。为 false(默认)时,包含身份提示。

Runtime HTTP API

Alice 在 127.0.0.1 上暴露一个本地认证的 HTTP API。Bundled skill、自动化脚本和薄运行时工具使用此 API。

认证

所有端点(/healthz 除外)都需要 Bearer token:

Authorization: Bearer <token>

Token 来自配置中的 bots.<id>.runtime_http_token,如果为空则自动生成。

Base URL

默认:http://127.0.0.1:7331。多 bot 设置会自动递增:73327333……。

限制

  • 请求体:最多 1 MB
  • 认证速率限制:每分钟 120 个请求
  • 列表端点:每次请求最多 200 条

健康检查

GET /healthz

无需认证。

响应 200 OK

{"status": "ok"}

消息

POST /api/v1/messages/image

向当前对话发送图片。

请求 multipart/form-data

字段类型必填说明
imagefile要上传的图片文件
captionstring可选的说明文字

响应 200 OK

{"message_id": "om_xxxxxxxxxxxxx"}

错误:

状态码说明
400图片文件无效或缺失
403permissions.runtime_message 已禁用
413请求体超过 1 MB

POST /api/v1/messages/file

向当前对话发送文件。

请求 multipart/form-data

字段类型必填说明
filefile要上传的文件
filenamestring显示文件名(默认:原始文件名)
captionstring可选的说明文字

响应 200 OK

{"message_id": "om_xxxxxxxxxxxxx"}

错误:

状态码说明
400文件无效或缺失
403permissions.runtime_message 已禁用
413请求体超过 1 MB

自动化任务

GET /api/v1/automation/tasks

列出自动化任务。

查询参数:

参数类型默认值说明
limitint50每页条数(最大 200)
offsetint0分页偏移
statusstring按状态筛选:activecompletedcancelled

响应 200 OK

[
  {
    "id": "task_abc123",
    "scope_type": "chat",
    "scope_id": "oc_xxxxxxxxxxxxx",
    "action": "send_text",
    "status": "active",
    "cron": "0 9 * * *",
    "created_at": "2025-01-15T09:00:00Z",
    "updated_at": "2025-01-15T09:00:00Z"
  }
]

POST /api/v1/automation/tasks

创建自动化任务。

请求 application/json

字段类型必填说明
scope_typestring"chat""user"
scope_idstring目标 ID(chat_id 或 user_id)
actionstring"send_text""run_llm""run_workflow"
textstringsend_text消息文本
promptstringrun_llmLLM prompt
cronstring重复任务的 cron 表达式
run_atstring一次性任务的 ISO 8601 时间戳

响应 201 Created

{
  "id": "task_abc123",
  "scope_type": "chat",
  "scope_id": "oc_xxxxxxxxxxxxx",
  "action": "send_text",
  "status": "active",
  "cron": "0 9 * * *",
  "created_at": "2025-01-15T09:00:00Z"
}

错误:

状态码说明
400请求体无效或缺少必填字段
403permissions.runtime_automation 已禁用

GET /api/v1/automation/tasks/:taskID

获取单个自动化任务。

响应 200 OK:与列表项相同的结构。

错误:

状态码说明
404任务未找到

PATCH /api/v1/automation/tasks/:taskID

更新自动化任务。发送 JSON merge-patch,包含要更新的字段。

请求 application/json

{"status": "cancelled"}

可更新字段:statuscronrun_attextprompt

响应 200 OK:更新后的任务对象。

错误:

状态码说明
400无效更新
403permissions.runtime_automation 已禁用
404任务未找到

DELETE /api/v1/automation/tasks/:taskID

删除自动化任务。

响应 204 No Content

错误:

状态码说明
403permissions.runtime_automation 已禁用
404任务未找到

Goal

GET /api/v1/goal

获取对话作用域的当前活跃 goal。

响应 200 OK

{
  "id": "goal_xyz",
  "description": "Review PR #42",
  "status": "in_progress",
  "created_at": "2025-01-15T10:00:00Z"
}

响应 204 No Content:无活跃 goal。

POST /api/v1/goal

为对话作用域创建新 goal。

请求 application/json

{"description": "Review PR #42"}

响应 201 Created:创建的 goal 对象。

POST /api/v1/goal/pause

暂停活跃 goal。

响应 200 OK

POST /api/v1/goal/resume

恢复已暂停的 goal。

响应 200 OK

POST /api/v1/goal/complete

将活跃 goal 标记为完成。

响应 200 OK

DELETE /api/v1/goal

删除活跃 goal。

响应 204 No Content


通用错误响应格式

所有错误遵循此格式:

{
  "error": "Human-readable error description"
}

HTTP 状态码按惯例使用:400 表示客户端错误,403 表示权限不足,404 表示未找到,413 表示载荷过大,429 表示速率限制,500 表示内部错误。

CLI 命令

Alice 提供多个 CLI 子命令用于不同操作。


主进程

alice --feishu-websocket

启动完整的飞书连接器运行时。连接飞书 WebSocket 并处理实时消息。

alice --feishu-websocket

alice --runtime-only

以 runtime-only 模式启动。本地 HTTP API 和自动化引擎运行,但飞书 WebSocket 不启动。

alice --runtime-only

alice-headless --runtime-only

无头 runtime-only 二进制。明确不能启动飞书连接器。

alice-headless --runtime-only

alice-headless 如果以 --feishu-websocket 调用会报错。


全局参数

参数说明
--alice-home <path>覆盖默认的运行时 home 目录
--config <path>config.yaml 路径(默认:<alice_home>/config.yaml
--log-level <level>覆盖日志级别(debuginfowarnerror
--version输出版本并退出

环境变量 ALICE_HOME 也会覆盖默认 home 目录。


alice setup

初始化 Alice 运行时环境。

alice setup

执行内容:

  1. ~/.alice/ 下创建目录结构
  2. 写入初始 config.yaml(基于 config.example.yaml
  3. 将内置 bundled skill 同步到 ${ALICE_HOME}/skills/
  4. Linux 上:在 ~/.config/systemd/user/alice.service 注册 systemd 用户单元
  5. ~/.config/opencode/plugins/alice-delegate.js 安装 OpenCode delegate 插件

安装后运行一次即可。


alice delegate

向配置好的 LLM 后端发送一次性 prompt。

alice delegate --provider <name> --prompt "<text>"

选项

参数说明
--provider <name>后端:opencodecodexclaudegeminikimi
--prompt <text>Prompt 文本(必填)
--model <name>覆盖默认模型
--workspace <path>覆盖工作目录

示例

alice delegate --provider codex --prompt "Fix the null check in auth.go"
alice delegate --provider claude --prompt "Review this diff" < changes.patch

alice runtime message

通过 runtime API 发送消息。

alice runtime message image <path> [--caption <text>]
alice runtime message file <path> [--filename <name>] [--caption <text>]
子命令说明
image <path>上传并发送图片
file <path>上传并发送文件
参数说明
--caption <text>可选的说明文字
--filename <name>覆盖文件显示名称(仅 file)

alice runtime automation

通过 runtime API 管理自动化任务。

alice runtime automation list [--status <status>] [--limit <n>]
alice runtime automation create <payload>
alice runtime automation get <task-id>
alice runtime automation update <task-id> <payload>
alice runtime automation delete <task-id>
子命令说明
list列出自动化任务
create <json>从 JSON payload 创建任务
get <id>获取单个任务
update <id> <json>通过 JSON merge-patch 更新任务
delete <id>删除任务
参数(list)说明
--status按状态筛选:activecompletedcancelled
--limit每页条数

alice runtime goal

管理对话作用域的活跃 goal。

alice runtime goal get
alice runtime goal create <description>
alice runtime goal pause
alice runtime goal resume
alice runtime goal complete
alice runtime goal delete
子命令说明
get获取当前活跃 goal
create <desc>创建新 goal
pause暂停活跃 goal
resume恢复已暂停的 goal
complete将活跃 goal 标记为完成
delete删除活跃 goal

alice skills

管理 bundled skill。

alice skills sync
alice skills list
子命令说明
sync将内嵌 bundled skill 同步到本地 skill 目录
list列出已安装的 bundled skill

alice skills sync 在启动时也会自动运行。


退出码

含义
0成功
1一般错误
2配置错误
3认证错误

架构概览

这是面向代码的 Alice 架构参考。包名、运行时对象和文件路径与 cmd/connectorinternal/prompts/skills/ 下的实际代码一致。

阅读路径

根据你的目标从对应部分开始:

目标入口
理解整个系统§1 进程模型 → §2 引导路径 → §5 消息流水线
新增 LLM 后端§2 引导路径 → §7 Prompt 拼装 → 新增 LLM Backend
修改消息处理§5 收信消息流水线 → §6 Session Key → §8 回复分发
新增 Runtime API 端点§9 Runtime API
新增或修改自动化§10 自动化子系统
理解配置§2 引导路径 → §12 配置模型

1. 进程模型

Alice 是一个多 bot 运行时。一个 alice 进程可以通过一份 config.yaml 托管多个 bot。

启动时,进程:

  1. 加载 config.yaml
  2. bots.* 展开为每个 bot 的运行时配置
  3. 按需验证 CLI 认证
  4. 将内嵌 bundled skill 同步到本地 skill 目录
  5. 为每个 bot 构建一个 ConnectorRuntime
  6. 在一个 RuntimeManager 下运行所有运行时

每个 bot 的主运行时对象:

ConnectorRuntime
  ├─ App
  ├─ Processor
  ├─ llm.MultiBackend
  ├─ LarkSender
  ├─ automation.Engine
  ├─ runtimeapi.Server
  ├─ automation.Store
  └─ campaign.Store

启动模式是显式的:

  • --feishu-websocket:连接飞书并处理实时事件
  • --runtime-only:运行自动化和本地 Runtime API,不启动飞书 WebSocket
  • alice-headless:仅 runtime-only;不得启动飞书连接器

2. 引导路径

进程入口点是 cmd/connector

关键引导步骤:

  • cmd/connector/root.go:CLI 参数、启动模式选择、配置创建、PID 锁定、日志、认证预检、bundled skill 同步和运行时管理器启动。
  • internal/config:纯多 bot 配置模型、路径派生、标准化、验证和每个 bot 的运行时展开。
  • internal/bootstrap:构建每个 bot 的运行时图并连接横切功能,如 prompt 加载、Runtime API 认证、campaign 调和循环和配置热重载。

BuildRuntimeManager 通过 RuntimeConfigs()Config 展开为 []Config,然后为每个 bot 构建一个 ConnectorRuntime

当前热重载行为:

  • 单 bot 模式:支持部分配置热重载
  • 多 bot 模式:热重载被刻意禁用;配置更改后重启进程

3. 运行时布局与持久化状态

每个 bot 拥有自己的运行时根目录:

${ALICE_HOME}/bots/<bot_id>/

重要的每个 bot 路径:

  • workspace/ — Bot 工作空间
  • prompts/ — 该 bot 的可选 prompt 覆盖
  • run/connector/automation.db — 持久化自动化任务存储(bbolt)
  • run/connector/campaigns.db — 持久化轻量级 campaign 索引(bbolt)
  • run/connector/session_state.json — Session 别名、provider thread id、用量计数器、work-thread 元数据
  • run/connector/runtime_state.json — 可变连接器运行时状态
  • run/connector/resources/scopes/<scope_type>/<scope_id>/ — 下载的附件和可上传的本地产物,作用域限定在当前对话

源码树也内嵌了:

  • prompts/
  • skills/
  • config.example.yaml
  • prompts/SOUL.md.example

磁盘文件在存在时会覆盖内嵌 prompt 文件;内嵌资产是后备方案。

4. 包映射

核心包

职责
cmd/connectorCLI 入口、runtime 子命令和 skills sync
internal/bootstrap运行时构建、路径解析、认证检查、skill 物化、campaign 调和桥接和配置重载
internal/config配置模式、验证、默认值、路径派生和多 bot 展开
internal/connector飞书收信、消息标准化、场景路由、排队、session 序列化、原生注入回退、/stop 中断、prompt 拼装、回复分发、附件下载、session 持久化和内置命令
internal/llm与 provider 无关的 Backend 接口及 codexclaudegeminikimiopencode 的 provider 适配器
internal/prompting模板加载器(磁盘优先 / 内嵌后备)、sprig 辅助函数和编译模板缓存
internal/runtimeapi本地认证 HTTP 服务器和客户端,供 bundled skill 和面向运行时的 shell 脚本使用
internal/automation任务模型、持久化、认领、执行、系统任务调度和工作流分派
internal/statusview/status 聚合用量和自动化数据
internal/platform/feishu飞书发送器实现、附件 I/O、bot 自我信息查找、消息查找和用户名解析辅助

支持包

职责
internal/sessionctxRuntime API 调用和 bundled skill 的 session 上下文环境桥接
internal/runtimecfg场景派生的 profile 选择和话题回复偏好的辅助
internal/sessionkey规范 session key 和可见性 key 的辅助
internal/messaging连接器和 Runtime API 层共享的窄发信/上传接口
internal/storeutil共享 bbolt 辅助和字符串工具
internal/loggingZerolog 加滚动文件输出配置
internal/buildinfo版本报告

5. 收信消息流水线

internal/connector.App 拥有实时飞书连接和每个 bot 的 job 队列。

高层流程:

  1. 飞书通过 WebSocket 投递 im.message.receive_v1
  2. App 将事件标准化为 Job
  3. routeIncomingJob 决定消息应被忽略、作为内置命令处理、作为 chat 处理还是作为 work 处理
  4. 如果同一 session 有活跃的 provider 原生交互式运行,Alice 首先尝试将新输入注入到该运行中
  5. 如果原生注入不可用,job 排队并按 session 序列化;较新的排队 job 取代较旧 job,不中断活跃的 LLM 运行
  6. /stop 仍会中断活跃运行,用户消息仍可中断获取了 session 锁的自动化任务
  7. Processor 执行被接受的 job

场景路由规则:

  • 群聊/话题群可以使用 group_scenes.chatgroup_scenes.work
  • Work 话题通过触发器加上稳定的 work-scene session key 来标识
  • 如果两个场景都被禁用,Alice 回退到旧版 trigger_mode / trigger_prefix
  • 内置命令如 /help/status/clear/stop 绕过 LLM 路径

6. Session Key、别名和序列化

Alice 通过规范 session key 和别名来路由和恢复工作。

常见格式:

  • {receive_id_type}:{receive_id}
  • {receive_id_type}:{receive_id}|scene:{scene}
  • {receive_id_type}:{receive_id}|scene:{scene}|thread:{thread_id}
  • {receive_id_type}:{receive_id}|scene:{scene}|message:{message_id}

特殊情况:

  • Work-scene 种子 key:{receive_id_type}:{receive_id}|scene:work|seed:{source_message_id}
  • Chat 重置别名:{chat_key}|reset:{message_id}

持久化到 session_state.json

  • Provider thread id
  • Work-thread id 别名
  • Session 别名
  • 用量计数器
  • 最后消息时间戳
  • 状态聚合的作用域 key

internal/connector/runtime_store.go 维护实时的内存协调状态:

  • 每个 session 的最新版本
  • 每个 session 的待处理 job
  • 活跃运行取消句柄
  • 每个 session 的序列化互斥锁
  • 已取代版本跟踪

7. Prompt 拼装与 LLM 执行

internal/connector.Processor 是每个被接受 job 的执行核心。

在 LLM 调用之前它:

  • 如果需要,加载并解析 SOUL.md
  • 将收到的附件下载到作用域资源目录
  • 为当前对话推导运行时环境变量
  • 准备 prompt 文本

当前 prompt 资产:

  • prompts/llm/initial_prompt.md.tmpl
  • prompts/connector/bot_soul.md.tmpl
  • prompts/connector/current_user_input.md.tmpl
  • prompts/connector/reply_context.md.tmpl
  • prompts/connector/runtime_skill_hint.md.tmpl
  • prompts/connector/synthetic_mention.md.tmpl

重要 prompt 行为:

  • 首轮或非恢复运行渲染 current-user-input 模板,并可能附加回复上下文、bot soul 和运行时 skill 提示
  • 已恢复的 provider thread 仅发送当前用户输入;Alice 依赖 provider 端 thread/session 持有之前的上下文
  • chat 运行可在 resume 时前置 SOUL.mdwork 运行刻意跳过 bot-soul 注入

LLM 层选择方式:

  1. 场景选择一个外部 llm_profiles.<name>
  2. 外部 profile 选择 provider / model / profile / reasoning / personality / prompt prefix
  3. llm.MultiBackend 分派到正确的 provider 适配器

当前支持的 provider:codexclaudegeminikimiopencode

8. 回复分发

Alice 区分:

  • 即时确认
  • 来自后端的流式进度消息
  • 最终回复
  • 文件/图片跟进

当前行为:

  • Work-scene 消息通常收到即时 reaction 或 收到!
  • 后端进度消息在可能时以话题回复方式发送
  • 最终回复通过回复分发器发布
  • 当飞书不支持对该目标的话题回复时,回退到直接回复

internal/connector/card.gointernal/connector/outgoing_mentions.gointernal/connector/outgoing_plaintext.go 及相关文件拥有:

  • 消息发送 / 回复 / 补丁卡片操作
  • Reaction
  • 图片和文件上传
  • 附件下载
  • 作用域资源根解析

9. Runtime API 与 Bundled Skill

Alice 暴露一个本地认证的 Runtime API,面向 bundled skill 和薄运行时脚本。

当前 HTTP 接口:

  • POST /api/v1/messages/image
  • POST /api/v1/messages/file
  • GET|POST|PATCH|DELETE /api/v1/automation/tasks
  • GET|POST /api/v1/goal + pause/resume/complete/delete

没有独立的纯文本发送端点。纯文本通常通过主回复流水线返回。

当前安全措施:

  • Bearer token 认证
  • 请求体大小限制(1 MB)
  • 进程内认证速率限制(120 req/min)
  • 本地上传需要可读、非空的常规文件,且仍受飞书大小限制约束

面向运行时的 shell 入口点:

  • alice runtime message ...
  • alice runtime automation ...
  • alice runtime goal ...

当前源码树中内置的 bundled skill:

  • skills/alice-message
  • skills/alice-scheduler
  • skills/alice-goal

运行时上下文通过环境变量注入(见 Runtime API 设计)。

10. 自动化子系统

internal/automation 将任务持久化在 bbolt 中并在进程内执行。

当前任务作用域:userchat 当前任务操作:send_textrun_llmrun_workflow

执行模型:

  • 到期任务在周期性 tick 中被认领
  • 长期系统任务单独调度
  • 任务环境继承与交互式运行相同的对话上下文桥接
  • Workflow 任务调用相同的 LLM 后端,但使用 workflow 专属的 agent name、环境变量和工作空间提示

引导期间注册的内建系统任务:

  • 周期性 session/runtime 状态刷新
  • 周期性 campaign-repo 调和

11. 配置模型

配置模型是纯多 bot 的。

重要 key:

  • bots.<id>
  • llm_profiles
  • group_scenes.chatgroup_scenes.work
  • private_scenes.chatprivate_scenes.work
  • permissions
  • runtime_http_addr
  • workspace_dirprompt_dircodex_home

值得注意的行为:

  • RuntimeConfigs() 为 bot 派生缺失路径,并跨 bot 递增默认 Runtime API 端口
  • 每个外部 llm_profiles key 是一个稳定的运行时选择器
  • Provider 专属的 profile 选择器仍通过内部 profile 字段存在于每个 profile 内部
  • 运行时权限独立控制 bundled skill 和 Runtime API 接口

12. 可观测性与调试

当前可观测性接口:

  • 通过 zerolog 的结构化日志
  • 通过 lumberjack 的滚动日志文件
  • 存储在 session_state.json 中的 session 用量计数器
  • statusview 驱动的 /status
  • log_level=debug 时每次运行的 markdown debug 跟踪

Debug 跟踪在后端暴露时包括:

  • Provider、agent name、thread/session id、model/profile
  • 渲染后的输入、观察到的工具活动、最终输出或错误

13. 扩展边界

支持的扩展面:

  • llm provider 适配器
  • prompts/ 下的 prompt 模板
  • skills/ 下的 bundled skill
  • Runtime API handlers

贡献指南

欢迎贡献。本指南涵盖所有贡献者(人类和 AI)的工作流、标准和评审要求。

English version see below.

1. 分支与变更范围

  • 日常开发基于最新 dev 分支,提交 PR 到 dev
  • main 只接受 dev -> main 的合并提交。
  • 分支命名:feat/*fix/*docs/*chore/*
  • 每次提交只做一件事,避免无关改动混在一起。

2. 提交信息规范

强制使用 Conventional Commits,由 commit-msg hook 校验:

type(scope): subject
type: subject

允许的 type:featfixdocsstylerefactorperftestbuildcichorerevert

示例:

  • feat(connector): support codex resume thread
  • fix: keep proxy env for codex exec
  • docs: add configuration reference

3. 提交前必须检查

首次执行:

make precommit-install

每次提交前必须通过:

make check

make check 按顺序运行:

门禁命令
密钥扫描secret-check
Shell 语法script-check
格式检查fmt-check(gofmt)
Vetgo vet ./...
单元测试go test ./...
Race 测试go test -race ./internal/connector

未通过 make check 不得提交。

对于涉及横切或并发相关的改动,还需运行:

go test -race ./...

格式不通过时先执行 make fmt

4. 代码规则

  • 统一使用 gofmt 格式化代码。
  • 单文件超过 500 行必须拆分(防止巨型文件增长)。
  • 新增或修改行为必须补充/更新测试。
  • 不要在日志中输出敏感信息(app secret、token、用户内容)。
  • CLI 参数变更允许破坏性调整,但必须明确文档说明并提供迁移指南。

5. 配置变更规则

  • 本项目使用 YAML 配置文件(${ALICE_HOME}/config.yaml),不用环境变量作主配置入口。
  • 新增配置项必须同步更新:
    • config.example.yaml
    • internal/config(默认值和验证)
    • 文档(英文 README 和文档站点)
  • 影响 session/记忆行为的配置项(如 idle_summary_hours)需补充对应测试。

6. 文档同步规则

任何用户可见变更(命令、参数、配置、行为)必须同步更新:

  • README.md
  • README.zh-CN.md
  • book/src/(文档站点)

保持中英文文档一致。

7. 合并前自检清单

  • 本地 make check 通过
  • 至少验证一次关键路径启动:
    go run ./cmd/connector --feishu-websocket
    
  • 文档已同步更新
  • 不包含无关文件或调试内容

8. 运行时隔离规则

调试或测试隔离 runtime 时:

  • 使用显式启动模式:--feishu-websocket--runtime-only
  • alice-headless 必须使用 --runtime-only
  • 不允许隔离调试 runtime 连接真实飞书 WebSocket
  • 启动后验证日志显示 runtime-only mode enabled; Feishu websocket connector disabled
  • 如果隔离 runtime 日志显示 feishu-codex connector started,立即停止

Contributing (English)

1. Branch and Change Scope

  • Base daily work on latest dev. Submit PRs to dev.
  • main only accepts merge commits from dev.
  • Branch naming: feat/*, fix/*, docs/*, chore/*.
  • One commit, one goal. Don't mix unrelated changes in one commit.

2. Commit Message Format

Conventional Commits are enforced by a commit-msg hook:

type(scope): subject
type: subject

Allowed types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert.

Examples:

  • feat(connector): support codex resume thread
  • fix: keep proxy env for codex exec

3. Pre-Commit Checks

First-time setup:

make precommit-install

Every commit must pass:

make check

make check runs in order: secret-check → script-check → fmt-check → go vet → go test → go test -race

Do not commit until make check passes with zero failures.

For cross-cutting or concurrency changes, also run:

go test -race ./...

If formatting fails, run make fmt first.

4. Code Rules

  • Use gofmt for all Go code.
  • Files over 500 lines must be split in the same change (prevent mega-files).
  • New or changed behavior must include/update tests.
  • Never log sensitive information.
  • CLI flag changes may be breaking but must be clearly documented with migration instructions.

5. Configuration Change Rules

  • YAML config is the primary configuration method, not environment variables.
  • New config keys require updates to: config.example.yaml, internal/config, docs.
  • Session-related config keys must have corresponding tests.

6. Documentation Sync

Any user-visible change must sync docs, keeping Chinese and English versions consistent.

7. Merge Checklist

  • Local make check passes
  • At least one startup test: go run ./cmd/connector --feishu-websocket
  • Documentation synced
  • No unrelated files or debug content included

8. Runtime Isolation Rules

When debugging or testing with isolated runtimes:

  • Use explicit startup mode
  • alice-headless must use --runtime-only only
  • Never connect isolated debug runtimes to the real Feishu WebSocket
  • After startup, verify logs show runtime-only mode enabled; Feishu websocket connector disabled

新增 LLM Backend

本指南将带你了解如何为 Alice 新增一个 LLM provider CLI 的支持。请遵循现有后端(codexclaudegeminikimiopencode)所使用的模式。

前提条件

  • Provider 必须有一个 CLI 工具,Alice 可以作为子进程运行
  • CLI 必须能通过 stdinCLI 参数接受 prompt
  • CLI 必须将结果输出到 stdout

第 1 步:理解 Backend 接口

核心接口在 internal/llm/backend.go 中:

type Backend interface {
    Run(ctx context.Context, req RunRequest) (RunResult, error)
}

type RunRequest struct {
    ThreadID        string
    UserText        string
    Model           string
    // ... 其他字段
    OnProgress      ProgressFunc
    OnRawEvent      RawEventFunc
}

type RunResult struct {
    Reply        string
    NextThreadID string
    GoalDone     bool
    Usage        Usage
}

你的后端必须:

  1. RunRequest 构建正确的 CLI 命令
  2. 将其作为子进程执行
  3. 将 stdout/stderr 解析为 RunResult
  4. 通过 OnProgress 流式传输中间进度
  5. 通过 ctx.Done() 处理取消

第 2 步:创建 Backend 文件

创建 internal/llm/<provider>_backend.go。遵循 codex_backend.go 中的模式:

package llm

import (
    "context"
    "os/exec"
)

type myProviderBackend struct {
    config MyProviderConfig
}

func newMyProviderBackend(cfg MyProviderConfig) *myProviderBackend {
    return &myProviderBackend{config: cfg}
}

func (b *myProviderBackend) Run(ctx context.Context, req RunRequest) (RunResult, error) {
    // 1. 构建命令
    args := []string{"run", "--model", req.Model}
    if req.ThreadID != "" {
        args = append(args, "--continue", req.ThreadID)
    }
    cmd := exec.CommandContext(ctx, b.config.Command, args...)
    cmd.Dir = req.WorkspaceDir
    cmd.Env = mergeEnv(b.config.Env)

    // 2. 将用户文本通过管道传入 stdin
    stdin, _ := cmd.StdinPipe()
    go func() {
        defer stdin.Close()
        io.WriteString(stdin, req.UserText)
    }()

    // 3. 流式读取和解析输出
    stdout, _ := cmd.StdoutPipe()
    // ... 从 stdout 解析 JSON-lines ...
    // ... 为中间消息调用 req.OnProgress ...

    // 4. 运行
    err := cmd.Run()

    // 5. 返回结果
    return RunResult{
        Reply:        finalReply,
        NextThreadID: nextThreadID,
        Usage:        usage,
    }, err
}

第 3 步:添加配置

internal/llm/factory.go 中添加配置结构体和 provider 常量:

const ProviderMyProvider = "myprovider"

type MyProviderConfig struct {
    Command      string
    Timeout      time.Duration
    Model        string
    Env          map[string]string
    WorkspaceDir string
    ProfileOverrides map[string]ProfileRunnerConfig
}

第 4 步:在 Factory 中注册

factory.goNewProvider 中添加你的 provider:

func NewProvider(cfg FactoryConfig) (Provider, error) {
    provider := normalizeProvider(cfg.Provider)
    switch provider {
    case ProviderCodex:
        return providerBundle{backend: newCodexBackend(cfg.Codex)}, nil
    case ProviderClaude:
        return providerBundle{backend: newClaudeBackend(cfg.Claude)}, nil
    case ProviderMyProvider:                                // NEW
        return providerBundle{backend: newMyProviderBackend(cfg.MyProvider)}, nil  // NEW
    default:
        return nil, fmt.Errorf("unsupported llm_provider %q", provider)
    }
}

同时将字段添加到 FactoryConfig

type FactoryConfig struct {
    Provider   string
    Codex      CodexConfig
    Claude     ClaudeConfig
    Gemini     GeminiConfig
    Kimi       KimiConfig
    OpenCode   OpenCodeConfig
    MyProvider MyProviderConfig   // NEW
}

第 5 步:从 config.yaml 接入配置

internal/config 中,扩展 LLM profile 以接受新 provider。Profile 配置应映射到你的 MyProviderConfig 字段(Command、Timeout、Model、Env 等)。

第 6 步:添加示例配置

config.example.yaml 中添加 profile 示例:

# 示例:MyProvider profile。
# chat_myprovider:
#   provider: "myprovider"
#   command: "myprovider"
#   model: "myprovider-model-v1"
#   permissions:
#     sandbox: "workspace-write"
#     ask_for_approval: "never"

第 7 步:编写测试

创建 internal/llm/<provider>_backend_test.go。至少测试:

  • 使用不同请求字段构建命令
  • 超时处理
  • 进度回调递送
  • 通过 context 取消
  • 无效输出的错误处理

参考 codex_backend_test.goopencode_appserver_driver_test.go 中的现有测试模式。

第 8 步:交互式 Session 支持(可选)

部分后端支持长时间运行的交互式 session,可以在不重启子进程的情况下注入新输入。如果你的 provider 支持此功能:

  1. 实现 InteractiveProviderSession 模式(参见 claude_stream_driver.goopencode_appserver_driver.go
  2. 将交互模式接入主 Run 方法
  3. 添加 DisableStream* 逃生舱用于回退

实现清单

  • internal/llm/<provider>_backend.go — 后端实现
  • internal/llm/factory.go — provider 常量 + 配置结构体 + switch case
  • internal/config — LLM profile 配置接入
  • config.example.yaml — 示例 profile
  • internal/llm/<provider>_backend_test.go — 测试
  • book/src/reference/configuration.md — 更新 provider 列表
  • book/src/how-to/configure-backend.md — 添加 provider 示例

参考实现

研究以下现有后端以了解模式:

Backend文件备注
Codexcodex_backend.go完整实现,包含 reasoning、personality、idle timeout
Claudeclaude_stream_driver.go流式交互 session
OpenCodeopencode_appserver_driver.goAppserver 模式与持久化服务器
Kimikimi_wire_driver.go线协议驱动

发版流程

Alice 版本的构建和发布方式。

分支策略

  • 日常开发在 dev 分支进行
  • 发版仅通过 dev → main 合并提交进行
  • 绝不直接推送到 main

CI 流水线

dev Push 时

  1. 运行质量门禁(make check
  2. 构建 dev 二进制
  3. 更新预发布 dev-latest

dev 合并到 main

  1. 运行质量门禁(make check
  2. 自动创建下一个 vX.Y.Z tag
  3. 构建所有平台的 release 二进制
  4. 发布 GitHub Release

手动 v* Tag

  • 推送 v* tag 会直接触发 release workflow

Release 产出物

每个 Release 发布:

  • 平台二进制构建:linux-amd64、linux-arm64、darwin-amd64、darwin-arm64、win32-x64
  • npm 包:@alice_space/alice
  • 安装脚本:scripts/alice-installer.sh

执行发版

  1. 确保 dev 通过所有检查且准备就绪
  2. 创建从 devmain 的 PR
  3. 使用 merge commit 合并(不要 squash 或 rebase)
  4. CI 自动创建 tag 并发布 Release
  5. 验证 GitHub Release 显示所有产出物

版本号

Tag 遵循 semver:vX.Y.Z。CI 从上一个 release tag 自动递增补丁版本号。

发版后

  • 安装脚本(alice-installer.sh)自动获取最新 release
  • npm 用户通过 npm update -g @alice_space/alice 获取更新

CI Workflow 文件

  • .github/workflows/ci.yml — Dev 分支质量门禁和 dev 二进制
  • .github/workflows/main-release.yml — Main 分支 release 构建
  • .github/workflows/release-on-tag.yml — 手动 tag release