原生 Function Calling 驱动的轻量通用 Agent Runtime —— 薄薄一层在 LLM 之上。 不约束它怎么做,只告诉它有什么能用,让它自己规划和决策。
Capricorn 是一个跑在 LLM 上层的调度层。它不硬编码业务规则、不规定步骤数、不堆 ReAct / 状态机——LLM 直接决定调什么工具,系统并发执行,循环到最终响应。消息进来 → LLM 判断何时调工具 → 记忆 / 技能 / 子 Agent 只在需要时作为上下文拉入。所有东西围绕一个小的 agent loop 组织,核心路径因此好读、好扩展。
它不是框架——二次开发不是「学一套框架」,而是「写一个工具 + 一段 prompt」。
一份引擎,通用 + 垂直。 通用能力(文件、命令、搜索、记忆……)是任何 agent 都要用的地基,永远加载;垂直能力(特定领域的工具 / 技能 / 角色)通过 vertical_hub/ 叠加,config 一行 active 开关切换。内置 note-taking 作为完整范例。
| 你想... | 去哪 |
|---|---|
| 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
spawnexecutor / 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 Key1. 启动(Gateway + WebUI 模式)
python run.py --mode gateway_with_webui
# 浏览器打开 http://localhost:80802. 发第一条消息 —— 在 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_bot(send()已用)。
| 能力 | 说明 |
|---|---|
| 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_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 | 内容 | 用途 |
|---|---|---|
note-taking |
笔记创建/搜索/统计工具、日记与知识图谱技能、周报 workflow、archivist 角色 | 完整结构范例,覆盖 tools / skills / workflows / roles / prompts / mcp 全部文件类型 |
从零搭一个领域包。先看最小可用形态,再逐类加内容——每一类都用内置范例 note-taking 做对照。
一个 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 包结构 表。
① 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_filespawn 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。
| 文件 | 规则 |
|---|---|
tools/ · workflows/ · mcp.json |
合并(工具 / 服务名冲突 → 直接报错) |
skills/ · roles/ |
后加载覆盖同名内置 |
prompts/{system,bia,cron}.md |
覆盖全局同名文件 |
# 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.json 的 channels 注册。
所有 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.py 的 Config 类定义。
pytest tests/ -q # 568 tests- 🌐 更多 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 自进化 的直接前身。
MIT