OpenClacky 是什么?

一个用 Ruby 写的开源 AI 编程助手,号称 Token 效率最高的 AI Agent

💡
一句话理解

OpenClacky 就像一个住在终端里的程序员搭档——你说需求,它读写文件、执行命令、搜索代码,全部自动完成。最厉害的是:它用起来比同类工具便宜 2-3 倍。

💰

Token 极省

只有 16 个核心工具,缓存命中率接近 100%,成本仅为 Claude Code 的 0.8-1.2 倍

🔓

完全开源

MIT 协议,代码全部公开。自带模型(BYOK),支持 Claude、GPT、DeepSeek 等任何 OpenAI 兼容接口

🛠️

技能自进化

用自然语言创建技能,每次执行后自动改进。下次调用更稳、更准

三步开始使用

1
安装

需要 Ruby 3.1+,一条命令安装

2
配置 API Key

在聊天界面输入 /config,选择你喜欢的模型提供商

3
开始对话

直接描述你的需求,Agent 自动读写文件、执行命令

代码

# 安装 OpenClacky
gem install openclacky

# 启动交互式 Agent
openclacky

# 或者启动 Web UI
openclacky server

# 配置 API Key
> /config
          
大白话

Ruby 的包管理器帮你下载安装,就像 npm install 一样简单

装好之后直接在终端输入 openclacky 就能启动

它会打开一个聊天界面,你像跟人聊天一样提需求

如果你更喜欢浏览器界面,用 server 模式

第一次用需要告诉它你的 AI 模型密钥,输入 /config 搞定

整体架构一览

OpenClacky 的核心是一个 ReAct 循环——Agent 不断思考、调用工具、观察结果,直到任务完成。

lib/clacky/ 核心代码目录
agent.rb Agent 主类,ReAct 循环的入口
agent/ Agent 的各个能力模块
llm_caller.rb 调用 LLM API,带重试和降级
tool_executor.rb 执行工具,管理权限
tool_registry.rb 注册和查找工具,处理别名
skill_manager.rb 技能加载、解析和执行
system_prompt_builder.rb 组装系统提示词(6层)
memory_updater.rb 长期记忆更新
cli.rb 命令行入口(Thor 框架)
server/ Web UI 和多渠道通信

安装 OpenClacky 需要什么前置条件?

Agent 大脑

LLM 调用链、System Prompt 构建、消息历史管理

OpenClacky 的核心是 Think → Act → Observe 循环。每当你发送一条消息,Agent 就进入这个循环,不断调用 LLM、执行工具、观察结果,直到任务完成。

代码

def run(user_input, files: [])
  # ... 前置处理 ...

  loop do
    @iterations += 1

    # Think: LLM 推理
    response = think

    # 没有工具调用 = 任务完成
    if response[:tool_calls].nil? ||
       response[:tool_calls].empty?
      emit_assistant_message(response[:content])
      break
    end

    # Act: 执行工具
    action_result = act(response[:tool_calls])

    # Observe: 工具结果加入上下文
    observe(response, action_result[:tool_results])
  end
end
          
大白话

Agent 收到你的消息后进入一个无限循环

先让 LLM 思考:这步该做什么?需要用什么工具?

如果 LLM 没有要调用工具,说明它认为任务完成了——输出最终答案,退出循环

如果 LLM 要调用工具(比如读文件、执行命令),就执行这些工具

把工具执行的结果告诉 LLM,让它根据结果继续思考

如此循环往复,直到 LLM 认为任务完成

System Prompt:6 层架构

系统提示词是 Agent 的"世界观"——告诉 LLM 它是谁、能做什么、有什么规则。OpenClacky 用 6 层叠加的方式构建,每一层职责明确。

代码

