Skip to content

Repository files navigation

Capricorn

version license python tests

原生 Function Calling 驱动的轻量通用 Agent Runtime —— 薄薄一层在 LLM 之上。 不约束它怎么做,只告诉它有什么能用,让它自己规划和决策。

Capricorn 是一个跑在 LLM 上层的调度层。它不硬编码业务规则、不规定步骤数、不堆 ReAct / 状态机——LLM 直接决定调什么工具,系统并发执行,循环到最终响应。消息进来 → LLM 判断何时调工具 → 记忆 / 技能 / 子 Agent 只在需要时作为上下文拉入。所有东西围绕一个小的 agent loop 组织,核心路径因此好读、好扩展。

它不是框架——二次开发不是「学一套框架」,而是「写一个工具 + 一段 prompt」。

一份引擎,通用 + 垂直。 通用能力(文件、命令、搜索、记忆……)是任何 agent 都要用的地基,永远加载;垂直能力(特定领域的工具 / 技能 / 角色)通过 vertical_hub/ 叠加,config 一行 active 开关切换。内置 note-taking 作为完整范例。


🧭 Start Here

你想... 去哪
5 分钟跑起来,先看到效果 快速开始
理解设计理念(为什么「薄」) 设计理念
一眼看完全部能力 能力全景
开启一个垂直领域 / 写一个 vertical 包 垂直领域系统
搞懂加载管线 / 改 prompt 改 config 加载机制
接入飞书 / 微信 / QQ 渠道层
做二次开发、换垂类、加工具 二次开发
看安全基线 安全

✨ 核心能力

  • 🧩 Vertical 系统 —— vertical_hub/<name>/ 领域包,config 一行 active 开关:通用层永远在、领域层往上叠加。内置 note-taking 范例,覆盖 tools / skills / workflows / prompts / roles / mcp.json 全部文件类型
  • 🧠 原生 FC 循环 —— LLM → tool_calls → 并发执行 → 循环到响应,无 ReAct、无状态机
  • 会话抢占 —— 同一会话发来新消息时自动中断上一轮 run、落盘中断标记、直接回复最新消息(CLI / WebUI / HTTP / 飞书全入口一致)
  • 🖥️ Claude-Code 式 TUI —— 边框分色对话框、自适应宽度、\+Enter 换行、后台实时渲染;非 TTY 自动降级阻塞输入
  • 👥 Agent Teams —— 主 Agent spawn executor / verifier 子 Agent,5 态任务状态机,何时拆任务由 LLM 自主决策
  • Cron 调度 —— once / recurring,结果推回来源渠道(飞书 / WebUI / CLI)
  • 🧠 三层记忆 + BIA 自进化 —— session / MEMORY.md / HISTORY.md 分工;行为规则自动去重、压缩、纠偏
  • 🛠️ 后台进程工具 —— background_start / background_status / background_stop 起停长跑命令,deliver_file 把成品发回来源渠道
  • 🔒 安全基线 —— exec 命令白名单注入防护 · 上传 per-file 大小检查 · 飞书 WebSocket 身份告警 · Gateway fail-secure 绑定

这个版本怎么来的(通用引擎与垂直领域的合并历程)见 版本历史


💡 设计理念

Thin layer on top of LLM. 不用 ReAct,不堆状态机——LLM 直接决定调什么工具,系统并发执行,循环直到最终响应。

Capricorn 做 LLM 做
注册工具、提供能力 判断用什么工具、怎么组合
管理 session / memory 决定什么时候需要 spawn team
调度 cron 决定任务拆分和执行策略
维护 bia 行为规则 发现模式、自我纠偏

这意味着:换一个更强的模型,Capricorn 不用改一行代码就自动变强。 也意味着二次开发很轻——你要做的不是「学一套框架」,而是「写一个工具 + 一段 prompt」。


🚀 快速开始

# 克隆本仓库后,进入目录:
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env   # 编辑 .env 填入 API Key

