diff --git a/packages/cli/skills/hermes/SKILL.md b/packages/cli/skills/hermes/SKILL.md new file mode 100644 index 0000000..69cab6d --- /dev/null +++ b/packages/cli/skills/hermes/SKILL.md @@ -0,0 +1,505 @@ +--- +name: nerve +version: 0.5.0 +description: > + Nerve — AI agent 观测引擎。掌握 nerve 的核心概念、CLI 操作、sense/workflow 开发。 + 加载此 skill 后你可以:查看系统状态、监控 sense、触发 workflow、开发新 sense 和 workflow。 +metadata: + hermes: + tags: [nerve, sense, workflow, monitoring, agent-kernel] + homepage: https://git.shazhou.work/uncaged/nerve +--- + +# Nerve — AI Agent 观测引擎 + +Nerve 是一个轻量级观测引擎守护进程。它持续观测外部状态,通过声明式规则响应变化,编排多步骤工作流。 + +## 核心架构 + +``` +External World → Sense → Signal → Workflow → Log +``` + +| 概念 | 说明 | +|------|------| +| **Sense** | 观测函数,`compute()` 采样或推导数据。返回非 null 则发出 Signal,可选触发 Workflow。每个 Sense 有独立 SQLite 数据库。 | +| **Signal** | Sense 返回非 null 时发出的通知。纯事实,无意图。通过内存 Signal Bus 分发,不持久化。 | +| **Workflow** | 有状态的多步骤执行。包含 Role(有副作用的执行者)和 Moderator(纯路由器)。每个实例是一个 Thread,有唯一 runId。 | +| **Log** | 不可变审计日志。记录执行、状态转换、错误。不能触发 Sense(防止反馈循环)。 | +| **Engine** | 内核,持有 Signal Bus、Process Manager、Workflow Manager。不直接加载用户代码。 | +| **Daemon** | 引擎运行时,作为后台进程运行。 | + +**关键规则:** +- 因果链单向:External → Sense → Signal → Workflow + Log +- 进程隔离:每个 Sense group 一个 worker(长期),每个 Workflow 类型一个 worker(按需) +- 两个扩展点:Sense(观测什么 + 何时)、Workflow(做什么) + +## 工作区结构 + +``` +~/.uncaged-nerve/ # 默认工作区(nerve init 创建) +├── nerve.yaml # 核心配置 +├── senses/ +│ └── / +│ ├── src/index.ts # exports compute() + table +│ ├── src/schema.ts # drizzle 表定义 +│ └── migrations/ # SQL 迁移 +├── workflows/ +│ └── / +│ ├── index.ts # exports WorkflowDefinition +│ └── roles// +│ ├── index.ts # role 实现 +│ └── prompt.md # 可选 system prompt +└── data/ # 运行时数据(SQLite、blobs) +``` + +--- + +## CLI 完整参考 + +全局选项:`--host `(连接远程 daemon)、`--api-token `(Bearer 认证) + +### 初始化与脚手架 + +```bash +nerve init # 初始化工作区 +nerve init --from # 从 git 仓库克隆工作区 +nerve init workspace # 只初始化工作区结构 + +nerve create sense # 创建 sense 脚手架 +nerve create sense --force # 覆盖已有 +nerve create workflow # 创建 workflow 脚手架 +nerve create workflow --force + +nerve validate # 验证 nerve.yaml 配置 +``` + +### Daemon 管理 + +```bash +nerve daemon start # 启动后台 daemon +nerve daemon start --port 3000 # 指定 HTTP API 端口 +nerve daemon stop # 停止 daemon +nerve daemon restart # 重启 +nerve daemon status # 查看状态 +nerve daemon logs # 查看日志 +nerve daemon logs --follow # 实时日志 +nerve daemon logs --n 50 # 最近 50 行 + +nerve dev # 前台开发模式(不 fork daemon) +nerve dev --port 3000 # 指定端口 +``` + +### Sense 操作 + +```bash +nerve sense list # 列出所有注册的 sense +nerve sense trigger # 手动触发 sense 计算 +nerve sense schema # 查看 sense 数据库表结构 +nerve sense schema --json # JSON 格式 +nerve sense query # 对 sense 数据库执行只读 SQL +nerve sense query "SELECT * FROM samples ORDER BY ts DESC LIMIT 10" --json +``` + +### Workflow 操作 + +```bash +nerve workflow list # 列出 nerve.yaml 中定义的 workflow +nerve workflow status # 查看运行中的 workflow 状态 +nerve workflow trigger # 触发 workflow +nerve workflow trigger --prompt "检查生产环境" +nerve workflow trigger --maxRounds 50 +nerve workflow trigger --dryRun # 干跑模式 +``` + +### Thread(Workflow 执行记录) + +```bash +nerve thread list # 列出最近的 workflow 执行 +nerve thread list --all # 包含已完成/失败的 +nerve thread list --workflow # 按 workflow 过滤 +nerve thread list --limit 50 # 最多 50 条 + +nerve thread show # 查看 role 对话轮次 +nerve thread show --budget 16000 # 增大输出预算(默认 8000 字符) + +nerve thread inspect # 查看详情和事件 + +nerve thread kill # 终止运行中/排队中的 thread +``` + +### Store(日志归档) + +```bash +nerve store archive # 导出旧日志到 JSONL 归档 +nerve store archive --vacuum # 归档后 VACUUM 数据库 +``` + +### Knowledge(知识库) + +```bash +nerve knowledge sync # 从 knowledge.yaml 重建索引 +nerve knowledge query "搜索内容" # 搜索知识库 +nerve knowledge query "内容" --limit 5 +nerve knowledge query "内容" -g # 搜索所有注册仓库 +``` + +### Remote(远程 daemon) + +```bash +nerve remote add --token +nerve remote list +nerve remote show +nerve remote set-url +nerve remote set-token +nerve remote remove +nerve remote default # 设为默认远程 +``` + +--- + +## nerve.yaml 配置参考 + +```yaml +# 引擎全局配置 +max_rounds: 100 # moderator 最大轮次(默认 100) + +# Sense 配置 +senses: + cpu-usage: + group: system # 必填,同 group 的 sense 共享 worker + interval: 10s # 轮询间隔(duration: 5s, 10m, 1h) + throttle: 5s # 最小计算间隔 + timeout: 10s # compute 超时 + grace_period: null # 优雅关闭等待 + retention: 10000 # _signals 表最大行数(默认 10000) + + system-health: + group: derived + on: [cpu-usage, disk-usage] # 响应式:被列出的 sense 发出 signal 时触发 + throttle: null + timeout: null + +# Workflow 配置 +workflows: + my-workflow: + concurrency: 1 # 必填,并发数 + overflow: drop # 必填,超并发时处理:drop | queue + max_queue: 100 # overflow=queue 时的队列上限(默认 100) + +# HTTP API +api: + port: 3000 # null = 不启用 HTTP + host: "127.0.0.1" # 监听地址 + token: null # 非 loopback 时必填 + +# LLM Extract(可选) +extract: + provider: anthropic + model: claude-sonnet-4-20250514 +``` + +--- + +## Sense 开发指南 + +### compute 函数签名 + +```typescript +import type { LibSQLDatabase } from "drizzle-orm/libsql"; +import type { ComputeResult, WorkflowTrigger } from "@uncaged/nerve-core"; + +export async function compute( + db: LibSQLDatabase, // 此 sense 的 Drizzle ORM 数据库 + peers: Record, // 其他 sense 的数据库(只读) + options: { signal: AbortSignal }, // 超时 abort signal +): Promise> +``` + +### 返回值 + +```typescript +// 返回 null = 静默,不发 signal +// 返回非 null = 发出 signal,可选触发 workflow +type ComputeResult = + | null + | { signal: T; workflow: WorkflowTrigger | null }; + +type WorkflowTrigger = { + name: string; // workflow 名称(对应 nerve.yaml 中的 key) + maxRounds: number; // moderator 最大轮次 + prompt: string; // 初始 prompt + dryRun: boolean; // 干跑模式 +}; +``` + +### Sense 模块导出 + +```typescript +// senses//src/index.ts +import type { SenseModule, ComputeResult } from "@uncaged/nerve-core"; +import { table } from "./schema.js"; + +export async function compute( + db: LibSQLDatabase, + _peers: Record, + _options: { signal: AbortSignal }, +): Promise> { + const value = Math.random(); // 替换为真实观测逻辑 + await db.insert(table).values({ ts: Date.now(), value }); + return { signal: value, workflow: null }; +} + +export { table }; +``` + +### Schema 定义 + +```typescript +// senses//src/schema.ts +import { sqliteTable, integer, real } from "drizzle-orm/sqlite-core"; + +export const table = sqliteTable("samples", { + ts: integer("ts").notNull(), + value: real("value").notNull(), +}); +``` + +### 调度方式 + +1. **interval 轮询**:`interval: 10s` — 每 10 秒执行一次 +2. **响应式触发**:`on: [cpu-usage]` — 当 cpu-usage 发出 signal 时触发 +3. 两者可以组合 + +### 调试 + +```bash +nerve dev # 前台运行,看实时输出 +nerve sense trigger # 手动触发一次 +nerve sense query "SELECT * FROM samples ORDER BY ts DESC LIMIT 5" +``` + +### 完整示例:CPU 监控 + +```typescript +// senses/cpu-usage/src/schema.ts +import { sqliteTable, integer, real } from "drizzle-orm/sqlite-core"; + +export const table = sqliteTable("samples", { + ts: integer("ts").notNull(), + value: real("value").notNull(), +}); + +// senses/cpu-usage/src/index.ts +import os from "node:os"; +import type { LibSQLDatabase } from "drizzle-orm/libsql"; +import type { ComputeResult } from "@uncaged/nerve-core"; +import { table } from "./schema.js"; + +export async function compute( + db: LibSQLDatabase, + _peers: Record, + _options: { signal: AbortSignal }, +): Promise> { + const oneMin = os.loadavg()[0]; + await db.insert(table).values({ ts: Date.now(), value: oneMin }); + return { signal: oneMin, workflow: null }; +} + +export { table }; +``` + +nerve.yaml: +```yaml +senses: + cpu-usage: + group: system + interval: 10s + throttle: 5s + timeout: 10s + retention: 10000 +``` + +--- + +## Workflow 开发指南 + +### 核心类型 + +```typescript +import type { + WorkflowDefinition, + RoleResult, + ThreadContext, + StartStep, + RoleStep, +} from "@uncaged/nerve-core"; +import { END } from "@uncaged/nerve-core"; + +// Role:执行者,接收上下文返回结果 +type Role = (ctx: ThreadContext) => Promise>; +type RoleResult = { content: string; meta: Meta }; + +// Moderator:路由器,决定下一个 role 或结束 +type Moderator = (ctx: ThreadContext) => (keyof M & string) | typeof END; + +// ThreadContext:对话上下文 +type ThreadContext = { + threadId: string; + start: StartStep; // 初始 prompt(role: "__start__") + steps: RoleStep[]; // 所有 role 的执行记录 +}; + +// WorkflowDefinition:完整定义 +type WorkflowDefinition = { + name: string; + roles: { [K in keyof M & string]: Role }; + moderator: Moderator; +}; +``` + +### 基本 Workflow 示例 + +```typescript +// workflows/example/index.ts +import type { RoleResult, ThreadContext, WorkflowDefinition } from "@uncaged/nerve-core"; +import { END } from "@uncaged/nerve-core"; + +type Meta = Record<"main", { round: number }>; + +async function main(ctx: ThreadContext): Promise> { + const prompt = ctx.start.content; + return { + content: `处理完成: ${prompt}`, + meta: { round: ctx.steps.length }, + }; +} + +const workflow: WorkflowDefinition = { + name: "example", + roles: { main }, + moderator(ctx: ThreadContext) { + // 执行一次 main 就结束 + return ctx.steps.length === 0 ? "main" : END; + }, +}; + +export default workflow; +``` + +### 多 Role Workflow 示例 + +```typescript +import type { WorkflowDefinition, RoleResult, ThreadContext } from "@uncaged/nerve-core"; +import { END } from "@uncaged/nerve-core"; + +type Roles = Record<"planner" | "executor" | "reviewer", { status: string }>; + +async function planner(ctx: ThreadContext): Promise> { + return { content: "计划: ...", meta: { status: "planned" } }; +} + +async function executor(ctx: ThreadContext): Promise> { + return { content: "执行: ...", meta: { status: "executed" } }; +} + +async function reviewer(ctx: ThreadContext): Promise> { + return { content: "审核通过", meta: { status: "approved" } }; +} + +const workflow: WorkflowDefinition = { + name: "plan-execute-review", + roles: { planner, executor, reviewer }, + moderator(ctx: ThreadContext) { + if (ctx.steps.length === 0) return "planner"; + const last = ctx.steps[ctx.steps.length - 1]; + if (last.role === "planner") return "executor"; + if (last.role === "executor") return "reviewer"; + return END; + }, +}; + +export default workflow; +``` + +### Agent 适配器 + +Workflow role 可以集成 AI agent。已知适配器 ID:`echo`、`cursor`、`hermes`、`codex`。 + +```typescript +type AgentFn = (ctx: ThreadContext, systemPrompt: string) => Promise; +``` + +### Workflow 运行状态 + +`queued` → `started` → `completed` | `failed` | `crashed` | `killed` | `interrupted` | `dropped` + +--- + +## 日常操作 Pattern + +### 查看系统整体状态 + +```bash +nerve daemon status # daemon 是否在运行 +nerve sense list # 所有 sense 及其调度配置 +nerve workflow status # 运行中的 workflow +nerve thread list # 最近的 workflow 执行记录 +``` + +### 检查某个 sense 的历史数据 + +```bash +nerve sense query cpu-usage "SELECT * FROM samples ORDER BY ts DESC LIMIT 10" --json +nerve sense schema cpu-usage # 查看表结构 +``` + +### 手动触发 workflow + +```bash +nerve workflow trigger my-workflow --prompt "手动检查" +nerve thread list --workflow my-workflow # 查看执行状态 +nerve thread show # 查看对话详情 +``` + +### 排查 sense 报错 + +```bash +nerve daemon logs --follow # 查看实时日志 +nerve sense trigger # 手动触发看报错 +nerve dev # 前台模式,更详细的输出 +``` + +### 开发新 sense + +```bash +nerve create sense my-sensor # 脚手架 +# 编辑 senses/my-sensor/src/index.ts 和 schema.ts +nerve validate # 验证配置 +nerve dev # 前台测试 +nerve sense trigger my-sensor # 单次触发验证 +nerve sense query my-sensor "SELECT * FROM ..." # 检查数据 +``` + +### 开发新 workflow + +```bash +nerve create workflow my-flow # 脚手架 +# 编辑 workflows/my-flow/index.ts 和 roles/ +nerve validate # 验证配置 +nerve workflow trigger my-flow --prompt "测试" --dryRun # 干跑 +nerve thread show # 查看执行轨迹 +``` + +--- + +## Pitfalls + +- **Sense 返回值**:返回 `null` 表示静默(不发 signal);返回 `{ signal, workflow }` 才发 signal。不要返回 undefined。 +- **no optional properties**:nerve 代码规范禁止 `?:`,用 `T | null` 代替。 +- **函数式风格**:用 `function` + `type`,不用 `class` + `interface`。 +- **workflow 用 default export**:这是唯一允许 default export 的场景。 +- **_signals 表**:每个 sense 自动有 `_signals` 表记录 signal 历史,受 `retention` 配置限制。 +- **peers 只读**:sense 的 `peers` 参数提供其他 sense 数据库的只读访问,不要写入。 +- **concurrency + overflow**:workflow 必须配置并发策略,否则验证失败。 +- **moderator 是同步函数**:不要加 async,moderator 是纯路由逻辑,不能有副作用。