@backendkit-labs/agent-enterprise 0.8.7 → 0.8.9
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.
- package/README.md +411 -524
- package/package.json +40 -40
package/README.md
CHANGED
|
@@ -1,524 +1,411 @@
|
|
|
1
|
-
# @backendkit-labs/agent-enterprise
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/@backendkit-labs/agent-enterprise)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- [
|
|
12
|
-
- [
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
//
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
|
125
|
-
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
ENTERPRISE_TOOLS.
|
|
159
|
-
ENTERPRISE_TOOLS.
|
|
160
|
-
ENTERPRISE_TOOLS.
|
|
161
|
-
ENTERPRISE_TOOLS.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
//
|
|
166
|
-
VAULT_FOLDERS.
|
|
167
|
-
VAULT_FOLDERS.
|
|
168
|
-
VAULT_FOLDERS.
|
|
169
|
-
VAULT_FOLDERS.
|
|
170
|
-
VAULT_FOLDERS.
|
|
171
|
-
VAULT_FOLDERS.
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### Adding custom profiles
|
|
175
|
-
|
|
176
|
-
```typescript
|
|
177
|
-
const enterprise = createEnterpriseSetup({
|
|
178
|
-
...opts,
|
|
179
|
-
extraProfiles: [
|
|
180
|
-
{
|
|
181
|
-
id: 'legal',
|
|
182
|
-
name: 'Legal Advisor',
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
import
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
const store = new VectorStore();
|
|
413
|
-
|
|
414
|
-
// Add pre-embedded chunks
|
|
415
|
-
store.add([
|
|
416
|
-
{ id: 'c1', content: 'Password reset procedure...', embedding: [...512 floats...], source: 'support.md' },
|
|
417
|
-
{ id: 'c2', content: 'Password policy requires...', embedding: [...512 floats...], source: 'policy.md' },
|
|
418
|
-
]);
|
|
419
|
-
|
|
420
|
-
// Search by query embedding
|
|
421
|
-
const queryEmbeddings = await embedder.embed(['reset password steps']);
|
|
422
|
-
const results: SearchResult[] = store.search(queryEmbeddings[0], { topK: 3, minScore: 0.1 });
|
|
423
|
-
|
|
424
|
-
// Persist index to disk
|
|
425
|
-
store.saveToFile('/cache/my-index.json');
|
|
426
|
-
|
|
427
|
-
// Load from disk (on restart)
|
|
428
|
-
const loaded = VectorStore.loadFromFile('/cache/my-index.json');
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
---
|
|
432
|
-
|
|
433
|
-
## Full production example with AgentServer
|
|
434
|
-
|
|
435
|
-
A complete on-prem enterprise deployment with Ollama embeddings, DeepSeek for general agents, local Ollama for HR/Finance, Redis sessions, and JWT auth:
|
|
436
|
-
|
|
437
|
-
```typescript
|
|
438
|
-
import { createEnterpriseSetup, OllamaEmbedder } from '@backendkit-labs/agent-enterprise';
|
|
439
|
-
import { ProviderRegistry } from '@backendkit-labs/agent-core';
|
|
440
|
-
import { AgentServer, JwtAuth, TriggerBus, RedisSessionMetaStore } from '@backendkit-labs/agent-web';
|
|
441
|
-
import Redis from 'ioredis';
|
|
442
|
-
|
|
443
|
-
// ── LLM Providers ─────────────────────────────────────────────────────────────
|
|
444
|
-
|
|
445
|
-
const providers = new ProviderRegistry();
|
|
446
|
-
|
|
447
|
-
// Cloud: general, support, sales, ops
|
|
448
|
-
providers.register('deepseek', new DeepSeekProvider({
|
|
449
|
-
apiKey: process.env.DEEPSEEK_API_KEY!,
|
|
450
|
-
model: 'deepseek-chat',
|
|
451
|
-
}));
|
|
452
|
-
|
|
453
|
-
// On-prem: HR and Finance (sensitive data)
|
|
454
|
-
providers.register('local', new OllamaProvider({
|
|
455
|
-
host: 'http://ollama.internal:11434',
|
|
456
|
-
model: 'llama3.2',
|
|
457
|
-
}));
|
|
458
|
-
|
|
459
|
-
// ── Enterprise Setup ───────────────────────────────────────────────────────────
|
|
460
|
-
|
|
461
|
-
const enterprise = createEnterpriseSetup({
|
|
462
|
-
vaultPath: process.env.VAULT_PATH!,
|
|
463
|
-
providers,
|
|
464
|
-
defaultProvider: 'deepseek',
|
|
465
|
-
embedder: new OllamaEmbedder({
|
|
466
|
-
host: 'http://ollama.internal:11434',
|
|
467
|
-
model: 'nomic-embed-text',
|
|
468
|
-
}),
|
|
469
|
-
indexDir: '/var/bk-agent/rag-indexes',
|
|
470
|
-
sessionsDir: '/var/bk-agent/sessions',
|
|
471
|
-
maxIterations: 20,
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
await enterprise.indexAll({ verbose: true });
|
|
475
|
-
|
|
476
|
-
// Re-index every night at 2 AM to pick up new vault notes
|
|
477
|
-
import { CronJob } from 'node-cron';
|
|
478
|
-
CronJob.schedule('0 2 * * *', () => enterprise.indexAll());
|
|
479
|
-
|
|
480
|
-
// ── Infrastructure ─────────────────────────────────────────────────────────────
|
|
481
|
-
|
|
482
|
-
const redis = new Redis(process.env.REDIS_URL!);
|
|
483
|
-
const jwt = new JwtAuth({ secret: process.env.JWT_SECRET!, sessionIdClaim: 'sub' });
|
|
484
|
-
const triggers = new TriggerBus(enterprise.engineFactory)
|
|
485
|
-
.addCron({
|
|
486
|
-
name: 'weekly-digest',
|
|
487
|
-
schedule: '0 9 * * 1',
|
|
488
|
-
buildPrompt: () => 'Summarize all knowledge base updates from the past week.',
|
|
489
|
-
onResult: (out) => intranet.post('/announcements', out),
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
// ── Server ─────────────────────────────────────────────────────────────────────
|
|
493
|
-
|
|
494
|
-
const server = new AgentServer({
|
|
495
|
-
port: 3000,
|
|
496
|
-
engineFactory: enterprise.engineFactory,
|
|
497
|
-
auth: jwt.hook(),
|
|
498
|
-
store: new RedisSessionMetaStore(redis),
|
|
499
|
-
cors: ['https://intranet.company.com'],
|
|
500
|
-
rateLimiting: { max: 30, timeWindow: '1 minute' },
|
|
501
|
-
metrics: { enabled: true, prefix: 'enterprise_' },
|
|
502
|
-
maxIdleMs: 30 * 60 * 1000,
|
|
503
|
-
triggers,
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
await server.start();
|
|
507
|
-
console.log('Enterprise agent server running on :3000');
|
|
508
|
-
|
|
509
|
-
process.on('SIGTERM', () => server.stop());
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
### Required vault folder structure
|
|
513
|
-
|
|
514
|
-
```
|
|
515
|
-
/obsidian-vault
|
|
516
|
-
├── General/ ← all agents can search here
|
|
517
|
-
├── Support/ ← Support agent
|
|
518
|
-
├── HR/ ← HR agent (on-prem LLM)
|
|
519
|
-
├── Sales/ ← Sales agent
|
|
520
|
-
├── Finance/ ← Finance agent (on-prem LLM)
|
|
521
|
-
└── Operations/ ← Ops agent
|
|
522
|
-
```
|
|
523
|
-
|
|
524
|
-
Create these folders in Obsidian and add `.md` files. The indexer discovers all markdown files recursively.
|
|
1
|
+
# @backendkit-labs/agent-enterprise
|
|
2
|
+
|
|
3
|
+
Business agents on top of `agent-core`: RAG over an Obsidian vault with per-department isolation, six department agent profiles, a vault writing tool, YAML flows with compliance gates, and a factory that wires it all into an `AgentEngine` per session.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@backendkit-labs/agent-enterprise)
|
|
6
|
+
|
|
7
|
+
## Table of contents
|
|
8
|
+
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [Quick start](#quick-start)
|
|
11
|
+
- [createEnterpriseSetup](#createenterprisesetup)
|
|
12
|
+
- [Enterprise agent profiles](#enterprise-agent-profiles)
|
|
13
|
+
- [RAG pipeline](#rag-pipeline)
|
|
14
|
+
- [ObsidianRAGProvider](#obsidianragprovider)
|
|
15
|
+
- [VaultWriter](#vaultwriter)
|
|
16
|
+
- [Embedders](#embedders)
|
|
17
|
+
- [Full production example with AgentServer](#full-production-example-with-agentserver)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @backendkit-labs/agent-enterprise @backendkit-labs/agent-core
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For the full web stack:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @backendkit-labs/agent-enterprise @backendkit-labs/agent-core @backendkit-labs/agent-web
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { createEnterpriseSetup } from '@backendkit-labs/agent-enterprise';
|
|
39
|
+
import { ProviderRegistry } from '@backendkit-labs/agent-core';
|
|
40
|
+
import { AgentServer } from '@backendkit-labs/agent-web';
|
|
41
|
+
|
|
42
|
+
// 1. Configure your LLM providers
|
|
43
|
+
const providers = new ProviderRegistry();
|
|
44
|
+
providers.register('openai', myOpenAIProvider);
|
|
45
|
+
|
|
46
|
+
// 2. Create the enterprise setup
|
|
47
|
+
const enterprise = createEnterpriseSetup({
|
|
48
|
+
vaultPath: '/shared/obsidian-vault',
|
|
49
|
+
providers,
|
|
50
|
+
defaultProvider: 'openai',
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// 3. Index the vault (once at startup — subsequent runs only re-index changed files)
|
|
54
|
+
await enterprise.indexAll({ verbose: true });
|
|
55
|
+
|
|
56
|
+
// 4. Start the web server
|
|
57
|
+
const server = new AgentServer({
|
|
58
|
+
port: 3000,
|
|
59
|
+
engineFactory: enterprise.engineFactory,
|
|
60
|
+
cors: '*',
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await server.start();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## createEnterpriseSetup
|
|
69
|
+
|
|
70
|
+
The main factory. Returns `{ indexAll, engineFactory }`.
|
|
71
|
+
|
|
72
|
+
### Options
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
interface EnterpriseSetupOptions {
|
|
76
|
+
// Required
|
|
77
|
+
vaultPath: string; // absolute path to the Obsidian vault
|
|
78
|
+
providers: ProviderRegistry;
|
|
79
|
+
defaultProvider: string; // provider ID used when no per-agent override is set
|
|
80
|
+
|
|
81
|
+
// Optional
|
|
82
|
+
indexDir?: string; // where to store index files
|
|
83
|
+
// default: ~/.bk-agent/rag/enterprise/
|
|
84
|
+
embedder?: Embedder; // custom embedder, default: SimpleEmbedder
|
|
85
|
+
sessionsDir?: string; // per-session memory dir
|
|
86
|
+
// default: ~/.bk-agent/sessions/
|
|
87
|
+
extraProfiles?: AgentProfile[]; // add custom agent profiles alongside the 6 defaults
|
|
88
|
+
maxIterations?: number; // per-engine run limit, default: 20
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Return value
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
interface EnterpriseSetup {
|
|
96
|
+
// Call once at startup (or periodically to pick up new vault notes)
|
|
97
|
+
indexAll(opts?: { verbose?: boolean }): Promise<void>;
|
|
98
|
+
|
|
99
|
+
// Compatible with AgentServer's engineFactory option
|
|
100
|
+
engineFactory: (sessionId: string, transport: Transport) => AgentEngine;
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### What gets created per session
|
|
105
|
+
|
|
106
|
+
Each call to `engineFactory` assembles a fresh, isolated `AgentEngine` with:
|
|
107
|
+
|
|
108
|
+
- One `ObsidianRAGProvider` per department scoped to that department's vault folders
|
|
109
|
+
- One global `ObsidianRAGProvider` for the General agent (all vault folders)
|
|
110
|
+
- A shared `VaultWriter` for persisting knowledge back to the vault
|
|
111
|
+
- All six enterprise `AgentProfile`s plus any `extraProfiles`
|
|
112
|
+
- Per-session episodic, semantic, and procedural `MemorySystem` stored in `sessionsDir/<sessionId>/`
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Enterprise agent profiles
|
|
117
|
+
|
|
118
|
+
Six department profiles are built in. Each has a system prompt, a scoped RAG tool, and an optional provider override for on-prem data isolation.
|
|
119
|
+
|
|
120
|
+
| Profile ID | Agent Name | RAG tool | Default provider |
|
|
121
|
+
|-----------|-----------|----------|-----------------|
|
|
122
|
+
| `general` | General | `search_knowledge` (whole vault) | `defaultProvider` |
|
|
123
|
+
| `support` | Support Agent | `search_support_kb` | `defaultProvider` |
|
|
124
|
+
| `hr` | HR Agent | `search_hr_kb` | `local` (sensitive) |
|
|
125
|
+
| `sales` | Sales Agent | `search_sales_kb` | `defaultProvider` |
|
|
126
|
+
| `finance` | Finance Agent | `search_finance_kb` | `local` (sensitive) |
|
|
127
|
+
| `ops` | Ops Agent | `search_ops_kb` | `defaultProvider` |
|
|
128
|
+
|
|
129
|
+
HR and Finance default to the `local` provider so sensitive payroll and financial data never leaves your network.
|
|
130
|
+
|
|
131
|
+
### Using profiles individually
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import {
|
|
135
|
+
GENERAL_PROFILE,
|
|
136
|
+
SUPPORT_PROFILE,
|
|
137
|
+
HR_PROFILE,
|
|
138
|
+
SALES_PROFILE,
|
|
139
|
+
FINANCE_PROFILE,
|
|
140
|
+
OPS_PROFILE,
|
|
141
|
+
ALL_ENTERPRISE_PROFILES,
|
|
142
|
+
ENTERPRISE_TOOLS,
|
|
143
|
+
VAULT_FOLDERS,
|
|
144
|
+
} from '@backendkit-labs/agent-enterprise';
|
|
145
|
+
|
|
146
|
+
// Register only the profiles you need
|
|
147
|
+
const agents = new AgentRegistry();
|
|
148
|
+
agents.register(GENERAL_PROFILE);
|
|
149
|
+
agents.register(SUPPORT_PROFILE);
|
|
150
|
+
agents.register({ ...HR_PROFILE, provider: 'ollama' }); // force on-prem
|
|
151
|
+
agents.register({ ...FINANCE_PROFILE, provider: 'ollama' }); // force on-prem
|
|
152
|
+
|
|
153
|
+
// Tool name constants
|
|
154
|
+
ENTERPRISE_TOOLS.SEARCH_GENERAL; // 'search_knowledge'
|
|
155
|
+
ENTERPRISE_TOOLS.SEARCH_SUPPORT; // 'search_support_kb'
|
|
156
|
+
ENTERPRISE_TOOLS.SEARCH_HR; // 'search_hr_kb'
|
|
157
|
+
ENTERPRISE_TOOLS.SEARCH_SALES; // 'search_sales_kb'
|
|
158
|
+
ENTERPRISE_TOOLS.SEARCH_FINANCE; // 'search_finance_kb'
|
|
159
|
+
ENTERPRISE_TOOLS.SEARCH_OPS; // 'search_ops_kb'
|
|
160
|
+
ENTERPRISE_TOOLS.WRITE_KNOWLEDGE; // 'write_knowledge' (VaultWriter)
|
|
161
|
+
ENTERPRISE_TOOLS.ASK_AGENT; // 'ask_agent'
|
|
162
|
+
|
|
163
|
+
// Vault folder name constants
|
|
164
|
+
VAULT_FOLDERS.GENERAL; // 'general'
|
|
165
|
+
VAULT_FOLDERS.SUPPORT; // 'soporte'
|
|
166
|
+
VAULT_FOLDERS.HR; // 'rrhh'
|
|
167
|
+
VAULT_FOLDERS.SALES; // 'ventas'
|
|
168
|
+
VAULT_FOLDERS.FINANCE; // 'finanzas'
|
|
169
|
+
VAULT_FOLDERS.OPS; // 'operaciones'
|
|
170
|
+
VAULT_FOLDERS.AGENTS; // 'agentes' (where VaultWriter writes)
|
|
171
|
+
VAULT_FOLDERS.POLICIES; // 'politicas' (indexed by every department)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Adding custom profiles
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
const enterprise = createEnterpriseSetup({
|
|
178
|
+
...opts,
|
|
179
|
+
extraProfiles: [
|
|
180
|
+
{
|
|
181
|
+
id: 'legal',
|
|
182
|
+
name: 'Legal Advisor',
|
|
183
|
+
icon: '⚖️',
|
|
184
|
+
description: 'Answers from the legal knowledge base',
|
|
185
|
+
systemPrompt: `You are a legal knowledge assistant.
|
|
186
|
+
Search the knowledge base for relevant precedents and policies.
|
|
187
|
+
Always recommend consulting a licensed attorney for case-specific advice.`,
|
|
188
|
+
allowedTools: ['search_knowledge'],
|
|
189
|
+
provider: 'local', // sensitive — stays on-prem
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
id: 'engineering',
|
|
193
|
+
name: 'Engineering Assistant',
|
|
194
|
+
icon: '🛠️',
|
|
195
|
+
description: 'Internal technical documentation',
|
|
196
|
+
systemPrompt: 'You are a software engineering assistant with access to our internal technical documentation.',
|
|
197
|
+
allowedTools: ['search_knowledge', 'write_knowledge'],
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## RAG pipeline
|
|
206
|
+
|
|
207
|
+
The RAG pipeline works in two phases: **indexing** and **search**.
|
|
208
|
+
|
|
209
|
+
### Indexing phase
|
|
210
|
+
|
|
211
|
+
At startup, `indexAll()` walks each vault folder, chunks the markdown files, embeds each chunk with the configured embedder, and saves the index to disk as JSON.
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
// Full or incremental re-index (skips unchanged files)
|
|
215
|
+
await enterprise.indexAll({ verbose: true });
|
|
216
|
+
// [VaultIndexer] Indexed 142 files, 1,847 chunks in 4.2s (38 skipped, unchanged)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Search phase
|
|
220
|
+
|
|
221
|
+
When an agent's RAG tool is called, it performs cosine-similarity search over the pre-built index and injects the top-K chunks as context into the LLM prompt:
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
User: "What is our parental leave policy?"
|
|
225
|
+
→ HR agent selected
|
|
226
|
+
→ search_hr_kb("parental leave policy")
|
|
227
|
+
→ Top 5 chunks from rrhh/, general/ and politicas/ returned
|
|
228
|
+
→ LLM answers using the retrieved excerpts as grounding context
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## ObsidianRAGProvider
|
|
234
|
+
|
|
235
|
+
Indexes an Obsidian vault into a vector store and answers searches. It is what `createEnterpriseSetup` uses for every department; use it directly for a custom setup.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import { ObsidianRAGProvider, OllamaEmbedder } from '@backendkit-labs/agent-enterprise';
|
|
239
|
+
|
|
240
|
+
const rag = new ObsidianRAGProvider({
|
|
241
|
+
vaultPath: '/shared/obsidian-vault',
|
|
242
|
+
folders: ['Support', 'General'], // only these folders (omit for the whole vault)
|
|
243
|
+
indexPath: '/cache/support-index.json', // default: ~/.bk-agent/rag/<vault-name>.json
|
|
244
|
+
embedder: new OllamaEmbedder({ model: 'nomic-embed-text' }),
|
|
245
|
+
topK: 5, // default 5
|
|
246
|
+
minScore: 0.3, // default 0.3
|
|
247
|
+
excludeTags: ['skill'], // notes with these frontmatter tags are skipped
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const { indexed, removed } = await rag.index({ verbose: true, onProgress: p => console.log(p.processed, '/', p.total) });
|
|
251
|
+
|
|
252
|
+
const text = await rag.search('How do I reset my password?'); // the best passages, formatted as text for a prompt
|
|
253
|
+
const tool = rag.createTool(); // the `search_knowledge` ToolDefinition
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
`index()` is incremental: it compares modification times and only embeds new or changed notes, and removes the chunks of deleted ones. Run it on start and whenever the vault changes (a cron job, a file watcher). For large vaults, `batchPacingMs`, `stageSize`, `stagePacingMs` and `checkpointIntervalMs` pace the embedding calls and save progress.
|
|
257
|
+
|
|
258
|
+
### VaultIndexer and VectorStore
|
|
259
|
+
|
|
260
|
+
The two pieces under `ObsidianRAGProvider`, for when you bring your own pipeline:
|
|
261
|
+
|
|
262
|
+
- `new VaultIndexer({ vaultPath, folders?, excludeTags?, excludeFiles? })` finds what changed (`getChangedFiles(knownMtimes)`) and splits notes into chunks (`buildChunks(filePath, mtime)`, `toVectorChunk(raw, embedding)`).
|
|
263
|
+
- `new VectorStore(storePath, embeddingModel)` keeps the embedded chunks on disk: `load()`, `addChunks(chunks)`, `removeFile(filePath)`, `search(queryEmbedding, topK)` and `save()`.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## VaultWriter
|
|
268
|
+
|
|
269
|
+
`VaultWriter` lets agents write knowledge back to the vault, closing the loop: the next incremental `index()` makes the note searchable. Notes go to one sub-folder (default `agentes`), with YAML frontmatter, and concurrent writes to the same note are serialised.
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
import { VaultWriter } from '@backendkit-labs/agent-enterprise';
|
|
273
|
+
|
|
274
|
+
const writer = new VaultWriter({ vaultPath: '/shared/obsidian-vault', agentFolder: 'agentes' });
|
|
275
|
+
|
|
276
|
+
// From your own code
|
|
277
|
+
const path = await writer.writeNote({
|
|
278
|
+
title: 'API auth pattern',
|
|
279
|
+
content: '# API auth pattern\n\nAll endpoints require a Bearer token...',
|
|
280
|
+
agentId: 'general',
|
|
281
|
+
tags: ['api', 'security'],
|
|
282
|
+
related_notes: ['General/api-overview'],
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// As a tool for an agent (the agent id is baked in, so the note records who wrote it)
|
|
286
|
+
const writeTool = writer.createTool('general');
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Embedders
|
|
292
|
+
|
|
293
|
+
Embedders convert text into vectors for similarity search.
|
|
294
|
+
|
|
295
|
+
### SimpleEmbedder (default, no external dependencies)
|
|
296
|
+
|
|
297
|
+
A deterministic, hash-based embedder. It needs no API key or service. Less accurate than neural models, but always available:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
import { SimpleEmbedder } from '@backendkit-labs/agent-enterprise';
|
|
301
|
+
|
|
302
|
+
const embedder = new SimpleEmbedder(); // prototyping, CI, air-gapped systems, fallback
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### OllamaEmbedder (recommended for production)
|
|
306
|
+
|
|
307
|
+
Uses a local Ollama embedding model:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
ollama pull nomic-embed-text # general purpose, fast
|
|
311
|
+
ollama pull mxbai-embed-large # higher accuracy
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
import { OllamaEmbedder, createEnterpriseSetup } from '@backendkit-labs/agent-enterprise';
|
|
316
|
+
|
|
317
|
+
const enterprise = createEnterpriseSetup({
|
|
318
|
+
vaultPath: '/vault',
|
|
319
|
+
providers,
|
|
320
|
+
defaultProvider: 'local',
|
|
321
|
+
embedder: new OllamaEmbedder({ host: 'http://localhost:11434', model: 'nomic-embed-text' }),
|
|
322
|
+
});
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Custom embedder
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
import type { Embedder } from '@backendkit-labs/agent-enterprise';
|
|
329
|
+
|
|
330
|
+
class OpenAIEmbedder implements Embedder {
|
|
331
|
+
async embed(texts: string[]): Promise<number[][]> {
|
|
332
|
+
const res = await openai.embeddings.create({ model: 'text-embedding-3-small', input: texts });
|
|
333
|
+
return res.data.map(d => d.embedding);
|
|
334
|
+
}
|
|
335
|
+
async embedOne(text: string): Promise<number[]> {
|
|
336
|
+
return (await this.embed([text]))[0];
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Full production example with AgentServer
|
|
344
|
+
|
|
345
|
+
An on-prem deployment: Ollama embeddings, DeepSeek for general agents, a local model for HR and Finance, Redis sessions, JWT auth and a weekly digest:
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
import { createEnterpriseSetup, OllamaEmbedder } from '@backendkit-labs/agent-enterprise';
|
|
349
|
+
import { ProviderRegistry } from '@backendkit-labs/agent-core';
|
|
350
|
+
import { DeepSeekProvider, OllamaProvider } from '@backendkit-labs/agent-providers';
|
|
351
|
+
import { AgentServer, JwtAuth, TriggerBus, RedisSessionMetaStore } from '@backendkit-labs/agent-web';
|
|
352
|
+
import Redis from 'ioredis';
|
|
353
|
+
|
|
354
|
+
const providers = new ProviderRegistry();
|
|
355
|
+
providers.register('deepseek', new DeepSeekProvider({ apiKey: process.env.DEEPSEEK_API_KEY!, model: 'deepseek-chat' }));
|
|
356
|
+
providers.register('local', new OllamaProvider({ host: 'http://ollama.internal:11434', model: 'llama3.2' })); // sensitive data stays on-prem
|
|
357
|
+
|
|
358
|
+
const enterprise = createEnterpriseSetup({
|
|
359
|
+
vaultPath: process.env.VAULT_PATH!,
|
|
360
|
+
providers,
|
|
361
|
+
defaultProvider: 'deepseek',
|
|
362
|
+
embedder: new OllamaEmbedder({ host: 'http://ollama.internal:11434', model: 'nomic-embed-text' }),
|
|
363
|
+
indexDir: '/var/bk-agent/rag-indexes',
|
|
364
|
+
sessionsDir: '/var/bk-agent/sessions',
|
|
365
|
+
maxIterations: 20,
|
|
366
|
+
});
|
|
367
|
+
await enterprise.indexAll({ verbose: true });
|
|
368
|
+
|
|
369
|
+
const triggers = new TriggerBus(enterprise.engineFactory)
|
|
370
|
+
.addCron({
|
|
371
|
+
name: 'weekly-digest',
|
|
372
|
+
schedule: '0 9 * * 1',
|
|
373
|
+
buildPrompt: () => 'Summarize the knowledge base updates of the past week.',
|
|
374
|
+
onComplete: (result) => intranet.post('/announcements', result.output),
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// Pick up new vault notes every night (incremental: only changed notes are embedded)
|
|
378
|
+
setInterval(() => void enterprise.indexAll(), 24 * 60 * 60 * 1000);
|
|
379
|
+
|
|
380
|
+
const server = new AgentServer({
|
|
381
|
+
port: 3000,
|
|
382
|
+
host: '0.0.0.0',
|
|
383
|
+
engineFactory: enterprise.engineFactory,
|
|
384
|
+
auth: new JwtAuth({ secret: process.env.JWT_SECRET! }).hook(),
|
|
385
|
+
store: new RedisSessionMetaStore(new Redis(process.env.REDIS_URL!)),
|
|
386
|
+
cors: ['https://intranet.company.com'],
|
|
387
|
+
rateLimiting: { max: 30, timeWindow: '1 minute' },
|
|
388
|
+
metrics: { enabled: true },
|
|
389
|
+
maxIdleMs: 30 * 60 * 1000,
|
|
390
|
+
triggers,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
await server.start();
|
|
394
|
+
process.on('SIGTERM', () => server.stop());
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Vault folder structure
|
|
398
|
+
|
|
399
|
+
```
|
|
400
|
+
/obsidian-vault
|
|
401
|
+
├── general/ ← shared by every department
|
|
402
|
+
├── soporte/ ← Support agent
|
|
403
|
+
├── rrhh/ ← HR agent (on-prem LLM)
|
|
404
|
+
├── ventas/ ← Sales agent
|
|
405
|
+
├── finanzas/ ← Finance agent (on-prem LLM)
|
|
406
|
+
├── operaciones/ ← Ops agent
|
|
407
|
+
├── politicas/ ← business policies, indexed by every department
|
|
408
|
+
└── agentes/ ← where VaultWriter (write_knowledge) saves notes
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
The names are the `VAULT_FOLDERS` constants. Each department agent searches only its own folder plus `general/` and `politicas/`, so HR content is not reachable from the Sales agent. The General agent's `search_knowledge` covers the whole vault. The indexer discovers markdown files recursively.
|
package/package.json
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@backendkit-labs/agent-enterprise",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "Business agents with local LLM and RAG knowledge base for agent-core",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"files": [
|
|
8
|
-
"dist/",
|
|
9
|
-
"README.md"
|
|
10
|
-
],
|
|
11
|
-
"scripts": {
|
|
12
|
-
"build": "tsc",
|
|
13
|
-
"dev": "tsc --watch",
|
|
14
|
-
"typecheck": "tsc --noEmit",
|
|
15
|
-
"test": "jest",
|
|
16
|
-
"prepublishOnly": "npm run build"
|
|
17
|
-
},
|
|
18
|
-
"dependencies": {
|
|
19
|
-
"@backendkit-labs/agent-core": "^0.
|
|
20
|
-
"@backendkit-labs/retry": "^0.1.2",
|
|
21
|
-
"yaml": "^2.4.0"
|
|
22
|
-
},
|
|
23
|
-
"optionalDependencies": {
|
|
24
|
-
"bullmq": "^5.0.0"
|
|
25
|
-
},
|
|
26
|
-
"devDependencies": {
|
|
27
|
-
"@types/node": "^20.0.0",
|
|
28
|
-
"@types/jest": "^29.0.0",
|
|
29
|
-
"jest": "^29.0.0",
|
|
30
|
-
"ts-jest": "^29.0.0",
|
|
31
|
-
"typescript": "^5.4.0",
|
|
32
|
-
"bullmq": "^5.0.0"
|
|
33
|
-
},
|
|
34
|
-
"engines": {
|
|
35
|
-
"node": ">=20.0.0"
|
|
36
|
-
},
|
|
37
|
-
"publishConfig": {
|
|
38
|
-
"access": "public"
|
|
39
|
-
}
|
|
40
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@backendkit-labs/agent-enterprise",
|
|
3
|
+
"version": "0.8.9",
|
|
4
|
+
"description": "Business agents with local LLM and RAG knowledge base for agent-core",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsc --watch",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
15
|
+
"test": "jest",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@backendkit-labs/agent-core": "^0.39.0",
|
|
20
|
+
"@backendkit-labs/retry": "^0.1.2",
|
|
21
|
+
"yaml": "^2.4.0"
|
|
22
|
+
},
|
|
23
|
+
"optionalDependencies": {
|
|
24
|
+
"bullmq": "^5.0.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^20.0.0",
|
|
28
|
+
"@types/jest": "^29.0.0",
|
|
29
|
+
"jest": "^29.0.0",
|
|
30
|
+
"ts-jest": "^29.0.0",
|
|
31
|
+
"typescript": "^5.4.0",
|
|
32
|
+
"bullmq": "^5.0.0"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=20.0.0"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
}
|
|
40
|
+
}
|