1. 启动(Gateway + WebUI 模式)

python run.py --mode gateway_with_webui
# 浏览器打开 http://localhost:8080

2. 发第一条消息 —— 在 WebUI 对话框输入任务,看 FC 循环实时推送(thinking → tool_call → round → response)。

3. 试试拆任务 —— 让主 Agent spawn 一个 executor 子任务,用 check_status / get_result 收口。或让它建个 cron 定时任务,到点自动跑、结果推回来源渠道。

其他模式:--mode cli(终端对话)、--mode gateway(纯 HTTP/SSE)。


🏗️ 架构

用户(CLI / WebUI / HTTP API / 飞书)
  │
  ▼
Gateway(aiohttp · Auth · SSE)
  │  POST /upload · /chat · /chat/stream
  ▼
Channel Manager(飞书 / 微信 / QQ / Telegram ...)
  │
  ▼
Capricorn Agent ── FC 循环:LLM → tool_calls → execute → repeat
  ├── 三层工具      builtin / MCP / workflow,自动发现
  ├── Agent Teams   spawn executor / verifier,LLM 自主决策
  ├── Cron          定时任务,支持角色,结果推回来源 Channel
  ├── 三层记忆      session / MEMORY.md / HISTORY.md
  ├── BIA 自进化    行为规则去重、压缩、上限管理
  └── Tasklist      任务列表,SSE 实时同步

核心理念:所有东西围绕一个小的 agent loop 组织——消息进来,LLM 决定何时调工具,记忆/技能只在需要时作为上下文拉入,而不是变成厚重的编排层。这让核心路径好读、好扩展。

想看某个模块的实现细节,直接读源码目录——每个子包的 __init__.py 顶部都有该模块的职责说明。


🧰 能力全景

渠道层

能力 说明
WebUI Streamlit 对话界面,支持文件上传 + 图片多模态
HTTP API POST /chat/chat/stream(SSE)、/upload/task/sessions/history
飞书 Channel WebSocket 长连接,无需公网 IP;收文本/图片/表情 + 回推成品文件/图片deliver_file 工具,走来源对话);群聊 @触发
可扩展 微信 / QQ / Telegram ——实现 BaseChannel 即可接入(send 发文本;覆写 send_file 即支持发文件)

飞书发文件/图片需在开放平台给应用开通 im:resource(上传图片/文件)权限;发消息用 im:message:send_as_botsend() 已用)。

核心层

能力 说明
FC 循环 LLM → tool_calls → execute,无 ReAct,无状态机
SSE 流式 FC 循环每步实时推送:run_start / thinking / tool_call_* / round_* / consolidation_* / tasklist_update / response / run_end
多模态 图片 base64 注入 LLM 原生视觉(不贯穿上下文,回复后丢弃省 token)
三层记忆 session(JSONL 每轮写盘)+ MEMORY.md(LLM 整合,token 上限)+ HISTORY.md(可搜索行动日志)
BIA 自进化 bia.md 行为规则:时间戳 + 去重 + token 上限 + LLM 压缩

工具层

通过 BaseTool 基类定义,按目录自动发现注册

  • builtin —— 文件读写(offset/limit + 行号)、glob/grep 搜索、exec 命令、task/spawn/check_status/get_result、quality_check、bia_update、tasklist、deliver_file(把成品文件发回用户对话渠道)等
  • MCP —— 通过 MCP 协议接入外部服务(搜索、图像理解、浏览器…)
  • workflow —— 多步编排的复杂任务

扩展层