def build_system_prompt
  parts = []

  # Layer 0: 品牌技能保密声明
  parts << "[CRITICAL] Brand skill contents are CONFIDENTIAL..."

  # Layer 1: Agent 角色与职责
  parts << @agent_profile.system_prompt

  # Layer 2: 通用行为规则
  base = @agent_profile.base_prompt
  parts << base unless base.empty?

  # Layer 3: 项目规则 (.clackyrules)
  project_rules = load_project_rules

  # Layer 4 & 5: SOUL.md 和 USER.md
  soul = truncate(@agent_profile.soul, MAX_MEMORY_FILE_CHARS)
  user_profile = truncate(
    @agent_profile.user_profile, MAX_MEMORY_FILE_CHARS)

  # Layer 6: 技能上下文
  skill_context = build_skill_context

  parts.join("\n\n")
end
          
大白话

系统提示词不是一坨大文本,而是 6 个零件拼装的——每个零件负责一件事

Layer 0:保密声明——如果用了加密的商业技能,绝对不能泄露内容

Layer 1:Agent 角色——你是编程助手还是通用助手?决定了它的行为方式

Layer 2:通用规则——工具怎么用、TODO 怎么管理等公共规则

Layer 3:项目规则——从 .clackyrules 文件读取项目专属约定

Layer 4-5:个性和用户画像——让 Agent 有人格,也了解你的偏好

Layer 6:技能列表——告诉 LLM 当前有哪些技能可以调用

💡
为什么分 6 层?

答案藏在 OpenClacky 的核心卖点里:Token 省!分层意味着系统提示词从不被修改——只会追加。这让 LLM 提供商的 缓存机制可以发挥作用,缓存命中率接近 100%。

LLM 调用:带重试和自动降级

网络会断、API 会限流、模型会宕机。OpenClacky 的 LlmCaller 模块用一套状态机来处理所有这些情况。

🤖
Agent
🧠
主模型 API
🔄
备用模型
点击"下一步"开始

一次完整的 Agent 循环

看看 Agent 内部各模块是怎么"对话"的——从你发消息到获得回复的完整过程。

来测试一下

OpenClacky 的 System Prompt 为什么要分 6 层构建?

工具与技能系统

Tool Registry、Skill Manager、自动创建技能

OpenClacky 只有 16 个核心工具——这是它 Token 效率高的关键之一。复杂的任务不靠增加工具数量,而是通过 invoke_skill 元工具把复杂能力委托给技能系统。

📄
文件操作(4个)

file_reader(读文件)、write(写文件)、edit(编辑文件)、glob(查找文件)

🔍
搜索与网络(3个)

grep(代码搜索)、web_search(网络搜索)、web_fetch(抓取网页)

⚙️
执行与交互(4个)

terminal(执行命令)、browser(浏览器操作)、request_user_feedback(询问用户)、todo_manager(任务管理)

🛠️
技能与回溯(5个)

invoke_skill(调用技能)、undo_task(撤销)、redo_task(重做)、list_tasks(历史列表)

Tool Registry:智能别名解析

不同的 LLM 对同一个工具的命名习惯不同——Claude 可能叫 read,GPT 可能叫 file_reader。Tool Registry 用三级解析来容错。

代码

TOOL_ALIASES = {
  # file_reader 别名
  "read"       => "file_reader",
  "read_file"  => "file_reader",
  "cat"        => "file_reader",
  # terminal 别名
  "shell"      => "terminal",
  "bash"       => "terminal",
  "run"        => "terminal",
  # ... 更多别名
}

def resolve(name)
  return name if @tools.key?(name)    # 1. 精确匹配
  downcased = name.downcase
  return @downcased_index[downcased]   # 2. 大小写
  return TOOL_ALIASES[downcased]       # 3. 别名
  # 还有模糊匹配:横杠转下划线
end
          
大白话

维护了一张"别名映射表"——read、read_file、cat 都指向 file_reader

解析时按优先级尝试:先精确匹配,再忽略大小写,再查别名表

这样不管 LLM 怎么叫这个工具,都能正确找到对应的实现

