@memberjunction/ai-agents 3.4.0 → 4.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 +232 -2584
- package/dist/AgentDataPreloader.d.ts +117 -1
- package/dist/AgentDataPreloader.d.ts.map +1 -1
- package/dist/AgentDataPreloader.js +156 -58
- package/dist/AgentDataPreloader.js.map +1 -1
- package/dist/AgentRunner.d.ts +212 -0
- package/dist/AgentRunner.d.ts.map +1 -1
- package/dist/AgentRunner.js +354 -103
- package/dist/AgentRunner.js.map +1 -1
- package/dist/PayloadChangeAnalyzer.d.ts +68 -0
- package/dist/PayloadChangeAnalyzer.d.ts.map +1 -1
- package/dist/PayloadChangeAnalyzer.js +68 -32
- package/dist/PayloadChangeAnalyzer.js.map +1 -1
- package/dist/PayloadFeedbackManager.d.ts +57 -1
- package/dist/PayloadFeedbackManager.d.ts.map +1 -1
- package/dist/PayloadFeedbackManager.js +66 -21
- package/dist/PayloadFeedbackManager.js.map +1 -1
- package/dist/PayloadManager.d.ts +286 -1
- package/dist/PayloadManager.d.ts.map +1 -1
- package/dist/PayloadManager.js +423 -50
- package/dist/PayloadManager.js.map +1 -1
- package/dist/__tests__/action-changes.test.d.ts +13 -0
- package/dist/__tests__/action-changes.test.d.ts.map +1 -1
- package/dist/__tests__/action-changes.test.js +57 -4
- package/dist/__tests__/action-changes.test.js.map +1 -1
- package/dist/__tests__/agent-memory-features.test.d.ts +52 -0
- package/dist/__tests__/agent-memory-features.test.d.ts.map +1 -1
- package/dist/__tests__/agent-memory-features.test.js +96 -13
- package/dist/__tests__/agent-memory-features.test.js.map +1 -1
- package/dist/__tests__/agent-type-prompt-params.test.d.ts +12 -0
- package/dist/__tests__/agent-type-prompt-params.test.d.ts.map +1 -1
- package/dist/__tests__/agent-type-prompt-params.test.js +112 -20
- package/dist/__tests__/agent-type-prompt-params.test.js.map +1 -1
- package/dist/__tests__/chat-handling-option.test.d.ts +26 -0
- package/dist/__tests__/chat-handling-option.test.d.ts.map +1 -1
- package/dist/__tests__/chat-handling-option.test.js +41 -2
- package/dist/__tests__/chat-handling-option.test.js.map +1 -1
- package/dist/agent-context-injector.d.ts +114 -0
- package/dist/agent-context-injector.d.ts.map +1 -1
- package/dist/agent-context-injector.js +138 -19
- package/dist/agent-context-injector.js.map +1 -1
- package/dist/agent-types/base-agent-type.d.ts +354 -0
- package/dist/agent-types/base-agent-type.d.ts.map +1 -1
- package/dist/agent-types/base-agent-type.js +288 -17
- package/dist/agent-types/base-agent-type.js.map +1 -1
- package/dist/agent-types/flow-agent-type.d.ts +328 -2
- package/dist/agent-types/flow-agent-type.d.ts.map +1 -1
- package/dist/agent-types/flow-agent-type.js +503 -63
- package/dist/agent-types/flow-agent-type.js.map +1 -1
- package/dist/agent-types/index.d.ts +14 -3
- package/dist/agent-types/index.d.ts.map +1 -1
- package/dist/agent-types/index.js +14 -23
- package/dist/agent-types/index.js.map +1 -1
- package/dist/agent-types/loop-agent-prompt-params.d.ts +190 -0
- package/dist/agent-types/loop-agent-prompt-params.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-prompt-params.js +23 -6
- package/dist/agent-types/loop-agent-prompt-params.js.map +1 -1
- package/dist/agent-types/loop-agent-response-type.d.ts +62 -0
- package/dist/agent-types/loop-agent-response-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-response-type.js +3 -2
- package/dist/agent-types/loop-agent-response-type.js.map +1 -1
- package/dist/agent-types/loop-agent-type.d.ts +139 -2
- package/dist/agent-types/loop-agent-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-type.js +207 -35
- package/dist/agent-types/loop-agent-type.js.map +1 -1
- package/dist/base-agent.d.ts +1344 -1
- package/dist/base-agent.d.ts.map +1 -1
- package/dist/base-agent.js +2282 -211
- package/dist/base-agent.js.map +1 -1
- package/dist/index.d.ts +24 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -35
- package/dist/index.js.map +1 -1
- package/dist/memory-cleanup-agent.d.ts +53 -1
- package/dist/memory-cleanup-agent.d.ts.map +1 -1
- package/dist/memory-cleanup-agent.js +89 -21
- package/dist/memory-cleanup-agent.js.map +1 -1
- package/dist/memory-manager-agent.d.ts +61 -1
- package/dist/memory-manager-agent.d.ts.map +1 -1
- package/dist/memory-manager-agent.js +260 -116
- package/dist/memory-manager-agent.js.map +1 -1
- package/dist/types/payload-operations.d.ts +51 -0
- package/dist/types/payload-operations.d.ts.map +1 -1
- package/dist/types/payload-operations.js +54 -15
- package/dist/types/payload-operations.js.map +1 -1
- package/dist/utils/ConversationMessageResolver.d.ts +79 -1
- package/dist/utils/ConversationMessageResolver.d.ts.map +1 -1
- package/dist/utils/ConversationMessageResolver.js +99 -9
- package/dist/utils/ConversationMessageResolver.js.map +1 -1
- package/package.json +20 -19
package/README.md
CHANGED
|
@@ -1,76 +1,68 @@
|
|
|
1
1
|
# @memberjunction/ai-agents
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
##
|
|
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
|
-
|
|
48
|
-
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
Advanced payload access control for hierarchical agent execution:
|
|
67
|
-
- Controls which payload paths sub-agents can read (downstream)
|
|
68
|
-
- Controls which payload paths sub-agents can write (upstream)
|
|
69
|
-
- Supports JSON path patterns with wildcards
|
|
70
|
-
- Detects suspicious changes with configurable rules
|
|
71
|
-
- Generates human-readable diffs for audit trails
|
|
72
|
-
- PayloadScope support for narrowing sub-agent data access
|
|
73
|
-
- Transformation for scoped payload merging
|
|
3
|
+
Complete framework for building and executing AI agents in MemberJunction. Provides the `BaseAgent` execution engine, pluggable agent type system (Loop and Flow agents), hierarchical sub-agent orchestration, action execution, memory management with notes and examples, payload management, conversation context with message lifecycle management, and reranker integration.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
graph TD
|
|
9
|
+
subgraph "@memberjunction/ai-agents"
|
|
10
|
+
BA["BaseAgent<br/>Core Execution Engine"]
|
|
11
|
+
style BA fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
12
|
+
|
|
13
|
+
AR["AgentRunner<br/>Orchestration Entry Point"]
|
|
14
|
+
style AR fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
15
|
+
|
|
16
|
+
subgraph "Agent Types"
|
|
17
|
+
BAT["BaseAgentType"]
|
|
18
|
+
style BAT fill:#7c5295,stroke:#563a6b,color:#fff
|
|
19
|
+
LAT["LoopAgentType"]
|
|
20
|
+
style LAT fill:#7c5295,stroke:#563a6b,color:#fff
|
|
21
|
+
FAT["FlowAgentType"]
|
|
22
|
+
style FAT fill:#7c5295,stroke:#563a6b,color:#fff
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
subgraph "Support Systems"
|
|
26
|
+
PM["PayloadManager<br/>Data Flow Between Steps"]
|
|
27
|
+
style PM fill:#b8762f,stroke:#8a5722,color:#fff
|
|
28
|
+
PCA["PayloadChangeAnalyzer"]
|
|
29
|
+
style PCA fill:#b8762f,stroke:#8a5722,color:#fff
|
|
30
|
+
PFM["PayloadFeedbackManager"]
|
|
31
|
+
style PFM fill:#b8762f,stroke:#8a5722,color:#fff
|
|
32
|
+
ACI["AgentContextInjector<br/>Notes, Examples, Data Sources"]
|
|
33
|
+
style ACI fill:#b8762f,stroke:#8a5722,color:#fff
|
|
34
|
+
ADP["AgentDataPreloader<br/>Batch Metadata Loading"]
|
|
35
|
+
style ADP fill:#b8762f,stroke:#8a5722,color:#fff
|
|
36
|
+
MMA["MemoryManagerAgent<br/>Note/Example Management"]
|
|
37
|
+
style MMA fill:#b8762f,stroke:#8a5722,color:#fff
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
BA --> BAT
|
|
42
|
+
BA --> PM
|
|
43
|
+
BA --> ACI
|
|
44
|
+
BA --> ADP
|
|
45
|
+
AR --> BA
|
|
46
|
+
|
|
47
|
+
subgraph Dependencies
|
|
48
|
+
AIP["@memberjunction/ai-prompts<br/>AIPromptRunner"]
|
|
49
|
+
style AIP fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
50
|
+
|
|
51
|
+
AIE["@memberjunction/aiengine<br/>AIEngine"]
|
|
52
|
+
style AIE fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
53
|
+
|
|
54
|
+
ACT["@memberjunction/actions<br/>ActionEngineServer"]
|
|
55
|
+
style ACT fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
56
|
+
|
|
57
|
+
RR["@memberjunction/ai-reranker<br/>RerankerService"]
|
|
58
|
+
style RR fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
AIP --> BA
|
|
62
|
+
AIE --> BA
|
|
63
|
+
ACT --> BA
|
|
64
|
+
RR --> BA
|
|
65
|
+
```
|
|
74
66
|
|
|
75
67
|
## Installation
|
|
76
68
|
|
|
@@ -78,2598 +70,254 @@ Advanced payload access control for hierarchical agent execution:
|
|
|
78
70
|
npm install @memberjunction/ai-agents
|
|
79
71
|
```
|
|
80
72
|
|
|
81
|
-
##
|
|
73
|
+
## Key Components
|
|
82
74
|
|
|
83
|
-
|
|
84
|
-
import { AgentRunner } from '@memberjunction/ai-agents';
|
|
85
|
-
import { UserInfo } from '@memberjunction/core';
|
|
75
|
+
### BaseAgent
|
|
86
76
|
|
|
87
|
-
|
|
88
|
-
const runner = new AgentRunner();
|
|
89
|
-
const result = await runner.RunAgent({
|
|
90
|
-
agent: agentEntity, // AIAgentEntity from database
|
|
91
|
-
conversationMessages: messages,
|
|
92
|
-
contextUser: user
|
|
93
|
-
});
|
|
77
|
+
The core execution engine that all agents use. Handles:
|
|
94
78
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
79
|
+
- Hierarchical prompt execution (agent type's system prompt as parent, agent's prompts as children)
|
|
80
|
+
- Action execution through the MJ Actions framework
|
|
81
|
+
- Sub-agent orchestration with full context propagation
|
|
82
|
+
- Conversation context management with automatic message compaction
|
|
83
|
+
- Memory retrieval (notes and examples) with optional reranking
|
|
84
|
+
- Payload data management across execution steps
|
|
85
|
+
- ForEach and While loop operations
|
|
86
|
+
- Comprehensive execution tracking (AIAgentRun, AIAgentRunStep records)
|
|
102
87
|
|
|
103
|
-
|
|
104
|
-
const followUpResult = await runner.RunAgent({
|
|
105
|
-
agent: agentEntity,
|
|
106
|
-
conversationMessages: newMessages,
|
|
107
|
-
contextUser: user,
|
|
108
|
-
lastRunId: result.agentRun.ID,
|
|
109
|
-
autoPopulateLastRunPayload: true // Automatically use previous run's final payload
|
|
110
|
-
});
|
|
111
|
-
```
|
|
88
|
+
### AgentRunner
|
|
112
89
|
|
|
113
|
-
|
|
90
|
+
High-level entry point for agent execution. Provides:
|
|
114
91
|
|
|
115
|
-
|
|
92
|
+
- Agent resolution by ID or entity reference
|
|
93
|
+
- Permission checking before execution
|
|
94
|
+
- Data preloading for performance
|
|
95
|
+
- Simplified execution interface
|
|
116
96
|
|
|
117
97
|
```typescript
|
|
118
98
|
import { AgentRunner } from '@memberjunction/ai-agents';
|
|
119
|
-
import { UserInfo } from '@memberjunction/core';
|
|
120
99
|
|
|
121
100
|
const runner = new AgentRunner();
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
contextUser: user
|
|
128
|
-
}, {
|
|
129
|
-
// Optional: Use existing conversation
|
|
130
|
-
conversationId: 'existing-conversation-id',
|
|
131
|
-
|
|
132
|
-
// Optional: Use existing conversation detail (skips creation)
|
|
133
|
-
conversationDetailId: 'existing-detail-id',
|
|
134
|
-
|
|
135
|
-
// Required if conversationDetailId not provided
|
|
136
|
-
userMessage: 'Analyze the sales data for Q4',
|
|
137
|
-
|
|
138
|
-
// Optional: Control artifact creation (default: true)
|
|
139
|
-
createArtifacts: true,
|
|
140
|
-
|
|
141
|
-
// Optional: Source artifact for versioning (continuity/refinement)
|
|
142
|
-
sourceArtifactId: 'base-artifact-id',
|
|
143
|
-
|
|
144
|
-
// Optional: Custom conversation name
|
|
145
|
-
conversationName: 'Q4 Sales Analysis'
|
|
101
|
+
const result = await runner.ExecuteAgent({
|
|
102
|
+
agentId: 'agent-uuid',
|
|
103
|
+
conversationMessages: [{ role: 'user', content: 'Analyze Q3 sales trends' }],
|
|
104
|
+
contextUser: currentUser,
|
|
105
|
+
onProgress: (step) => console.log(`${step.step}: ${step.message}`)
|
|
146
106
|
});
|
|
147
|
-
|
|
148
|
-
// Result includes everything you need
|
|
149
|
-
console.log('Agent result:', result.agentResult);
|
|
150
|
-
console.log('Conversation ID:', result.conversationId);
|
|
151
|
-
console.log('Detail ID:', result.conversationDetailId);
|
|
152
|
-
if (result.artifactInfo) {
|
|
153
|
-
console.log('Artifact created:', result.artifactInfo.artifactId);
|
|
154
|
-
console.log('Version:', result.artifactInfo.versionNumber);
|
|
155
|
-
}
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
#### What RunAgentInConversation Does
|
|
159
|
-
|
|
160
|
-
The method provides a complete workflow:
|
|
161
|
-
|
|
162
|
-
1. **Conversation Management**
|
|
163
|
-
- Creates new conversation if not provided
|
|
164
|
-
- Uses existing conversation if `conversationId` provided
|
|
165
|
-
- Skips creation entirely if `conversationDetailId` provided
|
|
166
|
-
|
|
167
|
-
2. **Conversation Detail Creation**
|
|
168
|
-
- Creates conversation detail record for user message
|
|
169
|
-
- Automatically handles message ordering via `__mj_CreatedAt`
|
|
170
|
-
- Skipped if `conversationDetailId` already provided
|
|
171
|
-
|
|
172
|
-
3. **Agent Execution**
|
|
173
|
-
- Runs agent with conversation context
|
|
174
|
-
- Links agent run to conversation detail
|
|
175
|
-
- Passes through all execution parameters (callbacks, data, context, etc.)
|
|
176
|
-
|
|
177
|
-
4. **Artifact Processing** (if `createArtifacts !== false`)
|
|
178
|
-
- Creates artifacts from agent payload
|
|
179
|
-
- Handles intelligent versioning:
|
|
180
|
-
- Uses `sourceArtifactId` if provided (explicit continuity)
|
|
181
|
-
- Otherwise checks for previous artifacts on this conversation detail
|
|
182
|
-
- Creates new artifact version or entirely new artifact as appropriate
|
|
183
|
-
- Respects agent's `ArtifactCreationMode` configuration
|
|
184
|
-
- Links artifacts to conversation details via junction table
|
|
185
|
-
- Extracts artifact names from payload attributes
|
|
186
|
-
|
|
187
|
-
#### Artifact Versioning Logic
|
|
188
|
-
|
|
189
|
-
The method implements smart artifact versioning:
|
|
190
|
-
|
|
191
|
-
```typescript
|
|
192
|
-
// Priority 1: Explicit source artifact (agent continuity/refinement)
|
|
193
|
-
{
|
|
194
|
-
sourceArtifactId: 'artifact-to-refine'
|
|
195
|
-
// Creates version 2, 3, 4, etc. of the specified artifact
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Priority 2: Previous artifact on this conversation detail (fallback)
|
|
199
|
-
// Automatically finds last artifact linked to the conversation detail
|
|
200
|
-
// Creates next version of that artifact
|
|
201
|
-
|
|
202
|
-
// Priority 3: No previous artifact
|
|
203
|
-
// Creates entirely new artifact with version 1
|
|
204
107
|
```
|
|
205
108
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
The method honors agent-level settings:
|
|
109
|
+
### Agent Type System
|
|
209
110
|
|
|
210
|
-
|
|
211
|
-
- **`ArtifactCreationMode === 'System Only'`**: Creates artifact with `Visibility='System Only'`
|
|
212
|
-
- **`DefaultArtifactTypeID`**: Uses agent's preferred artifact type (defaults to JSON type)
|
|
111
|
+
Agents execute using a pluggable type system. The type determines how the agent decides its next action after each LLM call.
|
|
213
112
|
|
|
214
|
-
####
|
|
113
|
+
#### BaseAgentType
|
|
215
114
|
|
|
216
|
-
|
|
217
|
-
```typescript
|
|
218
|
-
// Before: Manually manage conversations, artifacts, notifications
|
|
219
|
-
// After: One method call handles everything
|
|
220
|
-
const result = await runner.RunAgentInConversation({...}, {
|
|
221
|
-
conversationDetailId: args.conversationDetailId,
|
|
222
|
-
createArtifacts: args.createArtifacts,
|
|
223
|
-
sourceArtifactId: args.sourceArtifactId
|
|
224
|
-
});
|
|
225
|
-
```
|
|
115
|
+
Abstract base that all agent types extend. Defines the `DetermineNextStep()` interface that produces a `BaseAgentNextStep` decision:
|
|
226
116
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
117
|
+
| Step | Description |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `Chat` | Send a message back to the user |
|
|
120
|
+
| `Actions` | Execute one or more actions |
|
|
121
|
+
| `SubAgents` | Delegate to sub-agents |
|
|
122
|
+
| `MoreInfo` | Ask the user for additional information |
|
|
123
|
+
| `Retry` | Retry the current step (e.g., after validation failure) |
|
|
124
|
+
| `End` | Complete execution |
|
|
125
|
+
| `ForEach` | Iterate over a collection |
|
|
126
|
+
| `While` | Loop while a condition is true |
|
|
234
127
|
|
|
235
|
-
|
|
236
|
-
const followUp = await runner.RunAgentInConversation({...}, {
|
|
237
|
-
conversationId: firstResult.conversationId,
|
|
238
|
-
userMessage: 'Show me the trends',
|
|
239
|
-
createArtifacts: true
|
|
240
|
-
});
|
|
241
|
-
```
|
|
128
|
+
#### LoopAgentType
|
|
242
129
|
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
// Initial generation
|
|
246
|
-
const initial = await runner.RunAgentInConversation({...}, {
|
|
247
|
-
userMessage: 'Create a report',
|
|
248
|
-
createArtifacts: true
|
|
249
|
-
});
|
|
130
|
+
Conversational agent that runs in a loop: prompt -> decide -> act -> repeat. Best for interactive, chat-based agents. The LLM decides the next step at each iteration by producing a structured JSON response.
|
|
250
131
|
|
|
251
|
-
|
|
252
|
-
const refined = await runner.RunAgentInConversation({...}, {
|
|
253
|
-
userMessage: 'Make it more concise',
|
|
254
|
-
createArtifacts: true,
|
|
255
|
-
sourceArtifactId: initial.artifactInfo.artifactId
|
|
256
|
-
});
|
|
257
|
-
```
|
|
132
|
+
#### FlowAgentType
|
|
258
133
|
|
|
259
|
-
|
|
134
|
+
Step-based agent that follows a predefined flow graph. Each step has explicit paths to the next step based on conditions. Best for deterministic workflows where the execution path is known in advance.
|
|
260
135
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
136
|
+
```mermaid
|
|
137
|
+
graph LR
|
|
138
|
+
subgraph "Loop Agent"
|
|
139
|
+
L1["Prompt LLM"] --> L2["Parse Response"]
|
|
140
|
+
L2 --> L3{Decision}
|
|
141
|
+
L3 -->|"Actions"| L4["Execute Actions"]
|
|
142
|
+
L4 --> L1
|
|
143
|
+
L3 -->|"Chat"| L5["Reply to User"]
|
|
144
|
+
L5 --> L1
|
|
145
|
+
L3 -->|"End"| L6["Complete"]
|
|
146
|
+
end
|
|
267
147
|
|
|
268
|
-
|
|
148
|
+
subgraph "Flow Agent"
|
|
149
|
+
F1["Step 1"] -->|"Path A"| F2["Step 2a"]
|
|
150
|
+
F1 -->|"Path B"| F3["Step 2b"]
|
|
151
|
+
F2 --> F4["Step 3"]
|
|
152
|
+
F3 --> F4
|
|
153
|
+
F4 --> F5["End"]
|
|
154
|
+
end
|
|
269
155
|
|
|
270
|
-
|
|
156
|
+
style L1 fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
157
|
+
style L2 fill:#7c5295,stroke:#563a6b,color:#fff
|
|
158
|
+
style L3 fill:#b8762f,stroke:#8a5722,color:#fff
|
|
159
|
+
style L4 fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
160
|
+
style L5 fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
161
|
+
style L6 fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
271
162
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const previousArtifact = await runner.FindPreviousArtifactForMessage(detailId, user);
|
|
278
|
-
|
|
279
|
-
// Process agent artifacts manually
|
|
280
|
-
const artifactInfo = await runner.ProcessAgentArtifacts(
|
|
281
|
-
agentResult,
|
|
282
|
-
conversationDetailId,
|
|
283
|
-
sourceArtifactId,
|
|
284
|
-
user
|
|
285
|
-
);
|
|
163
|
+
style F1 fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
164
|
+
style F2 fill:#7c5295,stroke:#563a6b,color:#fff
|
|
165
|
+
style F3 fill:#7c5295,stroke:#563a6b,color:#fff
|
|
166
|
+
style F4 fill:#b8762f,stroke:#8a5722,color:#fff
|
|
167
|
+
style F5 fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
286
168
|
```
|
|
287
169
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
Both Flow and Loop agents support native ForEach and While iterations for efficient batch processing and retry logic.
|
|
170
|
+
### PayloadManager
|
|
291
171
|
|
|
292
|
-
|
|
172
|
+
Manages data flow through agent execution:
|
|
293
173
|
|
|
294
|
-
|
|
174
|
+
- Stores key-value data accessible across all steps and sub-agents
|
|
175
|
+
- Supports typed payload changes requested by the LLM
|
|
176
|
+
- Validates and applies changes through PayloadChangeAnalyzer
|
|
177
|
+
- Provides feedback to the LLM about successful/failed changes via PayloadFeedbackManager
|
|
295
178
|
|
|
296
|
-
**Flow Agent - ForEach Example:**
|
|
297
179
|
```typescript
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
forEachStep.LoopBodyType = 'Action';
|
|
302
|
-
forEachStep.ActionID = sendEmailActionId;
|
|
303
|
-
forEachStep.Configuration = JSON.stringify({
|
|
304
|
-
type: 'ForEach',
|
|
305
|
-
collectionPath: 'payload.customers',
|
|
306
|
-
itemVariable: 'customer',
|
|
307
|
-
maxIterations: 500
|
|
308
|
-
});
|
|
309
|
-
forEachStep.ActionInputMapping = JSON.stringify({
|
|
310
|
-
to: 'customer.email',
|
|
311
|
-
subject: 'Welcome!'
|
|
312
|
-
});
|
|
180
|
+
const manager = new PayloadManager();
|
|
181
|
+
manager.Set('customerData', { name: 'Acme', revenue: 1000000 });
|
|
182
|
+
const data = manager.Get('customerData');
|
|
313
183
|
```
|
|
314
184
|
|
|
315
|
-
|
|
316
|
-
```json
|
|
317
|
-
{
|
|
318
|
-
"taskComplete": false,
|
|
319
|
-
"message": "Processing all documents",
|
|
320
|
-
"nextStep": {
|
|
321
|
-
"type": "ForEach",
|
|
322
|
-
"forEach": {
|
|
323
|
-
"collectionPath": "payload.documents",
|
|
324
|
-
"action": {
|
|
325
|
-
"name": "Analyze Document",
|
|
326
|
-
"params": { "path": "item.path" }
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
**Benefits:**
|
|
334
|
-
- 90% token reduction (Loop agents)
|
|
335
|
-
- Deterministic iteration (Flow agents)
|
|
336
|
-
- Type-safe loop variables
|
|
337
|
-
- Built-in error handling
|
|
185
|
+
### AgentContextInjector
|
|
338
186
|
|
|
339
|
-
|
|
187
|
+
Injects contextual information into agent prompts:
|
|
340
188
|
|
|
341
|
-
|
|
189
|
+
- Retrieves relevant notes via vector similarity search
|
|
190
|
+
- Retrieves relevant examples for few-shot learning
|
|
191
|
+
- Injects data source content
|
|
192
|
+
- Applies reranking when configured
|
|
342
193
|
|
|
343
|
-
|
|
194
|
+
### AgentDataPreloader
|
|
344
195
|
|
|
345
|
-
|
|
346
|
-
- `DriverClass`: The TypeScript class implementing the behavior
|
|
347
|
-
- `SystemPromptID`: Base prompt providing foundational behavior
|
|
196
|
+
Optimizes agent startup by batch-loading all required metadata in parallel:
|
|
348
197
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
198
|
+
- Agent entity with all relationships
|
|
199
|
+
- Actions and their parameters
|
|
200
|
+
- Sub-agent data
|
|
201
|
+
- Prompt configurations
|
|
352
202
|
|
|
353
|
-
|
|
354
|
-
- Support for placeholders and dynamic content
|
|
355
|
-
- Can be chained hierarchically
|
|
203
|
+
### MemoryManagerAgent
|
|
356
204
|
|
|
357
|
-
|
|
358
|
-
- `ExecutionOrder`: Determines prompt execution sequence
|
|
359
|
-
- Links agents to their specific prompts
|
|
205
|
+
Handles persistent memory operations for agents:
|
|
360
206
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
- `TimeoutSeconds`: Step execution timeout
|
|
365
|
-
- `ActionOutputMapping`: JSON mapping for action results
|
|
366
|
-
|
|
367
|
-
6. **AIAgentStepPath**: Connects workflow steps
|
|
368
|
-
- `Condition`: Boolean expression for path evaluation
|
|
369
|
-
- `Priority`: Determines path selection order
|
|
370
|
-
|
|
371
|
-
## Creating Custom Agent Types
|
|
372
|
-
|
|
373
|
-
```typescript
|
|
374
|
-
import { BaseAgentType, RegisterClass, BaseAgentNextStep } from '@memberjunction/ai-agents';
|
|
375
|
-
import { AIPromptRunResult } from '@memberjunction/ai-prompts';
|
|
376
|
-
|
|
377
|
-
@RegisterClass(BaseAgentType, "MyCustomAgentType")
|
|
378
|
-
export class MyCustomAgentType extends BaseAgentType {
|
|
379
|
-
async DetermineNextStep(promptResult: AIPromptRunResult): Promise<BaseAgentNextStep> {
|
|
380
|
-
// Parse the prompt result
|
|
381
|
-
const response = JSON.parse(promptResult.FullResult);
|
|
382
|
-
|
|
383
|
-
// Determine next action based on response
|
|
384
|
-
if (response.taskComplete) {
|
|
385
|
-
return { type: 'stop', reason: 'Task completed successfully' };
|
|
386
|
-
} else if (response.action) {
|
|
387
|
-
return {
|
|
388
|
-
type: 'action',
|
|
389
|
-
actionName: response.action.name,
|
|
390
|
-
actionParams: response.action.params
|
|
391
|
-
};
|
|
392
|
-
} else {
|
|
393
|
-
return { type: 'continue' };
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
```
|
|
207
|
+
- Creating and updating agent notes
|
|
208
|
+
- Managing agent examples
|
|
209
|
+
- Scoped memory for multi-tenant deployments (UserScope support)
|
|
398
210
|
|
|
399
|
-
|
|
211
|
+
## Usage
|
|
400
212
|
|
|
401
|
-
|
|
213
|
+
### Basic Agent Execution
|
|
402
214
|
|
|
403
215
|
```typescript
|
|
404
|
-
import {
|
|
405
|
-
|
|
406
|
-
@RegisterClass(BaseAgentType, "StructuredResponseAgent")
|
|
407
|
-
export class StructuredResponseAgent extends BaseAgentType {
|
|
408
|
-
async DetermineNextStep(promptResult: AIPromptRunResult): Promise<BaseAgentNextStep> {
|
|
409
|
-
// For responses with embedded JSON strings
|
|
410
|
-
const outerResponse = promptResult.result as any;
|
|
411
|
-
|
|
412
|
-
// If the response contains an embedded JSON string
|
|
413
|
-
if (outerResponse.response && typeof outerResponse.response === 'string') {
|
|
414
|
-
// Parse the embedded JSON
|
|
415
|
-
const innerData = JSON.parse(outerResponse.response);
|
|
416
|
-
|
|
417
|
-
// Clean validation syntax that AI might have included
|
|
418
|
-
const validator = new JSONValidator();
|
|
419
|
-
const cleanedData = validator.cleanValidationSyntax<any>(innerData);
|
|
420
|
-
|
|
421
|
-
// Now work with cleaned data
|
|
422
|
-
if (cleanedData.analysisComplete) {
|
|
423
|
-
return { type: 'stop', reason: 'Analysis completed' };
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
return { type: 'continue' };
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
**Important Notes:**
|
|
433
|
-
- The AIPromptRunner automatically cleans validation syntax for top-level JSON objects when an OutputExample is defined
|
|
434
|
-
- However, agent types must handle cleaning for **embedded JSON strings** within the response
|
|
435
|
-
- This is common when the prompt response structure contains a JSON string as a field value
|
|
436
|
-
- The `cleanValidationSyntax` method preserves values while removing validation syntax from keys
|
|
216
|
+
import { AgentRunner } from '@memberjunction/ai-agents';
|
|
217
|
+
import { ExecuteAgentParams } from '@memberjunction/ai-core-plus';
|
|
437
218
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
219
|
+
const runner = new AgentRunner();
|
|
220
|
+
const result = await runner.ExecuteAgent({
|
|
221
|
+
agentId: 'my-agent-id',
|
|
222
|
+
conversationMessages: [
|
|
223
|
+
{ role: 'user', content: 'What are the top 5 customers by revenue?' }
|
|
224
|
+
],
|
|
225
|
+
contextUser: currentUser
|
|
226
|
+
});
|
|
445
227
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
"analysisComplete": true,
|
|
449
|
-
"recommendations": ["A", "B", "C"]
|
|
228
|
+
if (result.success) {
|
|
229
|
+
console.log(result.outputMessages);
|
|
450
230
|
}
|
|
451
231
|
```
|
|
452
232
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
1. **Initialization**:
|
|
456
|
-
- Creates AIAgentRun entity for tracking
|
|
457
|
-
- Loads agent configuration and type
|
|
458
|
-
- Initializes AI and Action engines
|
|
233
|
+
### With Sub-Agent Orchestration
|
|
459
234
|
|
|
460
|
-
|
|
461
|
-
- Loads AIAgentType with system prompt
|
|
462
|
-
- Loads agent's prompts ordered by ExecutionOrder
|
|
463
|
-
- Validates placeholders and dependencies
|
|
464
|
-
|
|
465
|
-
3. **Execution Loop**:
|
|
466
|
-
- Executes prompts hierarchically (system as parent)
|
|
467
|
-
- Agent type analyzes results via DetermineNextStep()
|
|
468
|
-
- Executes actions or sub-agents as determined
|
|
469
|
-
- Creates AIAgentRunStep for each operation
|
|
470
|
-
- Continues until stop condition met
|
|
471
|
-
|
|
472
|
-
4. **Result Tracking**:
|
|
473
|
-
- All steps recorded with full context
|
|
474
|
-
- Execution tree available for analysis
|
|
475
|
-
- Errors and outputs captured
|
|
476
|
-
|
|
477
|
-
### Early Run ID Callback
|
|
478
|
-
|
|
479
|
-
Get the AgentRun ID immediately after creation for real-time monitoring:
|
|
235
|
+
Sub-agents are automatically discovered from the agent's relationships and invoked when the LLM requests delegation:
|
|
480
236
|
|
|
481
237
|
```typescript
|
|
482
|
-
const
|
|
483
|
-
|
|
238
|
+
const result = await runner.ExecuteAgent({
|
|
239
|
+
agentId: 'orchestrator-agent-id',
|
|
484
240
|
conversationMessages: messages,
|
|
485
241
|
contextUser: currentUser,
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
console.log(`Agent run started: ${agentRunId}`);
|
|
490
|
-
|
|
491
|
-
// Use cases:
|
|
492
|
-
// - Link to parent records (e.g., AIAgentRunStep.TargetLogID for sub-agents)
|
|
493
|
-
// - Send to monitoring systems
|
|
494
|
-
// - Update UI with tracking info
|
|
495
|
-
// - Start real-time log streaming
|
|
496
|
-
}
|
|
497
|
-
};
|
|
498
|
-
|
|
499
|
-
const result = await runner.RunAgent(params);
|
|
500
|
-
```
|
|
501
|
-
|
|
502
|
-
The callback is invoked:
|
|
503
|
-
- **When**: Right after the AIAgentRun record is created and saved
|
|
504
|
-
- **Before**: The actual agent execution begins
|
|
505
|
-
- **Error Handling**: Callback errors are logged but don't fail the execution
|
|
506
|
-
- **Async Support**: Can be synchronous or asynchronous
|
|
507
|
-
- **Sub-Agent Tracking**: BaseAgent automatically uses this callback to link sub-agent runs to their parent step's TargetLogID
|
|
508
|
-
|
|
509
|
-
## Advanced Features
|
|
510
|
-
|
|
511
|
-
### Agent Data Preloading
|
|
512
|
-
|
|
513
|
-
Agents can declaratively preload reference data without requiring custom application code or action calls. Data sources are configured through the `AIAgentDataSource` entity and automatically loaded before agent execution.
|
|
514
|
-
|
|
515
|
-
#### Overview
|
|
516
|
-
|
|
517
|
-
Data preloading solves the common problem of agents needing access to reference data (like entity lists, configuration values, or initial state) that doesn't change during execution. Instead of:
|
|
518
|
-
- Writing custom application code to load data
|
|
519
|
-
- Having agents call actions to fetch data (which bloats conversation context)
|
|
520
|
-
- Manually passing the same data to every agent invocation
|
|
521
|
-
|
|
522
|
-
Agents can now specify data sources that are automatically loaded and injected into the appropriate destination (`data`, `context`, or `payload`).
|
|
523
|
-
|
|
524
|
-
#### Three Destination Types
|
|
525
|
-
|
|
526
|
-
**1. Data Destination** - For Nunjucks templates in prompts (visible to LLMs)
|
|
527
|
-
```typescript
|
|
528
|
-
// Configuration
|
|
529
|
-
{
|
|
530
|
-
"Name": "ALL_ENTITIES",
|
|
531
|
-
"SourceType": "RunView",
|
|
532
|
-
"EntityName": "Entities",
|
|
533
|
-
"OrderBy": "Name ASC",
|
|
534
|
-
"DestinationType": "Data",
|
|
535
|
-
"DestinationPath": null // Uses "ALL_ENTITIES" at root level
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// Result in agent prompt:
|
|
539
|
-
// params.data.ALL_ENTITIES = [{ Name: "Users", ... }, { Name: "Entities", ... }]
|
|
540
|
-
|
|
541
|
-
// Prompt can use Nunjucks:
|
|
542
|
-
// You have access to {{ALL_ENTITIES.length}} entities:
|
|
543
|
-
// {% for entity in ALL_ENTITIES %}
|
|
544
|
-
// - {{entity.Name}}: {{entity.Description}}
|
|
545
|
-
// {% endfor %}
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
**2. Context Destination** - For actions only (NOT visible to LLMs)
|
|
549
|
-
```typescript
|
|
550
|
-
// Configuration
|
|
551
|
-
{
|
|
552
|
-
"Name": "ORG_SETTINGS",
|
|
553
|
-
"SourceType": "RunView",
|
|
554
|
-
"EntityName": "Organization Settings",
|
|
555
|
-
"ExtraFilter": "OrgID='${context.organizationId}'",
|
|
556
|
-
"DestinationType": "Context",
|
|
557
|
-
"DestinationPath": "organization.settings"
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// Result:
|
|
561
|
-
// params.context.organization.settings = { apiEndpoint: "...", features: [...] }
|
|
562
|
-
|
|
563
|
-
// Actions can access context, but prompts/LLMs cannot
|
|
564
|
-
// This keeps API keys and sensitive configuration away from LLMs
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
**3. Payload Destination** - For agent state initialization
|
|
568
|
-
```typescript
|
|
569
|
-
// Configuration
|
|
570
|
-
{
|
|
571
|
-
"Name": "CustomerOrders",
|
|
572
|
-
"SourceType": "RunQuery",
|
|
573
|
-
"QueryName": "Recent Orders by Customer",
|
|
574
|
-
"Parameters": JSON.stringify({ customerId: "{{context.customerId}}" }),
|
|
575
|
-
"DestinationType": "Payload",
|
|
576
|
-
"DestinationPath": "analysis.orders.recent"
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// Result:
|
|
580
|
-
// params.payload.analysis.orders.recent = [{ OrderID: "123", ... }]
|
|
581
|
-
|
|
582
|
-
// Agent starts with rich initial state without caller manually loading it
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
#### Data Source Types
|
|
586
|
-
|
|
587
|
-
**RunView Data Sources** - Query entities with filters
|
|
588
|
-
```typescript
|
|
589
|
-
{
|
|
590
|
-
"Name": "ACTIVE_MODELS",
|
|
591
|
-
"SourceType": "RunView",
|
|
592
|
-
"EntityName": "AI Models",
|
|
593
|
-
"ExtraFilter": "IsActive=1 AND Vendor='OpenAI'",
|
|
594
|
-
"OrderBy": "Priority DESC",
|
|
595
|
-
"FieldsToRetrieve": JSON.stringify(["ID", "Name", "Vendor", "MaxInputTokens"]),
|
|
596
|
-
"ResultType": "simple", // or "entity_object"
|
|
597
|
-
"MaxRows": 100,
|
|
598
|
-
"DestinationType": "Data"
|
|
599
|
-
}
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
**RunQuery Data Sources** - Execute stored queries
|
|
603
|
-
```typescript
|
|
604
|
-
{
|
|
605
|
-
"Name": "MONTHLY_STATS",
|
|
606
|
-
"SourceType": "RunQuery",
|
|
607
|
-
"QueryName": "Monthly Analytics",
|
|
608
|
-
"CategoryPath": "/Reports/Analytics",
|
|
609
|
-
"Parameters": JSON.stringify({
|
|
610
|
-
month: "{{context.currentMonth}}",
|
|
611
|
-
year: "{{context.currentYear}}"
|
|
612
|
-
}),
|
|
613
|
-
"DestinationType": "Payload",
|
|
614
|
-
"DestinationPath": "stats.monthly"
|
|
615
|
-
}
|
|
616
|
-
```
|
|
617
|
-
|
|
618
|
-
#### Path Support
|
|
619
|
-
|
|
620
|
-
The `DestinationPath` field supports nested paths using dot notation:
|
|
621
|
-
|
|
622
|
-
```typescript
|
|
623
|
-
// Simple root-level
|
|
624
|
-
{
|
|
625
|
-
"Name": "ENTITIES",
|
|
626
|
-
"DestinationPath": null // Uses "ENTITIES" at root
|
|
627
|
-
}
|
|
628
|
-
// Result: data.ENTITIES
|
|
629
|
-
|
|
630
|
-
// Nested paths
|
|
631
|
-
{
|
|
632
|
-
"Name": "ModelList",
|
|
633
|
-
"DestinationPath": "config.ai.models"
|
|
634
|
-
}
|
|
635
|
-
// Result: data.config.ai.models
|
|
636
|
-
|
|
637
|
-
// Deep nesting
|
|
638
|
-
{
|
|
639
|
-
"Name": "CustomerData",
|
|
640
|
-
"DestinationPath": "analysis.customer.profile.orders"
|
|
641
|
-
}
|
|
642
|
-
// Result: payload.analysis.customer.profile.orders
|
|
643
|
-
```
|
|
644
|
-
|
|
645
|
-
#### Caching Policies
|
|
646
|
-
|
|
647
|
-
Data sources support three caching strategies:
|
|
648
|
-
|
|
649
|
-
**1. None** - No caching (default)
|
|
650
|
-
```typescript
|
|
651
|
-
{
|
|
652
|
-
"CachePolicy": "None"
|
|
653
|
-
// Data is loaded fresh every time
|
|
654
|
-
}
|
|
655
|
-
```
|
|
656
|
-
|
|
657
|
-
**2. PerRun** - Cache for duration of a single agent run
|
|
658
|
-
```typescript
|
|
659
|
-
{
|
|
660
|
-
"CachePolicy": "PerRun"
|
|
661
|
-
// Multiple data sources with same AgentID+Name share cached data within one run
|
|
662
|
-
// Cache is cleared when agent run completes
|
|
663
|
-
}
|
|
664
|
-
```
|
|
665
|
-
|
|
666
|
-
**3. PerAgent** - Global cache with TTL
|
|
667
|
-
```typescript
|
|
668
|
-
{
|
|
669
|
-
"CachePolicy": "PerAgent",
|
|
670
|
-
"CacheTimeoutSeconds": 3600 // 1 hour
|
|
671
|
-
// Cached across all runs for this agent until TTL expires
|
|
672
|
-
// Good for rarely-changing reference data like entity lists
|
|
673
|
-
}
|
|
674
|
-
```
|
|
675
|
-
|
|
676
|
-
#### Execution Control
|
|
677
|
-
|
|
678
|
-
**Disable data preloading** for specific executions:
|
|
679
|
-
```typescript
|
|
680
|
-
const result = await runner.RunAgent({
|
|
681
|
-
agent: myAgent,
|
|
682
|
-
conversationMessages: messages,
|
|
683
|
-
contextUser: user,
|
|
684
|
-
disableDataPreloading: true // Skip automatic data preloading
|
|
685
|
-
});
|
|
686
|
-
```
|
|
687
|
-
|
|
688
|
-
**Caller precedence**: Caller-provided data always takes precedence over preloaded data:
|
|
689
|
-
```typescript
|
|
690
|
-
const result = await runner.RunAgent({
|
|
691
|
-
agent: myAgent,
|
|
692
|
-
conversationMessages: messages,
|
|
693
|
-
contextUser: user,
|
|
694
|
-
data: {
|
|
695
|
-
CUSTOM_ENTITIES: myEntities // Overrides preloaded CUSTOM_ENTITIES
|
|
242
|
+
onProgress: (step) => {
|
|
243
|
+
// Track execution across agent hierarchy
|
|
244
|
+
console.log(`[${step.agentName}] ${step.message}`);
|
|
696
245
|
}
|
|
697
246
|
});
|
|
698
247
|
```
|
|
699
248
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
**Database Research Agent** - Preload entity metadata
|
|
703
|
-
```typescript
|
|
704
|
-
// Data source 1: All entities for reference
|
|
705
|
-
{
|
|
706
|
-
"AgentID": "database-research-agent-id",
|
|
707
|
-
"Name": "ALL_ENTITIES",
|
|
708
|
-
"SourceType": "RunView",
|
|
709
|
-
"EntityName": "Entities",
|
|
710
|
-
"OrderBy": "Name ASC",
|
|
711
|
-
"FieldsToRetrieve": JSON.stringify(["ID", "Name", "SchemaName", "Description", "BaseView"]),
|
|
712
|
-
"DestinationType": "Data",
|
|
713
|
-
"ExecutionOrder": 1,
|
|
714
|
-
"Status": "Active",
|
|
715
|
-
"CachePolicy": "PerAgent",
|
|
716
|
-
"CacheTimeoutSeconds": 3600
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
// Data source 2: Schema information
|
|
720
|
-
{
|
|
721
|
-
"AgentID": "database-research-agent-id",
|
|
722
|
-
"Name": "SCHEMA_INFO",
|
|
723
|
-
"SourceType": "RunView",
|
|
724
|
-
"EntityName": "Entity Fields",
|
|
725
|
-
"DestinationType": "Data",
|
|
726
|
-
"DestinationPath": "schema.fields",
|
|
727
|
-
"ExecutionOrder": 2,
|
|
728
|
-
"Status": "Active",
|
|
729
|
-
"CachePolicy": "PerAgent",
|
|
730
|
-
"CacheTimeoutSeconds": 3600
|
|
731
|
-
}
|
|
732
|
-
```
|
|
733
|
-
|
|
734
|
-
**Customer Service Agent** - Preload customer context
|
|
735
|
-
```typescript
|
|
736
|
-
// Preload customer data into payload
|
|
737
|
-
{
|
|
738
|
-
"AgentID": "customer-service-agent-id",
|
|
739
|
-
"Name": "CUSTOMER_PROFILE",
|
|
740
|
-
"SourceType": "RunView",
|
|
741
|
-
"EntityName": "Customers",
|
|
742
|
-
"ExtraFilter": "ID='{{context.customerId}}'",
|
|
743
|
-
"DestinationType": "Payload",
|
|
744
|
-
"DestinationPath": "customer.profile",
|
|
745
|
-
"Status": "Active",
|
|
746
|
-
"CachePolicy": "PerRun"
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
// Preload recent orders
|
|
750
|
-
{
|
|
751
|
-
"AgentID": "customer-service-agent-id",
|
|
752
|
-
"Name": "RECENT_ORDERS",
|
|
753
|
-
"SourceType": "RunQuery",
|
|
754
|
-
"QueryName": "Recent Orders by Customer",
|
|
755
|
-
"Parameters": JSON.stringify({ customerId: "{{context.customerId}}", days: 30 }),
|
|
756
|
-
"DestinationType": "Payload",
|
|
757
|
-
"DestinationPath": "customer.orders",
|
|
758
|
-
"Status": "Active",
|
|
759
|
-
"CachePolicy": "PerRun"
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
// Preload organization settings (for actions)
|
|
763
|
-
{
|
|
764
|
-
"AgentID": "customer-service-agent-id",
|
|
765
|
-
"Name": "ORG_CONFIG",
|
|
766
|
-
"SourceType": "RunView",
|
|
767
|
-
"EntityName": "Organization Settings",
|
|
768
|
-
"ExtraFilter": "OrgID='{{context.organizationId}}'",
|
|
769
|
-
"DestinationType": "Context",
|
|
770
|
-
"DestinationPath": "organization.config",
|
|
771
|
-
"Status": "Active",
|
|
772
|
-
"CachePolicy": "PerAgent",
|
|
773
|
-
"CacheTimeoutSeconds": 1800
|
|
774
|
-
}
|
|
775
|
-
```
|
|
776
|
-
|
|
777
|
-
#### Benefits
|
|
778
|
-
|
|
779
|
-
- **Declarative**: Configure data preloading through metadata, not code
|
|
780
|
-
- **Reusable**: Same agent works across different environments
|
|
781
|
-
- **Efficient**: Caching reduces redundant database queries
|
|
782
|
-
- **Clean Separation**: Keeps data in appropriate destinations (data/context/payload)
|
|
783
|
-
- **Flexible**: Supports both RunView and RunQuery with full parameter control
|
|
784
|
-
- **Secure**: Context destination keeps sensitive data away from LLMs
|
|
785
|
-
- **Performance**: Multiple caching strategies for different use cases
|
|
786
|
-
|
|
787
|
-
#### Database Schema
|
|
788
|
-
|
|
789
|
-
The `AIAgentDataSource` table includes:
|
|
790
|
-
- **AgentID**: The agent using this data source
|
|
791
|
-
- **Name**: Variable name (used as fallback if DestinationPath is null)
|
|
792
|
-
- **SourceType**: RunView or RunQuery
|
|
793
|
-
- **EntityName**, **ExtraFilter**, **OrderBy**, **FieldsToRetrieve**, **ResultType**: RunView parameters
|
|
794
|
-
- **QueryName**, **CategoryPath**, **Parameters**: RunQuery parameters
|
|
795
|
-
- **MaxRows**: Limit results (applies to both source types)
|
|
796
|
-
- **DestinationType**: Data, Context, or Payload
|
|
797
|
-
- **DestinationPath**: Nested path using dot notation (optional)
|
|
798
|
-
- **ExecutionOrder**: Order to execute when multiple sources exist
|
|
799
|
-
- **Status**: Active or Disabled
|
|
800
|
-
- **CachePolicy**: None, PerRun, or PerAgent
|
|
801
|
-
- **CacheTimeoutSeconds**: TTL for PerAgent cache
|
|
802
|
-
|
|
803
|
-
**Unique Constraint**: `AgentID + Name + DestinationType + DestinationPath`
|
|
804
|
-
- Allows same Name across different destinations/paths
|
|
805
|
-
- Example: "ENTITIES" can exist in both Data and Payload destinations
|
|
806
|
-
|
|
807
|
-
### Runtime Action Changes (v2.123.0)
|
|
808
|
-
|
|
809
|
-
The framework supports dynamic customization of which actions are available to agents at runtime, without modifying database configuration. This is particularly useful for:
|
|
810
|
-
|
|
811
|
-
- **Multi-tenant scenarios** where different executions need different integrations
|
|
812
|
-
- **Security restrictions** where sub-agents should have limited action access
|
|
813
|
-
- **Testing scenarios** with controlled action availability
|
|
814
|
-
|
|
815
|
-
#### ActionChange Interface
|
|
816
|
-
|
|
817
|
-
```typescript
|
|
818
|
-
interface ActionChange {
|
|
819
|
-
scope: ActionChangeScope; // Which agents to apply to
|
|
820
|
-
mode: ActionChangeMode; // 'add' or 'remove'
|
|
821
|
-
actionIds: string[]; // Action entity IDs to add/remove
|
|
822
|
-
agentIds?: string[]; // Required when scope is 'specific'
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
type ActionChangeScope = 'global' | 'root' | 'all-subagents' | 'specific';
|
|
826
|
-
type ActionChangeMode = 'add' | 'remove';
|
|
827
|
-
```
|
|
828
|
-
|
|
829
|
-
#### Scope Options
|
|
830
|
-
|
|
831
|
-
- **`global`**: Applies to all agents in the hierarchy (root + all sub-agents)
|
|
832
|
-
- **`root`**: Applies only to the root agent
|
|
833
|
-
- **`all-subagents`**: Applies to all sub-agents but NOT the root agent
|
|
834
|
-
- **`specific`**: Applies only to agents listed in `agentIds`
|
|
835
|
-
|
|
836
|
-
#### Usage Examples
|
|
249
|
+
### With Runtime Action Changes
|
|
837
250
|
|
|
838
251
|
```typescript
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
agent: myAgent,
|
|
842
|
-
conversationMessages: messages,
|
|
843
|
-
contextUser: user,
|
|
844
|
-
actionChanges: [
|
|
845
|
-
{
|
|
846
|
-
scope: 'global',
|
|
847
|
-
mode: 'add',
|
|
848
|
-
actionIds: ['lms-query-action-id', 'crm-search-action-id']
|
|
849
|
-
}
|
|
850
|
-
]
|
|
851
|
-
});
|
|
852
|
-
|
|
853
|
-
// Example 2: Tenant B context - different integrations
|
|
854
|
-
const result = await runner.RunAgent({
|
|
855
|
-
agent: myAgent,
|
|
856
|
-
conversationMessages: messages,
|
|
857
|
-
contextUser: user,
|
|
858
|
-
actionChanges: [
|
|
859
|
-
{
|
|
860
|
-
scope: 'global',
|
|
861
|
-
mode: 'add',
|
|
862
|
-
actionIds: ['membership-action-id', 'events-action-id']
|
|
863
|
-
}
|
|
864
|
-
]
|
|
865
|
-
});
|
|
866
|
-
|
|
867
|
-
// Example 3: Remove dangerous actions from sub-agents only
|
|
868
|
-
const result = await runner.RunAgent({
|
|
869
|
-
agent: myAgent,
|
|
252
|
+
const result = await runner.ExecuteAgent({
|
|
253
|
+
agentId: 'my-agent-id',
|
|
870
254
|
conversationMessages: messages,
|
|
871
|
-
contextUser:
|
|
255
|
+
contextUser: currentUser,
|
|
872
256
|
actionChanges: [
|
|
873
|
-
{
|
|
874
|
-
|
|
875
|
-
mode: 'remove',
|
|
876
|
-
actionIds: ['delete-record-action-id', 'execute-sql-action-id']
|
|
877
|
-
}
|
|
257
|
+
{ scope: 'global', mode: 'add', actionIds: ['crm-search-id'] },
|
|
258
|
+
{ scope: 'all-subagents', mode: 'remove', actionIds: ['delete-record-id'] }
|
|
878
259
|
]
|
|
879
260
|
});
|
|
261
|
+
```
|
|
880
262
|
|
|
881
|
-
|
|
882
|
-
const result = await runner.RunAgent({
|
|
883
|
-
agent: myAgent,
|
|
884
|
-
conversationMessages: messages,
|
|
885
|
-
contextUser: user,
|
|
886
|
-
actionChanges: [
|
|
887
|
-
{ scope: 'global', mode: 'add', actionIds: ['common-action-id'] },
|
|
888
|
-
{
|
|
889
|
-
scope: 'specific',
|
|
890
|
-
mode: 'add',
|
|
891
|
-
actionIds: ['special-data-action-id'],
|
|
892
|
-
agentIds: ['data-gatherer-sub-agent-id']
|
|
893
|
-
}
|
|
894
|
-
]
|
|
895
|
-
});
|
|
263
|
+
### With User Scope (Multi-Tenant)
|
|
896
264
|
|
|
897
|
-
|
|
898
|
-
const result = await runner.
|
|
899
|
-
|
|
265
|
+
```typescript
|
|
266
|
+
const result = await runner.ExecuteAgent({
|
|
267
|
+
agentId: 'my-agent-id',
|
|
900
268
|
conversationMessages: messages,
|
|
901
|
-
contextUser:
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
actionIds: ['default-crm-action-id']
|
|
908
|
-
},
|
|
909
|
-
// Then add the tenant-specific ones
|
|
910
|
-
{
|
|
911
|
-
scope: 'global',
|
|
912
|
-
mode: 'add',
|
|
913
|
-
actionIds: ['tenant-specific-crm-action-id']
|
|
914
|
-
}
|
|
915
|
-
]
|
|
269
|
+
contextUser: currentUser,
|
|
270
|
+
userScope: {
|
|
271
|
+
primaryEntityName: 'Organizations',
|
|
272
|
+
primaryRecordId: orgId,
|
|
273
|
+
secondary: { TeamID: teamId }
|
|
274
|
+
}
|
|
916
275
|
});
|
|
917
276
|
```
|
|
918
277
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
When sub-agents are executed, action changes are propagated based on scope:
|
|
922
|
-
|
|
923
|
-
| Original Scope | Propagated As | Behavior |
|
|
924
|
-
|---------------|---------------|----------|
|
|
925
|
-
| `global` | `global` | Propagated as-is to all sub-agents |
|
|
926
|
-
| `root` | (not propagated) | Only applied to root agent |
|
|
927
|
-
| `all-subagents` | `global` | Becomes global for sub-agent's perspective |
|
|
928
|
-
| `specific` | `specific` | Propagated as-is; each agent checks if it's in agentIds |
|
|
929
|
-
|
|
930
|
-
#### How It Works
|
|
931
|
-
|
|
932
|
-
1. **During prompt preparation** (`gatherPromptTemplateData`):
|
|
933
|
-
- Base actions are loaded from database configuration (`AIAgentAction` table)
|
|
934
|
-
- Runtime action changes are applied based on scope
|
|
935
|
-
- The modified action list is injected into the prompt template
|
|
936
|
-
- LLM sees only the effective actions
|
|
937
|
-
|
|
938
|
-
2. **During action execution** (`executeActionsStep`):
|
|
939
|
-
- When LLM requests an action, it's validated against the effective action list
|
|
940
|
-
- Actions not in the effective list are rejected with a clear error message
|
|
941
|
-
|
|
942
|
-
3. **During sub-agent execution** (`ExecuteSubAgent`):
|
|
943
|
-
- Action changes are filtered and transformed for propagation
|
|
944
|
-
- Sub-agents receive only applicable changes
|
|
945
|
-
|
|
946
|
-
#### Key Benefits
|
|
947
|
-
|
|
948
|
-
- **No database changes required** - Actions are modified at runtime
|
|
949
|
-
- **Tenant isolation** - Same agent, different action sets per tenant
|
|
950
|
-
- **Security** - Restrict sub-agents from dangerous operations
|
|
951
|
-
- **Flexibility** - Combine add/remove operations for complex scenarios
|
|
952
|
-
- **Type-safe** - Full TypeScript typing with ActionChange interface
|
|
953
|
-
|
|
954
|
-
**See:** [@memberjunction/ai-core-plus README](../CorePlus/README.md) for type definitions.
|
|
955
|
-
|
|
956
|
-
### Payload Scoping for Sub-Agents
|
|
957
|
-
|
|
958
|
-
The framework now supports narrowing the payload that sub-agents work with through the `PayloadScope` field:
|
|
278
|
+
### With Message Lifecycle Management
|
|
959
279
|
|
|
960
280
|
```typescript
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
"functionalRequirements": {
|
|
971
|
-
"features": ["A", "B", "C"],
|
|
972
|
-
"constraints": {...}
|
|
281
|
+
const result = await runner.ExecuteAgent({
|
|
282
|
+
agentId: 'my-agent-id',
|
|
283
|
+
conversationMessages: messages,
|
|
284
|
+
contextUser: currentUser,
|
|
285
|
+
messageExpirationOverride: {
|
|
286
|
+
expirationTurns: 3,
|
|
287
|
+
expirationMode: 'Compact',
|
|
288
|
+
compactMode: 'First N Chars',
|
|
289
|
+
compactLength: 500
|
|
973
290
|
},
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
// Sub-agent only sees:
|
|
979
|
-
{
|
|
980
|
-
"features": ["A", "B", "C"],
|
|
981
|
-
"constraints": {...}
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
// Sub-agent changes are merged back under the scope path
|
|
985
|
-
```
|
|
986
|
-
|
|
987
|
-
Benefits:
|
|
988
|
-
- **Reduced token usage**: Sub-agents only see relevant data
|
|
989
|
-
- **Improved focus**: Agents work with their specific domain
|
|
990
|
-
- **Automatic merging**: Changes are properly placed back in parent payload
|
|
991
|
-
- **Error handling**: Critical failures if scope path doesn't exist
|
|
992
|
-
|
|
993
|
-
### Input Payload Validation
|
|
994
|
-
|
|
995
|
-
Agents can validate their input payload before execution begins to ensure data quality and prevent errors:
|
|
996
|
-
|
|
997
|
-
```typescript
|
|
998
|
-
// Configure validation in AIAgent entity
|
|
999
|
-
{
|
|
1000
|
-
StartingPayloadValidation: JSON.stringify({
|
|
1001
|
-
"customerId": "string:!empty",
|
|
1002
|
-
"orderItems": "array:[1+]",
|
|
1003
|
-
"shippingAddress": {
|
|
1004
|
-
"street": "string:!empty",
|
|
1005
|
-
"city": "string:!empty",
|
|
1006
|
-
"zipCode": "string:[5]"
|
|
1007
|
-
},
|
|
1008
|
-
"priority": "string?:enum:normal,high,urgent" // Optional with enum values
|
|
1009
|
-
}),
|
|
1010
|
-
StartingPayloadValidationMode: "Fail" // or "Warn" (default: "Fail")
|
|
1011
|
-
}
|
|
1012
|
-
```
|
|
1013
|
-
|
|
1014
|
-
Input validation features:
|
|
1015
|
-
- **Early failure detection**: Validates before any processing begins
|
|
1016
|
-
- **Two modes**:
|
|
1017
|
-
- `Fail`: Reject invalid input immediately (default)
|
|
1018
|
-
- `Warn`: Log warning but proceed with execution
|
|
1019
|
-
- **Deterministic guardrails**: Ensures agents receive valid data
|
|
1020
|
-
- **Cost savings**: Prevents expensive operations with invalid input
|
|
1021
|
-
- **Parent responsibility**: Parent agents provide properly scoped payloads to children
|
|
1022
|
-
|
|
1023
|
-
### Final Payload Validation
|
|
1024
|
-
|
|
1025
|
-
Agents can validate their final output before marking execution as successful:
|
|
1026
|
-
|
|
1027
|
-
```typescript
|
|
1028
|
-
// Configure validation in AIAgent entity
|
|
1029
|
-
{
|
|
1030
|
-
FinalPayloadValidation: JSON.stringify({
|
|
1031
|
-
"analysis": {
|
|
1032
|
-
"summary": "string:!empty",
|
|
1033
|
-
"score": "number:[0-100]",
|
|
1034
|
-
"recommendations": "array:[3+]"
|
|
1035
|
-
},
|
|
1036
|
-
"metadata": {
|
|
1037
|
-
"processedAt": "string",
|
|
1038
|
-
"version": "string?" // Optional field
|
|
1039
|
-
}
|
|
1040
|
-
}),
|
|
1041
|
-
FinalPayloadValidationMode: "Retry", // or "Fail" or "Warn"
|
|
1042
|
-
FinalPayloadValidationMaxRetries: 3
|
|
1043
|
-
}
|
|
291
|
+
onMessageLifecycle: (event) => {
|
|
292
|
+
console.log(`${event.type}: ${event.reason} (saved ${event.tokensSaved} tokens)`);
|
|
293
|
+
}
|
|
294
|
+
});
|
|
1044
295
|
```
|
|
1045
296
|
|
|
1046
|
-
|
|
1047
|
-
- **JSON schema validation**: Using JSONValidator from @memberjunction/global
|
|
1048
|
-
- **Multiple modes**:
|
|
1049
|
-
- `Retry`: Re-execute with validation feedback (up to max retries)
|
|
1050
|
-
- `Fail`: Immediately fail the run
|
|
1051
|
-
- `Warn`: Log warning but allow success
|
|
1052
|
-
- **Retry tracking**: Prevents infinite validation loops
|
|
1053
|
-
- **Step-level logging**: Validation results stored in AIAgentRunStep
|
|
1054
|
-
|
|
1055
|
-
### Execution Guardrails
|
|
1056
|
-
|
|
1057
|
-
New fields provide comprehensive limits to prevent runaway agent execution:
|
|
1058
|
-
|
|
1059
|
-
```typescript
|
|
1060
|
-
// Configure guardrails in AIAgent entity
|
|
1061
|
-
{
|
|
1062
|
-
MaxCostPerRun: 10.00, // $10 maximum
|
|
1063
|
-
MaxTokensPerRun: 100000, // 100k tokens total
|
|
1064
|
-
MaxIterationsPerRun: 50, // 50 prompt iterations
|
|
1065
|
-
MaxTimePerRun: 300 // 5 minutes
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
// The framework monitors these in real-time and terminates if exceeded
|
|
1069
|
-
// Termination reason is logged in AIAgentRun.ErrorMessage
|
|
1070
|
-
```
|
|
1071
|
-
|
|
1072
|
-
Guardrail features:
|
|
1073
|
-
- **Cost tracking**: Monitors cumulative API costs
|
|
1074
|
-
- **Token counting**: Tracks input + output tokens
|
|
1075
|
-
- **Iteration limits**: Counts each prompt execution
|
|
1076
|
-
- **Time limits**: Enforces maximum execution duration
|
|
1077
|
-
- **Graceful termination**: Saves state before stopping
|
|
1078
|
-
|
|
1079
|
-
### Run Chaining
|
|
1080
|
-
|
|
1081
|
-
The framework supports linking multiple agent runs together to maintain context across interactions:
|
|
1082
|
-
|
|
1083
|
-
```typescript
|
|
1084
|
-
// Execute an agent with run chaining
|
|
1085
|
-
const result = await agent.Execute({
|
|
1086
|
-
agent: agentEntity,
|
|
1087
|
-
conversationMessages: messages,
|
|
1088
|
-
contextUser: user,
|
|
1089
|
-
lastRunId: previousRunId, // Links to previous run
|
|
1090
|
-
autoPopulateLastRunPayload: true // Auto-loads previous payload
|
|
1091
|
-
});
|
|
1092
|
-
|
|
1093
|
-
// The framework will:
|
|
1094
|
-
// 1. Load the FinalPayload from the previous run
|
|
1095
|
-
// 2. Set it as StartingPayload for the new run
|
|
1096
|
-
// 3. Use it as the initial payload if none provided
|
|
1097
|
-
// 4. Validate against circular references in the chain
|
|
1098
|
-
```
|
|
1099
|
-
|
|
1100
|
-
Key features:
|
|
1101
|
-
- **LastRunID**: Links runs in a chain (different from ParentRunID for sub-agents)
|
|
1102
|
-
- **StartingPayload**: Captures the initial state of each run
|
|
1103
|
-
- **Auto-population**: Reduces bandwidth by avoiding payload round-trips
|
|
1104
|
-
- **Circular reference detection**: Prevents infinite loops in run chains
|
|
1105
|
-
|
|
1106
|
-
### Payload Management and Change Detection
|
|
1107
|
-
|
|
1108
|
-
The framework includes sophisticated payload management with automatic change detection:
|
|
1109
|
-
|
|
1110
|
-
```typescript
|
|
1111
|
-
// Payload changes are automatically analyzed
|
|
1112
|
-
const changeResult = payloadManager.applyAgentChangeRequest(
|
|
1113
|
-
originalPayload,
|
|
1114
|
-
changeRequest,
|
|
1115
|
-
{
|
|
1116
|
-
analyzeChanges: true, // Detect suspicious changes
|
|
1117
|
-
generateDiff: true, // Create audit trail
|
|
1118
|
-
agentName: 'MyAgent'
|
|
1119
|
-
}
|
|
1120
|
-
);
|
|
1121
|
-
|
|
1122
|
-
// Suspicious changes are flagged:
|
|
1123
|
-
// - Content truncation (>70% reduction)
|
|
1124
|
-
// - Non-empty key removal
|
|
1125
|
-
// - Type changes (object→primitive)
|
|
1126
|
-
// - Pattern anomalies (placeholder replacement)
|
|
1127
|
-
```
|
|
1128
|
-
|
|
1129
|
-
**Sub-agent Payload Access Control**:
|
|
1130
|
-
```typescript
|
|
1131
|
-
// In AIAgent entity configuration:
|
|
1132
|
-
{
|
|
1133
|
-
PayloadDownstreamPaths: ["customer.id", "order.*"], // What sub-agent can read
|
|
1134
|
-
PayloadUpstreamPaths: ["analysis.*", "recommendations"] // What sub-agent can write
|
|
1135
|
-
}
|
|
1136
|
-
```
|
|
1137
|
-
|
|
1138
|
-
**Operation-Level Payload Control**:
|
|
1139
|
-
The framework supports fine-grained control over which operations (add, update, delete) are allowed on specific payload paths:
|
|
1140
|
-
|
|
1141
|
-
```typescript
|
|
1142
|
-
// Basic syntax - all operations allowed (backward compatible)
|
|
1143
|
-
PayloadUpstreamPaths: ["analysis.*", "recommendations"]
|
|
1144
|
-
|
|
1145
|
-
// Operation-specific syntax using colon notation
|
|
1146
|
-
PayloadUpstreamPaths: [
|
|
1147
|
-
"analysis.*:add,update", // Can add or update, but not delete
|
|
1148
|
-
"recommendations:add", // Can only add new recommendations
|
|
1149
|
-
"summary:update", // Can only update existing summary
|
|
1150
|
-
"temp.*:delete", // Can only delete temporary data
|
|
1151
|
-
"metadata.tags:add,delete" // Can add/remove tags but not modify existing
|
|
1152
|
-
]
|
|
1153
|
-
|
|
1154
|
-
// For agent's own payload access (PayloadSelfWritePaths)
|
|
1155
|
-
PayloadSelfWritePaths: [
|
|
1156
|
-
"workspace.*", // Full access to workspace
|
|
1157
|
-
"results:add", // Can only add results, not modify
|
|
1158
|
-
"status:update" // Can only update status field
|
|
1159
|
-
]
|
|
1160
|
-
```
|
|
1161
|
-
|
|
1162
|
-
Operation types:
|
|
1163
|
-
- `add` - Create new properties or array elements
|
|
1164
|
-
- `update` - Modify existing values
|
|
1165
|
-
- `delete` - Remove properties or array elements
|
|
1166
|
-
|
|
1167
|
-
When operations are restricted, the framework will:
|
|
1168
|
-
- Log warnings when unauthorized operations are attempted
|
|
1169
|
-
- Block the disallowed changes while preserving allowed ones
|
|
1170
|
-
- Include operation details in the audit trail
|
|
1171
|
-
|
|
1172
|
-
### Hierarchical Prompt Execution
|
|
1173
|
-
```typescript
|
|
1174
|
-
// System prompt provides base behavior
|
|
1175
|
-
// Agent prompts execute as children with shared context
|
|
1176
|
-
const result = await agent.ExecutePrompt({
|
|
1177
|
-
systemPrompt: agentType.SystemPrompt,
|
|
1178
|
-
agentPrompt: currentPrompt,
|
|
1179
|
-
messages: conversationContext
|
|
1180
|
-
});
|
|
1181
|
-
```
|
|
1182
|
-
|
|
1183
|
-
### Context Management
|
|
1184
|
-
Agents automatically manage conversation context:
|
|
1185
|
-
- Maintains message history across steps
|
|
1186
|
-
- **Intelligent message expiration** - Automatically compacts or removes old action results
|
|
1187
|
-
- Compresses context when approaching token limits
|
|
1188
|
-
- Handles placeholder replacement in prompts
|
|
1189
|
-
- Preserves important context during compression
|
|
1190
|
-
|
|
1191
|
-
### Message Expiration and Compaction
|
|
1192
|
-
|
|
1193
|
-
The framework provides sophisticated message lifecycle management to prevent context bloat from large action results:
|
|
1194
|
-
|
|
1195
|
-
**Per-Action Configuration** (in `AIAgentAction` table):
|
|
1196
|
-
- `ResultExpirationTurns`: Number of turns before message expires (e.g., 2)
|
|
1197
|
-
- `ResultExpirationMode`: 'None' | 'Remove' | 'Compact'
|
|
1198
|
-
- `CompactMode`: 'First N Chars' | 'AI Summary'
|
|
1199
|
-
- `CompactLength`: Character limit for 'First N Chars' mode
|
|
1200
|
-
- `CompactPromptID`: Custom AI prompt for 'AI Summary' mode
|
|
1201
|
-
|
|
1202
|
-
**How It Works**:
|
|
1203
|
-
```typescript
|
|
1204
|
-
// Configure a Google Search action to compact results after 2 turns
|
|
1205
|
-
await agentAction.Save({
|
|
1206
|
-
ResultExpirationTurns: 2,
|
|
1207
|
-
ResultExpirationMode: 'Compact',
|
|
1208
|
-
CompactMode: 'First N Chars',
|
|
1209
|
-
CompactLength: 500
|
|
1210
|
-
});
|
|
1211
|
-
|
|
1212
|
-
// Turn 1: Action returns 10,000 char search results
|
|
1213
|
-
// Turn 2: Results still in conversation (turn 1, limit 2)
|
|
1214
|
-
// Turn 3: Results still in conversation (turn 2, limit 2)
|
|
1215
|
-
// Turn 4: Results compacted to 500 chars (turn 3 > limit 2)
|
|
1216
|
-
// Original content preserved in metadata for expansion
|
|
1217
|
-
```
|
|
1218
|
-
|
|
1219
|
-
**Compaction Modes**:
|
|
1220
|
-
1. **First N Chars**: Fast truncation with annotation
|
|
1221
|
-
```
|
|
1222
|
-
First 500 chars of result...
|
|
1223
|
-
|
|
1224
|
-
[Compacted: showing first 500 of 10000 characters. Agent can request expansion if needed.]
|
|
1225
|
-
```
|
|
1226
|
-
|
|
1227
|
-
2. **AI Summary**: Intelligent LLM-based summarization
|
|
1228
|
-
```
|
|
1229
|
-
[AI Summary of 10000 chars. Agent can request full expansion if needed.]
|
|
1230
|
-
|
|
1231
|
-
Search found 47 results for "MemberJunction". Top results include...
|
|
1232
|
-
```
|
|
1233
|
-
|
|
1234
|
-
**Message Expansion**:
|
|
1235
|
-
Agents can restore compacted messages when needed:
|
|
1236
|
-
```typescript
|
|
1237
|
-
// In agent's JSON response
|
|
1238
|
-
{
|
|
1239
|
-
"taskComplete": false,
|
|
1240
|
-
"nextStep": {
|
|
1241
|
-
"type": "Retry",
|
|
1242
|
-
"messageIndex": 5, // Index of compacted message
|
|
1243
|
-
"reason": "Need full search results to answer user's question about item #47"
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
```
|
|
1247
|
-
|
|
1248
|
-
**Runtime Override**:
|
|
1249
|
-
Test different expiration strategies without modifying database:
|
|
1250
|
-
```typescript
|
|
1251
|
-
const result = await runner.RunAgent({
|
|
1252
|
-
agent: myAgent,
|
|
1253
|
-
conversationMessages: messages,
|
|
1254
|
-
contextUser: user,
|
|
1255
|
-
messageExpirationOverride: {
|
|
1256
|
-
expirationTurns: 1,
|
|
1257
|
-
expirationMode: 'Compact',
|
|
1258
|
-
compactMode: 'First N Chars',
|
|
1259
|
-
compactLength: 200,
|
|
1260
|
-
preserveOriginalContent: true
|
|
1261
|
-
}
|
|
1262
|
-
});
|
|
1263
|
-
```
|
|
1264
|
-
|
|
1265
|
-
**Lifecycle Monitoring**:
|
|
1266
|
-
Track message compaction for debugging and token savings analysis:
|
|
1267
|
-
```typescript
|
|
1268
|
-
const result = await runner.RunAgent({
|
|
1269
|
-
agent: myAgent,
|
|
1270
|
-
conversationMessages: messages,
|
|
1271
|
-
contextUser: user,
|
|
1272
|
-
onMessageLifecycle: (event) => {
|
|
1273
|
-
console.log(`[Turn ${event.turn}] ${event.type}: ${event.reason}`);
|
|
1274
|
-
if (event.tokensSaved) {
|
|
1275
|
-
console.log(` Tokens saved: ${event.tokensSaved}`);
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
});
|
|
1279
|
-
// Output:
|
|
1280
|
-
// [Turn 3] message-compacted: Compacted using First N Chars (saved 2375 tokens)
|
|
1281
|
-
// [Turn 5] message-removed: Removed due to expiration
|
|
1282
|
-
```
|
|
1283
|
-
|
|
1284
|
-
**Prompt Lookup Hierarchy**:
|
|
1285
|
-
For AI Summary mode, prompts are resolved in this order:
|
|
1286
|
-
1. Runtime override (`messageExpirationOverride.compactPromptId`)
|
|
1287
|
-
2. Agent action configuration (`AIAgentAction.CompactPromptID`)
|
|
1288
|
-
3. Action default (`Action.DefaultCompactPromptID`)
|
|
1289
|
-
4. System default ("Compact Agent Message" prompt)
|
|
1290
|
-
|
|
1291
|
-
**Benefits**:
|
|
1292
|
-
- **Addresses Large Action Results**: Automatically handles the most common cause of context bloat
|
|
1293
|
-
- **Configurable Per-Action**: Different expiration strategies for different action types
|
|
1294
|
-
- **Non-Destructive**: Original content preserved in metadata for on-demand expansion
|
|
1295
|
-
- **Token Savings**: Reduces context window usage by 70-95% for large results
|
|
1296
|
-
- **Agent-Aware**: Agents can detect compacted messages and request full expansion when needed
|
|
1297
|
-
|
|
1298
|
-
### Context Length Recovery
|
|
1299
|
-
|
|
1300
|
-
When a prompt execution fails due to context length overflow (even after model failover), BaseAgent provides **one-time automatic recovery** instead of immediately terminating. This gives the agent an opportunity to adapt its approach.
|
|
1301
|
-
|
|
1302
|
-
**How It Works**:
|
|
1303
|
-
1. **Prompt fails with ContextLengthExceeded** → Detected as fatal error
|
|
1304
|
-
2. **First occurrence**: Recovery is attempted automatically (once per run)
|
|
1305
|
-
3. **Last user message is trimmed** using smart strategies:
|
|
1306
|
-
- JSON arrays: Keeps first 10 items with truncation notice
|
|
1307
|
-
- CSV data: Keeps header + first 10 rows
|
|
1308
|
-
- Plain text: Keeps first 1000 characters
|
|
1309
|
-
4. **Agent receives clear guidance** explaining what happened and recommended actions
|
|
1310
|
-
5. **Agent gets Retry step** to choose alternative approach (e.g., more specific filters, batch requests)
|
|
1311
|
-
6. **If recovery fails again**: Normal fatal error handling (agent terminates)
|
|
1312
|
-
|
|
1313
|
-
**Example Recovery Message**:
|
|
1314
|
-
```
|
|
1315
|
-
⚠️ CONTEXT OVERFLOW RECOVERY ⚠️
|
|
1316
|
-
|
|
1317
|
-
The previous step returned a result that exceeded the context window (147,532 characters truncated).
|
|
1318
|
-
|
|
1319
|
-
Here is a PARTIAL result from the previous action:
|
|
1320
|
-
---
|
|
1321
|
-
[First 10 items from JSON array...]
|
|
1322
|
-
... (487 more items truncated due to context length)
|
|
1323
|
-
---
|
|
1324
|
-
|
|
1325
|
-
❗ THE ABOVE IS INCOMPLETE - the full result was too large for the context window.
|
|
1326
|
-
|
|
1327
|
-
RECOMMENDED ACTIONS:
|
|
1328
|
-
1. Use a different action with more specific filters to get smaller result sets
|
|
1329
|
-
2. Request data in batches or pages instead of all at once
|
|
1330
|
-
3. Ask the user to clarify scope to narrow the query
|
|
1331
|
-
4. If you need the full data, acknowledge the limitation and ask the user how to proceed
|
|
1332
|
-
|
|
1333
|
-
Please choose an alternative approach to complete your task.
|
|
1334
|
-
```
|
|
1335
|
-
|
|
1336
|
-
**Benefits**:
|
|
1337
|
-
- **Resilient**: Agents can adapt instead of failing immediately
|
|
1338
|
-
- **Informative**: Clear explanation of what went wrong and how to recover
|
|
1339
|
-
- **Safe**: ONE-TIME recovery prevents infinite loops
|
|
1340
|
-
- **Smart**: Preserves data structure when possible (JSON, CSV)
|
|
1341
|
-
|
|
1342
|
-
This feature is particularly useful when agents call actions that can return very large datasets (e.g., "Get Entity List" without filters).
|
|
1343
|
-
|
|
1344
|
-
### Action Integration
|
|
1345
|
-
```typescript
|
|
1346
|
-
// In agent type's DetermineNextStep
|
|
1347
|
-
return {
|
|
1348
|
-
type: 'action',
|
|
1349
|
-
actionName: 'SendEmail',
|
|
1350
|
-
actionParams: {
|
|
1351
|
-
to: 'user@example.com',
|
|
1352
|
-
subject: 'Analysis Complete',
|
|
1353
|
-
body: analysisResult
|
|
1354
|
-
}
|
|
1355
|
-
};
|
|
1356
|
-
```
|
|
1357
|
-
|
|
1358
|
-
### Sub-agent Orchestration
|
|
1359
|
-
```typescript
|
|
1360
|
-
// Agents can invoke other agents recursively
|
|
1361
|
-
return {
|
|
1362
|
-
type: 'sub_agent',
|
|
1363
|
-
agentName: 'DataValidationAgent',
|
|
1364
|
-
messages: [
|
|
1365
|
-
{ role: 'user', content: `Validate this data: ${JSON.stringify(data)}` }
|
|
1366
|
-
]
|
|
1367
|
-
};
|
|
1368
|
-
```
|
|
1369
|
-
|
|
1370
|
-
### Conversation Message Mapping for Actions and Sub-Agents
|
|
1371
|
-
|
|
1372
|
-
The framework includes a **ConversationMessageResolver** utility that enables flexible conversation message referencing in action input mappings and sub-agent configurations. This is particularly useful for passing conversation context to knowledge base assistants, chatbots, or analysis agents.
|
|
1373
|
-
|
|
1374
|
-
#### Basic Usage with Actions
|
|
1375
|
-
|
|
1376
|
-
**In Flow Agent Step Configuration** (`ActionInputMapping`):
|
|
1377
|
-
```typescript
|
|
1378
|
-
// Pass full conversation history to an action
|
|
1379
|
-
{
|
|
1380
|
-
"ActionInputMapping": {
|
|
1381
|
-
"ConversationMessages": "conversation.all"
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
```
|
|
1385
|
-
|
|
1386
|
-
**In Loop Agent Response**:
|
|
1387
|
-
```typescript
|
|
1388
|
-
{
|
|
1389
|
-
"taskComplete": false,
|
|
1390
|
-
"nextStep": {
|
|
1391
|
-
"type": "Actions",
|
|
1392
|
-
"actions": [{
|
|
1393
|
-
"name": "Betty", // Knowledge base assistant
|
|
1394
|
-
"params": {
|
|
1395
|
-
"ConversationMessages": "conversation.all"
|
|
1396
|
-
}
|
|
1397
|
-
}]
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
```
|
|
1401
|
-
|
|
1402
|
-
#### Supported Conversation Patterns
|
|
1403
|
-
|
|
1404
|
-
The `ConversationMessageResolver` supports powerful pattern-based message selection:
|
|
1405
|
-
|
|
1406
|
-
**1. All Messages**:
|
|
1407
|
-
```typescript
|
|
1408
|
-
"ConversationMessages": "conversation.all"
|
|
1409
|
-
// Returns entire conversation history
|
|
1410
|
-
```
|
|
1411
|
-
|
|
1412
|
-
**2. Role-Based Selection**:
|
|
1413
|
-
```typescript
|
|
1414
|
-
// Last N user messages
|
|
1415
|
-
"ConversationMessages": "conversation.user.last[5]"
|
|
1416
|
-
|
|
1417
|
-
// Last N assistant messages
|
|
1418
|
-
"ConversationMessages": "conversation.assistant.last[3]"
|
|
1419
|
-
|
|
1420
|
-
// Last N system messages
|
|
1421
|
-
"ConversationMessages": "conversation.system.last[1]"
|
|
1422
|
-
```
|
|
1423
|
-
|
|
1424
|
-
**3. All Messages of a Role**:
|
|
1425
|
-
```typescript
|
|
1426
|
-
// All user messages
|
|
1427
|
-
"ConversationMessages": "conversation.user.all"
|
|
1428
|
-
|
|
1429
|
-
// All assistant messages
|
|
1430
|
-
"ConversationMessages": "conversation.assistant.all"
|
|
1431
|
-
```
|
|
1432
|
-
|
|
1433
|
-
**4. Single Last Message by Role**:
|
|
1434
|
-
```typescript
|
|
1435
|
-
// Just the last user message
|
|
1436
|
-
"ConversationMessages": "conversation.user.last"
|
|
1437
|
-
|
|
1438
|
-
// Just the last assistant message
|
|
1439
|
-
"ConversationMessages": "conversation.assistant.last"
|
|
1440
|
-
```
|
|
1441
|
-
|
|
1442
|
-
#### Use Cases
|
|
1443
|
-
|
|
1444
|
-
**Knowledge Base Assistants** - Pass full conversation history for context-aware responses:
|
|
1445
|
-
```typescript
|
|
1446
|
-
// In Knowledge Base Research Agent step
|
|
1447
|
-
{
|
|
1448
|
-
"StepType": "Action",
|
|
1449
|
-
"ActionID": "betty-action-id",
|
|
1450
|
-
"ActionInputMapping": {
|
|
1451
|
-
"ConversationMessages": "conversation.all" // Betty sees full context
|
|
1452
|
-
}
|
|
1453
|
-
}
|
|
1454
|
-
```
|
|
1455
|
-
|
|
1456
|
-
**Sentiment Analysis** - Analyze just user messages:
|
|
1457
|
-
```typescript
|
|
1458
|
-
{
|
|
1459
|
-
"ActionInputMapping": {
|
|
1460
|
-
"messagesToAnalyze": "conversation.user.last[10]",
|
|
1461
|
-
"includeSentiment": true
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
```
|
|
1465
|
-
|
|
1466
|
-
**Context Summarization** - Summarize recent conversation:
|
|
1467
|
-
```typescript
|
|
1468
|
-
{
|
|
1469
|
-
"ActionInputMapping": {
|
|
1470
|
-
"recentMessages": "conversation.all", // or "conversation.last[20]" if supported
|
|
1471
|
-
"summarizeAs": "bullet_points"
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
```
|
|
1475
|
-
|
|
1476
|
-
**Follow-up Question Generation** - Based on assistant responses:
|
|
1477
|
-
```typescript
|
|
1478
|
-
{
|
|
1479
|
-
"ActionInputMapping": {
|
|
1480
|
-
"previousResponses": "conversation.assistant.last[3]",
|
|
1481
|
-
"generateFollowUps": true
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
```
|
|
1485
|
-
|
|
1486
|
-
#### Sub-Agent Usage
|
|
1487
|
-
|
|
1488
|
-
Sub-agents automatically receive the parent's conversation context, but you can control which messages are passed:
|
|
1489
|
-
|
|
1490
|
-
**In Loop Agent** (via agent prompt instructions):
|
|
1491
|
-
```typescript
|
|
1492
|
-
// The agent's system prompt can instruct:
|
|
1493
|
-
"When invoking sub-agents, you can specify which conversation messages to pass using the ConversationMessages parameter in your action input mappings."
|
|
1494
|
-
```
|
|
1495
|
-
|
|
1496
|
-
**In Flow Agent** (via SubAgentConfiguration):
|
|
1497
|
-
```typescript
|
|
1498
|
-
// Configure sub-agent relationships with conversation context
|
|
1499
|
-
{
|
|
1500
|
-
"AgentID": "parent-agent-id",
|
|
1501
|
-
"SubAgentID": "knowledge-base-research-agent-id",
|
|
1502
|
-
"SubAgentOutputMapping": { "*": "knowledgeBaseResearch" },
|
|
1503
|
-
"SubAgentContextPaths": ["*"] // Full context by default
|
|
1504
|
-
}
|
|
1505
|
-
```
|
|
1506
|
-
|
|
1507
|
-
#### How It Works
|
|
1508
|
-
|
|
1509
|
-
The resolver operates during the parameter mapping phase:
|
|
1510
|
-
|
|
1511
|
-
1. **Detection**: Identifies `conversation.` prefixed strings in action/sub-agent parameters
|
|
1512
|
-
2. **Pattern Matching**: Parses the pattern (role, selector, count)
|
|
1513
|
-
3. **Message Filtering**: Extracts matching messages from conversation history
|
|
1514
|
-
4. **Type Validation**: Ensures the target parameter expects an array of messages
|
|
1515
|
-
5. **Injection**: Replaces the pattern with actual message objects
|
|
1516
|
-
|
|
1517
|
-
**Example Resolution**:
|
|
1518
|
-
```typescript
|
|
1519
|
-
// Input mapping configuration
|
|
1520
|
-
{
|
|
1521
|
-
"ConversationMessages": "conversation.user.last[3]"
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
// Conversation history
|
|
1525
|
-
[
|
|
1526
|
-
{ role: 'system', content: 'You are a helpful assistant' },
|
|
1527
|
-
{ role: 'user', content: 'What is MemberJunction?' },
|
|
1528
|
-
{ role: 'assistant', content: 'MemberJunction is...' },
|
|
1529
|
-
{ role: 'user', content: 'How do agents work?' },
|
|
1530
|
-
{ role: 'assistant', content: 'Agents work by...' },
|
|
1531
|
-
{ role: 'user', content: 'Can you give an example?' }
|
|
1532
|
-
]
|
|
1533
|
-
|
|
1534
|
-
// Resolved parameter value (last 3 user messages)
|
|
1535
|
-
[
|
|
1536
|
-
{ role: 'user', content: 'What is MemberJunction?' },
|
|
1537
|
-
{ role: 'user', content: 'How do agents work?' },
|
|
1538
|
-
{ role: 'user', content: 'Can you give an example?' }
|
|
1539
|
-
]
|
|
1540
|
-
```
|
|
1541
|
-
|
|
1542
|
-
#### Benefits
|
|
1543
|
-
|
|
1544
|
-
- **Context-Aware Actions**: Actions receive relevant conversation history
|
|
1545
|
-
- **Flexible Filtering**: Select exactly which messages are needed
|
|
1546
|
-
- **Declarative Configuration**: No custom code needed in action implementations
|
|
1547
|
-
- **Type Safety**: Resolver validates parameter types at runtime
|
|
1548
|
-
- **Performance**: Only selected messages are passed, reducing token usage
|
|
1549
|
-
- **Composability**: Works seamlessly with Flow and Loop agents
|
|
1550
|
-
|
|
1551
|
-
#### Integration with Betty Knowledge Base Action
|
|
1552
|
-
|
|
1553
|
-
A prime example of this feature is the Betty action for knowledge base queries:
|
|
1554
|
-
|
|
1555
|
-
```typescript
|
|
1556
|
-
// Betty action accepts ConversationMessages parameter
|
|
1557
|
-
{
|
|
1558
|
-
"name": "Betty",
|
|
1559
|
-
"params": {
|
|
1560
|
-
"ConversationMessages": "conversation.all" // Full conversation context
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
// Betty uses the conversation history to provide context-aware responses
|
|
1565
|
-
// and can reference earlier questions/answers in its knowledge base queries
|
|
1566
|
-
```
|
|
1567
|
-
|
|
1568
|
-
This enables knowledge base agents to maintain conversation context across multiple queries, improving response relevance and follow-up question handling.
|
|
1569
|
-
|
|
1570
|
-
## Agent Permissions System
|
|
1571
|
-
|
|
1572
|
-
The agent framework includes a comprehensive ACL-based permissions system that controls who can view, run, edit, and delete agents.
|
|
1573
|
-
|
|
1574
|
-
### Permission Model
|
|
1575
|
-
|
|
1576
|
-
The permissions system uses **hierarchical permissions** with the following levels:
|
|
1577
|
-
|
|
1578
|
-
1. **View** - See agent configuration and details
|
|
1579
|
-
2. **Run** - Execute the agent (implies View)
|
|
1580
|
-
3. **Edit** - Modify agent configuration (implies Run and View)
|
|
1581
|
-
4. **Delete** - Remove the agent (implies Edit, Run, and View)
|
|
1582
|
-
|
|
1583
|
-
### Default Permission Behavior
|
|
1584
|
-
|
|
1585
|
-
The system uses an **"open by default"** approach to minimize administrative overhead:
|
|
1586
|
-
|
|
1587
|
-
- **No permission records exist**: Anyone can **View** and **Run** the agent
|
|
1588
|
-
- **Owner**: Always has full permissions (View, Run, Edit, Delete)
|
|
1589
|
-
- **Explicit permissions**: When permission records exist, only users/roles with matching permissions can access
|
|
1590
|
-
|
|
1591
|
-
**Why this approach?**
|
|
1592
|
-
- Minimizes setup overhead for most agents
|
|
1593
|
-
- Allows broad access for running agents (common use case)
|
|
1594
|
-
- Protects modification operations (Edit/Delete) through ownership
|
|
1595
|
-
- Explicit permissions provide fine-grained control when needed
|
|
1596
|
-
|
|
1597
|
-
### Ownership
|
|
1598
|
-
|
|
1599
|
-
Every agent has an `OwnerUserID` field:
|
|
1600
|
-
- Owners always have full permissions regardless of ACL records
|
|
1601
|
-
- Defaults to the user who created the agent
|
|
1602
|
-
- Can be transferred by editing the agent
|
|
1603
|
-
|
|
1604
|
-
### Permission Records
|
|
1605
|
-
|
|
1606
|
-
Permission records are stored in the `AIAgentPermission` table with these fields:
|
|
1607
|
-
|
|
1608
|
-
- **AgentID** - The agent being controlled
|
|
1609
|
-
- **UserID** - Direct user permission (mutually exclusive with RoleID)
|
|
1610
|
-
- **RoleID** - Role-based permission (mutually exclusive with UserID)
|
|
1611
|
-
- **CanView** - Boolean flag for view permission
|
|
1612
|
-
- **CanRun** - Boolean flag for run permission
|
|
1613
|
-
- **CanEdit** - Boolean flag for edit permission
|
|
1614
|
-
- **CanDelete** - Boolean flag for delete permission
|
|
1615
|
-
- **Comments** - Optional description of why permission was granted
|
|
1616
|
-
|
|
1617
|
-
**Important**: Each record must have either `UserID` OR `RoleID` set, but not both.
|
|
1618
|
-
|
|
1619
|
-
### Permission Resolution
|
|
1620
|
-
|
|
1621
|
-
When checking if a user can perform an operation:
|
|
1622
|
-
|
|
1623
|
-
1. **Check ownership** - If user is the owner, grant all permissions
|
|
1624
|
-
2. **Check if no permissions exist** - Grant View and Run by default
|
|
1625
|
-
3. **Find matching permissions** - Get all records for the user OR their roles
|
|
1626
|
-
4. **Apply OR logic** - If ANY permission grants access, allow the operation
|
|
1627
|
-
5. **Apply hierarchy** - Higher permissions automatically grant lower ones:
|
|
1628
|
-
- Delete → Edit → Run → View
|
|
1629
|
-
- If you have Run permission, you automatically get View
|
|
1630
|
-
- If you have Edit permission, you automatically get Run and View
|
|
1631
|
-
|
|
1632
|
-
### Using Permissions in Code
|
|
1633
|
-
|
|
1634
|
-
The framework provides helper methods for checking permissions:
|
|
1635
|
-
|
|
1636
|
-
```typescript
|
|
1637
|
-
import { AIEngineBase } from '@memberjunction/ai-engine-base';
|
|
1638
|
-
import { AIAgentPermissionHelper } from '@memberjunction/ai-engine-base';
|
|
1639
|
-
|
|
1640
|
-
// Check specific permission
|
|
1641
|
-
const canRun = await AIEngineBase.Instance.CanUserRunAgent(agentId, user);
|
|
1642
|
-
const canEdit = await AIEngineBase.Instance.CanUserEditAgent(agentId, user);
|
|
1643
|
-
|
|
1644
|
-
// Get all effective permissions
|
|
1645
|
-
const permissions = await AIEngineBase.Instance.GetUserAgentPermissions(agentId, user);
|
|
1646
|
-
console.log(permissions);
|
|
1647
|
-
// {
|
|
1648
|
-
// canView: true,
|
|
1649
|
-
// canRun: true,
|
|
1650
|
-
// canEdit: false,
|
|
1651
|
-
// canDelete: false,
|
|
1652
|
-
// isOwner: false
|
|
1653
|
-
// }
|
|
1654
|
-
|
|
1655
|
-
// Get all agents user can access with specific permission
|
|
1656
|
-
const runnableAgents = await AIEngineBase.Instance.GetAccessibleAgents(user, 'run');
|
|
1657
|
-
|
|
1658
|
-
// Using the helper directly
|
|
1659
|
-
const hasPermission = await AIAgentPermissionHelper.HasPermission(agentId, user, 'run');
|
|
1660
|
-
```
|
|
1661
|
-
|
|
1662
|
-
### Runtime Permission Enforcement
|
|
1663
|
-
|
|
1664
|
-
The BaseAgent class automatically enforces run permissions:
|
|
1665
|
-
|
|
1666
|
-
```typescript
|
|
1667
|
-
// BaseAgent.Execute() checks permissions before running
|
|
1668
|
-
const result = await agent.Execute({
|
|
1669
|
-
agent: agentEntity,
|
|
1670
|
-
conversationMessages: messages,
|
|
1671
|
-
contextUser: user
|
|
1672
|
-
});
|
|
1673
|
-
|
|
1674
|
-
// If user lacks run permission, execution fails with:
|
|
1675
|
-
// Error: "User {email} does not have permission to run agent '{name}'"
|
|
1676
|
-
```
|
|
1677
|
-
|
|
1678
|
-
### Managing Permissions in the UI
|
|
1679
|
-
|
|
1680
|
-
The AI Agent form includes a "Permissions" button that opens a dialog for managing permissions:
|
|
1681
|
-
|
|
1682
|
-
- View all existing permissions for the agent
|
|
1683
|
-
- Add new user or role-based permissions
|
|
1684
|
-
- Edit existing permissions with hierarchical checkboxes
|
|
1685
|
-
- Delete permissions to return to default behavior
|
|
1686
|
-
- See effective permissions after hierarchy is applied
|
|
1687
|
-
- Display owner information with visual indicator
|
|
1688
|
-
|
|
1689
|
-
### Permission Caching
|
|
1690
|
-
|
|
1691
|
-
Permissions are cached in the AIEngineBase metadata system for performance:
|
|
1692
|
-
|
|
1693
|
-
```typescript
|
|
1694
|
-
// Clear cache after modifying permissions
|
|
1695
|
-
AIEngineBase.Instance.ClearAgentPermissionsCache();
|
|
1696
|
-
|
|
1697
|
-
// Refresh cache for specific agent
|
|
1698
|
-
await AIEngineBase.Instance.RefreshAgentPermissionsCache(agentId, user);
|
|
1699
|
-
```
|
|
1700
|
-
|
|
1701
|
-
### Best Practices for Permissions
|
|
1702
|
-
|
|
1703
|
-
1. **Start Open**: Let anyone run new agents by default, add restrictions only when needed
|
|
1704
|
-
2. **Use Roles**: Grant permissions to roles instead of individual users when possible
|
|
1705
|
-
3. **Document Permissions**: Use the Comments field to explain why permissions were granted
|
|
1706
|
-
4. **Ownership Transfer**: Transfer ownership when primary maintainers change
|
|
1707
|
-
5. **Hierarchical Thinking**: Set the highest permission needed; lower ones are automatic
|
|
1708
|
-
6. **Test Access**: Verify permissions work as expected before deploying agents
|
|
1709
|
-
7. **Regular Audits**: Review permission records periodically to remove unnecessary entries
|
|
1710
|
-
|
|
1711
|
-
### Example Permission Scenarios
|
|
1712
|
-
|
|
1713
|
-
**Scenario 1: Public Agent**
|
|
1714
|
-
- No permission records
|
|
1715
|
-
- Anyone can view and run
|
|
1716
|
-
- Only owner can edit/delete
|
|
1717
|
-
|
|
1718
|
-
**Scenario 2: Department Agent**
|
|
1719
|
-
- Permission record: Role="Sales Team", CanRun=true
|
|
1720
|
-
- Sales team members can view and run
|
|
1721
|
-
- Owner can edit/delete
|
|
1722
|
-
- Others cannot access
|
|
1723
|
-
|
|
1724
|
-
**Scenario 3: Restricted Agent**
|
|
1725
|
-
- Permission record: Role="Admins", CanEdit=true
|
|
1726
|
-
- Admins can view, run, edit (and delete via hierarchy)
|
|
1727
|
-
- Permission record: Role="Developers", CanRun=true
|
|
1728
|
-
- Developers can view and run
|
|
1729
|
-
- Owner has full access
|
|
1730
|
-
- Others cannot access
|
|
1731
|
-
|
|
1732
|
-
**Scenario 4: Shared Ownership**
|
|
1733
|
-
- Permission record: User="alice@example.com", CanEdit=true
|
|
1734
|
-
- Alice can view, run, and edit
|
|
1735
|
-
- Permission record: User="bob@example.com", CanEdit=true
|
|
1736
|
-
- Bob can view, run, and edit
|
|
1737
|
-
- Owner can delete
|
|
1738
|
-
- Creates "co-owner" scenario for collaborative development
|
|
1739
|
-
|
|
1740
|
-
## Database Schema
|
|
1741
|
-
|
|
1742
|
-
Key entities used by the agent framework:
|
|
1743
|
-
|
|
1744
|
-
- **AIAgentType**: Agent behavior patterns and system prompts
|
|
1745
|
-
- **AIAgent**: Configured agent instances
|
|
1746
|
-
- `OwnerUserID`: User who owns the agent (defaults to creator, grants full permissions)
|
|
1747
|
-
- `PayloadDownstreamPaths`: JSON array of paths sub-agents can read
|
|
1748
|
-
- `PayloadUpstreamPaths`: JSON array of paths sub-agents can write
|
|
1749
|
-
- **NEW** `PayloadScope`: Path to narrow payload for sub-agents (e.g., "/functionalRequirements")
|
|
1750
|
-
- **NEW** `StartingPayloadValidation`: JSON validation schema for input validation
|
|
1751
|
-
- **NEW** `StartingPayloadValidationMode`: How to handle input validation failures (Fail/Warn)
|
|
1752
|
-
- **NEW** `FinalPayloadValidation`: JSON validation schema for success validation
|
|
1753
|
-
- **NEW** `FinalPayloadValidationMode`: How to handle validation failures (Retry/Fail/Warn)
|
|
1754
|
-
- **NEW** `FinalPayloadValidationMaxRetries`: Maximum retry attempts for validation (default: 3)
|
|
1755
|
-
- **NEW** `MaxCostPerRun`: Cost limit per agent run
|
|
1756
|
-
- **NEW** `MaxTokensPerRun`: Token limit per agent run
|
|
1757
|
-
- **NEW** `MaxIterationsPerRun`: Iteration limit per agent run
|
|
1758
|
-
- **NEW** `MaxTimePerRun`: Time limit in seconds per agent run
|
|
1759
|
-
- **AIAgentPermission**: Permission records for agent access control
|
|
1760
|
-
- `AgentID`: The agent being controlled
|
|
1761
|
-
- `UserID`: Direct user permission (exclusive with RoleID)
|
|
1762
|
-
- `RoleID`: Role-based permission (exclusive with UserID)
|
|
1763
|
-
- `CanView`: View agent configuration
|
|
1764
|
-
- `CanRun`: Execute the agent
|
|
1765
|
-
- `CanEdit`: Modify agent configuration
|
|
1766
|
-
- `CanDelete`: Remove the agent
|
|
1767
|
-
- `Comments`: Optional permission description
|
|
1768
|
-
- **AIPrompt**: Reusable prompt templates with placeholders
|
|
1769
|
-
- **AIAgentPrompt**: Links agents to prompts with execution order
|
|
1770
|
-
- **AIAgentRun**: Tracks complete agent executions
|
|
1771
|
-
- `LastRunID`: Links to previous run in a chain (for run chaining)
|
|
1772
|
-
- `StartingPayload`: Initial payload for the run
|
|
1773
|
-
- **NEW** `TotalPromptIterations`: Count of prompt executions in the run
|
|
1774
|
-
- **AIAgentRunStep**: Records individual steps within runs
|
|
1775
|
-
- `PayloadAtStart`: JSON snapshot of payload before step
|
|
1776
|
-
- `PayloadAtEnd`: JSON snapshot of payload after step
|
|
1777
|
-
- `OutputData`: Includes `payloadChangeResult` with analysis
|
|
1778
|
-
- **NEW** `FinalPayloadValidationResult`: Validation outcome (Pass/Retry/Fail/Warn)
|
|
1779
|
-
- **NEW** `FinalPayloadValidationMessages`: Validation error messages
|
|
1780
|
-
- **AIAgentRunStepAction**: Details of actions executed
|
|
1781
|
-
- **AIAgentRunStepPrompt**: Prompt execution details
|
|
1782
|
-
|
|
1783
|
-
## Best Practices
|
|
1784
|
-
|
|
1785
|
-
1. **Hierarchical Design**: Use system prompts for base behavior, agent prompts for specifics
|
|
1786
|
-
2. **Structured Responses**: Design prompts to return parseable JSON for agent types
|
|
1787
|
-
3. **Modular Prompts**: Break complex tasks into ordered, focused prompts
|
|
1788
|
-
4. **Proper Type Registration**: Register custom agent types with ClassFactory
|
|
1789
|
-
5. **Comprehensive Tracking**: Leverage built-in tracking for debugging and analysis
|
|
1790
|
-
6. **Context Efficiency**: Let the framework handle context compression automatically
|
|
1791
|
-
7. **Error Handling**: Implement robust error handling in custom agent types
|
|
1792
|
-
8. **Payload Security**: Use path-based access control for sub-agents
|
|
1793
|
-
9. **Change Monitoring**: Review payload change warnings in OutputData
|
|
1794
|
-
10. **Payload Scoping**: Use PayloadScope to reduce token usage for sub-agents
|
|
1795
|
-
11. **Input Validation**: Define StartingPayloadValidation to catch errors early
|
|
1796
|
-
12. **Output Validation**: Define FinalPayloadValidation for output quality control
|
|
1797
|
-
13. **Set Guardrails**: Configure cost/token/time limits to prevent runaway execution
|
|
1798
|
-
14. **Monitor Retries**: Track validation retry counts to avoid infinite loops
|
|
1799
|
-
15. **Fail Fast**: Use StartingPayloadValidation with 'Fail' mode for deterministic behavior
|
|
1800
|
-
16. **Permission Strategy**: Start with open access, add restrictions only when needed
|
|
1801
|
-
17. **Role-Based Permissions**: Use role-based permissions for easier management at scale
|
|
1802
|
-
18. **Document Access**: Use Comments field in permission records to explain grant rationale
|
|
1803
|
-
|
|
1804
|
-
## Examples
|
|
1805
|
-
|
|
1806
|
-
### Basic Loop Agent
|
|
1807
|
-
```typescript
|
|
1808
|
-
// Agent type configured with LoopAgentType driver
|
|
1809
|
-
// System prompt defines JSON response format
|
|
1810
|
-
// Agent prompts execute tasks iteratively
|
|
1811
|
-
const result = await runner.RunAgent({
|
|
1812
|
-
agent: loopAgent,
|
|
1813
|
-
conversationMessages: [
|
|
1814
|
-
{ role: 'user', content: 'Analyze these sales figures and create a report' }
|
|
1815
|
-
],
|
|
1816
|
-
contextUser: user
|
|
1817
|
-
});
|
|
1818
|
-
```
|
|
1819
|
-
|
|
1820
|
-
### Payload Operation Control Example
|
|
1821
|
-
```typescript
|
|
1822
|
-
// Configure an agent with specific operation permissions
|
|
1823
|
-
const analysisAgent = {
|
|
1824
|
-
Name: 'DataAnalysisAgent',
|
|
1825
|
-
PayloadSelfWritePaths: JSON.stringify([
|
|
1826
|
-
"workspace.*", // Full control over workspace
|
|
1827
|
-
"analysis.results:add", // Can only add new results
|
|
1828
|
-
"analysis.status:update", // Can only update status
|
|
1829
|
-
"temp.*:add,delete" // Can add/delete temp data, but not modify
|
|
1830
|
-
])
|
|
1831
|
-
};
|
|
1832
|
-
|
|
1833
|
-
// Configure a sub-agent with restricted write access
|
|
1834
|
-
const validationAgent = {
|
|
1835
|
-
Name: 'ValidationAgent',
|
|
1836
|
-
PayloadDownstreamPaths: JSON.stringify([
|
|
1837
|
-
"data.*", // Can read all data
|
|
1838
|
-
"analysis.results" // Can read analysis results
|
|
1839
|
-
]),
|
|
1840
|
-
PayloadUpstreamPaths: JSON.stringify([
|
|
1841
|
-
"data.validated:update", // Can only update validation flag
|
|
1842
|
-
"errors:add", // Can only add errors, not modify
|
|
1843
|
-
"warnings:add,delete" // Can add/remove warnings
|
|
1844
|
-
])
|
|
1845
|
-
};
|
|
1846
|
-
|
|
1847
|
-
// When the sub-agent tries unauthorized operations:
|
|
1848
|
-
// - Attempt to delete data.records → Blocked (no delete permission)
|
|
1849
|
-
// - Attempt to update errors → Blocked (only add permission)
|
|
1850
|
-
// - Add new warning → Allowed
|
|
1851
|
-
// - Update data.validated → Allowed
|
|
1852
|
-
```
|
|
1853
|
-
|
|
1854
|
-
### Custom Decision Tree Agent
|
|
1855
|
-
```typescript
|
|
1856
|
-
@RegisterClass(BaseAgentType, "DecisionTreeAgent")
|
|
1857
|
-
export class DecisionTreeAgent extends BaseAgentType {
|
|
1858
|
-
async DetermineNextStep(result: AIPromptRunResult): Promise<BaseAgentNextStep> {
|
|
1859
|
-
const decision = JSON.parse(result.FullResult);
|
|
1860
|
-
|
|
1861
|
-
switch(decision.branch) {
|
|
1862
|
-
case 'needs_data':
|
|
1863
|
-
return { type: 'action', actionName: 'FetchData', actionParams: decision.params };
|
|
1864
|
-
case 'analyze':
|
|
1865
|
-
return { type: 'sub_agent', agentName: 'AnalysisAgent', messages: decision.context };
|
|
1866
|
-
case 'complete':
|
|
1867
|
-
return { type: 'stop', reason: decision.summary };
|
|
1868
|
-
default:
|
|
1869
|
-
return { type: 'continue' };
|
|
1870
|
-
}
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
```
|
|
1874
|
-
|
|
1875
|
-
## Flow Agent Type - Deterministic Workflows
|
|
1876
|
-
|
|
1877
|
-
Flow agents execute **deterministic, graph-based workflows** where the execution path is determined by boolean conditions evaluated against the payload and step results. Unlike Loop agents that rely on LLM decision-making at each step, Flow agents follow predefined paths through a directed graph.
|
|
1878
|
-
|
|
1879
|
-
### When to Use Flow Agents
|
|
1880
|
-
|
|
1881
|
-
Flow agents are ideal for:
|
|
1882
|
-
- **Predictable workflows** with well-defined decision points
|
|
1883
|
-
- **Approval processes** with conditional routing
|
|
1884
|
-
- **Data pipelines** with validation and transformation steps
|
|
1885
|
-
- **Hybrid workflows** combining deterministic logic with AI prompts
|
|
1886
|
-
- **Multi-step processes** where you need guaranteed execution order
|
|
1887
|
-
|
|
1888
|
-
### Flow Agent Execution Parameters
|
|
1889
|
-
|
|
1890
|
-
Flow agents support specialized execution parameters via `FlowAgentExecuteParams` (v2.127+) that allow runtime customization of how the flow executes:
|
|
1891
|
-
|
|
1892
|
-
```typescript
|
|
1893
|
-
import { FlowAgentExecuteParams } from '@memberjunction/ai-agents';
|
|
1894
|
-
import { ExecuteAgentParams } from '@memberjunction/ai-core-plus';
|
|
1895
|
-
import { AIEngine } from '@memberjunction/ai-engine-base';
|
|
1896
|
-
|
|
1897
|
-
// Get the steps you want to work with
|
|
1898
|
-
const agentSteps = AIEngine.Instance.GetAgentSteps(myFlowAgent.ID);
|
|
1899
|
-
const approvalStep = agentSteps.find(s => s.Name === 'Approval Review');
|
|
1900
|
-
const notificationStep = agentSteps.find(s => s.Name === 'Send Notification');
|
|
1901
|
-
|
|
1902
|
-
// Execute with Flow Agent-specific parameters
|
|
1903
|
-
const params: ExecuteAgentParams<unknown, unknown, FlowAgentExecuteParams> = {
|
|
1904
|
-
agent: myFlowAgent,
|
|
1905
|
-
conversationMessages: messages,
|
|
1906
|
-
contextUser: user,
|
|
1907
|
-
agentTypeParams: {
|
|
1908
|
-
// Start at a specific step instead of the configured entry point
|
|
1909
|
-
startAtStep: approvalStep,
|
|
1910
|
-
|
|
1911
|
-
// Skip these steps during execution
|
|
1912
|
-
skipSteps: [notificationStep]
|
|
1913
|
-
}
|
|
1914
|
-
};
|
|
1915
|
-
|
|
1916
|
-
const result = await runner.RunAgent(params);
|
|
1917
|
-
```
|
|
1918
|
-
|
|
1919
|
-
#### FlowAgentExecuteParams Interface
|
|
1920
|
-
|
|
1921
|
-
```typescript
|
|
1922
|
-
interface FlowAgentExecuteParams {
|
|
1923
|
-
/**
|
|
1924
|
-
* Start execution at a specific step instead of the flow's entry point.
|
|
1925
|
-
*
|
|
1926
|
-
* When provided, the flow agent will begin execution at this step,
|
|
1927
|
-
* skipping all steps that would normally precede it. Useful for:
|
|
1928
|
-
* - Resuming a flow from a specific point
|
|
1929
|
-
* - Testing specific branches of a flow
|
|
1930
|
-
* - Re-running a portion of a flow after a failure
|
|
1931
|
-
*
|
|
1932
|
-
* The step must belong to the agent being executed.
|
|
1933
|
-
*/
|
|
1934
|
-
startAtStep?: AIAgentStepEntity;
|
|
1935
|
-
|
|
1936
|
-
/**
|
|
1937
|
-
* Steps to skip during execution.
|
|
1938
|
-
*
|
|
1939
|
-
* When the flow would normally execute one of these steps, it will
|
|
1940
|
-
* instead immediately evaluate the step's outgoing paths and continue
|
|
1941
|
-
* to the next valid step. Useful for:
|
|
1942
|
-
* - Bypassing steps that have already been completed externally
|
|
1943
|
-
* - Testing flows without certain side effects
|
|
1944
|
-
* - Conditional step execution based on runtime state
|
|
1945
|
-
*
|
|
1946
|
-
* Skipped steps are recorded in the execution path but marked as skipped.
|
|
1947
|
-
* The step's output mapping is not applied when skipped.
|
|
1948
|
-
*/
|
|
1949
|
-
skipSteps?: AIAgentStepEntity[];
|
|
1950
|
-
}
|
|
1951
|
-
```
|
|
1952
|
-
|
|
1953
|
-
#### Use Cases
|
|
1954
|
-
|
|
1955
|
-
**Resume Flow After Failure:**
|
|
1956
|
-
```typescript
|
|
1957
|
-
// User's approval was rejected, they fixed issues and want to resume
|
|
1958
|
-
const reviewStep = agentSteps.find(s => s.Name === 'Manager Review');
|
|
1959
|
-
|
|
1960
|
-
await runner.RunAgent({
|
|
1961
|
-
agent: approvalFlowAgent,
|
|
1962
|
-
agentTypeParams: {
|
|
1963
|
-
startAtStep: reviewStep // Skip validation, go straight to review
|
|
1964
|
-
},
|
|
1965
|
-
payload: fixedPayload
|
|
1966
|
-
});
|
|
1967
|
-
```
|
|
1968
|
-
|
|
1969
|
-
**Skip Steps Based on External State:**
|
|
1970
|
-
```typescript
|
|
1971
|
-
// Notification was already sent via another system
|
|
1972
|
-
const notifyStep = agentSteps.find(s => s.Name === 'Send Notification');
|
|
1973
|
-
const auditStep = agentSteps.find(s => s.Name === 'Create Audit Log');
|
|
1974
|
-
|
|
1975
|
-
await runner.RunAgent({
|
|
1976
|
-
agent: workflowAgent,
|
|
1977
|
-
agentTypeParams: {
|
|
1978
|
-
skipSteps: [notifyStep, auditStep] // Skip these, they're handled externally
|
|
1979
|
-
}
|
|
1980
|
-
});
|
|
1981
|
-
```
|
|
1982
|
-
|
|
1983
|
-
**Testing Specific Flow Branches:**
|
|
1984
|
-
```typescript
|
|
1985
|
-
// Test only the rejection path
|
|
1986
|
-
const rejectionStep = agentSteps.find(s => s.Name === 'Handle Rejection');
|
|
1987
|
-
|
|
1988
|
-
await runner.RunAgent({
|
|
1989
|
-
agent: approvalFlowAgent,
|
|
1990
|
-
agentTypeParams: {
|
|
1991
|
-
startAtStep: rejectionStep
|
|
1992
|
-
},
|
|
1993
|
-
payload: { decision: { approved: false, reason: 'Budget exceeded' } }
|
|
1994
|
-
});
|
|
1995
|
-
```
|
|
1996
|
-
|
|
1997
|
-
#### Behavior Notes
|
|
1998
|
-
|
|
1999
|
-
- **startAtStep validation**: The step must belong to the agent being executed. If invalid, execution fails with a descriptive error.
|
|
2000
|
-
- **skipSteps behavior**: Skipped steps are marked as completed in the flow state with `{ skipped: true, stepName: '...' }` as the result.
|
|
2001
|
-
- **Path evaluation**: When a step is skipped, its outgoing paths are still evaluated to determine the next step.
|
|
2002
|
-
- **Recursive skipping**: If the next step after a skipped step is also in `skipSteps`, it will also be skipped until a non-skipped step is reached.
|
|
2003
|
-
- **Output mapping**: Skipped steps do not apply their `ActionOutputMapping` since no action is executed.
|
|
2004
|
-
|
|
2005
|
-
### Core Concepts
|
|
2006
|
-
|
|
2007
|
-
#### 1. Workflow Steps (AIAgentStep)
|
|
2008
|
-
|
|
2009
|
-
Steps are the nodes in your workflow graph. Each step represents an action to perform:
|
|
2010
|
-
|
|
2011
|
-
```typescript
|
|
2012
|
-
// Three types of steps:
|
|
2013
|
-
{
|
|
2014
|
-
Name: 'ValidateInput',
|
|
2015
|
-
StepType: 'Action', // Execute a MJ Action
|
|
2016
|
-
ActionID: 'validation-action-id',
|
|
2017
|
-
StartingStep: true, // Marks this as an entry point
|
|
2018
|
-
Sequence: 0, // For parallel starting steps
|
|
2019
|
-
Status: 'Active', // Active, Disabled, or Pending
|
|
2020
|
-
TimeoutSeconds: 30 // Optional timeout
|
|
2021
|
-
}
|
|
2022
|
-
|
|
2023
|
-
{
|
|
2024
|
-
Name: 'AnalyzeData',
|
|
2025
|
-
StepType: 'Prompt', // Execute an AI prompt
|
|
2026
|
-
PromptID: 'analysis-prompt-id',
|
|
2027
|
-
Description: 'Analyze data quality and completeness'
|
|
2028
|
-
}
|
|
2029
|
-
|
|
2030
|
-
{
|
|
2031
|
-
Name: 'ProcessWithSubAgent',
|
|
2032
|
-
StepType: 'Sub-Agent', // Invoke another agent
|
|
2033
|
-
SubAgentID: 'processing-agent-id'
|
|
2034
|
-
}
|
|
2035
|
-
```
|
|
2036
|
-
|
|
2037
|
-
#### 2. Workflow Paths (AIAgentStepPath)
|
|
2038
|
-
|
|
2039
|
-
Paths are the edges connecting your workflow nodes. They determine the flow:
|
|
2040
|
-
|
|
2041
|
-
```typescript
|
|
2042
|
-
{
|
|
2043
|
-
OriginStepID: 'step-a-id',
|
|
2044
|
-
DestinationStepID: 'step-b-id',
|
|
2045
|
-
Condition: 'payload.amount > 1000 && payload.approved === true',
|
|
2046
|
-
Priority: 10 // Higher priority paths evaluated first
|
|
2047
|
-
}
|
|
2048
|
-
|
|
2049
|
-
// Path without condition (always valid)
|
|
2050
|
-
{
|
|
2051
|
-
OriginStepID: 'step-a-id',
|
|
2052
|
-
DestinationStepID: 'default-step-id',
|
|
2053
|
-
Condition: null, // No condition = always valid
|
|
2054
|
-
Priority: 0 // Lower priority = fallback
|
|
2055
|
-
}
|
|
2056
|
-
```
|
|
2057
|
-
|
|
2058
|
-
#### 3. Output Mapping with Array Append Syntax
|
|
2059
|
-
|
|
2060
|
-
**Array Append Syntax** - When mapping outputs that can occur multiple times, use the `[]` suffix to append values to an array instead of replacing them:
|
|
2061
|
-
|
|
2062
|
-
```typescript
|
|
2063
|
-
// SubAgentOutputMapping for agents that can be called multiple times
|
|
2064
|
-
{
|
|
2065
|
-
"*": "codeAnalysis[]" // Append each sub-agent result to array
|
|
2066
|
-
}
|
|
2067
|
-
|
|
2068
|
-
// ActionOutputMapping for actions that run in loops
|
|
2069
|
-
{
|
|
2070
|
-
"result": "findings[]", // Append to array
|
|
2071
|
-
"score": "scores[]", // Each iteration adds to array
|
|
2072
|
-
"*": "rawResults.allData[]" // Wildcard append
|
|
2073
|
-
}
|
|
2074
|
-
|
|
2075
|
-
// Without [] suffix (default behavior - replace)
|
|
2076
|
-
{
|
|
2077
|
-
"*": "latestResult" // Each call REPLACES the value
|
|
2078
|
-
}
|
|
2079
|
-
|
|
2080
|
-
// Array append features:
|
|
2081
|
-
// - Auto-initializes array if it doesn't exist
|
|
2082
|
-
// - Validates target is an array before appending
|
|
2083
|
-
// - Prevents data loss from multiple sub-agent/action calls
|
|
2084
|
-
// - Works with both simple and nested payload paths
|
|
2085
|
-
```
|
|
2086
|
-
|
|
2087
|
-
**When to Use Array Append**:
|
|
2088
|
-
- Sub-agents that can be invoked multiple times (e.g., Codesmith for different analysis tasks)
|
|
2089
|
-
- Actions in ForEach/While loops where each iteration produces a result
|
|
2090
|
-
- Accumulating multiple responses over agent execution
|
|
2091
|
-
- Building collections of findings, recommendations, or analysis results
|
|
2092
|
-
|
|
2093
|
-
**Example Use Case**:
|
|
2094
|
-
```typescript
|
|
2095
|
-
// Research Agent with Codesmith sub-agent for code-based analytics
|
|
2096
|
-
// Each time Codesmith is called, its output is appended to codeAnalysis[]
|
|
2097
|
-
|
|
2098
|
-
// First call to Codesmith
|
|
2099
|
-
{
|
|
2100
|
-
"name": "Sales Trend Analysis",
|
|
2101
|
-
"code": "...",
|
|
2102
|
-
"output": { trend: "increasing", rate: 0.15 }
|
|
2103
|
-
}
|
|
2104
|
-
|
|
2105
|
-
// Second call to Codesmith
|
|
2106
|
-
{
|
|
2107
|
-
"name": "Customer Segmentation",
|
|
2108
|
-
"code": "...",
|
|
2109
|
-
"output": { segments: [...] }
|
|
2110
|
-
}
|
|
2111
|
-
|
|
2112
|
-
// Final payload.codeAnalysis array contains BOTH results:
|
|
2113
|
-
[
|
|
2114
|
-
{ name: "Sales Trend Analysis", output: {...} },
|
|
2115
|
-
{ name: "Customer Segmentation", output: {...} }
|
|
2116
|
-
]
|
|
2117
|
-
```
|
|
2118
|
-
|
|
2119
|
-
#### 4. Action Input/Output Mapping
|
|
2120
|
-
|
|
2121
|
-
**Action Input Mapping** (`ActionInputMapping`) - Maps payload values to action parameters:
|
|
2122
|
-
|
|
2123
|
-
```typescript
|
|
2124
|
-
// In AIAgentStep.ActionInputMapping
|
|
2125
|
-
{
|
|
2126
|
-
"customerId": "payload.customer.id", // Map from payload
|
|
2127
|
-
"orderDate": "static:2024-01-01", // Static value
|
|
2128
|
-
"includeDetails": true, // Boolean literal
|
|
2129
|
-
"maxResults": 100, // Numeric literal
|
|
2130
|
-
"filters": { // Nested object
|
|
2131
|
-
"status": "payload.filters.orderStatus",
|
|
2132
|
-
"region": "static:US-WEST"
|
|
2133
|
-
},
|
|
2134
|
-
"itemIds": "payload.order.items" // Can map arrays
|
|
2135
|
-
}
|
|
2136
|
-
|
|
2137
|
-
// Supports nested resolution
|
|
2138
|
-
{
|
|
2139
|
-
"searchParams": {
|
|
2140
|
-
"query": "payload.searchTerm",
|
|
2141
|
-
"filters": {
|
|
2142
|
-
"category": "payload.category",
|
|
2143
|
-
"tags": "payload.selectedTags"
|
|
2144
|
-
},
|
|
2145
|
-
"options": {
|
|
2146
|
-
"maxResults": 50,
|
|
2147
|
-
"includeMetadata": true
|
|
2148
|
-
}
|
|
2149
|
-
}
|
|
2150
|
-
}
|
|
2151
|
-
```
|
|
2152
|
-
|
|
2153
|
-
**Action Output Mapping** (`ActionOutputMapping`) - Maps action results back to payload or special fields:
|
|
2154
|
-
|
|
2155
|
-
```typescript
|
|
2156
|
-
// In AIAgentStep.ActionOutputMapping
|
|
2157
|
-
{
|
|
2158
|
-
"userId": "payload.customer.id", // Map specific output param
|
|
2159
|
-
"orderTotal": "payload.order.total", // Nested path in payload
|
|
2160
|
-
"metadata": "payload.action.lastResult", // Arbitrary nesting
|
|
2161
|
-
"*": "payload.rawResults.fullData", // Wildcard = entire result
|
|
2162
|
-
"responseText": "$message", // Special field - user message
|
|
2163
|
-
"analysisDetails": "$reasoning", // Special field - reasoning
|
|
2164
|
-
"confidenceScore": "$confidence" // Special field - confidence
|
|
2165
|
-
}
|
|
2166
|
-
|
|
2167
|
-
// Case-insensitive output parameter matching
|
|
2168
|
-
// If action returns { UserId: "123" }, it matches "userId" in mapping
|
|
2169
|
-
```
|
|
2170
|
-
|
|
2171
|
-
**Special Fields** (Flow Agents Only):
|
|
2172
|
-
|
|
2173
|
-
Use the `$` prefix to map action outputs to special response fields instead of the payload:
|
|
2174
|
-
|
|
2175
|
-
- **`$message`**: Maps to the user-facing message in the final Success step
|
|
2176
|
-
- **`$reasoning`**: Optional reasoning/explanation shown with the response
|
|
2177
|
-
- **`$confidence`**: Optional confidence score (number) for the response
|
|
2178
|
-
|
|
2179
|
-
**Example - Betty Knowledge Base Agent:**
|
|
2180
|
-
```typescript
|
|
2181
|
-
// Betty action returns: { BettyResponse: "The answer is...", BettyReferences: [...] }
|
|
2182
|
-
{
|
|
2183
|
-
"BettyResponse": "$message", // Shows directly to user
|
|
2184
|
-
"BettyReferences": "references" // Stored in payload
|
|
2185
|
-
}
|
|
2186
|
-
|
|
2187
|
-
// When flow completes, user sees Betty's response as the message
|
|
2188
|
-
// No LLM processing needed - deterministic, single-step flow
|
|
2189
|
-
```
|
|
2190
|
-
|
|
2191
|
-
**Special Field Benefits:**
|
|
2192
|
-
- ✅ Eliminates need for LLM to format final response
|
|
2193
|
-
- ✅ Enables deterministic flows with dynamic user messages
|
|
2194
|
-
- ✅ Clearly separates UI content from payload data
|
|
2195
|
-
- ✅ No namespace pollution - can still have `message` in payload
|
|
2196
|
-
|
|
2197
|
-
#### 5. Prompt Result Merging
|
|
2198
|
-
|
|
2199
|
-
When a Prompt step executes, its JSON response is **deep merged** into the payload:
|
|
2200
|
-
|
|
2201
|
-
```typescript
|
|
2202
|
-
// Before prompt execution
|
|
2203
|
-
payload = {
|
|
2204
|
-
decision: {
|
|
2205
|
-
status: "pending",
|
|
2206
|
-
reviewerId: "user-123"
|
|
2207
|
-
},
|
|
2208
|
-
metadata: { startTime: "..." }
|
|
2209
|
-
};
|
|
2210
|
-
|
|
2211
|
-
// Prompt returns
|
|
2212
|
-
promptResponse = {
|
|
2213
|
-
decision: {
|
|
2214
|
-
approved: true,
|
|
2215
|
-
confidence: 0.95
|
|
2216
|
-
}
|
|
2217
|
-
};
|
|
2218
|
-
|
|
2219
|
-
// After deep merge (preserves existing keys!)
|
|
2220
|
-
payload = {
|
|
2221
|
-
decision: {
|
|
2222
|
-
approved: true, // NEW from prompt
|
|
2223
|
-
confidence: 0.95, // NEW from prompt
|
|
2224
|
-
status: "pending", // PRESERVED from before
|
|
2225
|
-
reviewerId: "user-123" // PRESERVED from before
|
|
2226
|
-
},
|
|
2227
|
-
metadata: { startTime: "..." } // PRESERVED
|
|
2228
|
-
};
|
|
2229
|
-
```
|
|
2230
|
-
|
|
2231
|
-
**Why Deep Merge?**
|
|
2232
|
-
- **Preserves context** - Existing payload data isn't lost
|
|
2233
|
-
- **Incremental updates** - Prompts can add fields without destroying structure
|
|
2234
|
-
- **Composable decisions** - Multiple prompts can build up complex objects
|
|
2235
|
-
|
|
2236
|
-
**Special Prompt Response Handling**:
|
|
2237
|
-
```typescript
|
|
2238
|
-
// If prompt response contains Chat step request
|
|
2239
|
-
{
|
|
2240
|
-
"nextStep": { "type": "Chat" },
|
|
2241
|
-
"message": "I need more information from the user",
|
|
2242
|
-
"taskComplete": false
|
|
2243
|
-
}
|
|
2244
|
-
// OR
|
|
2245
|
-
{
|
|
2246
|
-
"taskComplete": true,
|
|
2247
|
-
"message": "Here's the final result..."
|
|
2248
|
-
}
|
|
2249
|
-
|
|
2250
|
-
// Flow agent returns Chat step to bubble message to user
|
|
2251
|
-
// This allows prompts within flows to communicate with users
|
|
2252
|
-
```
|
|
2253
|
-
|
|
2254
|
-
### Complete Flow Agent Example
|
|
2255
|
-
|
|
2256
|
-
```typescript
|
|
2257
|
-
// Database configuration for a complete approval workflow
|
|
2258
|
-
// 1. Define the workflow steps
|
|
2259
|
-
const steps = [
|
|
2260
|
-
{
|
|
2261
|
-
Name: 'ValidateRequest',
|
|
2262
|
-
StepType: 'Action',
|
|
2263
|
-
ActionID: validateActionId,
|
|
2264
|
-
StartingStep: true,
|
|
2265
|
-
Sequence: 0,
|
|
2266
|
-
ActionInputMapping: JSON.stringify({
|
|
2267
|
-
"requestData": "payload.request",
|
|
2268
|
-
"validationRules": "payload.rules"
|
|
2269
|
-
}),
|
|
2270
|
-
ActionOutputMapping: JSON.stringify({
|
|
2271
|
-
"isValid": "payload.validation.isValid",
|
|
2272
|
-
"errors": "payload.validation.errors"
|
|
2273
|
-
})
|
|
2274
|
-
},
|
|
2275
|
-
{
|
|
2276
|
-
Name: 'CheckAmount',
|
|
2277
|
-
StepType: 'Prompt',
|
|
2278
|
-
PromptID: amountCheckPromptId,
|
|
2279
|
-
Description: 'AI analyzes amount and risk factors'
|
|
2280
|
-
// Prompt returns: { risk: "low"|"medium"|"high", reasoning: "..." }
|
|
2281
|
-
// Deep merged into payload.risk and payload.reasoning
|
|
2282
|
-
},
|
|
2283
|
-
{
|
|
2284
|
-
Name: 'AutoApprove',
|
|
2285
|
-
StepType: 'Action',
|
|
2286
|
-
ActionID: approveActionId,
|
|
2287
|
-
ActionInputMapping: JSON.stringify({
|
|
2288
|
-
"requestId": "payload.request.id",
|
|
2289
|
-
"approvedBy": "static:SYSTEM_AUTO"
|
|
2290
|
-
}),
|
|
2291
|
-
ActionOutputMapping: JSON.stringify({
|
|
2292
|
-
"approvalId": "payload.approval.id",
|
|
2293
|
-
"timestamp": "payload.approval.timestamp"
|
|
2294
|
-
})
|
|
2295
|
-
},
|
|
2296
|
-
{
|
|
2297
|
-
Name: 'ManagerReview',
|
|
2298
|
-
StepType: 'Sub-Agent',
|
|
2299
|
-
SubAgentID: managerReviewAgentId
|
|
2300
|
-
// Sub-agent payload inherits and can modify parent payload
|
|
2301
|
-
},
|
|
2302
|
-
{
|
|
2303
|
-
Name: 'NotifyUser',
|
|
2304
|
-
StepType: 'Action',
|
|
2305
|
-
ActionID: notificationActionId,
|
|
2306
|
-
ActionInputMapping: JSON.stringify({
|
|
2307
|
-
"userId": "payload.request.userId",
|
|
2308
|
-
"message": "payload.approval.notificationMessage",
|
|
2309
|
-
"channel": "static:email"
|
|
2310
|
-
})
|
|
2311
|
-
}
|
|
2312
|
-
];
|
|
2313
|
-
|
|
2314
|
-
// 2. Define the workflow paths
|
|
2315
|
-
const paths = [
|
|
2316
|
-
// From validation
|
|
2317
|
-
{
|
|
2318
|
-
OriginStepID: validateStepId,
|
|
2319
|
-
DestinationStepID: checkAmountStepId,
|
|
2320
|
-
Condition: 'payload.validation.isValid === true',
|
|
2321
|
-
Priority: 10
|
|
2322
|
-
},
|
|
2323
|
-
{
|
|
2324
|
-
OriginStepID: validateStepId,
|
|
2325
|
-
DestinationStepID: notifyUserStepId,
|
|
2326
|
-
Condition: 'payload.validation.isValid === false',
|
|
2327
|
-
Priority: 10
|
|
2328
|
-
},
|
|
2329
|
-
|
|
2330
|
-
// From AI risk assessment
|
|
2331
|
-
{
|
|
2332
|
-
OriginStepID: checkAmountStepId,
|
|
2333
|
-
DestinationStepID: autoApproveStepId,
|
|
2334
|
-
Condition: 'payload.risk === "low" && payload.request.amount <= 1000',
|
|
2335
|
-
Priority: 10
|
|
2336
|
-
},
|
|
2337
|
-
{
|
|
2338
|
-
OriginStepID: checkAmountStepId,
|
|
2339
|
-
DestinationStepID: managerReviewStepId,
|
|
2340
|
-
Condition: 'payload.risk === "medium" || payload.risk === "high"',
|
|
2341
|
-
Priority: 10
|
|
2342
|
-
},
|
|
2343
|
-
|
|
2344
|
-
// From manager review
|
|
2345
|
-
{
|
|
2346
|
-
OriginStepID: managerReviewStepId,
|
|
2347
|
-
DestinationStepID: autoApproveStepId,
|
|
2348
|
-
Condition: 'payload.managerDecision.approved === true',
|
|
2349
|
-
Priority: 10
|
|
2350
|
-
},
|
|
2351
|
-
{
|
|
2352
|
-
OriginStepID: managerReviewStepId,
|
|
2353
|
-
DestinationStepID: notifyUserStepId,
|
|
2354
|
-
Condition: 'payload.managerDecision.approved === false',
|
|
2355
|
-
Priority: 5
|
|
2356
|
-
},
|
|
2357
|
-
|
|
2358
|
-
// Final notification after approval
|
|
2359
|
-
{
|
|
2360
|
-
OriginStepID: autoApproveStepId,
|
|
2361
|
-
DestinationStepID: notifyUserStepId,
|
|
2362
|
-
Condition: null, // Always execute
|
|
2363
|
-
Priority: 0
|
|
2364
|
-
}
|
|
2365
|
-
];
|
|
2366
|
-
|
|
2367
|
-
// 3. Execute the flow agent
|
|
2368
|
-
const result = await runner.RunAgent({
|
|
2369
|
-
agent: flowAgentEntity,
|
|
2370
|
-
conversationMessages: messages,
|
|
2371
|
-
contextUser: user,
|
|
2372
|
-
payload: {
|
|
2373
|
-
request: {
|
|
2374
|
-
id: "req-123",
|
|
2375
|
-
userId: "user-456",
|
|
2376
|
-
amount: 5000,
|
|
2377
|
-
description: "Equipment purchase"
|
|
2378
|
-
},
|
|
2379
|
-
rules: {
|
|
2380
|
-
maxAutoApprove: 1000,
|
|
2381
|
-
requiresManagerReview: true
|
|
2382
|
-
}
|
|
2383
|
-
}
|
|
2384
|
-
});
|
|
2385
|
-
```
|
|
2386
|
-
|
|
2387
|
-
### Flow Agent Features
|
|
2388
|
-
|
|
2389
|
-
#### Safe Expression Evaluation
|
|
2390
|
-
Flow agents use the SafeExpressionEvaluator to securely evaluate path conditions without arbitrary code execution:
|
|
2391
|
-
|
|
2392
|
-
```typescript
|
|
2393
|
-
// Supported operations in conditions:
|
|
2394
|
-
// - Comparisons: ==, ===, !=, !==, <, >, <=, >=
|
|
2395
|
-
// - Logical: &&, ||, !
|
|
2396
|
-
// - Property access: payload.user.role, stepResult.score
|
|
2397
|
-
// - Safe methods: .includes(), .length, .some(), .every()
|
|
2398
|
-
// - Type checking: typeof
|
|
2399
|
-
|
|
2400
|
-
// Example conditions:
|
|
2401
|
-
"payload.status == 'approved' && payload.priority > 5"
|
|
2402
|
-
"stepResult.items.some(item => item.price > 100)"
|
|
2403
|
-
"payload.user.roles.includes('admin') || payload.override === true"
|
|
2404
|
-
```
|
|
2405
|
-
|
|
2406
|
-
#### Action Output Mapping
|
|
2407
|
-
Automatically map action results to the payload:
|
|
2408
|
-
|
|
2409
|
-
```typescript
|
|
2410
|
-
// In AIAgentStep.ActionOutputMapping
|
|
2411
|
-
{
|
|
2412
|
-
"userId": "payload.customer.id", // Map specific output
|
|
2413
|
-
"orderTotal": "payload.order.total", // Nested path mapping
|
|
2414
|
-
"*": "payload.actionResults.lastResult" // Wildcard for entire result
|
|
2415
|
-
}
|
|
2416
|
-
```
|
|
2417
|
-
|
|
2418
|
-
#### Flow Context Tracking
|
|
2419
|
-
The framework maintains flow execution state in `__flowContext`:
|
|
2420
|
-
|
|
2421
|
-
```typescript
|
|
2422
|
-
// Automatically tracked in payload.__flowContext
|
|
2423
|
-
{
|
|
2424
|
-
agentId: "flow-agent-id",
|
|
2425
|
-
currentStepId: "current-step-id",
|
|
2426
|
-
completedStepIds: ["step1", "step2"],
|
|
2427
|
-
stepResults: {
|
|
2428
|
-
"step1": { success: true, data: {...} },
|
|
2429
|
-
"step2": { approved: false }
|
|
2430
|
-
},
|
|
2431
|
-
executionPath: ["step1", "step2", "step3"]
|
|
2432
|
-
}
|
|
2433
|
-
```
|
|
2434
|
-
|
|
2435
|
-
#### Prompt Steps for AI Decisions
|
|
2436
|
-
Flow agents can incorporate AI decision points:
|
|
2437
|
-
|
|
2438
|
-
```typescript
|
|
2439
|
-
// Prompt step expects response format:
|
|
2440
|
-
{
|
|
2441
|
-
"nextStepName?": "StepToExecute",
|
|
2442
|
-
"reasoning?": "Why this decision was made",
|
|
2443
|
-
"confidence?": 0.95,
|
|
2444
|
-
"terminate?": false,
|
|
2445
|
-
"message?": "Decision explanation"
|
|
2446
|
-
}
|
|
2447
|
-
```
|
|
2448
|
-
|
|
2449
|
-
## Architecture Documentation
|
|
2450
|
-
|
|
2451
|
-
For detailed architecture information, see [agent-architecture.md](./agent-architecture.md).
|
|
2452
|
-
|
|
2453
|
-
## Contributing
|
|
2454
|
-
|
|
2455
|
-
Contributions are welcome! Please see the main MemberJunction [contributing guide](../../../CONTRIBUTING.md).
|
|
2456
|
-
|
|
2457
|
-
## API Keys
|
|
2458
|
-
|
|
2459
|
-
The AI Agents framework supports flexible API key management through integration with the AI Prompts system, including the new environment-based configuration features.
|
|
2460
|
-
|
|
2461
|
-
### Environment-Based Configuration for Agents
|
|
2462
|
-
|
|
2463
|
-
AI Agents benefit from MemberJunction's environment-based configuration system, allowing different API keys and settings per environment:
|
|
2464
|
-
|
|
2465
|
-
```typescript
|
|
2466
|
-
// Agents automatically use the correct configuration based on NODE_ENV
|
|
2467
|
-
// Development -> AIConfigSet(Name='development') -> Different API keys
|
|
2468
|
-
// Production -> AIConfigSet(Name='production') -> Production API keys
|
|
2469
|
-
|
|
2470
|
-
// This is especially useful for:
|
|
2471
|
-
// - Agent testing with development API keys
|
|
2472
|
-
// - Production agents with higher rate limits
|
|
2473
|
-
// - Environment-specific agent behaviors
|
|
2474
|
-
```
|
|
2475
|
-
|
|
2476
|
-
### Using Runtime API Keys with Agents
|
|
2477
|
-
|
|
2478
|
-
You can provide API keys at agent execution time for multi-tenant scenarios:
|
|
2479
|
-
|
|
2480
|
-
```typescript
|
|
2481
|
-
import { AgentRunner, ExecuteAgentParams } from '@memberjunction/ai-agents';
|
|
2482
|
-
import { AIAPIKey } from '@memberjunction/ai';
|
|
2483
|
-
|
|
2484
|
-
const runner = new AgentRunner();
|
|
2485
|
-
|
|
2486
|
-
// Execute agent with specific API keys
|
|
2487
|
-
const result = await runner.RunAgent({
|
|
2488
|
-
agent: agentEntity,
|
|
2489
|
-
conversationMessages: messages,
|
|
2490
|
-
contextUser: user,
|
|
2491
|
-
apiKeys: [
|
|
2492
|
-
{ driverClass: 'OpenAILLM', apiKey: 'sk-user-specific-key' },
|
|
2493
|
-
{ driverClass: 'AnthropicLLM', apiKey: 'sk-ant-department-key' }
|
|
2494
|
-
]
|
|
2495
|
-
});
|
|
2496
|
-
|
|
2497
|
-
// API keys are automatically propagated to:
|
|
2498
|
-
// - All prompt executions by the agent
|
|
2499
|
-
// - Sub-agent executions
|
|
2500
|
-
// - Context compression operations
|
|
2501
|
-
```
|
|
2502
|
-
|
|
2503
|
-
### API Key Resolution for Agents
|
|
2504
|
-
|
|
2505
|
-
When agents execute, API keys are resolved in this priority order:
|
|
2506
|
-
1. **Runtime API keys** passed to RunAgent (highest priority)
|
|
2507
|
-
2. **Configuration sets** from database based on environment
|
|
2508
|
-
3. **Environment variables** (traditional approach)
|
|
2509
|
-
4. **Custom implementations** via AIAPIKeys subclassing
|
|
2510
|
-
|
|
2511
|
-
### Multi-Environment Agent Setup
|
|
2512
|
-
|
|
2513
|
-
```typescript
|
|
2514
|
-
// Example: Different agent configurations per environment
|
|
2515
|
-
|
|
2516
|
-
// Development environment
|
|
2517
|
-
const devAgentConfig = {
|
|
2518
|
-
ConfigSet: { Name: 'development', Priority: 100 },
|
|
2519
|
-
Configurations: [
|
|
2520
|
-
{ ConfigKey: 'OPENAI_LLM_APIKEY', ConfigValue: 'sk-dev-...', Encrypted: true },
|
|
2521
|
-
{ ConfigKey: 'MAX_AGENT_ITERATIONS', ConfigValue: '10', Encrypted: false },
|
|
2522
|
-
{ ConfigKey: 'AGENT_DEBUG_MODE', ConfigValue: 'true', Encrypted: false }
|
|
2523
|
-
]
|
|
2524
|
-
};
|
|
2525
|
-
|
|
2526
|
-
// Production environment
|
|
2527
|
-
const prodAgentConfig = {
|
|
2528
|
-
ConfigSet: { Name: 'production', Priority: 100 },
|
|
2529
|
-
Configurations: [
|
|
2530
|
-
{ ConfigKey: 'OPENAI_LLM_APIKEY', ConfigValue: 'sk-prod-...', Encrypted: true },
|
|
2531
|
-
{ ConfigKey: 'MAX_AGENT_ITERATIONS', ConfigValue: '50', Encrypted: false },
|
|
2532
|
-
{ ConfigKey: 'AGENT_DEBUG_MODE', ConfigValue: 'false', Encrypted: false }
|
|
2533
|
-
]
|
|
2534
|
-
};
|
|
2535
|
-
|
|
2536
|
-
// Agents can access these configurations through the AI engine
|
|
2537
|
-
```
|
|
2538
|
-
|
|
2539
|
-
### Benefits for Agent Systems
|
|
2540
|
-
|
|
2541
|
-
Runtime API keys and environment-based configuration are particularly useful for agent architectures:
|
|
2542
|
-
- **Multi-tenant isolation**: Different customers use their own API keys
|
|
2543
|
-
- **Cost attribution**: Track API usage per department or project
|
|
2544
|
-
- **Security**: Limit exposure of production API keys
|
|
2545
|
-
- **Testing**: Use test API keys for development agents
|
|
2546
|
-
- **Environment-specific behavior**: Different limits and debugging per environment
|
|
2547
|
-
- **Centralized management**: Update configurations without code changes
|
|
2548
|
-
|
|
2549
|
-
### Agent-Specific Configuration Example
|
|
2550
|
-
|
|
2551
|
-
```typescript
|
|
2552
|
-
// Custom agent that uses environment-based configuration
|
|
2553
|
-
@RegisterClass(BaseAgent, "ConfigAwareAgent")
|
|
2554
|
-
export class ConfigAwareAgent extends BaseAgent {
|
|
2555
|
-
protected async getConfiguration(key: string): Promise<string | null> {
|
|
2556
|
-
// The framework automatically loads configurations based on NODE_ENV
|
|
2557
|
-
const envName = process.env.NODE_ENV || 'production';
|
|
2558
|
-
|
|
2559
|
-
// Query AIConfiguration for the current environment
|
|
2560
|
-
const config = await this.loadConfigValue(envName, key);
|
|
2561
|
-
return config;
|
|
2562
|
-
}
|
|
2563
|
-
|
|
2564
|
-
protected async setupExecution(): Promise<void> {
|
|
2565
|
-
// Load agent-specific configurations
|
|
2566
|
-
const maxIterations = await this.getConfiguration('MAX_AGENT_ITERATIONS');
|
|
2567
|
-
const debugMode = await this.getConfiguration('AGENT_DEBUG_MODE');
|
|
2568
|
-
|
|
2569
|
-
// Apply configurations to agent behavior
|
|
2570
|
-
this.maxIterations = parseInt(maxIterations || '50');
|
|
2571
|
-
this.debugMode = debugMode === 'true';
|
|
2572
|
-
}
|
|
2573
|
-
}
|
|
2574
|
-
```
|
|
2575
|
-
|
|
2576
|
-
For detailed information about API key configuration and management, see the [AI Prompts API Keys documentation](../Prompts/README.md#api-keys).
|
|
2577
|
-
|
|
2578
|
-
## AI Configuration for Agents
|
|
2579
|
-
|
|
2580
|
-
Agents fully support the AI Configuration system for environment-specific model selection. When you execute an agent with a `configurationId`, that configuration is automatically propagated to:
|
|
2581
|
-
|
|
2582
|
-
- All prompts executed by the agent
|
|
2583
|
-
- All sub-agents spawned by the agent
|
|
2584
|
-
- All sub-sub-agents in the hierarchy
|
|
2585
|
-
|
|
2586
|
-
### Using Configurations with Agents
|
|
2587
|
-
|
|
2588
|
-
```typescript
|
|
2589
|
-
const result = await runner.RunAgent({
|
|
2590
|
-
agent: myAgent,
|
|
2591
|
-
conversationMessages: messages,
|
|
2592
|
-
contextUser: user,
|
|
2593
|
-
configurationId: 'dev-config-id', // Optional - propagates to all prompts
|
|
2594
|
-
});
|
|
2595
|
-
```
|
|
2596
|
-
|
|
2597
|
-
### Configuration Benefits for Agents
|
|
2598
|
-
|
|
2599
|
-
- **Environment Isolation**: Test agents with development models without affecting production
|
|
2600
|
-
- **Consistent Model Selection**: All prompts in the agent hierarchy use the same configuration
|
|
2601
|
-
- **Easy Switching**: Change configurations without modifying agent code
|
|
2602
|
-
- **Fallback Support**: Agents continue to work even if specific models aren't configured
|
|
2603
|
-
|
|
2604
|
-
For comprehensive details about how AI Configurations work, including model selection logic and fallback behavior, see the [AI Configuration System documentation](../Prompts/README.md#ai-configuration-system).
|
|
2605
|
-
|
|
2606
|
-
## Effort Level Control in Agents
|
|
2607
|
-
|
|
2608
|
-
Agents support sophisticated effort level management that controls how much reasoning effort AI models apply to each prompt execution. The effort level uses a 1-100 integer scale where higher values request more thorough analysis.
|
|
2609
|
-
|
|
2610
|
-
### Effort Level Hierarchy
|
|
2611
|
-
|
|
2612
|
-
The effort level is resolved using hierarchical precedence:
|
|
2613
|
-
|
|
2614
|
-
1. **Runtime Override** (`ExecuteAgentParams.effortLevel`) - Highest priority
|
|
2615
|
-
2. **Agent Default** (`AIAgent.DefaultPromptEffortLevel`) - Medium priority
|
|
2616
|
-
3. **Prompt Setting** (`AIPrompt.EffortLevel`) - Lower priority
|
|
2617
|
-
4. **Provider Default** - Natural model behavior (lowest priority)
|
|
2618
|
-
|
|
2619
|
-
### Agent Execution with Effort Level
|
|
2620
|
-
|
|
2621
|
-
```typescript
|
|
2622
|
-
// Execute agent with high effort level for all prompts
|
|
2623
|
-
const result = await runner.RunAgent({
|
|
2624
|
-
agent: myAnalysisAgent,
|
|
2625
|
-
conversationMessages: messages,
|
|
2626
|
-
contextUser: user,
|
|
2627
|
-
effortLevel: 85 // High effort - applies to all prompts in execution
|
|
2628
|
-
});
|
|
2629
|
-
|
|
2630
|
-
// Execute with medium effort level
|
|
2631
|
-
const result = await runner.RunAgent({
|
|
2632
|
-
agent: myQuickAgent,
|
|
2633
|
-
conversationMessages: messages,
|
|
2634
|
-
contextUser: user,
|
|
2635
|
-
effortLevel: 30 // Low effort - for quick responses
|
|
2636
|
-
});
|
|
2637
|
-
```
|
|
2638
|
-
|
|
2639
|
-
### Sub-Agent Inheritance
|
|
2640
|
-
|
|
2641
|
-
Sub-agents automatically inherit the effort level from their parent unless explicitly overridden:
|
|
2642
|
-
|
|
2643
|
-
```typescript
|
|
2644
|
-
// Parent agent runs with effort level 70
|
|
2645
|
-
const parentResult = await runner.RunAgent({
|
|
2646
|
-
agent: parentAgent,
|
|
2647
|
-
effortLevel: 70, // Inherited by all sub-agents
|
|
2648
|
-
// ...
|
|
2649
|
-
});
|
|
2650
|
-
|
|
2651
|
-
// All sub-agents spawned during execution will use effort level 70
|
|
2652
|
-
// unless the sub-agent has its own DefaultPromptEffortLevel setting
|
|
2653
|
-
```
|
|
2654
|
-
|
|
2655
|
-
### Agent Configuration
|
|
2656
|
-
|
|
2657
|
-
You can configure default effort levels at the agent level:
|
|
2658
|
-
|
|
2659
|
-
- **`AIAgent.DefaultPromptEffortLevel`**: Sets the default effort level for all prompts executed by this agent
|
|
2660
|
-
- This takes precedence over individual prompt effort levels but can be overridden at runtime
|
|
2661
|
-
|
|
2662
|
-
### Provider-Specific Behavior
|
|
297
|
+
## Re-exports
|
|
2663
298
|
|
|
2664
|
-
|
|
299
|
+
For backward compatibility, this package re-exports the following from `@memberjunction/ai-reranker`:
|
|
2665
300
|
|
|
2666
|
-
-
|
|
2667
|
-
-
|
|
2668
|
-
-
|
|
2669
|
-
-
|
|
301
|
+
- `RerankerService`
|
|
302
|
+
- `RerankerConfiguration`
|
|
303
|
+
- `parseRerankerConfiguration`
|
|
304
|
+
- `RerankServiceResult`
|
|
305
|
+
- `RerankObservabilityOptions`
|
|
306
|
+
- `LLMReranker`
|
|
2670
307
|
|
|
2671
|
-
|
|
308
|
+
New code should import these directly from `@memberjunction/ai-reranker`.
|
|
2672
309
|
|
|
2673
|
-
##
|
|
310
|
+
## Dependencies
|
|
2674
311
|
|
|
2675
|
-
|
|
312
|
+
- `@memberjunction/ai-prompts` -- AIPromptRunner for prompt execution
|
|
313
|
+
- `@memberjunction/aiengine` -- AIEngine for metadata and vector search
|
|
314
|
+
- `@memberjunction/ai-core-plus` -- Shared types (ExecuteAgentParams, ExecuteAgentResult)
|
|
315
|
+
- `@memberjunction/ai-engine-base` -- Base metadata cache and permissions
|
|
316
|
+
- `@memberjunction/ai` -- Core AI abstractions
|
|
317
|
+
- `@memberjunction/ai-reranker` -- Two-stage retrieval reranking
|
|
318
|
+
- `@memberjunction/actions` -- Server-side action execution
|
|
319
|
+
- `@memberjunction/actions-base` -- Action framework base types
|
|
320
|
+
- `@memberjunction/core` -- MJ framework core
|
|
321
|
+
- `@memberjunction/core-entities` -- Generated entity classes
|
|
322
|
+
- `@memberjunction/global` -- Class factory and utilities
|
|
323
|
+
- `lodash` -- Utility functions
|