通用问题求解引擎:黑板架构 + 事实-意图图谱。给定一个起点(origin)和一个目标(goal),引擎在未知的状态空间中搜索一条通路。AI 渗透测试是第一个得到验证的领域。
fork 说明:Waystone 是 oritera/Cairn 的 AGPL-3.0 修改版。新增:WebUI/API 访问控制与自研登录页、项目耗时与 Token 统计、goal/origin 编辑、首屏性能优化(按需加载)等;移除了容器化执行。代码标识与协议术语沿用 Cairn 命名。
渗透测试本质上是在近乎无限的状态空间中进行有向搜索:
- 起点(Origin):已知(目标 IP、目标系统)
- 目标(Goal):已定义(拿到 shell、夺取 flag)
- 路径(Path):未知
这种结构并非渗透测试独有。漏洞研究、CTF 挑战——任何具有清晰起点、清晰成功条件、且中间路径未知的问题,都具有相同的形态。
引擎建立在**黑板架构(Blackboard Architecture)**之上,配以显式的事实-意图图谱(fact-intent graph)。只需要三种原语:
| 概念 | 含义 |
|---|---|
| 事实(Fact) | 已确认的客观发现,写入黑板 |
| 意图(Intent) | 声明的一个探索方向,尚未执行 |
| 提示(Hint) | 任意时刻注入的人工判断;agent 在下次读取时吸收 |
图谱从 origin 向 goal 生长。每一个新事实都是一块垫脚石;每一个意图都是迈向未知的一步。
Agent 执行器(Worker)运行 OODA 循环——Observe 观察全量图谱、Orient 定位当前状态、Decide 决策下一步意图、Act 执行探索——并把发现写回为新事实。执行器没有固定角色,任务由图谱的当前状态在运行期生成。Agent 之间仅通过共享黑板协调(Stigmergy,共识协作)。
三种任务类型,全部由同一个执行器执行:
| 任务 | 做什么 | 产出 |
|---|---|---|
| 引导(Bootstrap) | 项目开始时,尝试直接解决问题 | 事实 + 可能完成(Complete) |
| 推理(Reason) | 读取全量图谱:目标是否达成?接下来该探索什么? | 完成 / 新意图 / 无操作 |
| 探索(Explore) | 认领一个意图,执行探索,汇报发现 | 一个事实 |
系统架构:
┌────────────────────────────────────────┐
│ Cairn Server │
│ Fastify + SQLite: facts, intents, │
│ hints, settings, live agent events │
└───────────────────┬────────────────────┘
│ HTTP (REST)
┌───────────────────┴────────────────────┐
│ Dispatcher (TS) │
│ Scheduler · tasks · heartbeats │
│ pi SDK in-process: shared Model- │
│ Runtime, one AgentSession per task │
└───────────────────┬────────────────────┘
│ persistent sessions
<project_dir>/sessions/*.jsonl
Server 只维护图谱一致性。Dispatcher 读取图谱、调度任务,并且是协议的唯一写入方。任务通过 pi SDK 在进程内执行:一个共享的 ModelRuntime 为每个运行中任务服务一个 AgentSession,每个会话持久化到项目工作目录,重试与收尾回退(conclude fallback)时恢复同一对话。支持的执行器后端:Pi(进程内 SDK);测试用 mock 后端。
- WebUI(简体中文):项目列表、图谱视图(cytoscape)、会话文件查看、详情/提示/日志/会话面板、动态设置、项目回放
- 访问控制:
CAIRN_AUTH_USER/CAIRN_AUTH_PASS两环境变量启用后,浏览器跳转自研登录页(会话 cookie),调度器经 Basic 认证自动携带凭据 - 项目统计:详情页 header 实时显示项目总耗时与总 Token 消耗(跨会话文件聚合)
- goal/origin 编辑:目标或起点写错了随时改;agent 误判完成时用「继续」重开项目并注入外部反馈
- 动态设置:调度并发
worker_max_running、循环间隔interval、超时等运行期热调,无需重启 - 会话持久化:每个任务一个 pi AgentSession,
sessions/*.jsonl可恢复、可查看
环境要求
- macOS 或 Linux
- Node.js ≥ 23.4(内置
node:sqlite) - 已安装并登录
piCLI(agent 通过 pi SDK 运行;见 pi 文档)
cd cairn
npm install
cp ../dispatch.example.yaml ../dispatch.yaml启动服务器(Web UI 位于 http://127.0.0.1:8217):
npm run serve在第二个终端启动调度器(agent 以你的用户权限运行——无沙箱):
npm run dispatch -- --config ../dispatch.yaml启动时调度器会校验配置并检查 pi CLI 是否已安装可运行。数据默认持久化在 ~/.local/share/cairn/cairn.db(可用 CAIRN_DB 环境变量覆盖)。每个项目在 local.workspace_root(默认:调度器当前目录)下获得一个独立工作目录,包含 agent 的文件及其持久化会话日志(sessions/)。
配置双轨:dispatch.yaml(静态默认)与 WebUI 设置面板(热调覆盖,null = 回退 yaml)。worker 仅 pi/mock。
Web UI 中的服务器设置面板(或 PUT /settings)可热更新调优参数,无需重启调度器:
| 设置项 | 含义 |
|---|---|
worker_max_running |
最大并发运行任务数(留空 = 使用 dispatch.yaml 的 runtime.worker_max_running) |
intent_timeout |
explore 任务超时(秒);留空 = 回退 runtime.intent_timeout |
reason_timeout |
reason 任务超时(秒);留空 = 回退 runtime.reason_timeout |
interval |
调度循环周期(秒);留空 = 使用 dispatch.yaml 的 runtime.interval |
改动在下一个调度循环生效(≤ runtime.interval 秒);运行中任务不会被中断。interval 改动立即影响调度循环,而运行中任务的心跳保持任务启动时的值。
服务器默认监听 0.0.0.0(可用 CAIRN_HOST 收紧为 127.0.0.1)。Web UI 与 API 默认无鉴权;设置 CAIRN_AUTH_USER 与 CAIRN_AUTH_PASS 两个环境变量后启用访问控制(两者都必须设置,不提供默认密码,请使用随机强密码):
CAIRN_AUTH_USER=cairn CAIRN_AUTH_PASS='<随机强密码>' npm run serve启用后浏览器访问会跳转到自研登录页(/login),登录成功签发 7 天会话 cookie(内存态,服务器重启后需重新登录)。调度器(npm run dispatch)读取同一对环境变量,经 Basic 认证自动携带凭据。
docs/glossary.md:业务实体、任务阶段、配置项、UI 中英对照的术语表(开发前先读)docs/specs/:dispatcher 与 server 协议设计文档
cd cairn
npm test # vitest:server 路由 / 调度 / 配置 / worker 任务
npm run build:web # 构建 WebUI 产物到 src/server/static本项目基于 oritera/Cairn(AGPL-3.0)修改,采用 GNU AGPLv3 许可证分发(完整文本见 LICENSE)。
贡献:提交 Pull Request 即表示你同意你的贡献在 AGPL-3.0 下许可。