agentnet 0.0.7 → 0.1.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.
- package/README.md +1 -20
- package/examples/customer-support/agents.yaml +6 -6
- package/examples/customer-support/index.js +3 -3
- package/examples/event-planner/agents.yaml +2 -2
- package/examples/event-planner/index.js +4 -4
- package/examples/smartness/index.js +6 -4
- package/package.json +1 -1
- package/src/agent/runtime.js +2 -1
- package/src/transport/nats.js +11 -11
- package/_OLD_README.md +0 -554
package/README.md
CHANGED
|
@@ -179,25 +179,6 @@ When creating agent definitions, you should specify which API version you're tar
|
|
|
179
179
|
|
|
180
180
|
If you don't specify an `apiVersion`, Agentnet will default to `agentnet/v1alpha1` but will log a warning.
|
|
181
181
|
|
|
182
|
-
#### Version Migration Tool
|
|
183
|
-
|
|
184
|
-
Agentnet includes a command-line tool to help migrate your agent definitions to newer API versions:
|
|
185
|
-
|
|
186
|
-
```bash
|
|
187
|
-
# Migrate a YAML file to the latest version
|
|
188
|
-
node src/tools/migrate-version.js ./agents.yaml
|
|
189
|
-
|
|
190
|
-
# Specify a target version
|
|
191
|
-
node src/tools/migrate-version.js ./agents.yaml --version agentnet.io/v1alpha1
|
|
192
|
-
|
|
193
|
-
# Write to a specific output file
|
|
194
|
-
node src/tools/migrate-version.js ./agents.yaml --output ./agents-new.yaml
|
|
195
|
-
|
|
196
|
-
# Just check if migration is needed without modifying
|
|
197
|
-
node src/tools/migrate-version.js ./agents.yaml --check
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
This tool helps you keep your agent definitions up-to-date with the latest features while maintaining compatibility with the Agentnet platform.
|
|
201
182
|
|
|
202
183
|
### Dynamic Implementation (JavaScript)
|
|
203
184
|
|
|
@@ -286,7 +267,7 @@ const message = new Message({
|
|
|
286
267
|
|
|
287
268
|
// Query the agent using the client
|
|
288
269
|
console.log("Sending query to the entrypoint agent...");
|
|
289
|
-
const response = await client.queryIo(natsIO, 'entrypointAgent', message);
|
|
270
|
+
const response = await client.queryIo(natsIO, 'smartchat.entrypointAgent', message); // {namespace}.{name}
|
|
290
271
|
|
|
291
272
|
// Process the response
|
|
292
273
|
console.log("Agent Response:", response.getContent());
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
apiVersion:
|
|
2
|
+
apiVersion: agentnet/v1alpha1
|
|
3
3
|
kind: AgentDefinition
|
|
4
4
|
metadata:
|
|
5
5
|
name: triageAgent
|
|
@@ -67,7 +67,7 @@ spec:
|
|
|
67
67
|
- queryId
|
|
68
68
|
|
|
69
69
|
---
|
|
70
|
-
apiVersion:
|
|
70
|
+
apiVersion: agentnet/v1alpha1
|
|
71
71
|
kind: AgentDefinition
|
|
72
72
|
metadata:
|
|
73
73
|
name: productAgent
|
|
@@ -141,7 +141,7 @@ spec:
|
|
|
141
141
|
- question
|
|
142
142
|
|
|
143
143
|
---
|
|
144
|
-
apiVersion:
|
|
144
|
+
apiVersion: agentnet/v1alpha1
|
|
145
145
|
kind: AgentDefinition
|
|
146
146
|
metadata:
|
|
147
147
|
name: technicalAgent
|
|
@@ -221,7 +221,7 @@ spec:
|
|
|
221
221
|
- issue
|
|
222
222
|
|
|
223
223
|
---
|
|
224
|
-
apiVersion:
|
|
224
|
+
apiVersion: agentnet/v1alpha1
|
|
225
225
|
kind: AgentDefinition
|
|
226
226
|
metadata:
|
|
227
227
|
name: billingAgent
|
|
@@ -300,7 +300,7 @@ spec:
|
|
|
300
300
|
- question
|
|
301
301
|
|
|
302
302
|
---
|
|
303
|
-
apiVersion:
|
|
303
|
+
apiVersion: agentnet/v1alpha1
|
|
304
304
|
kind: AgentDefinition
|
|
305
305
|
metadata:
|
|
306
306
|
name: escalationAgent
|
|
@@ -380,7 +380,7 @@ spec:
|
|
|
380
380
|
- complexIssue
|
|
381
381
|
|
|
382
382
|
---
|
|
383
|
-
apiVersion:
|
|
383
|
+
apiVersion: agentnet/v1alpha1
|
|
384
384
|
kind: AgentDefinition
|
|
385
385
|
metadata:
|
|
386
386
|
name: followupAgent
|
|
@@ -349,7 +349,7 @@ async function main() {
|
|
|
349
349
|
|
|
350
350
|
// Start with the triage agent
|
|
351
351
|
const client = AgentClient();
|
|
352
|
-
const res = await client.queryIo(natsIO, 'triageAgent', new Message({
|
|
352
|
+
const res = await client.queryIo(natsIO, 'customerSupport.triageAgent', new Message({
|
|
353
353
|
content: customerQuery,
|
|
354
354
|
session: {
|
|
355
355
|
id: caseId,
|
|
@@ -363,7 +363,7 @@ async function main() {
|
|
|
363
363
|
|
|
364
364
|
// For demonstration purposes, let's simulate a complete flow through the technical agent
|
|
365
365
|
console.log("\nRouting to Technical Agent...");
|
|
366
|
-
const res2 = await client.queryIo(natsIO, 'technicalAgent', new Message({
|
|
366
|
+
const res2 = await client.queryIo(natsIO, 'customerSupport.technicalAgent', new Message({
|
|
367
367
|
content: customerQuery,
|
|
368
368
|
session: {
|
|
369
369
|
id: caseId,
|
|
@@ -388,7 +388,7 @@ async function main() {
|
|
|
388
388
|
|
|
389
389
|
// Now follow up with the customer
|
|
390
390
|
console.log("\nFollowing up with customer after resolution...");
|
|
391
|
-
const res3 = await client.queryIo(natsIO, 'followupAgent', new Message({
|
|
391
|
+
const res3 = await client.queryIo(natsIO, 'customerSupport.followupAgent', new Message({
|
|
392
392
|
content: `Please follow up on case ${caseId}`,
|
|
393
393
|
session: {
|
|
394
394
|
id: caseId
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
apiVersion:
|
|
2
|
+
apiVersion: agentnet/v1alpha1
|
|
3
3
|
kind: AgentDefinition
|
|
4
4
|
metadata:
|
|
5
5
|
name: plannerAgent
|
|
@@ -168,7 +168,7 @@ spec:
|
|
|
168
168
|
- request
|
|
169
169
|
|
|
170
170
|
---
|
|
171
|
-
apiVersion:
|
|
171
|
+
apiVersion: agentnet/v1alpha1
|
|
172
172
|
kind: AgentDefinition
|
|
173
173
|
metadata:
|
|
174
174
|
name: eventFinderAgent
|
|
@@ -479,7 +479,7 @@ async function main() {
|
|
|
479
479
|
const createRequest = "I need to schedule a design review meeting next Monday at 2pm for 1 hour with the design team. Today is 11th May 2025.";
|
|
480
480
|
|
|
481
481
|
console.log(`User request: "${createRequest}"`);
|
|
482
|
-
const createResponse = await client.queryIo(natsIO, 'plannerAgent', new Message({
|
|
482
|
+
const createResponse = await client.queryIo(natsIO, 'eventPlanner.plannerAgent', new Message({
|
|
483
483
|
content: createRequest,
|
|
484
484
|
session: {
|
|
485
485
|
id: uuidv4(),
|
|
@@ -495,7 +495,7 @@ async function main() {
|
|
|
495
495
|
const searchRequest = "Show me all meetings with Bob next month. Today is october 2023.";
|
|
496
496
|
|
|
497
497
|
console.log(`User request: "${searchRequest}"`);
|
|
498
|
-
const searchResponse = await client.queryIo(natsIO, 'eventFinderAgent', new Message({
|
|
498
|
+
const searchResponse = await client.queryIo(natsIO, 'eventPlanner.eventFinderAgent', new Message({
|
|
499
499
|
content: searchRequest,
|
|
500
500
|
session: {
|
|
501
501
|
id: uuidv4(),
|
|
@@ -511,7 +511,7 @@ async function main() {
|
|
|
511
511
|
const updateRequest = "Change the Weekly Team Meeting to start at 9:30am instead of 10am.";
|
|
512
512
|
|
|
513
513
|
console.log(`User request: "${updateRequest}"`);
|
|
514
|
-
const updateResponse = await client.queryIo(natsIO, 'plannerAgent', new Message({
|
|
514
|
+
const updateResponse = await client.queryIo(natsIO, 'eventPlanner.plannerAgent', new Message({
|
|
515
515
|
content: updateRequest,
|
|
516
516
|
session: {
|
|
517
517
|
id: uuidv4(),
|
|
@@ -527,7 +527,7 @@ async function main() {
|
|
|
527
527
|
const conflictRequest = "Do I have any scheduling conflicts next week?";
|
|
528
528
|
|
|
529
529
|
console.log(`User request: "${conflictRequest}"`);
|
|
530
|
-
const conflictResponse = await client.queryIo(natsIO, 'eventFinderAgent', new Message({
|
|
530
|
+
const conflictResponse = await client.queryIo(natsIO, 'eventPlanner.eventFinderAgent', new Message({
|
|
531
531
|
content: conflictRequest,
|
|
532
532
|
session: {
|
|
533
533
|
id: uuidv4(),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
1
2
|
import { AgentLoaderFile, AgentClient, NatsIO, Bindings, Message, PostgresStore, RedisStore, MemoryStore } from "../../src/index.js"
|
|
2
3
|
|
|
3
4
|
// NatsIO instance
|
|
@@ -53,23 +54,24 @@ await agentPricing.compile()
|
|
|
53
54
|
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
54
55
|
|
|
55
56
|
// Agent client
|
|
57
|
+
const sessionId = 'f3f4dfe9-3ca2-4e10-956f-e8d19ece5592' //uuidv4()
|
|
56
58
|
const agentClient = AgentClient()
|
|
57
59
|
const message = new Message({
|
|
58
60
|
content: "What rooms do you have from 2025-05-25 to 2025-05-30 for 3 guests For the hotel Flora? Give me the review of the hotel Flora",
|
|
59
61
|
session: {
|
|
60
|
-
id:
|
|
62
|
+
id: sessionId
|
|
61
63
|
}
|
|
62
64
|
})
|
|
63
|
-
const res = await agentClient.queryIo(io, 'entrypoint', message)
|
|
65
|
+
const res = await agentClient.queryIo(io, 'smartexample.entrypoint', message)
|
|
64
66
|
console.log("=======\n", res.getContent())
|
|
65
67
|
console.log("=======\n", res.getSession())
|
|
66
68
|
|
|
67
69
|
const message2 = new Message({
|
|
68
70
|
content: "Quanto costa la camera doppia del Flora per il 10-05-2025 per due persone? Prenotala se costa meno di 100€ la camera double con vista mare per il 10-05-2025 al hotel Flora",
|
|
69
71
|
session: {
|
|
70
|
-
id:
|
|
72
|
+
id: sessionId
|
|
71
73
|
}
|
|
72
74
|
})
|
|
73
|
-
const res2 = await agentClient.queryIo(io, 'entrypoint', message2)
|
|
75
|
+
const res2 = await agentClient.queryIo(io, 'smartexample.entrypoint', message2)
|
|
74
76
|
console.log("=======\n", res2.getContent())
|
|
75
77
|
console.log("=======\n", res2.getSession())
|
package/package.json
CHANGED
package/src/agent/runtime.js
CHANGED
|
@@ -67,7 +67,8 @@ export async function AgentRuntime(agentConfig) {
|
|
|
67
67
|
const content = message.getContent()
|
|
68
68
|
const session = message.getSession()
|
|
69
69
|
const sessionId = message.getSessionId()
|
|
70
|
-
const storeStateSessionId = agentName + "." + sessionId
|
|
70
|
+
const storeStateSessionId = namespace + "." + agentName + "." + sessionId
|
|
71
|
+
console.log("---->", storeStateSessionId)
|
|
71
72
|
|
|
72
73
|
// Load and merge session state and session data
|
|
73
74
|
let storeState = {
|
package/src/transport/nats.js
CHANGED
|
@@ -256,25 +256,24 @@ export class NatsTransport extends Transport {
|
|
|
256
256
|
content: input
|
|
257
257
|
});
|
|
258
258
|
const req = await this.request(
|
|
259
|
-
|
|
259
|
+
network,
|
|
260
260
|
message.serialize(),
|
|
261
261
|
{ timeout: TIMEOUT_TASK_REQUEST }
|
|
262
262
|
);
|
|
263
263
|
return req.string();
|
|
264
264
|
} catch (error) {
|
|
265
265
|
throw new HandoffError(
|
|
266
|
-
`Handoff to agent ${
|
|
267
|
-
|
|
268
|
-
discoveryMessage.agentName,
|
|
266
|
+
`Handoff to agent ${network} failed: ${error.message}`,
|
|
267
|
+
network,
|
|
269
268
|
{ schemaName: schema.name }
|
|
270
269
|
);
|
|
271
270
|
}
|
|
272
271
|
},
|
|
273
272
|
TIMEOUT_TASK_REQUEST,
|
|
274
|
-
`handoff to ${
|
|
273
|
+
`handoff to ${network}`
|
|
275
274
|
);
|
|
276
275
|
} catch (error) {
|
|
277
|
-
logger.error(`Handoff error to ${
|
|
276
|
+
logger.error(`Handoff error to ${network}`, {
|
|
278
277
|
error,
|
|
279
278
|
schema: schema.name
|
|
280
279
|
});
|
|
@@ -319,17 +318,18 @@ export class NatsTransport extends Transport {
|
|
|
319
318
|
* @param {Function} processingFunction - The function to process requests
|
|
320
319
|
* @returns {Promise<void>}
|
|
321
320
|
*/
|
|
322
|
-
async setupTaskHandler(agentName, processingFunction) {
|
|
321
|
+
async setupTaskHandler(namespace, agentName, processingFunction) {
|
|
323
322
|
let taskSub;
|
|
324
323
|
|
|
325
324
|
try {
|
|
326
|
-
|
|
325
|
+
const topic = `${namespace}.${agentName}`;
|
|
326
|
+
taskSub = await this.subscribe(topic, { queue: topic });
|
|
327
327
|
logger.info(`Agent ${agentName} subscribed for task handling`);
|
|
328
328
|
} catch (error) {
|
|
329
329
|
throw new TransportError(
|
|
330
330
|
`Failed to subscribe for task handling: ${error.message}`,
|
|
331
331
|
this.transportType,
|
|
332
|
-
{ agentName }
|
|
332
|
+
{ agentName, topic }
|
|
333
333
|
);
|
|
334
334
|
}
|
|
335
335
|
|
|
@@ -395,7 +395,7 @@ export class NatsTransport extends Transport {
|
|
|
395
395
|
if (this.connected) {
|
|
396
396
|
logger.info('Attempting to resubscribe for task handling');
|
|
397
397
|
try {
|
|
398
|
-
await this.setupTaskHandler(agentName, processingFunction);
|
|
398
|
+
await this.setupTaskHandler(namespace, agentName, processingFunction);
|
|
399
399
|
} catch (resubError) {
|
|
400
400
|
logger.error('Failed to resubscribe for task handling', { error: resubError });
|
|
401
401
|
throw new TransportError(
|
|
@@ -450,7 +450,7 @@ export class NatsTransport extends Transport {
|
|
|
450
450
|
if (typeof fn !== 'function') {
|
|
451
451
|
throw new Error('Task handler must be a function');
|
|
452
452
|
}
|
|
453
|
-
await this.setupTaskHandler(agentName, fn);
|
|
453
|
+
await this.setupTaskHandler(namespace, agentName, fn);
|
|
454
454
|
};
|
|
455
455
|
|
|
456
456
|
return { handleTask, discoveredAgents };
|
package/_OLD_README.md
DELETED
|
@@ -1,554 +0,0 @@
|
|
|
1
|
-
# Agentnet
|
|
2
|
-
|
|
3
|
-
Agentnet is a flexible, extensible library for building and orchestrating LLM-powered agents that can communicate, collaborate, and leverage tools to solve complex tasks. It is specifically designed to develop autonomous networks of agents that can work together to accomplish sophisticated objectives with minimal human intervention.
|
|
4
|
-
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
- [Declarative Agent Definitions](#declarative-agent-definitions)
|
|
8
|
-
- [Static Definitions (YAML)](#static-definitions-yaml)
|
|
9
|
-
- [Dynamic Implementation (JavaScript)](#dynamic-implementation-javascript)
|
|
10
|
-
- [Multi-Agent Systems with YAML](#multi-agent-systems-with-yaml)
|
|
11
|
-
- [Key Features](#key-features)
|
|
12
|
-
- [Quick Start](#quick-start)
|
|
13
|
-
- [Core Concepts](#core-concepts)
|
|
14
|
-
- [Agents](#agents)
|
|
15
|
-
- [Agent Configuration](#agent-configuration)
|
|
16
|
-
- [Tool Binding](#tool-binding)
|
|
17
|
-
- [Transport Mechanisms](#transport-mechanisms)
|
|
18
|
-
- [Agent Auto-Discovery](#agent-auto-discovery)
|
|
19
|
-
- [Agent Handoffs](#agent-handoffs)
|
|
20
|
-
- [Advanced Usage](#advanced-usage)
|
|
21
|
-
- [Events and Hooks](#events-and-hooks)
|
|
22
|
-
- [Multi-Agent Systems](#multi-agent-systems)
|
|
23
|
-
- [Session State Management](#session-state-management)
|
|
24
|
-
- [Installation](#installation)
|
|
25
|
-
- [License](#license)
|
|
26
|
-
|
|
27
|
-
## Declarative Agent Definitions
|
|
28
|
-
|
|
29
|
-
A key feature of Agentnet is the ability to define agents declaratively using YAML files, separating the static definition of agents from their dynamic runtime behavior:
|
|
30
|
-
|
|
31
|
-
### Static Definitions (YAML)
|
|
32
|
-
|
|
33
|
-
Agents can be defined in YAML files that specify:
|
|
34
|
-
- Metadata (name, description)
|
|
35
|
-
- LLM configuration (provider, model, system instructions)
|
|
36
|
-
- Transport mechanisms (NATS, etc.)
|
|
37
|
-
- Tool schemas (name, description, parameters)
|
|
38
|
-
- Discovery schemas for inter-agent communication
|
|
39
|
-
|
|
40
|
-
Example YAML definition:
|
|
41
|
-
|
|
42
|
-
```yaml
|
|
43
|
-
---
|
|
44
|
-
apiVersion: agentnet.io/v1alpha1
|
|
45
|
-
kind: AgentDefinition
|
|
46
|
-
metadata:
|
|
47
|
-
name: bookingAgent
|
|
48
|
-
namespace: smartchat
|
|
49
|
-
spec:
|
|
50
|
-
io:
|
|
51
|
-
- type: NatsIO
|
|
52
|
-
bindings:
|
|
53
|
-
discoveryTopic: smartness.discovery
|
|
54
|
-
acceptedNetworks:
|
|
55
|
-
- "smartchat.*"
|
|
56
|
-
|
|
57
|
-
llm:
|
|
58
|
-
provider: Gemini
|
|
59
|
-
model: gemini-2.0-flash
|
|
60
|
-
systemInstruction: |
|
|
61
|
-
You are a highly advanced booking agent.
|
|
62
|
-
Prioritize clarity and helpfulness.
|
|
63
|
-
Use tools effectively to gather information.
|
|
64
|
-
config:
|
|
65
|
-
temperature: 0.5
|
|
66
|
-
toolConfig:
|
|
67
|
-
functionCallingConfig:
|
|
68
|
-
mode: 'auto'
|
|
69
|
-
|
|
70
|
-
tools:
|
|
71
|
-
- name: bookRoomTool
|
|
72
|
-
description: Book a room to a specific hotel and room.
|
|
73
|
-
parameters:
|
|
74
|
-
type: object
|
|
75
|
-
properties:
|
|
76
|
-
hotelName:
|
|
77
|
-
type: string
|
|
78
|
-
description: The name of the hotel.
|
|
79
|
-
roomName:
|
|
80
|
-
type: string
|
|
81
|
-
description: The name of the room.
|
|
82
|
-
checkinDate:
|
|
83
|
-
type: string
|
|
84
|
-
description: The check-in date.
|
|
85
|
-
checkoutDate:
|
|
86
|
-
type: string
|
|
87
|
-
description: The check-out date.
|
|
88
|
-
required:
|
|
89
|
-
- hotelName
|
|
90
|
-
- roomName
|
|
91
|
-
|
|
92
|
-
discoverySchemas:
|
|
93
|
-
- name: booking_agent_query
|
|
94
|
-
description: Perform a booking to a specific hotel and room.
|
|
95
|
-
parameters:
|
|
96
|
-
type: object
|
|
97
|
-
properties:
|
|
98
|
-
hotelName:
|
|
99
|
-
type: string
|
|
100
|
-
description: The name of the hotel.
|
|
101
|
-
roomName:
|
|
102
|
-
type: string
|
|
103
|
-
description: The name of the room.
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
You can define multiple agents in a single YAML file using YAML document separators (`---`). Each agent can have its own specialized role in a multi-agent system.
|
|
107
|
-
|
|
108
|
-
### Dynamic Implementation (JavaScript)
|
|
109
|
-
|
|
110
|
-
The YAML definitions are loaded at runtime, and tool implementations are dynamically bound using JavaScript:
|
|
111
|
-
|
|
112
|
-
```javascript
|
|
113
|
-
// Load all agents from a YAML file
|
|
114
|
-
const agents = await AgentLoaderFile('./agents.yaml', {
|
|
115
|
-
bindings: { [Bindings.NatsIO]: natsInstance }
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// Access a specific agent
|
|
119
|
-
const bookingAgent = agents.bookingAgent;
|
|
120
|
-
const pricingAgent = agents.pricingAgent;
|
|
121
|
-
|
|
122
|
-
// Bind tool implementations
|
|
123
|
-
bookingAgent.tools.bookRoomTool.bind(async (state, input) => {
|
|
124
|
-
// Actual implementation to book a room
|
|
125
|
-
return {
|
|
126
|
-
confirmation: `Room ${input.roomName} booked at ${input.hotelName}
|
|
127
|
-
from ${input.checkinDate} to ${input.checkoutDate}`
|
|
128
|
-
};
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// Customize prompt/response handling
|
|
132
|
-
bookingAgent.prompt((state, input) => {
|
|
133
|
-
// Pre-process input before sending to LLM
|
|
134
|
-
console.log(`Received booking request: ${input}`);
|
|
135
|
-
return input;
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
bookingAgent.response((state, conversation, result) => {
|
|
139
|
-
// Post-process response from LLM
|
|
140
|
-
return `Booking confirmation: ${result}`;
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// Compile all agents to make them ready for use
|
|
144
|
-
await bookingAgent.compile();
|
|
145
|
-
await pricingAgent.compile();
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
This separation of concerns allows for:
|
|
149
|
-
- Version-controlled agent definitions
|
|
150
|
-
- Reusable tool schemas
|
|
151
|
-
- Dynamic runtime implementation
|
|
152
|
-
- Easy testing and deployment
|
|
153
|
-
- Clear separation between definition and implementation
|
|
154
|
-
|
|
155
|
-
### Multi-Agent Systems with YAML
|
|
156
|
-
|
|
157
|
-
The framework excels at creating autonomous multi-agent systems where specialized agents collaborate with minimal supervision. Each agent in the network can operate independently while maintaining awareness of other agents' capabilities. This creates a resilient, self-organizing system that can tackle complex tasks through agent collaboration. An example setup might include:
|
|
158
|
-
|
|
159
|
-
```javascript
|
|
160
|
-
// Load a set of specialized agents from YAML
|
|
161
|
-
const agents = await AgentLoaderFile('./agents-smartness.yaml', {
|
|
162
|
-
bindings: { [Bindings.NatsIO]: natsIO }
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
// Configure each agent with specific tool implementations
|
|
166
|
-
agents.accomodation.tools.getRoomsListTool.bind(async (state, input) => {
|
|
167
|
-
// Implementation for listing available rooms
|
|
168
|
-
return { rooms: ["Double room with sea view", "Single room with pool view"] };
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
agents.pricing.tools.getPricingTool.bind(async (state, input) => {
|
|
172
|
-
// Implementation for getting room prices
|
|
173
|
-
return { price: "200€ per night" };
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
agents.booking.tools.bookRoomTool.bind(async (state, input) => {
|
|
177
|
-
// Implementation for booking rooms
|
|
178
|
-
return { confirmation: "Booking confirmed" };
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// Compile all agents
|
|
182
|
-
await Promise.all(Object.values(agents).map(agent => agent.compile()));
|
|
183
|
-
|
|
184
|
-
// Wait for agent discovery to complete
|
|
185
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
186
|
-
|
|
187
|
-
// Query the main orchestrator agent
|
|
188
|
-
const client = AgentClient();
|
|
189
|
-
const response = await client.queryIo(
|
|
190
|
-
natsIO,
|
|
191
|
-
'entrypoint',
|
|
192
|
-
"What rooms do you have available for next weekend and how much do they cost?"
|
|
193
|
-
);
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
With this setup, the smartness agent will automatically discover and delegate to specialized agents for accommodation, pricing, and booking, creating an autonomous network that collectively solves the user's query.
|
|
197
|
-
|
|
198
|
-
## Key Features
|
|
199
|
-
|
|
200
|
-
- **Autonomous Agent Networks**: Create self-organizing networks of agents that can discover, communicate, and collaborate with minimal human intervention
|
|
201
|
-
- **Modular Agent Architecture**: Create specialized agents with distinct capabilities and compose them to solve complex problems
|
|
202
|
-
- **Transport Agnostic**: Work with agents directly or through transport mechanisms like NATS
|
|
203
|
-
- **Auto-Discovery**: Agents can discover each other's capabilities dynamically at runtime
|
|
204
|
-
- **Tool Binding**: Easily bind JavaScript functions to agent tools
|
|
205
|
-
- **Agent Handoffs**: Seamlessly delegate tasks between agents
|
|
206
|
-
- **LLM Provider Agnostic**: Support for multiple LLM providers (Gemini and extensible to others)
|
|
207
|
-
- **Persistent Sessions**: Maintain conversation context and state across interactions
|
|
208
|
-
|
|
209
|
-
## Quick Start
|
|
210
|
-
|
|
211
|
-
```javascript
|
|
212
|
-
import { Agent, Gemini, NatsIO } from "agentnet";
|
|
213
|
-
|
|
214
|
-
// Create a simple agent
|
|
215
|
-
const myAgent = Agent()
|
|
216
|
-
.setMetadata({
|
|
217
|
-
name: "myAgent",
|
|
218
|
-
description: "A helpful assistant"
|
|
219
|
-
})
|
|
220
|
-
.withLLM(Gemini, {
|
|
221
|
-
model: "gemini-pro",
|
|
222
|
-
systemInstruction: "You are a helpful assistant"
|
|
223
|
-
})
|
|
224
|
-
.addToolSchema({
|
|
225
|
-
name: "weatherTool",
|
|
226
|
-
description: "Get weather information",
|
|
227
|
-
parameters: {
|
|
228
|
-
location: "string"
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
// Bind tool implementation
|
|
233
|
-
const compiledAgent = await myAgent.compile();
|
|
234
|
-
compiledAgent.tools.weatherTool.bind(async (state, input) => {
|
|
235
|
-
return { weather: "Sunny", temperature: 25 };
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
// Query the agent
|
|
239
|
-
const response = await compiledAgent.query("What's the weather like in Paris?");
|
|
240
|
-
console.log(response);
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
## Core Concepts
|
|
244
|
-
|
|
245
|
-
### Agents
|
|
246
|
-
|
|
247
|
-
Agents are the core building blocks of the framework. Each agent:
|
|
248
|
-
|
|
249
|
-
- Has its own identity (name, description)
|
|
250
|
-
- Can be configured with an LLM provider
|
|
251
|
-
- Can define and implement tools
|
|
252
|
-
- Can discover and communicate with other agents
|
|
253
|
-
|
|
254
|
-
### Agent Configuration
|
|
255
|
-
|
|
256
|
-
Agents can be configured programmatically or via YAML definitions:
|
|
257
|
-
|
|
258
|
-
```javascript
|
|
259
|
-
// Programmatic configuration
|
|
260
|
-
const myAgent = Agent()
|
|
261
|
-
.setMetadata({ name: "myAgent" })
|
|
262
|
-
.withLLM(Gemini, { model: "gemini-pro" })
|
|
263
|
-
.addToolSchema(myToolSchema);
|
|
264
|
-
|
|
265
|
-
// YAML-based configuration
|
|
266
|
-
const agents = await AgentLoaderFile('./agents.yaml', {
|
|
267
|
-
bindings: { [Bindings.NatsIO]: natsInstance }
|
|
268
|
-
});
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
### Tool Binding
|
|
272
|
-
|
|
273
|
-
Tools allow agents to perform actions in the real world. Each tool:
|
|
274
|
-
|
|
275
|
-
- Has a schema that defines its interface (name, description, parameters)
|
|
276
|
-
- Has an implementation bound to it at runtime
|
|
277
|
-
|
|
278
|
-
```javascript
|
|
279
|
-
// Define tool schema
|
|
280
|
-
agent.addToolSchema({
|
|
281
|
-
name: "fetchData",
|
|
282
|
-
description: "Fetch data from an API",
|
|
283
|
-
parameters: {
|
|
284
|
-
url: "string",
|
|
285
|
-
method: "string"
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
// Bind implementation
|
|
290
|
-
agent.tools.fetchData.bind(async (state, input) => {
|
|
291
|
-
// Actual implementation
|
|
292
|
-
const response = await fetch(input.url, { method: input.method });
|
|
293
|
-
return await response.json();
|
|
294
|
-
});
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
### Transport Mechanisms
|
|
298
|
-
|
|
299
|
-
The framework supports different transport mechanisms for agent communication:
|
|
300
|
-
|
|
301
|
-
#### Direct Communication
|
|
302
|
-
|
|
303
|
-
Agents can be queried directly in the same process:
|
|
304
|
-
|
|
305
|
-
```javascript
|
|
306
|
-
const compiledAgent = await myAgent.compile();
|
|
307
|
-
const response = await compiledAgent.query("Hello, agent!");
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
#### NATS-based Communication
|
|
311
|
-
|
|
312
|
-
Agents can communicate through NATS for distributed deployments:
|
|
313
|
-
|
|
314
|
-
```javascript
|
|
315
|
-
// Initialize NATS transport
|
|
316
|
-
const natsIO = NatsIO({ servers: ['nats://localhost:4222'] });
|
|
317
|
-
|
|
318
|
-
// Configure agent with NATS transport
|
|
319
|
-
const myAgent = Agent()
|
|
320
|
-
.setMetadata({ name: "distributedAgent" })
|
|
321
|
-
.addIO(natsIO, {
|
|
322
|
-
bindings: {
|
|
323
|
-
discoveryTopic: "smartness.discovery",
|
|
324
|
-
acceptedNetworks: ["smartchat.*"]
|
|
325
|
-
}
|
|
326
|
-
})
|
|
327
|
-
.withLLM(Gemini, { model: "gemini-pro" });
|
|
328
|
-
|
|
329
|
-
// Query an agent through NATS
|
|
330
|
-
const client = AgentClient();
|
|
331
|
-
const response = await client.queryIo(natsIO, 'distributedAgent', "Hello!");
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
### Agent Auto-Discovery
|
|
335
|
-
|
|
336
|
-
Agents can discover each other's capabilities at runtime through a discovery protocol:
|
|
337
|
-
|
|
338
|
-
1. Agents publish their capabilities (available tools and schemas) to a discovery topic
|
|
339
|
-
2. Other agents subscribe to the discovery topic and build a catalog of available agents
|
|
340
|
-
3. Agents can then delegate tasks to the most appropriate agent
|
|
341
|
-
|
|
342
|
-
```javascript
|
|
343
|
-
// Auto-discovery happens automatically when agents share the same transport
|
|
344
|
-
// Just wait for discovery to complete
|
|
345
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
346
|
-
|
|
347
|
-
// Now agents can communicate with each other
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
### Network Filtering
|
|
351
|
-
|
|
352
|
-
The framework provides a powerful network filtering mechanism that allows agents to control which other agents they communicate with. This is configured through the `acceptedNetworks` property:
|
|
353
|
-
|
|
354
|
-
```javascript
|
|
355
|
-
// Configure agent with network filtering
|
|
356
|
-
const myAgent = Agent()
|
|
357
|
-
.setMetadata({ name: "filteredAgent" })
|
|
358
|
-
.addIO(natsIO, {
|
|
359
|
-
network: "smartchat",
|
|
360
|
-
bindings: {
|
|
361
|
-
discoveryTopic: "smartness.discovery",
|
|
362
|
-
acceptedNetworks: ["smartchat.*", "finance.pricing"]
|
|
363
|
-
}
|
|
364
|
-
})
|
|
365
|
-
.withLLM(Gemini, { model: "gemini-pro" });
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
#### Wildcard Patterns
|
|
369
|
-
|
|
370
|
-
Network filtering supports various wildcard patterns:
|
|
371
|
-
|
|
372
|
-
1. **Full wildcard** (`*.*`): Accept all networks regardless of namespace or name
|
|
373
|
-
2. **Namespace wildcard** (`*.serviceName`): Accept any network with the specified service name, regardless of namespace
|
|
374
|
-
3. **Service wildcard** (`namespace.*`): Accept any service within the specified namespace
|
|
375
|
-
|
|
376
|
-
For example:
|
|
377
|
-
|
|
378
|
-
```yaml
|
|
379
|
-
io:
|
|
380
|
-
- type: NatsIO
|
|
381
|
-
network: smartchat
|
|
382
|
-
bindings:
|
|
383
|
-
discoveryTopic: smartness.discovery
|
|
384
|
-
acceptedNetworks:
|
|
385
|
-
- "smartchat.*" # Accept all services in smartchat namespace
|
|
386
|
-
- "finance.pricing" # Accept only the pricing service in finance namespace
|
|
387
|
-
- "*.analytics" # Accept analytics services from any namespace
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
This filtering system allows for fine-grained control over agent communication, enhancing security and improving the efficiency of the agent network. Agents will only process discovery messages from networks that match their acceptance patterns.
|
|
391
|
-
|
|
392
|
-
### Agent Handoffs
|
|
393
|
-
|
|
394
|
-
Agents can delegate tasks to other agents with the right capabilities:
|
|
395
|
-
|
|
396
|
-
1. An agent receives a task it can't handle directly
|
|
397
|
-
2. It identifies another agent with the required capability
|
|
398
|
-
3. It hands off the task to that agent
|
|
399
|
-
4. The specialized agent processes the task and returns the result
|
|
400
|
-
|
|
401
|
-
This happens transparently from the user's perspective, creating a seamless experience.
|
|
402
|
-
|
|
403
|
-
## Advanced Usage
|
|
404
|
-
|
|
405
|
-
### Events and Hooks
|
|
406
|
-
|
|
407
|
-
Customize agent behavior with event hooks:
|
|
408
|
-
|
|
409
|
-
```javascript
|
|
410
|
-
agent.prompt((state, input) => {
|
|
411
|
-
// Customize input before it reaches the LLM
|
|
412
|
-
return `[Processed] ${input}`;
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
agent.response((state, conversation, result) => {
|
|
416
|
-
// Process the result before returning to the user
|
|
417
|
-
return `Agent says: ${result}`;
|
|
418
|
-
});
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
### Multi-Agent Systems
|
|
422
|
-
|
|
423
|
-
Create autonomous networks of specialized agents that collaborate without human intervention:
|
|
424
|
-
|
|
425
|
-
```javascript
|
|
426
|
-
// Create specialized agents
|
|
427
|
-
const weatherAgent = Agent()
|
|
428
|
-
.setMetadata({ name: "weatherAgent" })
|
|
429
|
-
.addIO(NatsIO({ servers: ['nats://localhost:4222'] }), {
|
|
430
|
-
network: "smartchat",
|
|
431
|
-
bindings: {
|
|
432
|
-
discoveryTopic: "smartness.discovery"
|
|
433
|
-
}
|
|
434
|
-
})
|
|
435
|
-
.addToolSchema(weatherToolSchema);
|
|
436
|
-
|
|
437
|
-
const travelAgent = Agent()
|
|
438
|
-
.setMetadata({ name: "travelAgent" })
|
|
439
|
-
.addIO(NatsIO({ servers: ['nats://localhost:4222'] }), {
|
|
440
|
-
network: "smartchat",
|
|
441
|
-
bindings: {
|
|
442
|
-
discoveryTopic: "smartness.discovery"
|
|
443
|
-
}
|
|
444
|
-
})
|
|
445
|
-
.addToolSchema(travelToolSchema);
|
|
446
|
-
|
|
447
|
-
// The main agent that orchestrates others
|
|
448
|
-
const smartAgent = Agent()
|
|
449
|
-
.setMetadata({ name: "smartAgent" })
|
|
450
|
-
.addIO(NatsIO({ servers: ['nats://localhost:4222'] }), {
|
|
451
|
-
network: "smartchat",
|
|
452
|
-
bindings: {
|
|
453
|
-
discoveryTopic: "smartness.discovery",
|
|
454
|
-
acceptedNetworks: ["smartchat.*"]
|
|
455
|
-
}
|
|
456
|
-
})
|
|
457
|
-
.addDiscoverySchema(weatherDiscoverySchema)
|
|
458
|
-
.addDiscoverySchema(travelDiscoverySchema);
|
|
459
|
-
|
|
460
|
-
// Compile and connect all agents
|
|
461
|
-
await weatherAgent.compile();
|
|
462
|
-
await travelAgent.compile();
|
|
463
|
-
await smartAgent.compile();
|
|
464
|
-
|
|
465
|
-
// Query through the main agent
|
|
466
|
-
const response = await smartAgent.query(
|
|
467
|
-
"Plan a trip to Paris and tell me about the weather"
|
|
468
|
-
);
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
In this autonomous network:
|
|
472
|
-
- Each agent is responsible for a specific domain of expertise
|
|
473
|
-
- The orchestrator agent (smartAgent) discovers and routes requests to appropriate specialists
|
|
474
|
-
- The network can scale by adding more specialized agents without changing existing ones
|
|
475
|
-
- Agents can be deployed across different environments while maintaining communication
|
|
476
|
-
|
|
477
|
-
### Session State Management
|
|
478
|
-
|
|
479
|
-
The Agentnet framework provides robust session management for maintaining state across conversations and agent interactions:
|
|
480
|
-
|
|
481
|
-
```javascript
|
|
482
|
-
// Creating a message with session information
|
|
483
|
-
const message = new Message({
|
|
484
|
-
content: "What rooms do you have available?",
|
|
485
|
-
session: {
|
|
486
|
-
id: "67a71e42-a7d8-1db2-ad17-64e1c8546b21", // Reserved system ID
|
|
487
|
-
propertySetId: "123", // Custom session data
|
|
488
|
-
userPreferences: { roomType: "suite" } // Custom session data
|
|
489
|
-
}
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
// Query the agent with session context
|
|
493
|
-
const result = await agentInstance.query(message);
|
|
494
|
-
```
|
|
495
|
-
|
|
496
|
-
#### Session ID
|
|
497
|
-
|
|
498
|
-
The `id` keyword in the session object is reserved for the system. It's used to uniquely identify the session for:
|
|
499
|
-
- Loading session state from persistent storage
|
|
500
|
-
- Saving session state back to storage
|
|
501
|
-
- Tracking conversation history
|
|
502
|
-
|
|
503
|
-
#### State Propagation
|
|
504
|
-
|
|
505
|
-
Session variables have different scopes:
|
|
506
|
-
|
|
507
|
-
1. **Regular variables** (without underscore prefix) are propagated between agents during handoffs, ensuring continuity of context across the agent system.
|
|
508
|
-
|
|
509
|
-
2. **Private variables** (with underscore prefix `_`) are agent-specific and not shared during handoffs. For example:
|
|
510
|
-
```javascript
|
|
511
|
-
message.session._agentPrivateData = "This stays with the current agent";
|
|
512
|
-
message.session.sharedData = "This is passed between agents";
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
When a session is saved to storage, private variables (starting with `_`) are saved on the agent store, but removed from response to the calling agent to keep the session data clean and focused on shareable information.
|
|
516
|
-
|
|
517
|
-
#### Stores Configuration
|
|
518
|
-
|
|
519
|
-
Agentnet supports different storage backends for persisting session state:
|
|
520
|
-
|
|
521
|
-
```javascript
|
|
522
|
-
// Configure the agent with a Postgres store
|
|
523
|
-
const agents = await AgentLoaderJSON(agentDefinition, {
|
|
524
|
-
bindings: {
|
|
525
|
-
[Bindings.Postgres]: PostgresStore({
|
|
526
|
-
url: "postgres://postgres:postgres@localhost:5432/postgres"
|
|
527
|
-
})
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
// Or with an in-memory store for testing
|
|
532
|
-
const agents = await AgentLoaderJSON(agentDefinition, {
|
|
533
|
-
bindings: {
|
|
534
|
-
[Bindings.Memory]: MemoryStore()
|
|
535
|
-
}
|
|
536
|
-
});
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
#### Session Life Cycle
|
|
540
|
-
|
|
541
|
-
1. When an agent receives a query with a session ID, it attempts to load the existing session state
|
|
542
|
-
2. The state is merged with any new session data provided in the query
|
|
543
|
-
3. The agent processes the query with access to this state
|
|
544
|
-
4. Before responding, the updated state is saved back to storage
|
|
545
|
-
5. Private variables (with `_` prefix) are removed from the response
|
|
546
|
-
|
|
547
|
-
This mechanism allows agents to maintain context across multiple interactions while keeping appropriate boundaries between agent-specific and shared data.
|
|
548
|
-
|
|
549
|
-
## Installation
|
|
550
|
-
|
|
551
|
-
```bash
|
|
552
|
-
npm install agentnet
|
|
553
|
-
```
|
|
554
|
-
|