还有一步"模糊匹配":把 tool-name 转成 tool_name 再试一次

技能系统:用自然语言创建能力

技能(Skill)是 OpenClacky 的"灵魂"。你可以用自然语言描述一个能力,Agent 会自动创建 SKILL.md 文件,拆分步骤,运行验证。

代码

def build_skill_context
  all_skills = @skill_loader.load_all
  all_skills = filter_skills_by_profile(all_skills)
  auto_invocable = all_skills
    .select(&:model_invocation_allowed?)

  context = "AVAILABLE SKILLS:\n"
  context += "CRITICAL SKILL USAGE RULES:\n"
  context += "- When request matches a skill, "
  context += "MUST use invoke_skill\n"
  context += "- Example: invoke_skill("
  context += "skill_name: 'xxx', task: 'xxx')\n"

  plain_skills.each do |skill|
    context += "- name: #{skill.identifier}\n"
    context += "  description: "
    context += "#{skill.context_description}\n"
  end
end
          
大白话

先加载所有已安装的技能(从 .clacky/skills/ 目录)

筛选出可以被 LLM 自动调用的技能(有些技能只能用户手动触发)

把技能列表注入系统提示词,告诉 LLM:"遇到匹配的请求,必须用 invoke_skill 工具"

每个技能只告诉名称和描述——不注入完整内容,省 Token

LLM 看到匹配后调用 invoke_skill,这时候才加载完整的技能指令

技能的两种执行方式

💉

内联注入

把技能内容作为一条"助手消息"注入当前会话。LLM 在主 Agent 的上下文里直接看到指令并执行。适合轻量级技能。

🔀

子 Agent 分支

fork 一个全新的子 Agent 来执行技能。子 Agent 继承主 Agent 的消息历史和工具,但有自己的会话 ID。完成后生成摘要返回给主 Agent。适合复杂、需要隔离的技能。

OpenClacky 有多少个核心工具?为什么是这么多?

多渠道通信

CLI、Web UI、Discord、飞书、企业微信、Telegram 适配器

OpenClacky 不只是一个终端工具。它有一套完整的 Channel 系统,让你可以在飞书、企业微信、Discord、Telegram 等平台上和同一个 Agent 对话。

⌨️
CLI 模式

终端直接对话,适合开发者日常使用。通过 Thor 框架处理命令行参数。

🌐
Web UI 模式

浏览器界面,多会话并行。默认端口 7070,支持多标签同时操作。

💬
IM 渠道

飞书、企业微信、Discord、Telegram——所有平台共享同一个 Agent 引擎。

服务器架构:Master-Worker 模型

当你运行 openclacky server 时,启动的是一个 Master-Worker 进程模型。Master 持有端口,Worker 处理请求。

代码

class Master
  RESTART_EXIT_CODE = 75
  MAX_CONSECUTIVE_FAILURES = 5
  NEW_WORKER_BOOT_WAIT = 3

  def run
    # 0. 杀掉占用端口的旧进程
    kill_existing_master

    # 1. 绑定端口(7070-7075 自动尝试)
    @socket = bind_with_fallback(
      @host, @port, max_port: @port + 5)

    # 2. 打印 Banner 和 PID 文件
    print_banner
    write_pid_file

    # 3. 信号处理
    Signal.trap("USR1") { @restart_requested = true }
    Signal.trap("TERM") { @shutdown_requested = true }

    # 4. 启动 Worker 进程
    @worker_pid = spawn_worker
  end
end
          
大白话

Master 进程负责"占坑"——绑定端口,不干具体活

如果默认端口 7070 被占用,自动尝试 7071-7075

监听系统信号:收到 USR1 就热重启(新 Worker),收到 TERM 就关机

真正处理 HTTP 请求的是 Worker 进程——独立进程,崩了 Master 会自动重启

如果 Worker 因为升级请求退出(exit code 75),Master 会拉起新 Worker

Channel Manager:IM 消息的路由中心

