opc-agent 1.1.3 → 1.2.0

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.
@@ -27,14 +27,14 @@ const TEMPLATES_HTML = `<!DOCTYPE html>
27
27
  <title>Agent Templates</title>
28
28
  <style>
29
29
  *{margin:0;padding:0;box-sizing:border-box}
30
- body{background:#0a0a0f;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px}
30
+ body{background:#0f0f23;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px}
31
31
  h1{font-size:28px;margin-bottom:8px;color:#fff}
32
- .sub{color:#888;margin-bottom:32px;font-size:14px}
32
+ .sub{color:#8a8aa0;margin-bottom:32px;font-size:14px}
33
33
  nav{margin-bottom:24px}
34
34
  nav a{color:#818cf8;text-decoration:none;margin-right:16px;font-size:14px}
35
35
  .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}
36
- .card{background:#12121a;border:1px solid #1e1e2e;border-radius:12px;padding:24px;cursor:pointer;transition:all .2s}
37
- .card:hover{border-color:#818cf8;transform:translateY(-2px)}
36
+ .card{background:#1a1a3a;border:1px solid #2d2d4e;border-radius:14px;padding:24px;cursor:pointer;transition:all .2s}
37
+ .card:hover{border-color:#818cf8;transform:translateY(-2px);box-shadow:0 4px 20px rgba(129,140,248,.15)}
38
38
  .card .icon{font-size:32px;margin-bottom:12px}
39
39
  .card h3{font-size:16px;color:#fff;margin-bottom:8px}
40
40
  .card p{font-size:13px;color:#888;line-height:1.5}
@@ -65,7 +65,7 @@ const CHAT_HTML = `<!DOCTYPE html>
65
65
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
66
66
  <title>OPC Agent</title>
67
67
  <style>
