Loading...
Loading...
Compare original and translation side by side
Skill by ara.so — Daily 2026 Skills collection.
由ara.so提供的技能——2026每日技能合集。
npx paperclipai onboard --yesnpx paperclipai onboard --yesgit clone https://github.com/paperclipai/paperclip.git
cd paperclip
pnpm install
pnpm devhttp://localhost:3100git clone https://github.com/paperclipai/paperclip.git
cd paperclip
pnpm install
pnpm devhttp://localhost:3100undefinedundefined
---
---pnpm dev # Start API + UI in development mode
pnpm build # Build for production
pnpm start # Start production server
pnpm db:migrate # Run pending database migrations
pnpm db:seed # Seed demo data
pnpm test # Run test suite
npx paperclipai onboard --yes # Full automated onboardingpnpm dev # 以开发模式启动API + UI
pnpm build # 构建生产环境版本
pnpm start # 启动生产服务器
pnpm db:migrate # 执行待处理的数据库迁移
pnpm db:seed # 初始化演示数据
pnpm test # 运行测试套件
npx paperclipai onboard --yes # 完整自动化初始化| Concept | Description |
|---|---|
| Company | Top-level namespace. All agents, goals, tasks, and budgets are scoped to a company. |
| Agent | An AI worker (OpenClaw, Claude Code, Codex, Cursor, HTTP bot, Bash script). |
| Goal | Hierarchical business objective. Tasks inherit goal ancestry so agents know the "why". |
| Task / Ticket | A unit of work assigned to an agent. Conversations and tool calls are threaded to it. |
| Heartbeat | A cron-style schedule that wakes an agent to check for work or perform recurring tasks. |
| Org Chart | Hierarchical reporting structure. Agents have managers, direct reports, roles, and titles. |
| Budget | Monthly token/cost cap per agent. Atomic enforcement — agent stops when budget exhausted. |
| Governance | Approval gates for hires, strategy changes, and config rollbacks. You are the board. |
| 概念 | 描述 |
|---|---|
| Company(公司) | 顶级命名空间。所有Agent、目标、任务和预算都隶属于某个公司。 |
| Agent | AI工作单元(OpenClaw、Claude Code、Codex、Cursor、HTTP机器人、Bash脚本)。 |
| Goal(目标) | 分层业务目标。任务会继承目标层级,让Agent理解工作的“意义”。 |
| Task / Ticket(任务/工单) | 分配给Agent的工作单元。对话和工具调用会与该任务关联。 |
| Heartbeat(心跳) | 类似cron的调度机制,唤醒Agent以检查工作或执行周期性任务。 |
| Org Chart(组织架构图) | 分层汇报结构。Agent拥有上级、下属、角色和职位。 |
| Budget(预算) | 每个Agent的月度令牌/费用上限。原子化管控——预算耗尽时Agent会停止工作。 |
| Governance(治理) | 针对招聘、战略变更和配置回滚的审批机制。你就是董事会。 |
http://localhost:3100/api/v1http://localhost:3100/api/v1// All requests require a bearer token
const headers = {
'Authorization': `Bearer ${process.env.PAPERCLIP_API_KEY}`,
'Content-Type': 'application/json',
};// 所有请求都需要Bearer令牌
const headers = {
'Authorization': `Bearer ${process.env.PAPERCLIP_API_KEY}`,
'Content-Type': 'application/json',
};const response = await fetch('http://localhost:3100/api/v1/companies', {
method: 'POST',
headers,
body: JSON.stringify({
name: 'NoteGenius Inc.',
mission: 'Build the #1 AI note-taking app to $1M MRR.',
slug: 'notegenius',
}),
});
const { company } = await response.json();
console.log(company.id); // "cmp_abc123"const response = await fetch('http://localhost:3100/api/v1/companies', {
method: 'POST',
headers,
body: JSON.stringify({
name: 'NoteGenius Inc.',
mission: '打造排名第一的AI笔记应用,实现100万美元月 recurring revenue。',
slug: 'notegenius',
}),
});
const { company } = await response.json();
console.log(company.id); // "cmp_abc123"const agent = await fetch(`http://localhost:3100/api/v1/companies/${companyId}/agents`, {
method: 'POST',
headers,
body: JSON.stringify({
name: 'Alice',
role: 'CTO',
runtime: 'claude-code', // 'openclaw' | 'claude-code' | 'codex' | 'cursor' | 'bash' | 'http'
endpoint: process.env.ALICE_AGENT_ENDPOINT,
budget: {
monthly_usd: 200,
},
heartbeat: {
cron: '0 * * * *', // every hour
enabled: true,
},
reports_to: ceoAgentId, // parent in org chart
}),
}).then(r => r.json());const agent = await fetch(`http://localhost:3100/api/v1/companies/${companyId}/agents`, {
method: 'POST',
headers,
body: JSON.stringify({
name: 'Alice',
role: 'CTO',
runtime: 'claude-code', // 'openclaw' | 'claude-code' | 'codex' | 'cursor' | 'bash' | 'http'
endpoint: process.env.ALICE_AGENT_ENDPOINT,
budget: {
monthly_usd: 200,
},
heartbeat: {
cron: '0 * * * *', // 每小时一次
enabled: true,
},
reports_to: ceoAgentId, // 组织架构中的上级
}),
}).then(r => r.json());const goal = await fetch(`http://localhost:3100/api/v1/companies/${companyId}/goals`, {
method: 'POST',
headers,
body: JSON.stringify({
title: 'Launch v1 to Product Hunt',
description: 'Ship the MVP and generate 500 upvotes on launch day.',
parent_goal_id: null, // null = top-level goal
owner_agent_id: ctoAgentId,
due_date: '2026-06-01',
}),
}).then(r => r.json());const goal = await fetch(`http://localhost:3100/api/v1/companies/${companyId}/goals`, {
method: 'POST',
headers,
body: JSON.stringify({
title: '在Product Hunt发布v1版本',
description: '交付MVP并在发布日获得500个点赞。',
parent_goal_id: null, // null = 顶级目标
owner_agent_id: ctoAgentId,
due_date: '2026-06-01',
}),
}).then(r => r.json());const task = await fetch(`http://localhost:3100/api/v1/companies/${companyId}/tasks`, {
method: 'POST',
headers,
body: JSON.stringify({
title: 'Implement offline sync for notes',
description: 'Use CRDTs to merge note edits made offline. See ADR-004.',
assigned_to: engineerAgentId,
goal_id: goal.id, // links task to goal ancestry
priority: 'high',
}),
}).then(r => r.json());
console.log(task.id); // "tsk_xyz789"const task = await fetch(`http://localhost:3100/api/v1/companies/${companyId}/tasks`, {
method: 'POST',
headers,
body: JSON.stringify({
title: '实现笔记离线同步功能',
description: '使用CRDT合并离线编辑的笔记。参考ADR-004。',
assigned_to: engineerAgentId,
goal_id: goal.id, // 将任务关联到目标层级
priority: 'high',
}),
}).then(r => r.json());
console.log(task.id); // "tsk_xyz789"const { tasks } = await fetch(
`http://localhost:3100/api/v1/agents/${agentId}/tasks?status=open`,
{ headers }
).then(r => r.json());const { tasks } = await fetch(
`http://localhost:3100/api/v1/agents/${agentId}/tasks?status=open`,
{ headers }
).then(r => r.json());await fetch(`http://localhost:3100/api/v1/tasks/${taskId}/messages`, {
method: 'POST',
headers,
body: JSON.stringify({
role: 'agent',
content: 'Implemented CRDT merge logic. Tests passing. Ready for review.',
tool_calls: [
{
tool: 'bash',
input: 'pnpm test --filter=sync',
output: '42 tests passed in 3.1s',
},
],
}),
});await fetch(`http://localhost:3100/api/v1/tasks/${taskId}/messages`, {
method: 'POST',
headers,
body: JSON.stringify({
role: 'agent',
content: '已实现CRDT合并逻辑,测试通过,等待审核。',
tool_calls: [
{
tool: 'bash',
input: 'pnpm test --filter=sync',
output: '42个测试用例通过,耗时3.1秒',
},
],
}),
});await fetch(`http://localhost:3100/api/v1/agents/${agentId}/cost`, {
method: 'POST',
headers,
body: JSON.stringify({
tokens_in: 12400,
tokens_out: 3800,
model: 'claude-opus-4-5',
task_id: taskId,
}),
});await fetch(`http://localhost:3100/api/v1/agents/${agentId}/cost`, {
method: 'POST',
headers,
body: JSON.stringify({
tokens_in: 12400,
tokens_out: 3800,
model: 'claude-opus-4-5',
task_id: taskId,
}),
});const { instructions, tasks } = await fetch(
`http://localhost:3100/api/v1/agents/${agentId}/heartbeat`,
{ method: 'POST', headers }
).then(r => r.json());
// instructions — what the org says to focus on now
// tasks — open tasks assigned to this agentconst { instructions, tasks } = await fetch(
`http://localhost:3100/api/v1/agents/${agentId}/heartbeat`,
{ method: 'POST', headers }
).then(r => r.json());
// instructions — 组织当前要求聚焦的内容
// tasks — 分配给该Agent的未完成任务// lib/paperclip-client.ts
export class PaperclipClient {
private base: string;
private headers: Record<string, string>;
constructor(
base = process.env.PAPERCLIP_BASE_URL ?? 'http://localhost:3100',
apiKey = process.env.PAPERCLIP_API_KEY ?? '',
) {
this.base = `${base}/api/v1`;
this.headers = {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
};
}
private async req<T>(path: string, init?: RequestInit): Promise<T> {
const res = await fetch(`${this.base}${path}`, {
...init,
headers: { ...this.headers, ...init?.headers },
});
if (!res.ok) {
const body = await res.text();
throw new Error(`Paperclip API ${res.status}: ${body}`);
}
return res.json() as Promise<T>;
}
heartbeat(agentId: string) {
return this.req<{ instructions: string; tasks: Task[] }>(
`/agents/${agentId}/heartbeat`,
{ method: 'POST' },
);
}
completeTask(taskId: string, summary: string) {
return this.req(`/tasks/${taskId}`, {
method: 'PATCH',
body: JSON.stringify({ status: 'done', completion_summary: summary }),
});
}
reportCost(agentId: string, payload: CostPayload) {
return this.req(`/agents/${agentId}/cost`, {
method: 'POST',
body: JSON.stringify(payload),
});
}
}// lib/paperclip-client.ts
export class PaperclipClient {
private base: string;
private headers: Record<string, string>;
constructor(
base = process.env.PAPERCLIP_BASE_URL ?? 'http://localhost:3100',
apiKey = process.env.PAPERCLIP_API_KEY ?? '',
) {
this.base = `${base}/api/v1`;
this.headers = {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
};
}
private async req<T>(path: string, init?: RequestInit): Promise<T> {
const res = await fetch(`${this.base}${path}`, {
...init,
headers: { ...this.headers, ...init?.headers },
});
if (!res.ok) {
const body = await res.text();
throw new Error(`Paperclip API ${res.status}: ${body}`);
}
return res.json() as Promise<T>;
}
heartbeat(agentId: string) {
return this.req<{ instructions: string; tasks: Task[] }>(
`/agents/${agentId}/heartbeat`,
{ method: 'POST' },
);
}
completeTask(taskId: string, summary: string) {
return this.req(`/tasks/${taskId}`, {
method: 'PATCH',
body: JSON.stringify({ status: 'done', completion_summary: summary }),
});
}
reportCost(agentId: string, payload: CostPayload) {
return this.req(`/agents/${agentId}/cost`, {
method: 'POST',
body: JSON.stringify(payload),
});
}
}// agent.ts
import { PaperclipClient } from './lib/paperclip-client';
const client = new PaperclipClient();
const AGENT_ID = process.env.PAPERCLIP_AGENT_ID!;
async function runHeartbeat() {
console.log('[agent] heartbeat ping');
const { instructions, tasks } = await client.heartbeat(AGENT_ID);
for (const task of tasks) {
console.log(`[agent] working on task: ${task.title}`);
try {
// --- your agent logic here ---
const result = await doWork(task, instructions);
await client.completeTask(task.id, result.summary);
await client.reportCost(AGENT_ID, {
tokens_in: result.tokensIn,
tokens_out: result.tokensOut,
model: result.model,
task_id: task.id,
});
console.log(`[agent] task ${task.id} done`);
} catch (err) {
console.error(`[agent] task ${task.id} failed`, err);
// Paperclip will reassign or escalate based on governance rules
}
}
}
// Heartbeat is usually driven by Paperclip's cron, but you can also self-poll:
setInterval(runHeartbeat, 60_000);
runHeartbeat();// agent.ts
import { PaperclipClient } from './lib/paperclip-client';
const client = new PaperclipClient();
const AGENT_ID = process.env.PAPERCLIP_AGENT_ID!;
async function runHeartbeat() {
console.log('[agent] 心跳请求');
const { instructions, tasks } = await client.heartbeat(AGENT_ID);
for (const task of tasks) {
console.log(`[agent] 处理任务: ${task.title}`);
try {
// --- 在此处编写你的Agent逻辑 ---
const result = await doWork(task, instructions);
await client.completeTask(task.id, result.summary);
await client.reportCost(AGENT_ID, {
tokens_in: result.tokensIn,
tokens_out: result.tokensOut,
model: result.model,
task_id: task.id,
});
console.log(`[agent] 任务 ${task.id} 完成`);
} catch (err) {
console.error(`[agent] 任务 ${task.id} 失败`, err);
// Paperclip会根据治理规则重新分配或升级任务
}
}
}
// 心跳通常由Paperclip的cron驱动,你也可以自行轮询:
setInterval(runHeartbeat, 60_000);
runHeartbeat();// Paperclip calls POST /work on your agent with this shape:
interface PaperclipWorkPayload {
agent_id: string;
task: {
id: string;
title: string;
description: string;
goal_ancestry: string[]; // full chain: company mission → goal → sub-goal
};
instructions: string; // current org-level directives
context: Record<string, unknown>;
}interface PaperclipWorkResponse {
status: 'done' | 'blocked' | 'delegated';
summary: string;
tokens_in?: number;
tokens_out?: number;
model?: string;
delegate_to?: string; // agent_id if status === 'delegated'
}// Paperclip会向你的Agent的POST /work端点发送以下格式的数据:
interface PaperclipWorkPayload {
agent_id: string;
task: {
id: string;
title: string;
description: string;
goal_ancestry: string[]; // 完整层级:公司使命 → 目标 → 子目标
};
instructions: string; // 当前组织级指令
context: Record<string, unknown>;
}interface PaperclipWorkResponse {
status: 'done' | 'blocked' | 'delegated';
summary: string;
tokens_in?: number;
tokens_out?: number;
model?: string;
delegate_to?: string; // 当status === 'delegated'时,填写目标Agent的ID
}// Create isolated companies in one deployment
const companies = await Promise.all([
createCompany({ name: 'NoteGenius', mission: 'Best note app' }),
createCompany({ name: 'ShipFast', mission: 'Fastest deploy tool' }),
]);
// Each company has its own agents, goals, tasks, budgets, and audit log
// No data leaks between companies// 在一个部署中创建相互隔离的公司
const companies = await Promise.all([
createCompany({ name: 'NoteGenius', mission: '最佳笔记应用' }),
createCompany({ name: 'ShipFast', mission: '最快部署工具' }),
]);
// 每个公司都有独立的Agent、目标、任务、预算和审计日志
// 公司之间不会发生数据泄露// Fetch pending approval requests (you are the board)
const { approvals } = await fetch(
`http://localhost:3100/api/v1/companies/${companyId}/approvals?status=pending`,
{ headers }
).then(r => r.json());
// Approve a hire
await fetch(`http://localhost:3100/api/v1/approvals/${approvals[0].id}`, {
method: 'PATCH',
headers,
body: JSON.stringify({ decision: 'approved', note: 'Looks good.' }),
});
// Roll back a bad config change
await fetch(`http://localhost:3100/api/v1/agents/${agentId}/config/rollback`, {
method: 'POST',
headers,
body: JSON.stringify({ revision: 3 }),
});// 获取待审批请求(你作为董事会)
const { approvals } = await fetch(
`http://localhost:3100/api/v1/companies/${companyId}/approvals?status=pending`,
{ headers }
).then(r => r.json());
// 批准招聘请求
await fetch(`http://localhost:3100/api/v1/approvals/${approvals[0].id}`, {
method: 'PATCH',
headers,
body: JSON.stringify({ decision: 'approved', note: '看起来不错。' }),
});
// 回滚错误的配置变更
await fetch(`http://localhost:3100/api/v1/agents/${agentId}/config/rollback`, {
method: 'POST',
headers,
body: JSON.stringify({ revision: 3 }),
});undefinedundefined
---
---// In your manager agent's heartbeat handler:
const { tasks } = await client.heartbeat(MANAGER_AGENT_ID);
for (const task of tasks) {
if (task.complexity === 'high') {
// Delegate down the org chart
await fetch(`http://localhost:3100/api/v1/tasks/${task.id}/delegate`, {
method: 'POST',
headers,
body: JSON.stringify({ to_agent_id: engineerAgentId }),
});
}
}// 在经理Agent的心跳处理逻辑中:
const { tasks } = await client.heartbeat(MANAGER_AGENT_ID);
for (const task of tasks) {
if (task.complexity === 'high') {
// 向下属委派任务
await fetch(`http://localhost:3100/api/v1/tasks/${task.id}/delegate`, {
method: 'POST',
headers,
body: JSON.stringify({ to_agent_id: engineerAgentId }),
});
}
}await fetch(`http://localhost:3100/api/v1/tasks/${taskId}/messages`, {
method: 'POST',
headers,
body: JSON.stringify({
role: 'human',
content: `@${designerAgentId} Can you review the UI for this feature?`,
}),
});
// Paperclip delivers the mention as a trigger to the designer agent's next heartbeatawait fetch(`http://localhost:3100/api/v1/tasks/${taskId}/messages`, {
method: 'POST',
headers,
body: JSON.stringify({
role: 'human',
content: `@${designerAgentId} 你能审核这个功能的UI吗?`,
}),
});
// Paperclip会在设计师Agent的下一次心跳时推送该提及通知const blob = await fetch(
`http://localhost:3100/api/v1/companies/${companyId}/export`,
{ headers }
).then(r => r.blob());
// Saves a .paperclip bundle with secrets scrubbed
fs.writeFileSync('my-saas-company.paperclip', Buffer.from(await blob.arrayBuffer()));const blob = await fetch(
`http://localhost:3100/api/v1/companies/${companyId}/export`,
{ headers }
).then(r => r.blob());
// 保存一个清除了敏感信息的.paperclip包
fs.writeFileSync('my-saas-company.paperclip', Buffer.from(await blob.arrayBuffer()));const form = new FormData();
form.append('file', fs.createReadStream('my-saas-company.paperclip'));
await fetch('http://localhost:3100/api/v1/companies/import', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.PAPERCLIP_API_KEY}` },
body: form,
});const form = new FormData();
form.append('file', fs.createReadStream('my-saas-company.paperclip'));
await fetch('http://localhost:3100/api/v1/companies/import', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.PAPERCLIP_API_KEY}` },
body: form,
});| Problem | Fix |
|---|---|
| Server not running. Run |
| Check |
| Agent never wakes up | Verify |
| Budget exhausted immediately | |
Task stuck in | Agent may be offline or heartbeat misconfigured. Check |
| Database migration errors | Run |
| Embedded Postgres won't start | Port 5433 may be in use. Set |
| Org chart not resolving | |
| 问题 | 解决方法 |
|---|---|
| 服务器未运行。先执行 |
| 检查 |
| Agent始终不唤醒 | 验证 |
| 预算立即耗尽 | |
任务一直处于 | Agent可能离线或心跳配置错误。检查 |
| 数据库迁移错误 | 拉取新提交后执行 |
| 嵌入式Postgres无法启动 | 端口5433可能被占用。在 |
| 组织架构图无法解析 | 创建下属Agent前, |