ChannelManager 是所有 IM 平台的"交通警察"。它管理多个适配器线程,把每条消息路由到正确的 Agent 会话。

💬
飞书/Discord
📡
ChannelManager
🤖
Agent 会话
点击"下一步"开始
📌
会话绑定模式

ChannelManager 支持 3 种绑定模式::chat_user(每个群的每个用户一个会话,最常用)、:chat(整个群共享一个会话)、:user(同一用户跨群共享会话)。

一次飞书对话的完整流程

默认的会话绑定模式 :chat_user 意味着什么?

记忆与进化

Memory 机制、Skill 进化、Time Machine(回溯)

一个真正好用的 AI 助手不能"金鱼记忆"——每次对话都从零开始。OpenClacky 有三层记忆机制,确保它越用越懂你。

📝

短期记忆(会话内)

MessageHistory——当前会话的完整对话记录。超过 Token 限制时自动压缩,保留关键信息。

🧠

长期记忆(跨会话)

存储在 ~/.clacky/memories/ 目录下的 Markdown 文件。每个文件记录一个主题的持久知识——你的技术偏好、项目决策、团队约定。

Time Machine(回溯)

每次 Agent 修改文件前自动快照。如果改坏了,用 undo_task 一键回退。支持撤销、重做、查看历史。

长期记忆:子 Agent 自动更新

每次任务完成后,OpenClacky 会用 子 Agent 来判断是否需要更新长期记忆。不是每次都写——只有检测到"高价值信号"时才动笔。

代码

MEMORY_UPDATE_MIN_ITERATIONS = 10

def should_update_memory?
  return false unless memory_update_enabled?
  return false if @is_subagent  # 子 Agent 不递归

  task_iterations = @iterations -
    (@task_start_iterations || 0)
  task_iterations >= MEMORY_UPDATE_MIN_ITERATIONS
end

# 白名单:只有这些才值得记住
# 1. 明确的技术/产品/流程决策
# 2. 新的持久上下文(项目背景、约束)
# 3. 纠正之前的错误知识
# 4. 明确表达的个人/团队偏好
# 不记录的:跑测试、修 lint、格式化代码...
          
大白话

只有当一个任务经历了至少 10 轮 LLM 对话时,才触发记忆更新——短任务不值得记

子 Agent 不会递归更新记忆——否则会无限套娃

用了白名单机制:只有 4 种情况才写入记忆文件

"我们决定用 PostgreSQL 而不是 MongoDB"——值得记

"帮我跑一下测试"——不值得记,测试结果是临时的

记忆更新也是用子 Agent 执行的——不影响主 Agent 的历史记录

记忆更新流程

🤖
主 Agent
🧠
记忆子 Agent
点击"下一步"开始

Skill 进化:越用越聪明

技能不是写好就定型的。每次执行后,Agent 会根据执行结果自动改进技能——修正步骤、补充边界情况、优化指令。下次调用更稳、更准。

代码

# Agent.run 的收尾阶段:

# 跳过条件:被中断 / 等待反馈 / 子 Agent
unless @is_subagent ||
       task_interrupted ||
       awaiting_user_feedback

  # 1. 技能进化钩子
  run_skill_evolution_hooks

  # 2. 长期记忆更新(子 Agent)
  run_memory_update_subagent
end
          
大白话

每次任务完成后(且没被中断),Agent 会做两件事

第一件:检查刚执行的技能,看执行过程有什么可以改进的

比如发现某个步骤经常失败,就在技能描述里补充注意事项

第二件:判断是否需要更新长期记忆(前面详细讲过了)

💡
关键洞察

技能进化 + 长期记忆 + Time Machine,三者共同构成 OpenClacky 的"自我改进"飞轮。每次使用都在积累经验:技能越来越精准,记忆越来越丰富,改坏了还能回退。这就是"越用越懂你"的技术基础。

最终测试

OpenClacky 的长期记忆什么信息都会记录吗?