@statelyai/agent 2.0.0-next.1 → 2.0.0-next.3
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/.changeset/grumpy-dolphins-think.md +17 -0
- package/.changeset/old-teachers-tap.md +5 -0
- package/.changeset/pink-eagles-deliver.md +13 -0
- package/.changeset/pre.json +9 -1
- package/.changeset/quiet-turtles-do.md +7 -0
- package/.changeset/smart-yaks-pull.md +23 -0
- package/.changeset/sweet-clouds-mix.md +16 -0
- package/.changeset/swift-mangos-rush.md +5 -0
- package/.changeset/tough-ways-rhyme.md +5 -0
- package/CHANGELOG.md +79 -0
- package/dist/index.d.mts +116 -100
- package/dist/index.d.ts +116 -100
- package/dist/index.js +102 -72
- package/dist/index.mjs +105 -75
- package/examples/chatbot.ts +2 -2
- package/examples/cot.ts +21 -73
- package/examples/customer-service-sim.ts +3 -3
- package/examples/email.ts +3 -5
- package/examples/example.ts +2 -2
- package/examples/goal.ts +2 -2
- package/examples/joke.ts +12 -12
- package/examples/jugs.ts +4 -7
- package/examples/learn-from-feedback.ts +123 -0
- package/examples/number.ts +2 -2
- package/examples/raffle.ts +2 -2
- package/examples/river-crossing.ts +4 -7
- package/examples/simple.ts +13 -10
- package/examples/summary.ts +2 -5
- package/examples/support.ts +38 -38
- package/examples/ticTacToe.ts +46 -4
- package/examples/todo.ts +3 -3
- package/examples/tutor.ts +2 -2
- package/examples/verify.ts +2 -2
- package/examples/weather-agent.ts +139 -0
- package/examples/weather.ts +26 -23
- package/examples/word.ts +8 -6
- package/package.json +2 -1
- package/src/agent.test.ts +37 -52
- package/src/agent.ts +93 -60
- package/src/decide.test.ts +56 -8
- package/src/decide.ts +42 -32
- package/src/strategies/chainOfThought.ts +50 -0
- package/src/{planners → strategies}/shortestPath.test.ts +4 -7
- package/src/strategies/shortestPath.ts +178 -0
- package/src/{planners → strategies}/simple.ts +25 -26
- package/src/templates/defaultText.ts +3 -0
- package/src/text.ts +13 -13
- package/src/types.ts +124 -83
- package/src/utils.ts +13 -1
- package/src/planners/shortestPath.ts +0 -177
- package/src/strategies/chain-of-note.ts +0 -106
package/src/agent.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Actor,
|
|
3
3
|
ActorRefLike,
|
|
4
|
-
AnyEventObject,
|
|
5
|
-
AnyStateMachine,
|
|
6
4
|
EventObject,
|
|
7
5
|
fromTransition,
|
|
8
6
|
Subscription,
|
|
@@ -11,7 +9,7 @@ import { ZodContextMapping, ZodEventMapping } from './schemas';
|
|
|
11
9
|
import {
|
|
12
10
|
AgentLogic,
|
|
13
11
|
AgentMessage,
|
|
14
|
-
|
|
12
|
+
AgentStrategy,
|
|
15
13
|
EventsFromZodEventMapping,
|
|
16
14
|
GenerateTextOptions,
|
|
17
15
|
AgentLongTermMemory,
|
|
@@ -23,13 +21,12 @@ import {
|
|
|
23
21
|
AgentFeedback,
|
|
24
22
|
AgentMessageInput,
|
|
25
23
|
AgentFeedbackInput,
|
|
26
|
-
|
|
27
|
-
Compute,
|
|
28
|
-
AgentDecisionInput,
|
|
24
|
+
AgentDecision,
|
|
29
25
|
AgentDecideOptions,
|
|
30
26
|
AnyAgent,
|
|
27
|
+
AgentInteractInput,
|
|
31
28
|
} from './types';
|
|
32
|
-
import {
|
|
29
|
+
import { simpleStrategy } from './strategies/simple';
|
|
33
30
|
import { agentDecide } from './decide';
|
|
34
31
|
import { getMachineHash, isActorRef, isMachineActor, randomId } from './utils';
|
|
35
32
|
import {
|
|
@@ -39,7 +36,7 @@ import {
|
|
|
39
36
|
} from 'ai';
|
|
40
37
|
import { createAgentMiddleware } from './middleware';
|
|
41
38
|
|
|
42
|
-
export const agentLogic: AgentLogic<
|
|
39
|
+
export const agentLogic: AgentLogic<any> = fromTransition(
|
|
43
40
|
(state, event, { emit }) => {
|
|
44
41
|
switch (event.type) {
|
|
45
42
|
case 'agent.feedback': {
|
|
@@ -69,12 +66,11 @@ export const agentLogic: AgentLogic<AnyEventObject> = fromTransition(
|
|
|
69
66
|
});
|
|
70
67
|
break;
|
|
71
68
|
}
|
|
72
|
-
case 'agent.
|
|
73
|
-
state.
|
|
69
|
+
case 'agent.decision': {
|
|
70
|
+
state.decisions.push(event.decision);
|
|
74
71
|
emit({
|
|
75
|
-
type: '
|
|
76
|
-
|
|
77
|
-
plan: event.plan,
|
|
72
|
+
type: 'decision',
|
|
73
|
+
decision: event.decision,
|
|
78
74
|
});
|
|
79
75
|
break;
|
|
80
76
|
}
|
|
@@ -91,23 +87,25 @@ export const agentLogic: AgentLogic<AnyEventObject> = fromTransition(
|
|
|
91
87
|
feedback: [],
|
|
92
88
|
messages: [],
|
|
93
89
|
observations: [],
|
|
94
|
-
|
|
95
|
-
} as AgentMemoryContext)
|
|
90
|
+
decisions: [],
|
|
91
|
+
} as AgentMemoryContext<any>)
|
|
96
92
|
);
|
|
97
93
|
|
|
98
94
|
export function createAgent<
|
|
99
95
|
const TContextSchema extends ZodContextMapping,
|
|
100
96
|
const TEventSchemas extends ZodEventMapping,
|
|
101
97
|
TEvents extends EventObject = EventsFromZodEventMapping<TEventSchemas>,
|
|
102
|
-
TContext = ContextFromZodContextMapping<TContextSchema
|
|
98
|
+
TContext = ContextFromZodContextMapping<TContextSchema>,
|
|
99
|
+
TAgent extends AnyAgent = Agent<TContextSchema, TEventSchemas>
|
|
103
100
|
>({
|
|
104
101
|
id,
|
|
105
102
|
description: description,
|
|
106
103
|
model,
|
|
107
104
|
events,
|
|
108
105
|
context,
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
episodeId,
|
|
107
|
+
strategy = simpleStrategy,
|
|
108
|
+
logic = agentLogic as AgentLogic<any>,
|
|
111
109
|
}: {
|
|
112
110
|
/**
|
|
113
111
|
* The unique identifier for the agent.
|
|
@@ -134,28 +132,30 @@ export function createAgent<
|
|
|
134
132
|
*/
|
|
135
133
|
events: TEventSchemas;
|
|
136
134
|
context?: TContextSchema;
|
|
137
|
-
|
|
135
|
+
strategy?: AgentStrategy<Agent<TContextSchema, TEventSchemas>>;
|
|
138
136
|
stringify?: typeof JSON.stringify;
|
|
139
137
|
/**
|
|
140
138
|
* A function that retrieves the agent's long term memory
|
|
141
139
|
*/
|
|
142
140
|
getMemory?: (
|
|
143
141
|
agent: Agent<TContextSchema, TEventSchemas>
|
|
144
|
-
) => AgentLongTermMemory
|
|
142
|
+
) => AgentLongTermMemory<TAgent>;
|
|
145
143
|
/**
|
|
146
144
|
* Agent logic
|
|
147
145
|
*/
|
|
148
|
-
logic?: AgentLogic<
|
|
146
|
+
logic?: AgentLogic<TAgent>;
|
|
149
147
|
model: LanguageModel;
|
|
150
|
-
|
|
148
|
+
episodeId?: string;
|
|
149
|
+
}): Agent<TContextSchema, TEventSchemas> {
|
|
151
150
|
return new Agent({
|
|
152
151
|
id,
|
|
153
152
|
context,
|
|
154
153
|
events,
|
|
155
154
|
description,
|
|
156
|
-
|
|
155
|
+
strategy: strategy,
|
|
157
156
|
model,
|
|
158
157
|
logic,
|
|
158
|
+
episodeId,
|
|
159
159
|
}) as any;
|
|
160
160
|
}
|
|
161
161
|
|
|
@@ -164,7 +164,7 @@ export class Agent<
|
|
|
164
164
|
const TEventSchemas extends ZodEventMapping,
|
|
165
165
|
TEvents extends EventObject = EventsFromZodEventMapping<TEventSchemas>,
|
|
166
166
|
TContext = ContextFromZodContextMapping<TContextSchema>
|
|
167
|
-
> extends Actor<AgentLogic<
|
|
167
|
+
> extends Actor<AgentLogic<any>> {
|
|
168
168
|
/**
|
|
169
169
|
* The name of the agent. All agents with the same name are related and
|
|
170
170
|
* able to share experiences (observations, feedback) with each other.
|
|
@@ -177,43 +177,45 @@ export class Agent<
|
|
|
177
177
|
public description?: string;
|
|
178
178
|
public events: TEventSchemas;
|
|
179
179
|
public context?: TContextSchema;
|
|
180
|
-
public
|
|
181
|
-
public types: {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
};
|
|
180
|
+
public strategy: AgentStrategy<Agent<TContextSchema, TEventSchemas>>;
|
|
181
|
+
// public types: {
|
|
182
|
+
// events: TEvents;
|
|
183
|
+
// context: Compute<TContext>;
|
|
184
|
+
// };
|
|
185
185
|
public model: LanguageModel;
|
|
186
|
-
public memory: AgentLongTermMemory | undefined;
|
|
186
|
+
public memory: AgentLongTermMemory<this> | undefined;
|
|
187
187
|
public defaultOptions: AgentDecideOptions<AnyAgent> | undefined; // todo
|
|
188
188
|
|
|
189
189
|
constructor({
|
|
190
|
-
logic = agentLogic as AgentLogic<
|
|
190
|
+
logic = agentLogic as AgentLogic<any>,
|
|
191
191
|
id,
|
|
192
192
|
name,
|
|
193
193
|
description,
|
|
194
194
|
model,
|
|
195
195
|
events,
|
|
196
196
|
context,
|
|
197
|
-
|
|
197
|
+
episodeId,
|
|
198
|
+
strategy = simpleStrategy,
|
|
198
199
|
}: {
|
|
199
|
-
logic: AgentLogic<
|
|
200
|
+
logic: AgentLogic<any>;
|
|
200
201
|
id?: string;
|
|
201
202
|
name?: string;
|
|
202
203
|
description?: string;
|
|
203
204
|
model: GenerateTextOptions['model'];
|
|
204
205
|
events: TEventSchemas;
|
|
205
206
|
context?: TContextSchema;
|
|
206
|
-
|
|
207
|
+
strategy?: AgentStrategy<Agent<TContextSchema, TEventSchemas>>;
|
|
208
|
+
episodeId?: string;
|
|
207
209
|
}) {
|
|
208
210
|
super(logic);
|
|
209
211
|
this.model = model;
|
|
210
|
-
this.episodeId =
|
|
212
|
+
this.episodeId = episodeId ?? randomId('episode-');
|
|
211
213
|
this.name = name;
|
|
212
214
|
this.description = description;
|
|
213
215
|
this.events = events;
|
|
214
216
|
this.context = context;
|
|
215
|
-
this.
|
|
216
|
-
this.
|
|
217
|
+
this.strategy = strategy;
|
|
218
|
+
this.id = id ?? randomId();
|
|
217
219
|
|
|
218
220
|
this.start();
|
|
219
221
|
}
|
|
@@ -225,6 +227,13 @@ export class Agent<
|
|
|
225
227
|
return this.on('message', (ev) => fn(ev.message));
|
|
226
228
|
}
|
|
227
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Called whenever the agent (LLM assistant) receives some feedback.
|
|
232
|
+
*/
|
|
233
|
+
public onFeedback(fn: (feedback: AgentFeedback) => void) {
|
|
234
|
+
return this.on('feedback', (ev) => fn(ev.feedback));
|
|
235
|
+
}
|
|
236
|
+
|
|
228
237
|
/**
|
|
229
238
|
* Retrieves messages from the agent's short-term (local) memory.
|
|
230
239
|
*/
|
|
@@ -250,8 +259,8 @@ export class Agent<
|
|
|
250
259
|
public addFeedback(feedbackInput: AgentFeedbackInput) {
|
|
251
260
|
const feedback = {
|
|
252
261
|
...feedbackInput,
|
|
262
|
+
comment: feedbackInput.comment ?? undefined,
|
|
253
263
|
attributes: { ...feedbackInput.attributes },
|
|
254
|
-
reward: feedbackInput.reward ?? 0,
|
|
255
264
|
timestamp: feedbackInput.timestamp ?? Date.now(),
|
|
256
265
|
episodeId: this.episodeId,
|
|
257
266
|
} satisfies AgentFeedback;
|
|
@@ -270,7 +279,7 @@ export class Agent<
|
|
|
270
279
|
}
|
|
271
280
|
|
|
272
281
|
public addObservation(
|
|
273
|
-
observationInput: AgentObservationInput
|
|
282
|
+
observationInput: AgentObservationInput<this>
|
|
274
283
|
): AgentObservation<any> {
|
|
275
284
|
const { prevState, event, state } = observationInput;
|
|
276
285
|
const observation = {
|
|
@@ -300,17 +309,17 @@ export class Agent<
|
|
|
300
309
|
return this.getSnapshot().context.observations;
|
|
301
310
|
}
|
|
302
311
|
|
|
303
|
-
public
|
|
312
|
+
public addDecision(decision: AgentDecision<this>) {
|
|
304
313
|
this.send({
|
|
305
|
-
type: 'agent.
|
|
306
|
-
|
|
314
|
+
type: 'agent.decision',
|
|
315
|
+
decision,
|
|
307
316
|
});
|
|
308
317
|
}
|
|
309
318
|
/**
|
|
310
319
|
* Retrieves strategies from the agent's short-term (local) memory.
|
|
311
320
|
*/
|
|
312
|
-
public
|
|
313
|
-
return this.getSnapshot().context.
|
|
321
|
+
public getDecisions() {
|
|
322
|
+
return this.getSnapshot().context.decisions;
|
|
314
323
|
}
|
|
315
324
|
|
|
316
325
|
/**
|
|
@@ -361,36 +370,40 @@ export class Agent<
|
|
|
361
370
|
actorRef: TActor,
|
|
362
371
|
getInput: (
|
|
363
372
|
observation: AgentObservation<TActor>
|
|
364
|
-
) =>
|
|
373
|
+
) => AgentInteractInput<this> | void
|
|
365
374
|
): Subscription;
|
|
366
375
|
public interact<TActor extends ActorRefLike>(
|
|
367
376
|
actorRef: TActor,
|
|
368
377
|
getInput?: (
|
|
369
378
|
observation: AgentObservation<TActor>
|
|
370
|
-
) =>
|
|
379
|
+
) => AgentInteractInput<this> | void
|
|
371
380
|
): Subscription {
|
|
372
381
|
const actorRefCheck = isActorRef(actorRef) && actorRef.src;
|
|
373
382
|
const machine = isMachineActor(actorRef) ? actorRef.src : undefined;
|
|
374
383
|
|
|
375
|
-
let prevState: ObservedState | undefined = undefined;
|
|
384
|
+
let prevState: ObservedState<this> | undefined = undefined;
|
|
376
385
|
let subscribed = true;
|
|
377
386
|
|
|
378
387
|
const agent = this;
|
|
379
388
|
|
|
380
|
-
async function handleObservation(
|
|
389
|
+
async function handleObservation(
|
|
390
|
+
observationInput: AgentObservationInput<any>
|
|
391
|
+
) {
|
|
381
392
|
const observation = agent.addObservation(observationInput);
|
|
382
393
|
|
|
383
|
-
const
|
|
394
|
+
const interactInput = getInput?.(observation);
|
|
384
395
|
|
|
385
|
-
if (
|
|
386
|
-
const
|
|
396
|
+
if (interactInput) {
|
|
397
|
+
const decision = await agentDecide(agent, {
|
|
387
398
|
machine,
|
|
388
399
|
state: observation.state,
|
|
389
|
-
...
|
|
400
|
+
...interactInput,
|
|
390
401
|
});
|
|
391
402
|
|
|
392
|
-
if (
|
|
393
|
-
|
|
403
|
+
if (decision?.nextEvent) {
|
|
404
|
+
// @ts-ignore
|
|
405
|
+
decision.nextEvent['_decision'] = decision.id;
|
|
406
|
+
actorRef.send(decision.nextEvent);
|
|
394
407
|
}
|
|
395
408
|
}
|
|
396
409
|
|
|
@@ -409,12 +422,21 @@ export class Agent<
|
|
|
409
422
|
return;
|
|
410
423
|
}
|
|
411
424
|
|
|
425
|
+
const decisionId = inspEvent.event['_decision'] as
|
|
426
|
+
| string
|
|
427
|
+
| undefined;
|
|
428
|
+
|
|
429
|
+
const decision = decisionId
|
|
430
|
+
? agent.getDecisions().find((d) => d.id === decisionId)
|
|
431
|
+
: undefined;
|
|
432
|
+
|
|
412
433
|
const observationInput = {
|
|
413
434
|
event: inspEvent.event,
|
|
414
435
|
prevState,
|
|
415
436
|
state: inspEvent.snapshot as any,
|
|
416
437
|
machine: (actorRef as any).src,
|
|
417
|
-
|
|
438
|
+
goal: decision?.goal,
|
|
439
|
+
} satisfies AgentObservationInput<any>;
|
|
418
440
|
|
|
419
441
|
await handleObservation(observationInput);
|
|
420
442
|
},
|
|
@@ -425,9 +447,10 @@ export class Agent<
|
|
|
425
447
|
if ((actorRef as any)._processingStatus === 1) {
|
|
426
448
|
handleObservation({
|
|
427
449
|
prevState: undefined,
|
|
428
|
-
event:
|
|
450
|
+
event: undefined,
|
|
429
451
|
state: actorRef.getSnapshot(),
|
|
430
452
|
machine: (actorRef as any).src,
|
|
453
|
+
goal: undefined,
|
|
431
454
|
});
|
|
432
455
|
}
|
|
433
456
|
|
|
@@ -440,7 +463,7 @@ export class Agent<
|
|
|
440
463
|
}
|
|
441
464
|
|
|
442
465
|
public observe<TActor extends ActorRefLike>(actorRef: TActor): Subscription {
|
|
443
|
-
let prevState: ObservedState = actorRef.getSnapshot();
|
|
466
|
+
let prevState: ObservedState<this> = actorRef.getSnapshot();
|
|
444
467
|
const actorRefCheck = isActorRef(actorRef);
|
|
445
468
|
|
|
446
469
|
const sub = actorRefCheck
|
|
@@ -453,12 +476,20 @@ export class Agent<
|
|
|
453
476
|
return;
|
|
454
477
|
}
|
|
455
478
|
|
|
479
|
+
const decisionId = inspEvent.event['_decision'] as
|
|
480
|
+
| string
|
|
481
|
+
| undefined;
|
|
482
|
+
const decision = decisionId
|
|
483
|
+
? this.getDecisions().find((d) => d.id === decisionId)
|
|
484
|
+
: undefined;
|
|
485
|
+
|
|
456
486
|
const observationInput = {
|
|
457
487
|
event: inspEvent.event,
|
|
458
488
|
prevState,
|
|
459
489
|
state: inspEvent.snapshot as any,
|
|
460
490
|
machine: (actorRef as any).src,
|
|
461
|
-
|
|
491
|
+
goal: decision?.goal,
|
|
492
|
+
} satisfies AgentObservationInput<this>;
|
|
462
493
|
|
|
463
494
|
prevState = observationInput.state;
|
|
464
495
|
|
|
@@ -478,14 +509,16 @@ export class Agent<
|
|
|
478
509
|
}
|
|
479
510
|
|
|
480
511
|
/**
|
|
481
|
-
* Resolves with an `
|
|
512
|
+
* Resolves with an `AgentDecision` based on the information provided in the `options`, including:
|
|
482
513
|
*
|
|
483
514
|
* - The `goal` for the agent to achieve
|
|
484
515
|
* - The observed current `state`
|
|
485
516
|
* - The `machine` (e.g. a state machine) that specifies what can happen next
|
|
486
517
|
* - Additional `context`
|
|
487
518
|
*/
|
|
488
|
-
public decide(
|
|
519
|
+
public async decide(
|
|
520
|
+
opts: AgentDecideOptions<this>
|
|
521
|
+
): Promise<AgentDecision<this> | undefined> {
|
|
489
522
|
return agentDecide(this, opts);
|
|
490
523
|
}
|
|
491
524
|
}
|
package/src/decide.test.ts
CHANGED
|
@@ -154,7 +154,7 @@ test('interacts with an actor (late interaction)', async () => {
|
|
|
154
154
|
expect(actor.getSnapshot().value).toBe('third');
|
|
155
155
|
});
|
|
156
156
|
|
|
157
|
-
test('agent.decide() makes a decision based on goal and state (simple
|
|
157
|
+
test('agent.decide() makes a decision based on goal and state (simple strategy)', async () => {
|
|
158
158
|
const model = new MockLanguageModelV1({
|
|
159
159
|
doGenerate,
|
|
160
160
|
});
|
|
@@ -167,7 +167,7 @@ test('agent.decide() makes a decision based on goal and state (simple planner)',
|
|
|
167
167
|
},
|
|
168
168
|
});
|
|
169
169
|
|
|
170
|
-
const
|
|
170
|
+
const decision = await agent.decide({
|
|
171
171
|
goal: 'Make the best move',
|
|
172
172
|
state: {
|
|
173
173
|
value: 'playing',
|
|
@@ -188,8 +188,8 @@ test('agent.decide() makes a decision based on goal and state (simple planner)',
|
|
|
188
188
|
}),
|
|
189
189
|
});
|
|
190
190
|
|
|
191
|
-
expect(
|
|
192
|
-
expect(
|
|
191
|
+
expect(decision).toBeDefined();
|
|
192
|
+
expect(decision!.nextEvent).toEqual(
|
|
193
193
|
expect.objectContaining({
|
|
194
194
|
type: 'MOVE',
|
|
195
195
|
})
|
|
@@ -243,7 +243,7 @@ test.each([
|
|
|
243
243
|
},
|
|
244
244
|
});
|
|
245
245
|
|
|
246
|
-
const
|
|
246
|
+
const decision = await agent.decide({
|
|
247
247
|
goal: 'Make the best move',
|
|
248
248
|
state: {
|
|
249
249
|
value: 'playing',
|
|
@@ -263,10 +263,10 @@ test.each([
|
|
|
263
263
|
});
|
|
264
264
|
|
|
265
265
|
if (!succeed) {
|
|
266
|
-
expect(
|
|
266
|
+
expect(decision).toBeUndefined();
|
|
267
267
|
} else {
|
|
268
|
-
expect(
|
|
269
|
-
expect(
|
|
268
|
+
expect(decision).toBeDefined();
|
|
269
|
+
expect(decision!.nextEvent).toEqual(
|
|
270
270
|
expect.objectContaining({
|
|
271
271
|
type: 'MOVE',
|
|
272
272
|
})
|
|
@@ -274,3 +274,51 @@ test.each([
|
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
276
|
);
|
|
277
|
+
|
|
278
|
+
test.each([['MOVE'], ['FORFEIT']] as const)(
|
|
279
|
+
'agent.decide() respects allowedEvents constraint (event: %s)',
|
|
280
|
+
async (allowedEventType) => {
|
|
281
|
+
const model = new MockLanguageModelV1({
|
|
282
|
+
doGenerate: async (params: LanguageModelV1CallOptions) => {
|
|
283
|
+
const keys =
|
|
284
|
+
params.mode.type === 'regular'
|
|
285
|
+
? params.mode.tools?.map((t) => t.name)
|
|
286
|
+
: [];
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
...dummyResponseValues,
|
|
290
|
+
finishReason: 'tool-calls',
|
|
291
|
+
toolCalls: [
|
|
292
|
+
{
|
|
293
|
+
toolCallType: 'function',
|
|
294
|
+
toolCallId: 'call-1',
|
|
295
|
+
toolName: keys![0],
|
|
296
|
+
args: `{ "type": "${keys?.[0]}" }`,
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
} as any;
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const agent = createAgent({
|
|
304
|
+
id: 'test',
|
|
305
|
+
model,
|
|
306
|
+
events: {
|
|
307
|
+
MOVE: z.object({}),
|
|
308
|
+
SKIP: z.object({}),
|
|
309
|
+
FORFEIT: z.object({}),
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const decision = await agent.decide({
|
|
314
|
+
goal: 'Make the best move',
|
|
315
|
+
state: {
|
|
316
|
+
value: 'playing',
|
|
317
|
+
context: {},
|
|
318
|
+
},
|
|
319
|
+
allowedEvents: [allowedEventType],
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
expect(decision?.nextEvent?.type).toEqual(allowedEventType);
|
|
323
|
+
}
|
|
324
|
+
);
|
package/src/decide.ts
CHANGED
|
@@ -3,65 +3,77 @@ import {
|
|
|
3
3
|
AnyAgent,
|
|
4
4
|
AgentDecideOptions,
|
|
5
5
|
AgentDecisionLogic,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
AgentPlan,
|
|
9
|
-
EventsFromZodEventMapping,
|
|
10
|
-
AgentPlanInput,
|
|
6
|
+
AgentDecision,
|
|
7
|
+
AgentDecideInput,
|
|
11
8
|
TransitionData,
|
|
9
|
+
EventFromAgent,
|
|
12
10
|
} from './types';
|
|
13
|
-
import { simplePlanner } from './planners/simple';
|
|
14
11
|
import { getTransitions } from './utils';
|
|
15
12
|
import { CoreMessage, CoreTool, tool } from 'ai';
|
|
16
13
|
|
|
17
|
-
export async function agentDecide<
|
|
18
|
-
agent:
|
|
19
|
-
options: AgentDecideOptions<
|
|
20
|
-
): Promise<
|
|
14
|
+
export async function agentDecide<TAgent extends AnyAgent>(
|
|
15
|
+
agent: TAgent,
|
|
16
|
+
options: AgentDecideOptions<TAgent>
|
|
17
|
+
): Promise<AgentDecision<TAgent> | undefined> {
|
|
21
18
|
const resolvedOptions = {
|
|
22
19
|
...agent.defaultOptions,
|
|
23
20
|
...options,
|
|
24
21
|
};
|
|
25
22
|
const {
|
|
26
|
-
|
|
23
|
+
strategy = agent.strategy,
|
|
27
24
|
goal,
|
|
25
|
+
allowedEvents,
|
|
28
26
|
events = agent.events,
|
|
29
27
|
state,
|
|
30
28
|
machine,
|
|
31
29
|
model = agent.model,
|
|
32
30
|
messages,
|
|
33
|
-
...
|
|
31
|
+
...otherDecideInput
|
|
34
32
|
} = resolvedOptions;
|
|
35
33
|
|
|
34
|
+
const filteredEventSchemas = allowedEvents
|
|
35
|
+
? Object.fromEntries(
|
|
36
|
+
Object.entries(events).filter(([key]) => {
|
|
37
|
+
return allowedEvents.includes(key);
|
|
38
|
+
})
|
|
39
|
+
)
|
|
40
|
+
: events;
|
|
41
|
+
|
|
36
42
|
let attempts = 0;
|
|
37
43
|
|
|
38
44
|
const maxAttempts = resolvedOptions.maxAttempts ?? 2;
|
|
39
45
|
|
|
40
|
-
let
|
|
46
|
+
let decision: AgentDecision<any> | undefined;
|
|
47
|
+
|
|
48
|
+
const minimalState = {
|
|
49
|
+
value: state.value,
|
|
50
|
+
context: state.context,
|
|
51
|
+
};
|
|
41
52
|
|
|
42
53
|
while (attempts++ < maxAttempts) {
|
|
43
|
-
|
|
54
|
+
decision = await strategy(agent, {
|
|
44
55
|
model,
|
|
45
56
|
goal,
|
|
46
|
-
events,
|
|
47
|
-
state,
|
|
57
|
+
events: filteredEventSchemas,
|
|
58
|
+
state: minimalState,
|
|
48
59
|
machine,
|
|
49
60
|
messages: messages as CoreMessage[], // TODO: fix UIMessage thing
|
|
50
|
-
...
|
|
61
|
+
...otherDecideInput,
|
|
51
62
|
});
|
|
52
63
|
|
|
53
|
-
if (
|
|
54
|
-
agent.
|
|
55
|
-
await resolvedOptions.execute?.(
|
|
64
|
+
if (decision?.nextEvent) {
|
|
65
|
+
agent.addDecision(decision);
|
|
66
|
+
await resolvedOptions.execute?.(decision.nextEvent);
|
|
67
|
+
break;
|
|
56
68
|
}
|
|
57
69
|
}
|
|
58
70
|
|
|
59
|
-
return
|
|
71
|
+
return decision;
|
|
60
72
|
}
|
|
61
73
|
|
|
62
|
-
export function fromDecision(
|
|
63
|
-
agent:
|
|
64
|
-
defaultInput?:
|
|
74
|
+
export function fromDecision<T extends AnyAgent>(
|
|
75
|
+
agent: T,
|
|
76
|
+
defaultInput?: AgentDecideInput<EventFromAgent<T>>
|
|
65
77
|
): AgentDecisionLogic<any> {
|
|
66
78
|
return fromPromise(async ({ input, self }) => {
|
|
67
79
|
const parentRef = self._parent;
|
|
@@ -75,27 +87,25 @@ export function fromDecision(
|
|
|
75
87
|
...defaultInput,
|
|
76
88
|
...inputObject,
|
|
77
89
|
};
|
|
78
|
-
const state = {
|
|
79
|
-
value: snapshot.value,
|
|
80
|
-
context: resolvedInput.context,
|
|
81
|
-
};
|
|
82
90
|
|
|
83
|
-
const
|
|
91
|
+
const decision = await agentDecide(agent, {
|
|
84
92
|
machine: (parentRef as AnyActor).logic,
|
|
85
|
-
state,
|
|
93
|
+
state: snapshot,
|
|
86
94
|
execute: async (event) => {
|
|
87
95
|
parentRef.send(event);
|
|
88
96
|
},
|
|
89
97
|
...resolvedInput,
|
|
98
|
+
// @ts-ignore
|
|
99
|
+
messages: resolvedInput.messages,
|
|
90
100
|
});
|
|
91
101
|
|
|
92
|
-
return
|
|
102
|
+
return decision;
|
|
93
103
|
}) as AgentDecisionLogic<any>;
|
|
94
104
|
}
|
|
95
105
|
|
|
96
106
|
export function getToolMap<T extends AnyAgent>(
|
|
97
107
|
_agent: T,
|
|
98
|
-
input:
|
|
108
|
+
input: AgentDecideInput<any>
|
|
99
109
|
): Record<string, CoreTool<any, any>> | undefined {
|
|
100
110
|
// Get all of the possible next transitions
|
|
101
111
|
const transitions: TransitionData[] = input.machine
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { generateText } from 'ai';
|
|
2
|
+
import {
|
|
3
|
+
AnyAgent,
|
|
4
|
+
AgentDecideInput,
|
|
5
|
+
AgentDecision,
|
|
6
|
+
PromptTemplate,
|
|
7
|
+
} from '../types';
|
|
8
|
+
import { getMessages } from '../text';
|
|
9
|
+
import { simpleStrategy } from './simple';
|
|
10
|
+
import { convertToXml } from '../utils';
|
|
11
|
+
|
|
12
|
+
const chainOfThoughtPromptTemplate: PromptTemplate<any> = ({
|
|
13
|
+
stateValue,
|
|
14
|
+
context,
|
|
15
|
+
goal,
|
|
16
|
+
}) => {
|
|
17
|
+
return `
|
|
18
|
+
${convertToXml({ stateValue, context, goal })}
|
|
19
|
+
|
|
20
|
+
How would you achieve the goal? Think step-by-step.
|
|
21
|
+
`.trim();
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export async function chainOfThoughtStrategy<T extends AnyAgent>(
|
|
25
|
+
agent: T,
|
|
26
|
+
input: AgentDecideInput<any>
|
|
27
|
+
): Promise<AgentDecision<any> | undefined> {
|
|
28
|
+
const prompt = chainOfThoughtPromptTemplate({
|
|
29
|
+
stateValue: input.state.value,
|
|
30
|
+
context: input.context ?? input.state.context,
|
|
31
|
+
goal: input.goal,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const messages = await getMessages(agent, prompt, input);
|
|
35
|
+
|
|
36
|
+
const model = input.model ? agent.wrap(input.model) : agent.model;
|
|
37
|
+
|
|
38
|
+
const result = await generateText({
|
|
39
|
+
model,
|
|
40
|
+
system: input.system ?? agent.description,
|
|
41
|
+
messages,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const decision = await simpleStrategy(agent, {
|
|
45
|
+
...input,
|
|
46
|
+
messages: messages.concat(result.response.messages),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return decision;
|
|
50
|
+
}
|