68
- :root{--bg:#0a0a0f;--surface:#12121a;--border:#1e1e2e;--text:#e0e0e0;--text-dim:#888;--accent:#818cf8;--accent-hover:#6366f1;--user-bg:#2563eb;--user-hover:#1d4ed8;--error-bg:#7f1d1d;--error-text:#fca5a5;--success:#22c55e;--radius:12px}
68
+ :root{--bg:#0f0f23;--surface:#1a1a3a;--border:#2d2d4e;--text:#e0e0e0;--text-dim:#8a8aa0;--accent:#818cf8;--accent-hover:#6366f1;--user-bg:#667eea;--user-hover:#5a6fd6;--error-bg:#7f1d1d;--error-text:#fca5a5;--success:#22c55e;--radius:14px}
69
69
  *{margin:0;padding:0;box-sizing:border-box}
70
70
  body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',sans-serif;height:100vh;height:100dvh;display:flex;flex-direction:column;overflow:hidden}
71
71
  header{background:var(--surface);padding:14px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px;flex-shrink:0;backdrop-filter:blur(12px)}
@@ -88,8 +88,8 @@ nav.header-nav a:hover{color:#fff;background:rgba(255,255,255,.06)}
88
88
  .msg-wrap.user{align-items:flex-end}
89
89
  .msg-wrap.assistant{align-items:flex-start}
90
90
  .msg{max-width:min(720px,85%);padding:10px 14px;border-radius:var(--radius);line-height:1.7;font-size:14px;word-break:break-word;position:relative;transition:all .2s}
91
- .msg.user{background:var(--user-bg);color:#fff;border-bottom-right-radius:4px}
92
- .msg.assistant{background:var(--surface);color:var(--text);border:1px solid var(--border);border-bottom-left-radius:4px}
91
+ .msg.user{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border-bottom-right-radius:4px;box-shadow:0 2px 8px rgba(102,126,234,.35)}
92
+ .msg.assistant{background:#2a2a4a;color:var(--text);border:1px solid var(--border);border-bottom-left-radius:4px;box-shadow:0 2px 8px rgba(0,0,0,.25)}
93
93
  .msg.error{background:var(--error-bg);color:var(--error-text);border:1px solid rgba(239,68,68,.3)}
94
94
  .msg pre{background:rgba(0,0,0,.4);padding:12px;border-radius:8px;overflow-x:auto;margin:8px 0;font-size:13px;font-family:'JetBrains Mono','Fira Code','Cascadia Code',monospace;line-height:1.5}
95
95
  .msg code{font-family:'JetBrains Mono','Fira Code','Cascadia Code',monospace;font-size:13px;background:rgba(0,0,0,.3);padding:1px 5px;border-radius:4px}
@@ -113,7 +113,7 @@ nav.header-nav a:hover{color:#fff;background:rgba(255,255,255,.06)}
113
113
  #input{flex:1;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);padding:10px 14px;color:#fff;font-size:14px;outline:none;resize:none;max-height:150px;min-height:42px;font-family:inherit;line-height:1.5;transition:border-color .2s}
114
114
  #input:focus{border-color:var(--accent)}
115
115
  #input::placeholder{color:var(--text-dim)}
116
- #send{background:var(--user-bg);color:#fff;border:none;border-radius:var(--radius);width:42px;height:42px;font-size:18px;cursor:pointer;transition:all .2s;display:flex;align-items:center;justify-content:center;flex-shrink:0}
116
+ #send{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border:none;border-radius:var(--radius);padding:0 16px;height:42px;font-size:14px;font-weight:600;cursor:pointer;transition:all .2s;display:flex;align-items:center;justify-content:center;flex-shrink:0;letter-spacing:.5px}
117
117
  #send:hover{background:var(--user-hover);transform:scale(1.05)}
118
118
  #send:disabled{background:#334155;cursor:not-allowed;transform:none}
119
119
  .empty-state{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-dim);gap:12px;padding:40px;text-align:center}
@@ -132,15 +132,15 @@ nav.header-nav a:hover{color:#fff;background:rgba(255,255,255,.06)}
132
132
  <body>
133
133
  <header>
134
134
  <div class="avatar" id="avatar">🤖</div>
135
- <div class="info"><h1 id="title">OPC Agent</h1><div class="status"><span class="dot"></span>Online</div></div>
135
+ <div class="info"><h1 id="title">OPC Agent</h1><div class="status"><span class="dot"></span>在线</div></div>
136
136
  <nav class="header-nav"><a href="/dashboard">Dashboard</a><a href="/templates">Templates</a></nav>
137
137
  </header>
138
138
  <div id="messages">
139
- <div class="empty-state" id="empty"><div class="logo">💬</div><h2>Start a conversation</h2><p>Type a message below to chat with your AI agent.</p></div>
139
+ <div class="empty-state" id="empty"><div class="logo">💬</div><h2>开始对话</h2><p>在下方输入消息与 AI 助手对话。</p></div>
140
140
  </div>
141
141
  <div id="input-area">
142
- <textarea id="input" rows="1" placeholder="Type a message…" autocomplete="off"></textarea>
143
- <button id="send" aria-label="Send">↑</button>
142
+ <textarea id="input" rows="1" placeholder="输入消息…" autocomplete="off"></textarea>
143
+ <button id="send" aria-label="发送">发送</button>
144
144
  </div>
145
145
  <script>
146
146
  const msgs=document.getElementById('messages'),input=document.getElementById('input'),btn=document.getElementById('send'),empty=document.getElementById('empty');
@@ -193,7 +193,7 @@ async function send(){
193
193
  addMsg('user',text);
194
194
  const wrap=document.createElement('div');wrap.className='msg-wrap assistant';
195
195
  const d=document.createElement('div');d.className='msg assistant';
196
- d.innerHTML='<div class="typing"><span></span><span></span><span></span></div>';
196
+ d.innerHTML='<div class="typing"><span></span><span></span><span></span><small style="margin-left:6px;font-size:12px;color:#8a8aa0">思考中…</small></div>';
197
197
  wrap.appendChild(d);
198
198
  const time=document.createElement('div');time.className='msg-time';time.textContent=fmtTime();
199
199
  wrap.appendChild(time);
@@ -236,17 +236,17 @@ const DASHBOARD_HTML = `<!DOCTYPE html>
236
236
  <title>OPC Dashboard</title>
237
237
  <style>
238
238
  *{margin:0;padding:0;box-sizing:border-box}
239
- body{background:#0a0a0f;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px}
239
+ body{background:#0f0f23;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px}
240
240
  h1{font-size:24px;margin-bottom:24px;color:#fff}
241
241
  .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;margin-bottom:32px}
242
- .card{background:#12121a;border:1px solid #1e1e2e;border-radius:12px;padding:20px}
243
- .card .label{font-size:12px;color:#888;text-transform:uppercase;letter-spacing:1px}
242
+ .card{background:#1a1a3a;border:1px solid #2d2d4e;border-radius:14px;padding:20px}
243
+ .card .label{font-size:12px;color:#8a8aa0;text-transform:uppercase;letter-spacing:1px}
244
244
  .card .value{font-size:32px;font-weight:700;color:#818cf8;margin-top:4px}
245
245
  .card .sub{font-size:12px;color:#555;margin-top:4px}
246
246
  nav{margin-bottom:24px}
247
247
  nav a{color:#818cf8;text-decoration:none;margin-right:16px;font-size:14px}
248
248
  nav a:hover{text-decoration:underline}
249
- .chart{background:#12121a;border:1px solid #1e1e2e;border-radius:12px;padding:20px;margin-bottom:16px}
249
+ .chart{background:#1a1a3a;border:1px solid #2d2d4e;border-radius:14px;padding:20px;margin-bottom:16px}
250
250
  .chart h3{font-size:14px;color:#888;margin-bottom:12px}
251
251
  </style>
252
252
  </head>
@@ -17,6 +17,11 @@ export declare class KnowledgeBase {
17
17
  }>>;
18
18
  /** Build context string for injection into LLM calls */
19
19
  getContext(query: string, topK?: number, minScore?: number): Promise<string>;
20
+ /**
21
+ * Query DeepBrain for semantic search enhancement.
22
+ * Activated when OPC_DEEPBRAIN_ENABLED=true and deepbrain CLI is globally installed.
23
+ */
24
+ private queryDeepBrain;
20
25
  getStats(): {
21
26
  totalEntries: number;
22
27
  sources: string[];
@@ -36,10 +36,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.KnowledgeBase = void 0;
37
37
  /**
38
38
  * Knowledge Base / RAG - Local vector storage with semantic search
39
+ * Supports optional DeepBrain semantic search enhancement via OPC_DEEPBRAIN_ENABLED=true
39
40
  */
40
41
  const fs = __importStar(require("fs"));
41
42
  const path = __importStar(require("path"));
42
43
  const crypto = __importStar(require("crypto"));
44
+ const child_process_1 = require("child_process");
43
45
  const CHUNK_SIZE = 500; // chars per chunk
44
46
  const CHUNK_OVERLAP = 50;
45
47
  const STORE_FILE = '.opc-knowledge.json';
@@ -185,9 +187,44 @@ class KnowledgeBase {
185
187
  async getContext(query, topK = 3, minScore = 0.1) {
186
188
  const results = await this.search(query, topK);
187
189
  const relevant = results.filter(r => r.score >= minScore);
188
- if (relevant.length === 0)
190
+ let context = '';
191
+ if (relevant.length > 0) {
192
+ context = `\n\n--- Relevant Knowledge ---\n${relevant.map((r, i) => `[${i + 1}] (source: ${r.source}, relevance: ${(r.score * 100).toFixed(0)}%)\n${r.content}`).join('\n\n')}\n--- End Knowledge ---\n`;
193
+ }
194
+ // Enhance with DeepBrain semantic search if enabled
195
+ const deepBrainCtx = this.queryDeepBrain(query, topK);
196
+ return context + deepBrainCtx;
197
+ }
198
+ /**
199
+ * Query DeepBrain for semantic search enhancement.
200
+ * Activated when OPC_DEEPBRAIN_ENABLED=true and deepbrain CLI is globally installed.
201
+ */
202
+ queryDeepBrain(query, topK) {
203
+ if (process.env.OPC_DEEPBRAIN_ENABLED !== 'true')
189
204
  return '';
190
- return `\n\n--- Relevant Knowledge ---\n${relevant.map((r, i) => `[${i + 1}] (source: ${r.source}, relevance: ${(r.score * 100).toFixed(0)}%)\n${r.content}`).join('\n\n')}\n--- End Knowledge ---\n`;
205
+ // Verify deepbrain is installed
206
+ const check = (0, child_process_1.spawnSync)('deepbrain', ['--version'], { encoding: 'utf-8', timeout: 3000 });
207
+ if (check.error || check.status !== 0)
208
+ return '';
209
+ try {
210
+ const result = (0, child_process_1.spawnSync)('deepbrain', ['query', query, '--top', String(topK), '--format', 'json'], { encoding: 'utf-8', timeout: 5000 });
211
+ if (result.status !== 0 || !result.stdout?.trim())
212
+ return '';
213
+ const parsed = JSON.parse(result.stdout);
214
+ const items = Array.isArray(parsed)
215
+ ? parsed
216
+ : (parsed.results ?? []);
217
+ if (items.length === 0)
218
+ return '';
219
+ return `\n\n--- DeepBrain Knowledge ---\n${items.map((r, i) => {
220
+ const relevance = r.score != null ? `${(r.score * 100).toFixed(0)}%` : 'n/a';
221
+ const text = r.content ?? r.text ?? '';
222
+ return `[${i + 1}] (source: ${r.source ?? 'deepbrain'}, relevance: ${relevance})\n${text}`;
223
+ }).join('\n\n')}\n--- End DeepBrain Knowledge ---\n`;
224
+ }
225
+ catch {
226
+ return '';
227
+ }
191
228
  }
192
229
  getStats() {
193
230
  const sources = [...new Set(this.store.entries.map(e => String(e.metadata.source)))];
@@ -5,5 +5,5 @@ export interface LLMProvider {
5
5
  chatStream(messages: Message[], systemPrompt?: string): AsyncIterable<string>;
6
6
  }
7
7
  export declare function createProvider(name?: string, model?: string, baseUrl?: string, apiKey?: string): LLMProvider;
8
- export declare const SUPPORTED_PROVIDERS: readonly ["openai", "deepseek", "qwen", "gemini"];
8
+ export declare const SUPPORTED_PROVIDERS: readonly ["openai", "deepseek", "qwen", "gemini", "dashscope", "zhipu", "moonshot"];
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -316,11 +316,20 @@ function isGeminiNative() {
316
316
  function createProvider(name = 'openai', model, baseUrl, apiKey) {
317
317
  const finalModel = model || process.env.OPC_LLM_MODEL || 'gpt-4o-mini';
318
318
  const finalKey = apiKey || getApiKey();
319
- // Auto-detect Gemini native when key is new format
319
+ const finalBaseUrl = baseUrl || getBaseUrl();
320
+ // Auto-detect Gemini native when key is new format or base URL points to googleapis
320
321
  if (finalKey.startsWith('AQ.') || isGeminiNative()) {
321
322
  return new GeminiNativeProvider(finalModel, finalKey);
322
323
  }
323
- return new OpenAICompatibleProvider(name, finalModel, baseUrl, apiKey);
324
+ // Auto-detect provider name from base URL
325
+ let resolvedName = name;
326
+ if (finalBaseUrl.includes('deepseek.com')) {
327
+ resolvedName = 'deepseek';
328
+ }
329
+ else if (finalBaseUrl.includes('dashscope.aliyuncs.com')) {
330
+ resolvedName = 'qwen';
331
+ }
332
+ return new OpenAICompatibleProvider(resolvedName, finalModel, baseUrl, apiKey);
324
333
  }
325
- exports.SUPPORTED_PROVIDERS = ['openai', 'deepseek', 'qwen', 'gemini'];
334
+ exports.SUPPORTED_PROVIDERS = ['openai', 'deepseek', 'qwen', 'gemini', 'dashscope', 'zhipu', 'moonshot'];
326
335
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,50 +1,50 @@
1
- {
2
- "name": "opc-agent",
3
- "version": "1.1.3",
4
- "description": "Open Agent Framework — Build, test, and run AI Agents for business workstations",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "bin": {
8
- "opc": "dist/cli.js"
9
- },
10
- "scripts": {
11
- "build": "tsc",
12
- "test": "vitest run",
13
- "dev": "tsc --watch",
14
- "lint": "tsc --noEmit",
15
- "docs:dev": "vitepress dev docs",
16
- "docs:build": "vitepress build docs",
17
- "docs:preview": "vitepress preview docs"
18
- },
19
- "keywords": [
20
- "agent",
21
- "ai",
22
- "llm",
23
- "framework",
24
- "typescript",
25
- "agent-framework"
26
- ],
27
- "author": "Deepleaper",
28
- "license": "Apache-2.0",
29
- "repository": {
30
- "type": "git",
31
- "url": "https://github.com/Deepleaper/opc-agent.git"
32
- },
33
- "dependencies": {
34
- "agentkits": "^0.1.0",
35
- "commander": "^12.0.0",
36
- "express": "^4.21.0",
37
- "js-yaml": "^4.1.0",
38
- "ws": "^8.20.0",
39
- "zod": "^3.23.0"
40
- },
41
- "devDependencies": {
42
- "@types/express": "^4.17.21",
43
- "@types/js-yaml": "^4.0.9",
44
- "@types/node": "^20.11.0",
45
- "@types/ws": "^8.18.1",
46
- "typescript": "^5.5.0",
47
- "vitest": "^2.0.0",
48
- "vitepress": "^1.5.0"
49
- }
50
- }
1
+ {
2
+ "name": "opc-agent",
3
+ "version": "1.2.0",
4
+ "description": "Open Agent Framework — Build, test, and run AI Agents for business workstations",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "opc": "dist/cli.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "test": "vitest run",
13
+ "dev": "tsc --watch",
14
+ "lint": "tsc --noEmit",
15
+ "docs:dev": "vitepress dev docs",
16
+ "docs:build": "vitepress build docs",
17
+ "docs:preview": "vitepress preview docs"
18
+ },
19
+ "keywords": [
20
+ "agent",
21
+ "ai",
22
+ "llm",
23
+ "framework",
24
+ "typescript",
25
+ "agent-framework"
26
+ ],
27
+ "author": "Deepleaper",
28
+ "license": "Apache-2.0",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/Deepleaper/opc-agent.git"
32
+ },
33
+ "dependencies": {
34
+ "agentkits": "^0.1.0",
35
+ "commander": "^12.0.0",
36
+ "express": "^4.21.0",
37
+ "js-yaml": "^4.1.0",
38
+ "ws": "^8.20.0",
39
+ "zod": "^3.23.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/express": "^4.17.21",
43
+ "@types/js-yaml": "^4.0.9",
44
+ "@types/node": "^20.11.0",
45
+ "@types/ws": "^8.18.1",
46
+ "typescript": "^5.5.0",
47
+ "vitest": "^2.0.0",
48
+ "vitepress": "^1.5.0"
49
+ }
50
+ }
@@ -26,14 +26,14 @@ const TEMPLATES_HTML = `<!DOCTYPE html>
26
26
  <title>Agent Templates</title>
27
27
  <style>
28
28
  *{margin:0;padding:0;box-sizing:border-box}
29
- body{background:#0a0a0f;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px}
29
+ body{background:#0f0f23;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px}
30
30
  h1{font-size:28px;margin-bottom:8px;color:#fff}
31
- .sub{color:#888;margin-bottom:32px;font-size:14px}
31
+ .sub{color:#8a8aa0;margin-bottom:32px;font-size:14px}
32
32
  nav{margin-bottom:24px}
33
33
  nav a{color:#818cf8;text-decoration:none;margin-right:16px;font-size:14px}
34
34
  .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}
35
- .card{background:#12121a;border:1px solid #1e1e2e;border-radius:12px;padding:24px;cursor:pointer;transition:all .2s}
36
- .card:hover{border-color:#818cf8;transform:translateY(-2px)}
35
+ .card{background:#1a1a3a;border:1px solid #2d2d4e;border-radius:14px;padding:24px;cursor:pointer;transition:all .2s}
36
+ .card:hover{border-color:#818cf8;transform:translateY(-2px);box-shadow:0 4px 20px rgba(129,140,248,.15)}
37
37
  .card .icon{font-size:32px;margin-bottom:12px}
38
38
  .card h3{font-size:16px;color:#fff;margin-bottom:8px}
39
39
  .card p{font-size:13px;color:#888;line-height:1.5}
@@ -65,7 +65,7 @@ const CHAT_HTML = `<!DOCTYPE html>
65
65
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
66
66
  <title>OPC Agent</title>
67
67
  <style>
68
- :root{--bg:#0a0a0f;--surface:#12121a;--border:#1e1e2e;--text:#e0e0e0;--text-dim:#888;--accent:#818cf8;--accent-hover:#6366f1;--user-bg:#2563eb;--user-hover:#1d4ed8;--error-bg:#7f1d1d;--error-text:#fca5a5;--success:#22c55e;--radius:12px}
68
+ :root{--bg:#0f0f23;--surface:#1a1a3a;--border:#2d2d4e;--text:#e0e0e0;--text-dim:#8a8aa0;--accent:#818cf8;--accent-hover:#6366f1;--user-bg:#667eea;--user-hover:#5a6fd6;--error-bg:#7f1d1d;--error-text:#fca5a5;--success:#22c55e;--radius:14px}
69
69
  *{margin:0;padding:0;box-sizing:border-box}
70
70
  body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',sans-serif;height:100vh;height:100dvh;display:flex;flex-direction:column;overflow:hidden}
71
71
  header{background:var(--surface);padding:14px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px;flex-shrink:0;backdrop-filter:blur(12px)}
@@ -88,8 +88,8 @@ nav.header-nav a:hover{color:#fff;background:rgba(255,255,255,.06)}
88
88
  .msg-wrap.user{align-items:flex-end}
89
89
  .msg-wrap.assistant{align-items:flex-start}
90
90
  .msg{max-width:min(720px,85%);padding:10px 14px;border-radius:var(--radius);line-height:1.7;font-size:14px;word-break:break-word;position:relative;transition:all .2s}
91
- .msg.user{background:var(--user-bg);color:#fff;border-bottom-right-radius:4px}
92
- .msg.assistant{background:var(--surface);color:var(--text);border:1px solid var(--border);border-bottom-left-radius:4px}
91
+ .msg.user{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border-bottom-right-radius:4px;box-shadow:0 2px 8px rgba(102,126,234,.35)}
92
+ .msg.assistant{background:#2a2a4a;color:var(--text);border:1px solid var(--border);border-bottom-left-radius:4px;box-shadow:0 2px 8px rgba(0,0,0,.25)}
93
93
  .msg.error{background:var(--error-bg);color:var(--error-text);border:1px solid rgba(239,68,68,.3)}
94
94
  .msg pre{background:rgba(0,0,0,.4);padding:12px;border-radius:8px;overflow-x:auto;margin:8px 0;font-size:13px;font-family:'JetBrains Mono','Fira Code','Cascadia Code',monospace;line-height:1.5}
95
95
  .msg code{font-family:'JetBrains Mono','Fira Code','Cascadia Code',monospace;font-size:13px;background:rgba(0,0,0,.3);padding:1px 5px;border-radius:4px}
@@ -113,7 +113,7 @@ nav.header-nav a:hover{color:#fff;background:rgba(255,255,255,.06)}
113
113
  #input{flex:1;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);padding:10px 14px;color:#fff;font-size:14px;outline:none;resize:none;max-height:150px;min-height:42px;font-family:inherit;line-height:1.5;transition:border-color .2s}
114
114
  #input:focus{border-color:var(--accent)}
115
115
  #input::placeholder{color:var(--text-dim)}
116
- #send{background:var(--user-bg);color:#fff;border:none;border-radius:var(--radius);width:42px;height:42px;font-size:18px;cursor:pointer;transition:all .2s;display:flex;align-items:center;justify-content:center;flex-shrink:0}
116
+ #send{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border:none;border-radius:var(--radius);padding:0 16px;height:42px;font-size:14px;font-weight:600;cursor:pointer;transition:all .2s;display:flex;align-items:center;justify-content:center;flex-shrink:0;letter-spacing:.5px}
117
117
  #send:hover{background:var(--user-hover);transform:scale(1.05)}
118
118
  #send:disabled{background:#334155;cursor:not-allowed;transform:none}
119
119
  .empty-state{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-dim);gap:12px;padding:40px;text-align:center}
@@ -132,15 +132,15 @@ nav.header-nav a:hover{color:#fff;background:rgba(255,255,255,.06)}
132
132
  <body>
133
133
  <header>
134
134
  <div class="avatar" id="avatar">🤖</div>
135
- <div class="info"><h1 id="title">OPC Agent</h1><div class="status"><span class="dot"></span>Online</div></div>
135
+ <div class="info"><h1 id="title">OPC Agent</h1><div class="status"><span class="dot"></span>在线</div></div>
136
136
  <nav class="header-nav"><a href="/dashboard">Dashboard</a><a href="/templates">Templates</a></nav>
137
137
  </header>
138
138
  <div id="messages">
139
- <div class="empty-state" id="empty"><div class="logo">💬</div><h2>Start a conversation</h2><p>Type a message below to chat with your AI agent.</p></div>
139
+ <div class="empty-state" id="empty"><div class="logo">💬</div><h2>开始对话</h2><p>在下方输入消息与 AI 助手对话。</p></div>
140
140
  </div>
141
141
  <div id="input-area">
142
- <textarea id="input" rows="1" placeholder="Type a message…" autocomplete="off"></textarea>
143
- <button id="send" aria-label="Send">↑</button>
142
+ <textarea id="input" rows="1" placeholder="输入消息…" autocomplete="off"></textarea>
143
+ <button id="send" aria-label="发送">发送</button>
144
144
  </div>
145
145
  <script>
146
146
  const msgs=document.getElementById('messages'),input=document.getElementById('input'),btn=document.getElementById('send'),empty=document.getElementById('empty');
@@ -193,7 +193,7 @@ async function send(){
193
193
  addMsg('user',text);
194
194
  const wrap=document.createElement('div');wrap.className='msg-wrap assistant';
195
195
  const d=document.createElement('div');d.className='msg assistant';
196
- d.innerHTML='<div class="typing"><span></span><span></span><span></span></div>';
196
+ d.innerHTML='<div class="typing"><span></span><span></span><span></span><small style="margin-left:6px;font-size:12px;color:#8a8aa0">思考中…</small></div>';
197
197
  wrap.appendChild(d);
198
198
  const time=document.createElement('div');time.className='msg-time';time.textContent=fmtTime();
199
199
  wrap.appendChild(time);
@@ -237,17 +237,17 @@ const DASHBOARD_HTML = `<!DOCTYPE html>
237
237
  <title>OPC Dashboard</title>
238
238
  <style>
239
239
  *{margin:0;padding:0;box-sizing:border-box}
240
- body{background:#0a0a0f;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px}
240
+ body{background:#0f0f23;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px}
241
241
  h1{font-size:24px;margin-bottom:24px;color:#fff}
242
242
  .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;margin-bottom:32px}
243
- .card{background:#12121a;border:1px solid #1e1e2e;border-radius:12px;padding:20px}
244
- .card .label{font-size:12px;color:#888;text-transform:uppercase;letter-spacing:1px}
243
+ .card{background:#1a1a3a;border:1px solid #2d2d4e;border-radius:14px;padding:20px}
244
+ .card .label{font-size:12px;color:#8a8aa0;text-transform:uppercase;letter-spacing:1px}
245
245
  .card .value{font-size:32px;font-weight:700;color:#818cf8;margin-top:4px}
246
246
  .card .sub{font-size:12px;color:#555;margin-top:4px}
247
247
  nav{margin-bottom:24px}
248
248
  nav a{color:#818cf8;text-decoration:none;margin-right:16px;font-size:14px}
249
249
  nav a:hover{text-decoration:underline}
250
- .chart{background:#12121a;border:1px solid #1e1e2e;border-radius:12px;padding:20px;margin-bottom:16px}
250
+ .chart{background:#1a1a3a;border:1px solid #2d2d4e;border-radius:14px;padding:20px;margin-bottom:16px}
251
251
  .chart h3{font-size:14px;color:#888;margin-bottom:12px}
252
252
  </style>
253
253
  </head>
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * Knowledge Base / RAG - Local vector storage with semantic search
3
+ * Supports optional DeepBrain semantic search enhancement via OPC_DEEPBRAIN_ENABLED=true
3
4
  */
4
5
  import * as fs from 'fs';
5
6
  import * as path from 'path';
6
7
  import * as crypto from 'crypto';
8
+ import { spawnSync } from 'child_process';
7
9
 
8
10
  // Simple in-memory vector store (PGlite-compatible interface for future migration)
9
11
  interface VectorEntry {
@@ -180,11 +182,54 @@ export class KnowledgeBase {
180
182
  async getContext(query: string, topK: number = 3, minScore: number = 0.1): Promise<string> {
181
183
  const results = await this.search(query, topK);
182
184
  const relevant = results.filter(r => r.score >= minScore);
183
- if (relevant.length === 0) return '';
184
185
 
185
- return `\n\n--- Relevant Knowledge ---\n${relevant.map((r, i) =>
186
- `[${i + 1}] (source: ${r.source}, relevance: ${(r.score * 100).toFixed(0)}%)\n${r.content}`
187
- ).join('\n\n')}\n--- End Knowledge ---\n`;
186
+ let context = '';
187
+ if (relevant.length > 0) {
188
+ context = `\n\n--- Relevant Knowledge ---\n${relevant.map((r, i) =>
189
+ `[${i + 1}] (source: ${r.source}, relevance: ${(r.score * 100).toFixed(0)}%)\n${r.content}`
190
+ ).join('\n\n')}\n--- End Knowledge ---\n`;
191
+ }
192
+
193
+ // Enhance with DeepBrain semantic search if enabled
194
+ const deepBrainCtx = this.queryDeepBrain(query, topK);
195
+ return context + deepBrainCtx;
196
+ }
197
+
198
+ /**
199
+ * Query DeepBrain for semantic search enhancement.
200
+ * Activated when OPC_DEEPBRAIN_ENABLED=true and deepbrain CLI is globally installed.
201
+ */
202
+ private queryDeepBrain(query: string, topK: number): string {
203
+ if (process.env.OPC_DEEPBRAIN_ENABLED !== 'true') return '';
204
+
205
+ // Verify deepbrain is installed
206
+ const check = spawnSync('deepbrain', ['--version'], { encoding: 'utf-8', timeout: 3000 });
207
+ if (check.error || check.status !== 0) return '';
208
+
209
+ try {
210
+ const result = spawnSync(
211
+ 'deepbrain',
212
+ ['query', query, '--top', String(topK), '--format', 'json'],
213
+ { encoding: 'utf-8', timeout: 5000 },
214
+ );
215
+ if (result.status !== 0 || !result.stdout?.trim()) return '';
216
+
217
+ type DeepBrainResult = { source?: string; score?: number; content?: string; text?: string };
218
+ const parsed: unknown = JSON.parse(result.stdout);
219
+ const items: DeepBrainResult[] = Array.isArray(parsed)
220
+ ? (parsed as DeepBrainResult[])
221
+ : ((parsed as { results?: DeepBrainResult[] }).results ?? []);
222
+
223
+ if (items.length === 0) return '';
224
+
225
+ return `\n\n--- DeepBrain Knowledge ---\n${items.map((r, i) => {
226
+ const relevance = r.score != null ? `${(r.score * 100).toFixed(0)}%` : 'n/a';
227
+ const text = r.content ?? r.text ?? '';
228
+ return `[${i + 1}] (source: ${r.source ?? 'deepbrain'}, relevance: ${relevance})\n${text}`;
229
+ }).join('\n\n')}\n--- End DeepBrain Knowledge ---\n`;
230
+ } catch {
231
+ return '';
232
+ }
188
233
  }
189
234
 
190
235
  getStats(): { totalEntries: number; sources: string[]; updatedAt: string } {
@@ -310,13 +310,22 @@ function isGeminiNative(): boolean {
310
310
  export function createProvider(name: string = 'openai', model?: string, baseUrl?: string, apiKey?: string): LLMProvider {
311
311
  const finalModel = model || process.env.OPC_LLM_MODEL || 'gpt-4o-mini';
312
312
  const finalKey = apiKey || getApiKey();
313
+ const finalBaseUrl = baseUrl || getBaseUrl();
313
314
 
314
- // Auto-detect Gemini native when key is new format
315
+ // Auto-detect Gemini native when key is new format or base URL points to googleapis
315
316
  if (finalKey.startsWith('AQ.') || isGeminiNative()) {
316
317
  return new GeminiNativeProvider(finalModel, finalKey);
317
318
  }
318
319
 
319
- return new OpenAICompatibleProvider(name, finalModel, baseUrl, apiKey);
320
+ // Auto-detect provider name from base URL
321
+ let resolvedName = name;
322
+ if (finalBaseUrl.includes('deepseek.com')) {
323
+ resolvedName = 'deepseek';
324
+ } else if (finalBaseUrl.includes('dashscope.aliyuncs.com')) {
325
+ resolvedName = 'qwen';
326
+ }
327
+
328
+ return new OpenAICompatibleProvider(resolvedName, finalModel, baseUrl, apiKey);
320
329
  }
321
330
 
322
- export const SUPPORTED_PROVIDERS = ['openai', 'deepseek', 'qwen', 'gemini'] as const;
331
+ export const SUPPORTED_PROVIDERS = ['openai', 'deepseek', 'qwen', 'gemini', 'dashscope', 'zhipu', 'moonshot'] as const;
@@ -0,0 +1,45 @@
1
+ # Ecommerce Assistant — 电商助手工位
2
+
3
+ 专为电商平台设计的 AI 导购与售后工位,覆盖商品推荐、订单查询、退换货处理全链路。
4
+
5
+ ## 快速开始
6
+
7
+ ```bash
8
+ opc init --template ecommerce-assistant
9
+ opc start
10
+ ```
11
+
12
+ 访问 http://localhost:3000 即可使用电商助手聊天界面。
13
+
14
+ ## 功能
15
+
16
+ | 技能 | 说明 |
17
+ |------|------|
18
+ | product-search | 关键词/类目/价格区间商品搜索 |
19
+ | order-query | 订单状态与物流实时查询 |
20
+ | after-sale | 退换货受理与投诉处理 |
21
+ | promotion | 个性化优惠券与促销推送 |
22
+ | recommendation | 基于偏好的个性化商品推荐 |
23
+
24
+ ## 配置
25
+
26
+ 在 `oad.yaml` 中修改以下参数:
27
+
28
+ - `spec.provider.default` — 切换 LLM 提供商(deepseek / openai / qwen)
29
+ - `spec.model` — 指定模型版本
30
+ - `spec.systemPrompt` — 定制品牌话术、商品范围和售后规则
31
+
32
+ ### 环境变量
33
+
34
+ ```bash
35
+ OPC_LLM_API_KEY=your_key
36
+ OPC_LLM_MODEL=deepseek-chat # 可选,覆盖 oad.yaml 中的 model
37
+ OPC_DEEPBRAIN_ENABLED=true # 启用 DeepBrain 知识库增强(需全局安装 deepbrain)
38
+ ```
39
+
40
+ ## 推荐搭配
41
+
42
+ - 将商品目录、售后政策、FAQ 上传至知识库(`/api/kb/upload`)
43
+ - 开启 `OPC_DEEPBRAIN_ENABLED=true` 提升商品语义匹配精度
44
+ - 通过 Dashboard(`/dashboard`)监控转化率、客满率和退单率
45
+ - 结合 CRM 系统 Webhook 实现订单状态实时同步
@@ -0,0 +1,47 @@
1
+ apiVersion: opc/v1
2
+ kind: Agent
3
+ metadata:
4
+ name: ecommerce-assistant
5
+ version: 1.0.0
6
+ description: "电商助手工位 - 商品推荐、订单查询、售后服务一体化"
7
+ author: Deepleaper
8
+ license: Apache-2.0
9
+ marketplace:
10
+ certified: false
11
+ category: ecommerce
12
+ spec:
13
+ provider:
14
+ default: deepseek
15
+ allowed: [openai, deepseek, qwen, gemini]
16
+ model: deepseek-chat
17
+ systemPrompt: |
18
+ 你是一名专业的电商购物助手。
19
+ 你帮助用户查询商品信息、比较价格、处理订单状态和售后问题。
20
+ 根据用户需求推荐合适的商品,推送当前促销活动,并协助用户做出购买决策。
21
+ 处理退换货申请时,需先核实订单信息,再按平台规则指引用户操作。
22
+ 始终保持友好、热情、专业的服务态度。
23
+ skills:
24
+ - name: product-search
25
+ description: "搜索商品信息,支持关键词、类目、价格区间筛选"
26
+ - name: order-query
27
+ description: "查询订单状态、物流轨迹和预计送达时间"
28
+ - name: after-sale
29
+ description: "处理退换货申请、投诉受理和赔偿协商"
30
+ - name: promotion
31
+ description: "推送个性化优惠券、限时活动和会员权益"
32
+ - name: recommendation
33
+ description: "基于用户偏好和购买历史做商品推荐"
34
+ channels:
35
+ - type: web
36
+ port: 3000
37
+ memory:
38
+ shortTerm: true
39
+ longTerm: true
40
+ knowledge:
41
+ enabled: true
42
+ deepbrain: auto
43
+ dtv:
44
+ trust:
45
+ level: sandbox
46
+ value:
47
+ metrics: [conversion_rate, customer_satisfaction, avg_order_value, refund_rate]
@@ -0,0 +1,43 @@
1
+ # Tech Support Agent — 技术支持工位
2
+
3
+ 专为 IT 支持团队设计的 AI 工位,覆盖软件故障、系统配置、网络问题等场景。
4
+
5
+ ## 快速开始
6
+
7
+ ```bash
8
+ opc init --template tech-support
9
+ opc start
10
+ ```
11
+
12
+ 访问 http://localhost:3000 即可使用技术支持聊天界面。
13
+
14
+ ## 功能
15
+
16
+ | 技能 | 说明 |
17
+ |------|------|
18
+ | troubleshoot | 分步骤诊断和解决技术问题 |
19
+ | knowledge-lookup | 查询技术文档与历史解决方案 |
20
+ | ticket-create | 创建并跟踪技术支持工单 |
21
+ | escalate | 升级复杂问题至专项团队 |
22
+
23
+ ## 配置
24
+
25
+ 在 `oad.yaml` 中修改以下参数:
26
+
27
+ - `spec.provider.default` — 切换 LLM 提供商(deepseek / openai / qwen)
28
+ - `spec.model` — 指定模型版本
29
+ - `spec.systemPrompt` — 定制支持范围和话术风格
30
+
31
+ ### 环境变量
32
+
33
+ ```bash
34
+ OPC_LLM_API_KEY=your_key
35
+ OPC_LLM_MODEL=deepseek-chat # 可选,覆盖 oad.yaml 中的 model
36
+ OPC_DEEPBRAIN_ENABLED=true # 启用 DeepBrain 知识库增强(需全局安装 deepbrain)
37
+ ```
38
+
39
+ ## 推荐搭配
40
+
41
+ - 将内部技术文档、SOP、FAQ 上传至知识库(`/api/kb/upload`)
42
+ - 开启 `OPC_DEEPBRAIN_ENABLED=true` 获得更精准的语义检索
43
+ - 通过 Dashboard(`/dashboard`)监控首次解决率和平均响应时间
@@ -0,0 +1,45 @@
1
+ apiVersion: opc/v1
2
+ kind: Agent
3
+ metadata:
4
+ name: tech-support
5
+ version: 1.0.0
6
+ description: "技术支持工位 - 处理用户技术问题、故障排查和解决方案推荐"
7
+ author: Deepleaper
8
+ license: Apache-2.0
9
+ marketplace:
10
+ certified: false
11
+ category: it-support
12
+ spec:
13
+ provider:
14
+ default: deepseek
15
+ allowed: [openai, deepseek, qwen, gemini]
16
+ model: deepseek-chat
17
+ systemPrompt: |
18
+ 你是一名专业的技术支持工程师。
19
+ 你帮助用户解决技术问题,包括软件故障、系统配置、网络问题、硬件故障等。
20
+ 回答时请保持专业、耐心,并提供清晰的步骤指引。
21
+ 优先使用知识库中的已知解决方案。
22
+ 如果问题无法远程解决,请指导用户联系线下支持或提交工单。
23
+ skills:
24
+ - name: troubleshoot
25
+ description: "诊断和解决技术问题,提供分步骤操作指南"
26
+ - name: knowledge-lookup
27
+ description: "查询技术文档、FAQ 和历史解决方案库"
28
+ - name: ticket-create
29
+ description: "为复杂问题创建技术支持工单并跟踪进展"
30
+ - name: escalate
31
+ description: "将高优先级问题升级到高级工程师或专项团队"
32
+ channels:
33
+ - type: web
34
+ port: 3000
35
+ memory:
36
+ shortTerm: true
37
+ longTerm: true
38
+ knowledge:
39
+ enabled: true
40
+ deepbrain: auto
41
+ dtv:
42
+ trust:
43
+ level: internal
44
+ value:
45
+ metrics: [resolution_time, first_contact_resolution, customer_satisfaction]