能力 说明
Agent Teams 主 Agent spawn 子 Agent:executor 执行 / verifier 验收(对抗式)。不硬编码何时 spawn,LLM 自行判断
任务状态机 producing → running → done / need_decision / error(5 态);验收/重试由主 Agent 读结论后手动驱动
Cron once(延迟/时刻/绝对时间)/ recurring(间隔/每天/标准 cron);role 套角色模板;fresh_session 独立人格;结果推回来源渠道
技能系统 autoload + on-demand,按需加载领域技能(self-evolution / fullstack-dev / minimax-pdf/xlsx …)
角色化 身份(WHO) / 权限(WHAT) / 指令(HOW) 三层解耦:roles/*.md + roles/*.yaml + spawn brief

🧭 垂直领域系统

核心洞察:通用能力(exec / read_file / memory …)是任何 agent 都要用的地基,永远加载;垂直能力(领域专用的工具 / 技能 / 角色)按需叠加。两者在同一份引擎里,config 一行 active 开关切换——不是两套代码,是同一引擎的通用层 + 领域层。

非对称设计

capabilities/         = 平台内置(universal,永远加载,关不掉)
vertical_hub/<name>/  = 领域扩展(domain-specific,active 才加载)
  • active: null → 只加载 capabilities/ = 纯通用(向后兼容)
  • active: "note-taking"capabilities/ + vertical_hub/note-taking/ = 通用 + 领域

开启一个垂直领域

config/config.json 一行:

{
  "vertical": {
    "hub_dir": "vertical_hub",
    "active": "note-taking"          // null = 纯通用;"<name>" = 叠加该 vertical
  }
}

启动时会自动:扫描该 vertical 的 tools/skills/workflows/roles,合并 mcp.json,用它的 system.md / bia.md / cron.md 覆盖内置,并在 system prompt 注入「你现在运行在 「note-taking」 垂直领域」的 signal。手把手写法见下方 vertical 制作指南

vertical 包结构(约定优于配置)

每个 vertical_hub/<name>/ 是一个文件夹,子目录按约定命名,全部可选(只有 vertical.yaml 必需):

文件/目录 作用 加载规则
vertical.yaml {name, description} → prompt signal 元数据 必需
tools/ 领域工具(.py,BaseTool 子类,auto_discover=True 合并进 registry
skills/<n>/SKILL.md 领域技能(纯 prompt 知识) 合并进 SkillManager
workflows/ 领域 workflow(.py,BaseWorkflow 子类) 合并(经 Wrapper)
roles/*.yaml 领域角色 合并,同名 vertical 覆盖内置
prompts/bia.md 领域行为纠偏规则 覆盖全局 bia.md
prompts/cron.md 领域定时任务提示词 覆盖内置 cron.md
mcp.json 领域 MCP 服务(同 config.mcp_servers 结构) 合并进 mcp_servers

工具名全局唯一:vertical 工具不能和内置重名,冲突会直接报错(不静默覆盖)。

内置范例 vertical

vertical 内容 用途
note-taking 笔记创建/搜索/统计工具、日记与知识图谱技能、周报 workflow、archivist 角色 完整结构范例,覆盖 tools / skills / workflows / roles / prompts / mcp 全部文件类型

手把手写一个 vertical

从零搭一个领域包。先看最小可用形态,再逐类加内容——每一类都用内置范例 note-taking 做对照。

1. 最小可用:只有 vertical.yaml

一个 vertical 最少只要一个文件:

mkdir vertical_hub/my-domain
# vertical_hub/my-domain/vertical.yaml
name: my-domain
description: "一句话说清这个领域做什么"

config 里开 "active": "my-domain" 就能加载——没有任何工具/技能,但 system prompt 会注入「你现在运行在 my-domain 垂直领域」的 signal。从这里起步,按需往下加。

包结构总览(哪些文件可选、加载规则)见上方 vertical 包结构 表。

2. 逐类文件的写法

tools/ —— 领域工具(.py,BaseTool 子类)

继承 BaseTool,实现 4 个成员即可。最小骨架:

# vertical_hub/my-domain/tools/my_tool.py
from core.base_tool import BaseTool
from core.sandbox import resolve_path   # 写文件务必走它,防路径穿越

class MyTool(BaseTool):
    def __init__(self, workspace_root="./workspace", sandbox=True):
        self._workspace_root = workspace_root
        self._sandbox = sandbox

    @property
    def name(self) -> str:
        return "my_tool"

    @property
    def description(self) -> str:
        return "一句话告诉 LLM 这个工具做什么、输入输出是什么"

    @property
    def parameters(self) -> dict:
        return {
            "type": "object",
            "properties": {
                "target": {"type": "string", "description": "..."},
            },
            "required": ["target"],
        }

    async def execute(self, target: str) -> str:
        return f"done: {target}"
  • from_config 默认用 {workspace_root, sandbox} 构造;参数类型校验由基类 cast_params / validate_params 兜底。
  • parameters 是 JSON Schema,LLM function calling 按它传参。
  • 文件丢进 tools/ 即自动发现(auto_discover=True),无需注册。范例:note-taking/tools/note_tools.py

skills/<n>/SKILL.md —— 领域技能(纯 prompt 知识)

没有代码,就是一段 Markdown 提示词,按需 skill_view 加载。frontmatter 必须有 name + description

---
name: my-skill
description: |
  这个技能什么时候该激活。触发场景:
  - 用户说"……"
  - 需要按某固定流程做事时
available: true
---

# My Skill 规范

正文写领域知识 / 执行步骤 / 模板。LLM 加载后按这里行事。

description 是按需加载的判分依据——写清触发场景比写正文更重要。范例:note-taking/skills/daily-journal/SKILL.md

workflows/ —— 领域 workflow(.py,BaseWorkflow 子类)

多步编排:做掉机械的收集/拼装,把需要理解的归纳交给上层 LLM。骨架:

# vertical_hub/my-domain/workflows/my_workflow.py
from core.base_workflow import BaseWorkflow

class MyWorkflow(BaseWorkflow):
    @property
    def name(self) -> str:
        return "my_workflow"

    @property
    def description(self) -> str:
        return "这个 workflow 做什么,输入输出是什么"

    async def execute(self, tools, **kwargs) -> dict:
        # tools 是工具 registry,按名取用:await tools.get("my_tool").execute(...)
        ...
        return {"stage": "planned", "draft": "...", "next_action": "..."}

要点:通过 tools.get("<name>") 调用其它工具;返回结构化 dict 交 LLM 收尾。范例:note-taking/workflows/weekly_review.py

roles/*.yaml + prompts/roles/*.md —— 领域角色

身份(WHO) / 权限(WHAT) / 指令(HOW) 三层解耦。yaml 声明角色 + 工具白名单,md 写角色 prompt:

# vertical_hub/my-domain/roles/my-role.yaml
name: my-role
prompt: ../prompts/roles/my-role.md
description: "这个角色负责什么"
tools:
  - my_tool
  - read_file

spawn role=my-role 时,子 Agent 只能用 tools 里列出的工具。范例:note-taking/roles/archivist.yaml + prompts/roles/archivist.md

prompts/{bia,cron}.md —— 覆盖内置提示词

放一份 prompts/bia.md(行为纠偏规则)或 prompts/cron.md(定时任务提示词),会覆盖全局同名文件。范例:note-taking/prompts/bia.md

进阶还有 prompts/system.md(覆盖内置 system prompt,需保留 8 个 {{占位符}})和 mcp.json(领域 MCP 服务,合并config.mcp_servers),按需参考 note-taking。

3. 加载规则速查

文件 规则
tools/ · workflows/ · mcp.json 合并(工具 / 服务名冲突 → 直接报错
skills/ · roles/ 后加载覆盖同名内置
prompts/{system,bia,cron}.md 覆盖全局同名文件

4. 开起来验证

# config.json: "active": "my-domain"
python run.py --mode gateway_with_webui

看启动日志是否注入了 my-domain signal;让 agent 调一下你的工具确认注册成功。最快的起步方式仍是 cp -r vertical_hub/note-taking vertical_hub/my-domain,改 vertical.yaml 的 name+description,再按需替换内容。


📦 加载机制

启动期围绕 8 个加载源把 capabilities/vertical_hub/ 拼成一个 agent——每个源有内置位置、vertical 位置和明确的加载规则(合并 / 覆盖 / 报错):

内置位置 vertical 位置 规则
Config config/config.json env var ${VAR} 递归替换
Vertical config.vertical.active=null vertical_hub/<n>/ active 非空时叠加
System Prompt config/prompts/system.md vertical prompts/{system,bia,cron}.md system/bia/cron 覆盖(system 需保留 8 个占位符)
Tool capabilities/tools/{builtin,workflow}/extensions/ vertical_hub/<n>/{tools,workflows}/ 合并,同名报错
Skill capabilities/skills/skills/<n>/SKILL.md vertical_hub/<n>/skills/<n>/SKILL.md 后加载覆盖前者
Role config/roles/*.yaml vertical_hub/<n>/roles/*.yaml 同名 vertical 覆盖内置
MCP config.mcp_servers vertical_hub/<n>/mcp.json 合并,vertical 覆盖 config
Memory workspace/{memory,sessions}/

加载是 fail-loud 的:工具 / 服务名冲突直接报错(不静默覆盖),system prompt 占位符缺失会提示。改 config 或 vertical 内容后重启生效。


🔒 安全

把安全从「纸上」做成「代码层」:

维度 机制
文件路径 sandbox=true 时所有文件操作限定在 workspace 内,resolve + 路径穿越校验
命令执行 黑名单 blocked_commands + 可选白名单 allowed_commands(OPT-IN);白名单启用时拒绝命令注入元字符
上传 MAX_UPLOAD_SIZE=30MB < CLIENT_MAX_SIZE=50MB,per-file 检查在请求上限前生效;文件名去路径组件;base64 CTE 解码
飞书身份 WebSocket 模式 SDK 不校验发送者,靠 allow_from 白名单兜底;["*"] 时告警
Gateway 非回环绑定且未设 _api_key → 启动直接 fail-secure 报错;MAX_CONCURRENT_AGENT_RUNS=20 防成本滥用

公开/多用户部署前,务必配置 allowed_commands 与显式 allow_from


🧩 二次开发

因为薄,所以好改。三件事覆盖绝大多数定制:

1. 换/加垂类 —— 写一个 vertical_hub/<name>/ 包(tools + skills + prompts),config 开 active。完整结构范例见 垂直领域系统note-taking

2. 加一个内置工具 —— 继承 BaseTool,实现 name / description / parameters / execute,丢进 capabilities/tools/builtin/extensions/,自动发现注册(所有 vertical 都能用)。

3. 接一个 Channel —— 继承 BaseChannel,实现 start / send / 消息解析,在 config.jsonchannels 注册。

所有 prompt 都是 Markdown 模板,通过 {{placeholder}} 组装(workspace / tools / skills / memory / bia / vertical_section 等,共 8 个占位符)。


⚙️ 配置

环境变量用 ${VAR_NAME} 注入 config/config.json

{
  "llm": {
    "model": "MiniMax-M3",
    "api_key": "${MINIMAX_API_KEY}",
    "api_base": "https://api.minimaxi.com/v1"
  },
  "workspace": { "sandbox": true },
  "allowed_commands": [],          // OPT-IN:留空=不启用白名单,公开部署前请配
  "team":    { "max_concurrent": 5, "max_attempts": 3, "max_questions": 3 },
  "memory":  { "max_memory_tokens": 3000, "max_history_entries": 100 },
  "vertical": { "hub_dir": "vertical_hub", "active": null }   // null=纯通用;"<name>"=开启该领域
}

完整字段见 config/settings.pyConfig 类定义。


🧪 测试

pytest tests/ -q          # 568 tests

🗺️ Roadmap

  • 🌐 更多 Channel —— 微信 / QQ / Telegram / Discord
  • 🔧 更多 builtin 工具 —— 浏览器、代码执行沙箱
  • 🧠 深度自进化 —— bia 规则结构化 + 效果量化
  • 📦 包分发 —— pip install 一键接入垂类

欢迎开 Issue / PR。


📋 版本历史

本仓库(v0.1.0+)是 通用线 Capricorn-x + 垂直线 Capricorn-V 合并后的 canonical 版本——同一份引擎同时承载通用与垂直能力。合并前两条线各自独立演进,历史分列于下。

版本 主题
v0.2.0 会话抢占(新消息中断旧 run + 落盘中断标记,CLI/WebUI/HTTP/飞书全入口一致)+ Claude-Code 式 TUI(边框分色、自适应宽度、\+Enter 换行)+ 后台进程工具(background_start/status/stop + deliver_file)+ WebUI secrets 兜底
v0.1.0 X + V 合并版:vertical 系统(vertical_hub/ + config active 开关)+ note-taking 范例 vertical + 通用/垂直非对称加载 + 安全基线(exec 白名单注入防护 / 上传 per-file 检查 / 飞书身份告警 / spawn 5 态)
合并前 · Capricorn-x(通用线,末版 v0.3.0)

通用 Agent Runtime:渠道层(飞书 / WebUI / HTTP)、SSE 流式、文件上传与多模态、安全基线。

版本 主题
v0.3.0 安全基线(exec 白名单注入防护 + 上传 per-file 检查 + 飞书身份告警)+ spawn 状态机瘦身(7 态→5 态)
v0.2.12 飞书 Channel(WebSocket 长连接 + 图片/表情接收 + Channel Prompt)+ Cron 源路由(结果推回来源 Channel)+ Config 清理
v0.2.11 SSE 断连后台执行 + 进度持久化 + sandbox 统一 + config 简化
v0.2.10 glob/grep 搜索工具 + read_file offset/limit + 代码简化清理
v0.2.9 SSE 流式事件 + Tasklist 工具 + 指数退避重试
v0.2.8 Memory 优化(整合逻辑重构、配置调优)
v0.2.7 文件上传 + 图片多模态 + 安全修复
v0.2.6 简化 LLM 约束 + BIA/memory 上限管理
v0.2.5 BIA / Team / Cron / Quality 工具 + 代码简化清理
v0.2.4 scheduler + cron prompt 调整
v0.2.3 gateway/notification/scheduler + 文档生成能力(minimax-pdf/docx)
v0.2.2 builtin tools extensions 扩展
v0.2.1 skills 子系统调整
v0.2.0 早期重构(exec/file tools + mcp client)
v0.1.0-legacy 初始版本 — 完整脚手架(agent + capabilities + skills + tools)
合并前 · Capricorn-V(垂直线,末版 v0.2.2)

垂直领域 Agent Runtime:Agent Teams 对抗协作 + 角色化 Cron + 自进化纠偏 + 垂直一键加载。V 未将逐版本 changelog 入仓,下表为其合并前的最终能力形态。

能力 合并前最终形态(v0.2.2)
Agent Teams Executor / Verifier 对抗协作,spawn 异步派发,文件交接
角色化 Cron cron 支持 role(executor / verifier),身份 / 权限 / 指令三层分离
自进化 verifier cron 检测质量 → bia_update / skill 编辑 → changelog 追溯回滚
垂直领域一键加载 tools / MCP / skills / workflows / roles 按 vertical_hub 组织
三层记忆 JSONL 会话 + MEMORY.md + HISTORY.md

V 的 vertical_hub 非对称加载、角色三层解耦、verifier 自进化闭环,在合并后成为本仓库 vertical 系统 + 角色系统 + BIA 自进化 的直接前身。


📄 License

MIT

About

原生 Function Calling 驱动的轻量 Agent Runtime —— 一份引擎,可通用也可垂直,config 一行切换

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages