opc-agent 4.1.3 → 4.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,92 @@
1
+ # OPC vs OpenClaw vs Hermes — 体验差距对照表
2
+
3
+ ## 状态说明
4
+ - ✅ OPC 已有且不差于竞品
5
+ - 🟡 OPC 有但不如竞品
6
+ - ❌ OPC 缺失,竞品有
7
+ - 🔵 OPC 独有优势
8
+
9
+ ---
10
+
11
+ ## 一、安装 & 初始化
12
+
13
+ | # | 体验点 | OpenClaw | Hermes | OPC 现状 | 差距 | 优先级 |
14
+ |---|--------|----------|--------|----------|------|--------|
15
+ | 1 | 一行安装 | `npx openclaw@latest init` | `curl \| sh` one-liner | `npm i -g opc-agent && opc init` | 🟡 两步 vs 一步 | P1 |
16
+ | 2 | 本地模型支持 | ✅ 支持 Ollama | ✅ 支持 | ✅ v4.1.4 Ollama-first | ✅ | - |
17
+ | 3 | 智能模型推荐 | ❌ 手动选 | ❌ 手动选 | ✅ 硬件检测 + 分级推荐 | 🔵 领先 | - |
18
+ | 4 | Setup Wizard | 交互式向导 | `hermes setup` 向导 | 交互式模板+LLM选择 | ✅ | - |
19
+ | 5 | 迁移工具 | - | 从 OpenClaw 迁移 | ❌ 无迁移工具 | ❌ | P3 |
20
+ | 6 | Doctor 诊断 | ❌ 无 | ❌ 无 | ✅ `opc doctor` 11项检查 | 🔵 领先 | - |
21
+ | 7 | `npx opc-agent init` 一行启动 | - | - | ❌ 需要先全局安装 | 🟡 | P1 |
22
+
23
+ ## 二、自主学习 & 技能进化
24
+
25
+ | # | 体验点 | OpenClaw | Hermes | OPC 现状 | 差距 | 优先级 |
26
+ |---|--------|----------|--------|----------|------|--------|
27
+ | 8 | Agent 自写 Skill | ✅ AgentSkills | ✅ 自动创建 Skill MD | ❌ 代码未实现 | ❌ 关键差距 | P0 |
28
+ | 9 | 学习触发条件 | 自动 | 5步闭环(execute→evaluate→extract→refine→retrieve) | ❌ | ❌ | P0 |
29
+ | 10 | Skill 格式标准 | AgentSkills | agentskills.io MD | 无标准 | ❌ | P0 |
30
+ | 11 | 预置 Skill 数量 | 100+ ClawHub | 40+ bundled | 12 模板 | 🟡 | P2 |
31
+ | 12 | Skill 市场 | ClawHub | 社区共享 | ❌ 无 | ❌ | P3 |
32
+ | 13 | 知识进化(evolve) | ❌ | ❌ | ✅ DeepBrain evolve 架构 | 🔵 领先 | - |
33
+ | 14 | 多层知识体系 | ❌ | ❌ | ✅ 行业→岗位→工位→Agent | 🔵 领先 | - |
34
+
35
+ ## 三、用户建模 & 记忆
36
+
37
+ | # | 体验点 | OpenClaw | Hermes | OPC 现状 | 差距 | 优先级 |
38
+ |---|--------|----------|--------|----------|------|--------|
39
+ | 15 | USER.md 用户画像 | Workspace MD | ✅ USER.md + Honcho | ❌ 无 | ❌ | P1 |
40
+ | 16 | SOUL.md 人格 | ✅ | ❌ | ✅ | ✅ | - |
41
+ | 17 | 持久记忆 | SQLite + Memory | SQLite + FTS5 | FileBackedStore JSON | 🟡 | P2 |
42
+ | 18 | 对话历史搜索 | ✅ | FTS5 全文搜索 | ❌ | ❌ | P2 |
43
+
44
+ ## 四、渠道 & 交互
45
+
46
+ | # | 体验点 | OpenClaw | Hermes | OPC 现状 | 差距 | 优先级 |
47
+ |---|--------|----------|--------|----------|------|--------|
48
+ | 19 | 多渠道(TG/Discord/Slack) | ✅ 全部 | ✅ TG/Discord/Slack | ✅ 代码有 | ✅ | - |
49
+ | 20 | Web UI 聊天 | ✅ | ✅ | ✅ | ✅ | - |
50
+ | 21 | Studio 管理面板 | 无(CLI only) | 无(CLI only) | ✅ OPC Studio | 🔵 领先 | - |
51
+ | 22 | CLI 对话模式 | ❌ | ✅ `hermes chat` | ❌ | 🟡 | P2 |
52
+
53
+ ## 五、开发者体验
54
+
55
+ | # | 体验点 | OpenClaw | Hermes | OPC 现状 | 差距 | 优先级 |
56
+ |---|--------|----------|--------|----------|------|--------|
57
+ | 23 | TypeScript SDK | ❌ | ❌ | ✅ | 🔵 领先 | - |
58
+ | 24 | A2A 协议 | ❌ | ❌ | ✅ | 🔵 领先 | - |
59
+ | 25 | MCP 协议 | ✅ | ✅ | ✅ | ✅ | - |
60
+ | 26 | Workstation 模板 | ❌ | ❌ | ✅ 100+ 角色 | 🔵 领先 | - |
61
+ | 27 | `npx opc-agent` 零安装运行 | ✅ npx openclaw | ❌ | ❌ | 🟡 | P1 |
62
+
63
+ ## 六、自动化 & 调度
64
+
65
+ | # | 体验点 | OpenClaw | Hermes | OPC 现状 | 差距 | 优先级 |
66
+ |---|--------|----------|--------|----------|------|--------|
67
+ | 28 | Cron 定时任务 | ✅ Heartbeat | ❌ | ✅ cron-engine | ✅ | - |
68
+ | 29 | 主动触达 | ✅ Proactive | ❌ | 🟡 有 cron 但无主动逻辑 | 🟡 | P2 |
69
+
70
+ ---
71
+
72
+ ## 优先级行动计划
73
+
74
+ ### P0 — 今天必须启动(自主学习闭环)
75
+ 1. **#8 Agent 自写 Skill** — 实现 auto-skill-creation
76
+ 2. **#9 学习触发条件** — 实现 5 步闭环
77
+ 3. **#10 Skill 格式标准** — 采用 agentskills.io 兼容格式
78
+
79
+ ### P1 — 今天尽量完成(安装体验 + 用户建模)
80
+ 4. **#1/#7/#27 一行安装** — 支持 `npx opc-agent init`
81
+ 5. **#15 USER.md 用户建模** — Agent 自动学习用户偏好
82
+
83
+ ### P2 — 本周完成
84
+ 6. **#11 更多预置 Skill** — 扩充到 40+
85
+ 7. **#17 SQLite 持久记忆** — 替代 JSON
86
+ 8. **#18 对话历史搜索** — FTS5
87
+ 9. **#22 CLI 对话模式** — `opc chat`
88
+ 10. **#29 主动触达** — Agent 主动发消息
89
+
90
+ ### P3 — 后续
91
+ 11. **#5 迁移工具** — 从 OpenClaw/Hermes 迁移
92
+ 12. **#12 Skill 市场** — OPC Hub
package/dist/cli.js CHANGED
@@ -37,10 +37,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
37
37
  const commander_1 = require("commander");
38
38
  const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
- const os = __importStar(require("os"));
41
40
  const yaml = __importStar(require("js-yaml"));
42
41
  const readline = __importStar(require("readline"));
43
42
  const runtime_1 = require("./core/runtime");
43
+ const model_recommender_1 = require("./core/model-recommender");
44
44
  const customer_service_1 = require("./templates/customer-service");
45
45
  const sales_assistant_1 = require("./templates/sales-assistant");
46
46
  const knowledge_base_1 = require("./templates/knowledge-base");
@@ -366,32 +366,9 @@ export class EchoSkill extends BaseSkill {
366
366
  template = await select('Select a template:', Object.entries(TEMPLATES).map(([value, { label }]) => ({ value, label })));
367
367
  }
368
368
  // ── 硬件检测 + 智能模型推荐 ──
369
- const totalRAM = Math.round(os.totalmem() / (1024 ** 3)); // GB
370
- const freeMem = Math.round(os.freemem() / (1024 ** 3));
371
- const cpuCount = os.cpus().length;
372
- const MODEL_RECOMMENDATIONS = [
373
- // Tier 1: 超轻量 (≤4GB RAM)
374
- { name: 'qwen2.5:0.5b', size: '0.4GB', minRAM: 2, desc: '超轻量,适合低配机器', priority: 1 },
375
- { name: 'qwen2.5:1.5b', size: '1.0GB', minRAM: 4, desc: '轻量但更智能', priority: 2 },
376
- // Tier 2: 轻量 (4-8GB RAM)
377
- { name: 'qwen2.5:3b', size: '2.0GB', minRAM: 6, desc: '性价比最优', priority: 3 },
378
- { name: 'llama3.2:3b', size: '2.0GB', minRAM: 6, desc: 'Meta 最新轻量模型', priority: 3 },
379
- { name: 'phi3:mini', size: '2.3GB', minRAM: 6, desc: '微软高效小模型', priority: 3 },
380
- // Tier 3: 标准 (8-16GB RAM)
381
- { name: 'qwen2.5:7b', size: '4.7GB', minRAM: 8, desc: '推荐:中文最强 7B', priority: 4 },
382
- { name: 'llama3.1:8b', size: '4.7GB', minRAM: 8, desc: 'Meta 通用 8B', priority: 4 },
383
- { name: 'mistral:7b', size: '4.1GB', minRAM: 8, desc: 'Mistral 经典 7B', priority: 4 },
384
- { name: 'gemma2:9b', size: '5.4GB', minRAM: 10, desc: 'Google 高效 9B', priority: 4 },
385
- // Tier 4: 高配 (16-32GB RAM)
386
- { name: 'qwen2.5:14b', size: '9.0GB', minRAM: 16, desc: '中文强力 14B', priority: 5 },
387
- { name: 'deepseek-coder-v2:16b', size: '9.0GB', minRAM: 16, desc: '编程专用', priority: 5 },
388
- // Tier 5: 旗舰 (32GB+ RAM)
389
- { name: 'qwen2.5:32b', size: '20GB', minRAM: 32, desc: '接近 GPT-4 水平', priority: 6 },
390
- { name: 'llama3.1:70b', size: '40GB', minRAM: 64, desc: '开源最强', priority: 7 },
391
- ];
392
- // 根据可用内存筛选合适的模型
393
- const suitableModels = MODEL_RECOMMENDATIONS.filter(m => m.minRAM <= freeMem + 2); // +2GB 容差
394
- const bestRec = suitableModels.length > 0 ? suitableModels[suitableModels.length - 1] : MODEL_RECOMMENDATIONS[0];
369
+ // ── 硬件检测 + 远程模型推荐 ──
370
+ const sys = (0, model_recommender_1.detectSystem)();
371
+ const allModels = await (0, model_recommender_1.fetchModelList)();
395
372
  // ── LLM Provider 选择(Ollama-first)──
396
373
  let llmProvider = 'ollama';
397
374
  let llmModel = 'qwen2.5';
@@ -409,20 +386,23 @@ export class EchoSkill extends BaseSkill {
409
386
  modelNames = (ollamaData.models || []).map((m) => m.name || m.model);
410
387
  ollamaRunning = true;
411
388
  if (opts.yes && modelNames.length > 0) {
412
- // --yes 模式:优先用推荐模型(如果已安装),否则用第一个已有模型
413
- const bestInstalled = suitableModels.reverse().find(m => modelNames.includes(m.name));
389
+ const rec = (0, model_recommender_1.recommendModels)(allModels, sys, modelNames);
390
+ // --yes: prefer best installed recommended model
391
+ const bestInstalled = rec.installed.length > 0 ? rec.installed[rec.installed.length - 1] : null;
414
392
  llmModel = bestInstalled ? bestInstalled.name : modelNames[0];
415
393
  }
416
394
  }
417
395
  catch {
418
396
  ollamaRunning = false;
419
397
  }
398
+ // Compute recommendation (used by both interactive branches)
399
+ const rec = (0, model_recommender_1.recommendModels)(allModels, sys, modelNames);
420
400
  if (!opts.yes) {
421
401
  if (ollamaRunning) {
422
402
  console.log(`\n ${icon.info} ${color.dim('正在检测 Ollama...')}`);
423
403
  console.log(` ${icon.success} Ollama 已运行,发现 ${modelNames.length} 个模型`);
424
- console.log(` ${icon.info} 系统: ${totalRAM}GB RAM (${freeMem}GB 可用), ${cpuCount} CPU cores`);
425
- console.log(` ${icon.info} 推荐模型: ${color.cyan(bestRec.name)} (${bestRec.size}) - ${bestRec.desc}`);
404
+ console.log(` ${icon.info} 系统: ${sys.totalRAM}GB RAM (${sys.freeRAM}GB 可用), ${sys.cpuCount} CPU cores`);
405
+ console.log(` ${icon.info} 推荐模型: ${color.cyan(rec.best.name)} (${rec.best.size}) - ${rec.best.desc}`);
426
406
  // 选择 provider
427
407
  llmProvider = await select('选择 LLM 引擎:', [
428
408
  { value: 'ollama', label: '🟢 Ollama (免费本地,推荐) - 已检测到运行中' },
@@ -433,16 +413,14 @@ export class EchoSkill extends BaseSkill {
433
413
  ]);
434
414
  if (llmProvider === 'ollama') {
435
415
  // 已有模型 + 推荐未下载的模型
436
- const existingSet = new Set(modelNames);
437
- const recommendedNotInstalled = suitableModels.filter(m => !existingSet.has(m.name)).slice(-3); // 推荐最多3个未下载的
438
416
  const modelOptions = [
439
- ...modelNames.map((m) => {
440
- const rec = MODEL_RECOMMENDATIONS.find(r => r.name === m);
441
- const recLabel = rec ? ` (${rec.size}, ${rec.desc})` : '';
442
- const isBest = m === bestRec.name ? ' ⭐推荐' : '';
443
- return { value: m, label: `${m}${recLabel}${isBest} [已安装]` };
417
+ ...rec.installed.map((m) => {
418
+ const isBest = m.name === rec.best.name ? ' ⭐推荐' : '';
419
+ return { value: m.name, label: `${m.name} (${m.size}, ${m.desc})${isBest} [已安装]` };
444
420
  }),
445
- ...recommendedNotInstalled.map(m => ({
421
+ // Also show installed models not in recommendation list
422
+ ...modelNames.filter(n => !rec.installed.find(m => m.name === n)).map(n => ({ value: n, label: `${n} [已安装]` })),
423
+ ...rec.toDownload.map((m) => ({
446
424
  value: `pull:${m.name}`,
447
425
  label: `${m.name} (${m.size}, ${m.desc}) [需下载]`,
448
426
  })),
@@ -463,11 +441,11 @@ export class EchoSkill extends BaseSkill {
463
441
  else {
464
442
  // 没有本地模型,推荐下载
465
443
  console.log(` ${color.yellow('⚠️')} 没有发现已下载的模型`);
466
- console.log(` ${icon.info} 根据你的硬件 (${freeMem}GB 可用),推荐下载:`);
467
- for (const m of suitableModels.slice(-3)) {
444
+ console.log(` ${icon.info} 根据你的硬件 (${sys.freeRAM}GB 可用),推荐下载:`);
445
+ for (const m of rec.suitable.slice(-3)) {
468
446
  console.log(` ${color.cyan(`ollama pull ${m.name}`)} (${m.size}, ${m.desc})`);
469
447
  }
470
- llmModel = bestRec.name;
448
+ llmModel = rec.best.name;
471
449
  }
472
450
  }
473
451
  }
@@ -485,12 +463,12 @@ export class EchoSkill extends BaseSkill {
485
463
  if (llmProvider === 'ollama') {
486
464
  console.log(`\n ${icon.info} Ollama 安装指南:`);
487
465
  console.log(` 1. 访问 ${color.cyan('https://ollama.ai')} 下载并安装`);
488
- console.log(` ${icon.info} 根据你的硬件 (${totalRAM}GB RAM, ${freeMem}GB 可用),推荐:`);
489
- for (const m of suitableModels.slice(-3)) {
466
+ console.log(` ${icon.info} 根据你的硬件 (${sys.totalRAM}GB RAM, ${sys.freeRAM}GB 可用),推荐:`);
467
+ for (const m of rec.suitable.slice(-3)) {
490
468
  console.log(` ${color.cyan(`ollama pull ${m.name}`)} (${m.size}, ${m.desc})`);
491
469
  }
492
470
  console.log(` 3. 然后 ${color.cyan('opc run')} 即可开始对话\n`);
493
- llmModel = bestRec.name;
471
+ llmModel = rec.best.name;
494
472
  }
495
473
  }
496
474
  // 商业模型需要 API key
@@ -2436,6 +2414,64 @@ program
2436
2414
  await voice.start();
2437
2415
  `));
2438
2416
  });
2417
+ // ── Models command ──────────────────────────────────────────────
2418
+ program
2419
+ .command('models')
2420
+ .description('Show recommended Ollama models for your system')
2421
+ .option('--refresh', 'Force refresh model list from remote')
2422
+ .option('--json', 'Output as JSON')
2423
+ .action(async (opts) => {
2424
+ if (opts.refresh) {
2425
+ (0, model_recommender_1.clearModelCache)();
2426
+ console.log(`${icon.success} 模型推荐缓存已清除`);
2427
+ }
2428
+ const sys = (0, model_recommender_1.detectSystem)();
2429
+ const models = await (0, model_recommender_1.fetchModelList)();
2430
+ const cache = (0, model_recommender_1.cacheInfo)();
2431
+ // Detect Ollama
2432
+ let installedModels = [];
2433
+ try {
2434
+ const ctrl = new AbortController();
2435
+ const t = setTimeout(() => ctrl.abort(), 3000);
2436
+ const res = await fetch('http://localhost:11434/api/tags', { signal: ctrl.signal });
2437
+ clearTimeout(t);
2438
+ const data = await res.json();
2439
+ installedModels = (data.models || []).map((m) => m.name || m.model);
2440
+ }
2441
+ catch { /* Ollama not running */ }
2442
+ const rec = (0, model_recommender_1.recommendModels)(models, sys, installedModels);
2443
+ if (opts.json) {
2444
+ console.log(JSON.stringify({ system: sys, cache, recommendation: rec }, null, 2));
2445
+ return;
2446
+ }
2447
+ console.log(`\n${icon.rocket} ${color.bold('OPC 模型推荐')}\n`);
2448
+ console.log(` 系统: ${sys.totalRAM}GB RAM (${sys.freeRAM}GB 可用), ${sys.cpuCount} cores, ${sys.platform}/${sys.arch}`);
2449
+ if (cache.exists) {
2450
+ console.log(` 推荐列表: v${cache.version} (${cache.age})`);
2451
+ }
2452
+ else {
2453
+ console.log(` 推荐列表: 内置 (运行 ${color.cyan('opc models --refresh')} 获取最新)`);
2454
+ }
2455
+ console.log(` Ollama: ${installedModels.length > 0 ? color.green(`运行中, ${installedModels.length} 个模型`) : color.yellow('未运行')}`);
2456
+ console.log(`\n ${color.bold('⭐ 推荐:')} ${color.cyan(rec.best.name)} (${rec.best.size}) - ${rec.best.desc}\n`);
2457
+ // Table
2458
+ console.log(` ${'模型'.padEnd(28)} ${'大小'.padEnd(8)} ${'最低RAM'.padEnd(8)} ${'状态'.padEnd(10)} 说明`);
2459
+ console.log(` ${'─'.repeat(28)} ${'─'.repeat(8)} ${'─'.repeat(8)} ${'─'.repeat(10)} ${'─'.repeat(20)}`);
2460
+ for (const m of rec.suitable) {
2461
+ const installed = installedModels.includes(m.name);
2462
+ const isBest = m.name === rec.best.name;
2463
+ const status = installed ? color.green('已安装') : color.dim('未安装');
2464
+ const star = isBest ? ' ⭐' : (m.recommended ? ' 💎' : '');
2465
+ console.log(` ${(m.name + star).padEnd(28)} ${m.size.padEnd(8)} ${(m.minRAM + 'GB').padEnd(8)} ${status.padEnd(10)} ${m.desc}`);
2466
+ }
2467
+ if (rec.toDownload.length > 0) {
2468
+ console.log(`\n ${color.bold('推荐下载:')}`);
2469
+ for (const m of rec.toDownload) {
2470
+ console.log(` ${color.cyan(`ollama pull ${m.name}`)} (${m.size}, ${m.desc})`);
2471
+ }
2472
+ }
2473
+ console.log();
2474
+ });
2439
2475
  program.parse();
2440
2476
  // ── Keys command ──────────────────────────────────────────────
2441
2477
  const keys_1 = require("./security/keys");