soe-ai 0.2.0b1__py3-none-any.whl

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.
Files changed (145) hide show
  1. soe/__init__.py +50 -0
  2. soe/broker.py +168 -0
  3. soe/builtin_tools/__init__.py +51 -0
  4. soe/builtin_tools/soe_add_signal.py +82 -0
  5. soe/builtin_tools/soe_call_tool.py +111 -0
  6. soe/builtin_tools/soe_copy_context.py +80 -0
  7. soe/builtin_tools/soe_explore_docs.py +290 -0
  8. soe/builtin_tools/soe_get_available_tools.py +42 -0
  9. soe/builtin_tools/soe_get_context.py +50 -0
  10. soe/builtin_tools/soe_get_context_schema.py +56 -0
  11. soe/builtin_tools/soe_get_identities.py +63 -0
  12. soe/builtin_tools/soe_get_workflows.py +63 -0
  13. soe/builtin_tools/soe_inject_context_schema_field.py +80 -0
  14. soe/builtin_tools/soe_inject_identity.py +64 -0
  15. soe/builtin_tools/soe_inject_node.py +86 -0
  16. soe/builtin_tools/soe_inject_workflow.py +105 -0
  17. soe/builtin_tools/soe_list_contexts.py +73 -0
  18. soe/builtin_tools/soe_remove_context_schema_field.py +61 -0
  19. soe/builtin_tools/soe_remove_identity.py +61 -0
  20. soe/builtin_tools/soe_remove_node.py +72 -0
  21. soe/builtin_tools/soe_remove_workflow.py +62 -0
  22. soe/builtin_tools/soe_update_context.py +54 -0
  23. soe/docs/_config.yml +10 -0
  24. soe/docs/advanced_patterns/guide_fanout_and_aggregations.md +318 -0
  25. soe/docs/advanced_patterns/guide_inheritance.md +435 -0
  26. soe/docs/advanced_patterns/hybrid_intelligence.md +237 -0
  27. soe/docs/advanced_patterns/index.md +49 -0
  28. soe/docs/advanced_patterns/operational.md +781 -0
  29. soe/docs/advanced_patterns/self_evolving_workflows.md +385 -0
  30. soe/docs/advanced_patterns/swarm_intelligence.md +211 -0
  31. soe/docs/builtins/context.md +164 -0
  32. soe/docs/builtins/context_schema.md +158 -0
  33. soe/docs/builtins/identity.md +139 -0
  34. soe/docs/builtins/soe_explore_docs.md +135 -0
  35. soe/docs/builtins/tools.md +164 -0
  36. soe/docs/builtins/workflows.md +199 -0
  37. soe/docs/guide_00_getting_started.md +341 -0
  38. soe/docs/guide_01_tool.md +206 -0
  39. soe/docs/guide_02_llm.md +143 -0
  40. soe/docs/guide_03_router.md +146 -0
  41. soe/docs/guide_04_patterns.md +475 -0
  42. soe/docs/guide_05_agent.md +159 -0
  43. soe/docs/guide_06_schema.md +397 -0
  44. soe/docs/guide_07_identity.md +540 -0
  45. soe/docs/guide_08_child.md +612 -0
  46. soe/docs/guide_09_ecosystem.md +690 -0
  47. soe/docs/guide_10_infrastructure.md +427 -0
  48. soe/docs/guide_11_builtins.md +126 -0
  49. soe/docs/index.md +104 -0
  50. soe/docs/primitives/backends.md +281 -0
  51. soe/docs/primitives/context.md +256 -0
  52. soe/docs/primitives/node_reference.md +259 -0
  53. soe/docs/primitives/primitives.md +331 -0
  54. soe/docs/primitives/signals.md +865 -0
  55. soe/docs_index.py +2 -0
  56. soe/init.py +165 -0
  57. soe/lib/__init__.py +0 -0
  58. soe/lib/child_context.py +46 -0
  59. soe/lib/context_fields.py +51 -0
  60. soe/lib/inheritance.py +172 -0
  61. soe/lib/jinja_render.py +113 -0
  62. soe/lib/operational.py +51 -0
  63. soe/lib/parent_sync.py +71 -0
  64. soe/lib/register_event.py +75 -0
  65. soe/lib/schema_validation.py +134 -0
  66. soe/lib/yaml_parser.py +14 -0
  67. soe/local_backends/__init__.py +18 -0
  68. soe/local_backends/factory.py +124 -0
  69. soe/local_backends/in_memory/context.py +38 -0
  70. soe/local_backends/in_memory/conversation_history.py +60 -0
  71. soe/local_backends/in_memory/identity.py +52 -0
  72. soe/local_backends/in_memory/schema.py +40 -0
  73. soe/local_backends/in_memory/telemetry.py +38 -0
  74. soe/local_backends/in_memory/workflow.py +33 -0
  75. soe/local_backends/storage/context.py +57 -0
  76. soe/local_backends/storage/conversation_history.py +82 -0
  77. soe/local_backends/storage/identity.py +118 -0
  78. soe/local_backends/storage/schema.py +96 -0
  79. soe/local_backends/storage/telemetry.py +72 -0
  80. soe/local_backends/storage/workflow.py +56 -0
  81. soe/nodes/__init__.py +13 -0
  82. soe/nodes/agent/__init__.py +10 -0
  83. soe/nodes/agent/factory.py +134 -0
  84. soe/nodes/agent/lib/loop_handlers.py +150 -0
  85. soe/nodes/agent/lib/loop_state.py +157 -0
  86. soe/nodes/agent/lib/prompts.py +65 -0
  87. soe/nodes/agent/lib/tools.py +35 -0
  88. soe/nodes/agent/stages/__init__.py +12 -0
  89. soe/nodes/agent/stages/parameter.py +37 -0
  90. soe/nodes/agent/stages/response.py +54 -0
  91. soe/nodes/agent/stages/router.py +37 -0
  92. soe/nodes/agent/state.py +111 -0
  93. soe/nodes/agent/types.py +66 -0
  94. soe/nodes/agent/validation/__init__.py +11 -0
  95. soe/nodes/agent/validation/config.py +95 -0
  96. soe/nodes/agent/validation/operational.py +24 -0
  97. soe/nodes/child/__init__.py +3 -0
  98. soe/nodes/child/factory.py +61 -0
  99. soe/nodes/child/state.py +59 -0
  100. soe/nodes/child/validation/__init__.py +11 -0
  101. soe/nodes/child/validation/config.py +126 -0
  102. soe/nodes/child/validation/operational.py +28 -0
  103. soe/nodes/lib/conditions.py +71 -0
  104. soe/nodes/lib/context.py +24 -0
  105. soe/nodes/lib/conversation_history.py +77 -0
  106. soe/nodes/lib/identity.py +64 -0
  107. soe/nodes/lib/llm_resolver.py +142 -0
  108. soe/nodes/lib/output.py +68 -0
  109. soe/nodes/lib/response_builder.py +91 -0
  110. soe/nodes/lib/signal_emission.py +79 -0
  111. soe/nodes/lib/signals.py +54 -0
  112. soe/nodes/lib/tools.py +100 -0
  113. soe/nodes/llm/__init__.py +7 -0
  114. soe/nodes/llm/factory.py +103 -0
  115. soe/nodes/llm/state.py +76 -0
  116. soe/nodes/llm/types.py +12 -0
  117. soe/nodes/llm/validation/__init__.py +11 -0
  118. soe/nodes/llm/validation/config.py +89 -0
  119. soe/nodes/llm/validation/operational.py +23 -0
  120. soe/nodes/router/__init__.py +3 -0
  121. soe/nodes/router/factory.py +37 -0
  122. soe/nodes/router/state.py +32 -0
  123. soe/nodes/router/validation/__init__.py +11 -0
  124. soe/nodes/router/validation/config.py +58 -0
  125. soe/nodes/router/validation/operational.py +16 -0
  126. soe/nodes/tool/factory.py +66 -0
  127. soe/nodes/tool/lib/__init__.py +11 -0
  128. soe/nodes/tool/lib/conditions.py +35 -0
  129. soe/nodes/tool/lib/failure.py +28 -0
  130. soe/nodes/tool/lib/parameters.py +67 -0
  131. soe/nodes/tool/state.py +66 -0
  132. soe/nodes/tool/types.py +27 -0
  133. soe/nodes/tool/validation/__init__.py +15 -0
  134. soe/nodes/tool/validation/config.py +132 -0
  135. soe/nodes/tool/validation/operational.py +16 -0
  136. soe/types.py +209 -0
  137. soe/validation/__init__.py +18 -0
  138. soe/validation/config.py +195 -0
  139. soe/validation/jinja.py +54 -0
  140. soe/validation/operational.py +110 -0
  141. soe_ai-0.2.0b1.dist-info/METADATA +262 -0
  142. soe_ai-0.2.0b1.dist-info/RECORD +145 -0
  143. soe_ai-0.2.0b1.dist-info/WHEEL +5 -0
  144. soe_ai-0.2.0b1.dist-info/licenses/LICENSE +21 -0
  145. soe_ai-0.2.0b1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,475 @@
1
+
2
+ # SOE Guide: Chapter 4 - Building Custom Workflows
3
+
4
+ ## The Three Core Node Types
5
+
6
+ You now know SOE's three core node types:
7
+
8
+ | Node | What It Does | Key Capability |
9
+ |------|--------------|----------------|
10
+ | **Tool** | Executes Python functions | Real-world actions |
11
+ | **LLM** | Calls language models | Intelligence & generation |
12
+ | **Router** | Routes based on context | Control flow & branching |
13
+
14
+ These three nodes are **all you need** to build sophisticated AI workflows. This chapter shows you how to combine them into powerful patterns—including building your own agent loops from scratch.
15
+
16
+ ## Why Build Custom Workflows?
17
+
18
+ The built-in **Agent Node** (covered in the next chapter) provides a convenient ReAct loop. But sometimes you need:
19
+
20
+ - **Custom reasoning patterns** (chain-of-thought, tree-of-thought, metacognition)
21
+ - **Fine-grained control** over each step
22
+ - **Hybrid logic** mixing deterministic and AI-driven decisions
23
+ - **Domain-specific agent architectures**
24
+
25
+ With these three core nodes, you can build any pattern—the Agent Node is just one opinionated implementation.
26
+
27
+ ---
28
+
29
+ ## Pattern 1: Chain of Thought
30
+
31
+ **The Pattern**: Break complex reasoning into explicit steps, where each LLM call builds on the previous one.
32
+
33
+ ### The Workflow
34
+
35
+ ```yaml
36
+ example_workflow:
37
+ Understand:
38
+ node_type: llm
39
+ event_triggers: [START]
40
+ prompt: "Analyze this problem and restate it in your own words: {{ context.problem }}"
41
+ output_field: understanding
42
+ event_emissions:
43
+ - signal_name: UNDERSTOOD
44
+
45
+ Plan:
46
+ node_type: llm
47
+ event_triggers: [UNDERSTOOD]
48
+ prompt: |
49
+ Based on your understanding: {{ context.understanding }}
50
+
51
+ Create a step-by-step plan to solve this problem.
52
+ output_field: plan
53
+ event_emissions:
54
+ - signal_name: PLANNED
55
+
56
+ Execute:
57
+ node_type: llm
58
+ event_triggers: [PLANNED]
59
+ prompt: |
60
+ Understanding: {{ context.understanding }}
61
+ Plan: {{ context.plan }}
62
+
63
+ Now execute the plan and provide the final answer.
64
+ output_field: answer
65
+ event_emissions:
66
+ - signal_name: COMPLETE
67
+ ```
68
+
69
+ ### How It Works
70
+
71
+ 1. **Understand**: LLM analyzes and restates the problem
72
+ 2. **Plan**: LLM creates a step-by-step plan based on its understanding
73
+ 3. **Execute**: LLM generates the final answer using the plan
74
+
75
+ Each step stores its output in context, and the next step reads it.
76
+
77
+ ### Why This Pattern?
78
+
79
+ - **Transparency**: Each reasoning step is visible and inspectable
80
+ - **Debuggability**: If the answer is wrong, you can see exactly where reasoning failed
81
+ - **Control**: You can add validation routers between steps
82
+
83
+ ---
84
+
85
+ ## Pattern 2: Custom ReAct Loop
86
+
87
+ **The Pattern**: Build your own Reasoning + Acting loop using the three core node types.
88
+
89
+ ### The Workflow
90
+
91
+ ```yaml
92
+ example_workflow:
93
+ Reason:
94
+ node_type: llm
95
+ event_triggers: [START, TOOL_RESULT]
96
+ prompt: |
97
+ Task: {{ context.task }}
98
+ {% if context.tool_results %}
99
+ Previous tool results: {{ context.tool_results }}
100
+ {% endif %}
101
+
102
+ Decide what to do next. If you need to use a tool, output:
103
+ {"action": "use_tool", "tool": "tool_name", "args": {...}}
104
+
105
+ If you have the final answer, output:
106
+ {"action": "finish", "answer": "your answer"}
107
+ output_field: decision
108
+ event_emissions:
109
+ - signal_name: DECIDED
110
+
111
+ Route:
112
+ node_type: router
113
+ event_triggers: [DECIDED]
114
+ event_emissions:
115
+ - signal_name: USE_TOOL
116
+ condition: "{{ context.decision.action == 'use_tool' }}"
117
+ - signal_name: FINISH
118
+ condition: "{{ context.decision.action == 'finish' }}"
119
+
120
+ Act:
121
+ node_type: tool
122
+ event_triggers: [USE_TOOL]
123
+ tool_name: dynamic_tool
124
+ context_parameter_field: decision
125
+ output_field: tool_results
126
+ event_emissions:
127
+ - signal_name: TOOL_RESULT
128
+
129
+ Complete:
130
+ node_type: router
131
+ event_triggers: [FINISH]
132
+ event_emissions:
133
+ - signal_name: TASK_COMPLETE
134
+ ```
135
+
136
+ ### How It Works
137
+
138
+ 1. **Reason**: LLM analyzes the situation and decides what to do
139
+ 2. **Route**: Router checks if LLM wants to use a tool or finish
140
+ 3. **Act**: If tool needed, execute it and loop back to Reason
141
+ 4. **Complete**: When done, emit final signal
142
+
143
+ ### The Loop Structure
144
+
145
+ ```
146
+ ┌──────────────────────────────────────────────────────┐
147
+ │ │
148
+ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
149
+ │ │ Reason │───▶│ Route │───▶│ Act │──────────┘
150
+ │ │ (LLM) │ │(Router) │ │ (Tool) │
151
+ │ └─────────┘ └────┬────┘ └─────────┘
152
+ │ │
153
+ │ ▼
154
+ │ ┌─────────┐
155
+ │ │Complete │
156
+ │ │(Router) │
157
+ │ └─────────┘
158
+ ```
159
+
160
+ This is exactly what the Agent Node does internally—but now you control every piece.
161
+
162
+ ---
163
+
164
+ ## Pattern 3: Metacognition (Self-Reflection)
165
+
166
+ **The Pattern**: Have the LLM review and critique its own output before finalizing.
167
+
168
+ ### The Workflow
169
+
170
+ ```yaml
171
+ example_workflow:
172
+ Generate:
173
+ node_type: llm
174
+ event_triggers: [START]
175
+ prompt: "Write a response to: {{ context.request }}"
176
+ output_field: draft
177
+ event_emissions:
178
+ - signal_name: DRAFT_READY
179
+
180
+ Critique:
181
+ node_type: llm
182
+ event_triggers: [DRAFT_READY]
183
+ prompt: |
184
+ Review this response for errors, unclear language, or improvements:
185
+ {{ context.draft }}
186
+
187
+ Output JSON with needs_revision (true or false) and critique fields.
188
+ output_field: review
189
+ event_emissions:
190
+ - signal_name: REVIEWED
191
+
192
+ CheckRevision:
193
+ node_type: router
194
+ event_triggers: [REVIEWED]
195
+ event_emissions:
196
+ - signal_name: REVISE
197
+ condition: "{{ context.review.needs_revision == true }}"
198
+ - signal_name: FINALIZE
199
+ condition: "{{ context.review.needs_revision == false }}"
200
+
201
+ Refine:
202
+ node_type: llm
203
+ event_triggers: [REVISE]
204
+ prompt: |
205
+ Original: {{ context.draft }}
206
+ Critique: {{ context.review.critique }}
207
+
208
+ Write an improved version addressing the feedback.
209
+ output_field: final_response
210
+ event_emissions:
211
+ - signal_name: COMPLETE
212
+
213
+ AcceptDraft:
214
+ node_type: router
215
+ event_triggers: [FINALIZE]
216
+ event_emissions:
217
+ - signal_name: COMPLETE
218
+ ```
219
+
220
+ ### How It Works
221
+
222
+ 1. **Generate**: LLM produces an initial response
223
+ 2. **Critique**: A second LLM call reviews the response for errors or improvements
224
+ 3. **Route**: Check if revision is needed
225
+ 4. **Refine** (if needed): Generate improved response based on critique
226
+ 5. **Finalize**: Output the best version
227
+
228
+ ### Why This Pattern?
229
+
230
+ - **Quality improvement**: Catches errors the first pass missed
231
+ - **Self-correction**: The model identifies its own weaknesses
232
+ - **Controllable iteration**: You decide when to stop refining
233
+
234
+ ---
235
+
236
+ ## Pattern 4: Parallel Analysis with Voting
237
+
238
+ **The Pattern**: Run multiple LLM analyses in parallel, then aggregate results.
239
+
240
+ ### The Workflow
241
+
242
+ ```yaml
243
+ example_workflow:
244
+ FanOut:
245
+ node_type: router
246
+ event_triggers: [START]
247
+ event_emissions:
248
+ - signal_name: ANALYZE_SAFETY
249
+ - signal_name: ANALYZE_QUALITY
250
+ - signal_name: ANALYZE_RELEVANCE
251
+
252
+ SafetyCheck:
253
+ node_type: llm
254
+ event_triggers: [ANALYZE_SAFETY]
255
+ prompt: |
256
+ Check if this content is safe: {{ context.content }}
257
+ Return JSON with safe (boolean) and reason (string) fields.
258
+ output_field: safety_result
259
+ event_emissions:
260
+ - signal_name: SAFETY_DONE
261
+
262
+ QualityCheck:
263
+ node_type: llm
264
+ event_triggers: [ANALYZE_QUALITY]
265
+ prompt: |
266
+ Rate the quality of this content: {{ context.content }}
267
+ Return JSON with score (1-10) and feedback (string) fields.
268
+ output_field: quality_result
269
+ event_emissions:
270
+ - signal_name: QUALITY_DONE
271
+
272
+ RelevanceCheck:
273
+ node_type: llm
274
+ event_triggers: [ANALYZE_RELEVANCE]
275
+ prompt: |
276
+ Check relevance to topic '{{ context.topic }}': {{ context.content }}
277
+ Return JSON with relevant (boolean) field.
278
+ output_field: relevance_result
279
+ event_emissions:
280
+ - signal_name: RELEVANCE_DONE
281
+
282
+ Aggregate:
283
+ node_type: tool
284
+ event_triggers: [SAFETY_DONE, QUALITY_DONE, RELEVANCE_DONE]
285
+ tool_name: aggregate_votes
286
+ context_parameter_field: vote_params
287
+ output_field: final_decision
288
+ event_emissions:
289
+ - signal_name: APPROVED
290
+ condition: "{{ result.approved == true }}"
291
+ - signal_name: REJECTED
292
+ condition: "{{ result.approved == false }}"
293
+ ```
294
+
295
+ ### How It Works
296
+
297
+ 1. **Fan-Out**: Router emits multiple signals simultaneously
298
+ 2. **Parallel Analysis**: Multiple LLM nodes run independently
299
+ 3. **Aggregate**: Tool collects all analyses and determines consensus
300
+ 4. **Route Result**: Based on the aggregated vote
301
+
302
+ ### Why This Pattern?
303
+
304
+ - **Diversity of perspective**: Different prompts catch different issues
305
+ - **Robustness**: Single LLM errors are outvoted
306
+ - **Speed**: Parallel execution (if your infrastructure supports it)
307
+
308
+ ---
309
+
310
+ ## Pattern 5: Iterative Refinement with Tools
311
+
312
+ **The Pattern**: LLM generates, tool validates, loop until valid.
313
+
314
+ ### The Workflow
315
+
316
+ ```yaml
317
+ example_workflow:
318
+ Generate:
319
+ node_type: llm
320
+ event_triggers: [START, RETRY]
321
+ prompt: |
322
+ Generate Python code for: {{ context.task }}
323
+ {% if context.errors %}
324
+ Previous attempt had these errors: {{ context.errors }}
325
+ Fix them in this attempt.
326
+ {% endif %}
327
+ output_field: code
328
+ event_emissions:
329
+ - signal_name: CODE_GENERATED
330
+
331
+ Validate:
332
+ node_type: tool
333
+ event_triggers: [CODE_GENERATED]
334
+ tool_name: lint_code
335
+ context_parameter_field: code
336
+ output_field: validation
337
+ event_emissions:
338
+ - signal_name: VALID
339
+ condition: "{{ result.errors|length == 0 }}"
340
+ - signal_name: INVALID
341
+ condition: "{{ result.errors|length > 0 }}"
342
+
343
+ PrepareRetry:
344
+ node_type: router
345
+ event_triggers: [INVALID]
346
+ event_emissions:
347
+ - signal_name: RETRY
348
+
349
+ Complete:
350
+ node_type: router
351
+ event_triggers: [VALID]
352
+ event_emissions:
353
+ - signal_name: CODE_COMPLETE
354
+ ```
355
+
356
+ ### How It Works
357
+
358
+ 1. **Generate**: LLM creates code/content
359
+ 2. **Validate**: Tool runs linter, tests, or other validation
360
+ 3. **Route**: Check validation result
361
+ 4. **Loop or Complete**: If invalid, inject errors and regenerate
362
+
363
+ ### The Loop Structure
364
+
365
+ ```
366
+ ┌────────────────────────────────────────────────┐
367
+ │ │
368
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
369
+ │ │ Generate │───▶│ Validate │───▶│ Route │─┘
370
+ │ │ (LLM) │ │ (Tool) │ │ (Router) │
371
+ │ └──────────┘ └──────────┘ └────┬─────┘
372
+ │ │
373
+ │ ▼
374
+ │ ┌──────────┐
375
+ │ │ Complete │
376
+ │ └──────────┘
377
+ ```
378
+
379
+ ---
380
+
381
+ ## Pattern 6: Hierarchical Task Decomposition
382
+
383
+ **The Pattern**: Break complex tasks into subtasks, solve each, then synthesize.
384
+
385
+ ### The Workflow
386
+
387
+ ```yaml
388
+ example_workflow:
389
+ Decompose:
390
+ node_type: llm
391
+ event_triggers: [START]
392
+ prompt: |
393
+ Break this task into subtasks: {{ context.task }}
394
+ Output JSON with subtasks array and types array (research, code, writing).
395
+ output_field: breakdown
396
+ event_emissions:
397
+ - signal_name: DECOMPOSED
398
+
399
+ RouteSubtasks:
400
+ node_type: router
401
+ event_triggers: [DECOMPOSED]
402
+ event_emissions:
403
+ - signal_name: DO_RESEARCH
404
+ condition: "{{ 'research' in context.breakdown.types }}"
405
+ - signal_name: DO_CODING
406
+ condition: "{{ 'code' in context.breakdown.types }}"
407
+ - signal_name: DO_WRITING
408
+ condition: "{{ 'writing' in context.breakdown.types }}"
409
+
410
+ ResearchSubtask:
411
+ node_type: llm
412
+ event_triggers: [DO_RESEARCH]
413
+ prompt: "Research: {{ context.breakdown.subtasks | selectattr('type', 'equalto', 'research') | list }}"
414
+ output_field: research_result
415
+ event_emissions:
416
+ - signal_name: SUBTASK_DONE
417
+
418
+ CodingSubtask:
419
+ node_type: llm
420
+ event_triggers: [DO_CODING]
421
+ prompt: "Code: {{ context.breakdown.subtasks | selectattr('type', 'equalto', 'code') | list }}"
422
+ output_field: code_result
423
+ event_emissions:
424
+ - signal_name: SUBTASK_DONE
425
+
426
+ Synthesize:
427
+ node_type: llm
428
+ event_triggers: [SUBTASK_DONE]
429
+ prompt: |
430
+ Combine these results into a final answer:
431
+ Research: {{ context.research_result }}
432
+ Code: {{ context.code_result }}
433
+ output_field: final_answer
434
+ event_emissions:
435
+ - signal_name: COMPLETE
436
+ ```
437
+
438
+ ### How It Works
439
+
440
+ 1. **Decompose**: LLM breaks the task into subtasks
441
+ 2. **Fan-Out**: Router emits signals for each subtask type
442
+ 3. **Solve**: Specialized nodes handle each subtask
443
+ 4. **Synthesize**: Combine subtask results into final answer
444
+
445
+ ---
446
+
447
+ ## Combining Patterns
448
+
449
+ The real power comes from combining patterns. For example:
450
+
451
+ - **Chain-of-Thought + Metacognition**: Reason step-by-step, then self-review
452
+ - **Custom ReAct + Iterative Refinement**: Agent loop with validation gates
453
+ - **Parallel Voting + Hierarchical Decomposition**: Ensemble of specialized agents
454
+
455
+ Since everything is YAML, you can compose patterns freely.
456
+
457
+ ---
458
+
459
+ ## When to Use Custom Workflows vs Agent Node
460
+
461
+ | Use Custom Workflows When... | Use Agent Node When... |
462
+ |------------------------------|------------------------|
463
+ | You need non-standard reasoning patterns | Standard ReAct loop is sufficient |
464
+ | You want explicit control over each step | You want hands-off tool-using agent |
465
+ | You're debugging or iterating on agent logic | You need quick prototyping |
466
+ | You need deterministic checkpoints | Tool selection is the main complexity |
467
+ | You're building production systems with audit requirements | Development speed matters most |
468
+
469
+ The Agent Node is a **convenience**—it encapsulates a common pattern. These custom workflows show you can build anything.
470
+
471
+ ---
472
+
473
+ ## Next Steps
474
+
475
+ Now that you can build custom agent patterns, see how the built-in [Agent Node](guide_05_agent.md) encapsulates the ReAct pattern for convenience →
@@ -0,0 +1,159 @@
1
+
2
+ # SOE Guide: Chapter 5 - Agent Nodes
3
+
4
+ ## Introduction to Agent Nodes
5
+
6
+ The **Agent Node** is a convenience wrapper that encapsulates the **ReAct pattern** (Reasoning + Acting) into a single, configurable component.
7
+
8
+ In the previous chapter, you learned how to build custom agent loops using Tool, LLM, and Router nodes. The Agent Node does the same thing—but packaged for common use cases where you want a "hands-off" tool-using agent.
9
+
10
+ ### Why Use Agent Nodes?
11
+
12
+ | Building Custom (Ch. 4) | Using Agent Node |
13
+ |------------------------|------------------|
14
+ | Full control over each step | Quick to configure |
15
+ | Custom reasoning patterns | Standard ReAct loop |
16
+ | Explicit debugging points | Batteries included |
17
+ | Any architecture you want | Opinionated but effective |
18
+
19
+ **Use Agent Node when**: You need a straightforward tool-using agent and don't need custom reasoning patterns.
20
+
21
+ **Use custom workflows when**: You need chain-of-thought, metacognition, voting, or other advanced patterns.
22
+
23
+ **Note**: Agent nodes can also be used for other patterns (routing, summarization, etc.), but they involve more LLM calls than specialized nodes. Use the right node type for your use case.
24
+
25
+ ### The Agent Loop
26
+
27
+ The Agent Node runs this loop internally:
28
+
29
+ 1. **Router Stage**: The agent decides what to do (Call a Tool or Finish)
30
+ 2. **Parameter Stage**: If calling a tool, it generates the arguments
31
+ 3. **Execution Stage**: The tool is executed
32
+ 4. **Loop**: The results are fed back into the history, and the agent decides again
33
+
34
+ This is exactly what you built manually in Chapter 4's "Custom ReAct Loop" pattern—but as a single node.
35
+
36
+ ### Multiple Tool Calls
37
+
38
+ The agent can call multiple tools in sequence during a single node execution:
39
+
40
+ 1. Agent decides to call Tool A → executes → sees result
41
+ 2. Agent decides to call Tool B → executes → sees result
42
+ 3. Agent decides to call Tool A again → executes → sees result
43
+ 4. Agent decides to Finish
44
+
45
+ **Exhaustive tool calling**: The agent will keep calling tools until it decides to finish. There's no hard limit on tool calls—the agent uses its judgment. To prevent runaway loops, use operational limits (see [Operational Features](advanced_patterns/operational.md)).
46
+
47
+ ## Configuring Tools
48
+
49
+ Agents have access to the tools you provide in their `tools` list.
50
+
51
+ > [!IMPORTANT]
52
+ > **Tool Restriction**: Agent nodes can ONLY call tools explicitly listed in their `tools` configuration, even if a tool is a SOE built-in. If you want an agent to be able to use a built-in tool like `soe_inject_node`, you must include it in the `tools` list for that node.
53
+
54
+ ### Example: Tool Restriction
55
+
56
+ ```yaml
57
+ MyAgent:
58
+ node_type: agent
59
+ tools:
60
+ - calculate_total # User tool
61
+ - soe_inject_node # Built-in tool (MUST be listed here)
62
+ ```
63
+
64
+ ### The Workflow
65
+
66
+ ```yaml
67
+ example_workflow:
68
+ CalculatorAgent:
69
+ node_type: agent
70
+ event_triggers: [START]
71
+ prompt: "You are a calculator. Solve the user's math problem: {{ context.problem }}"
72
+ tools: [calculator]
73
+ output_field: result
74
+ event_emissions:
75
+ - signal_name: CALCULATION_DONE
76
+ ```
77
+
78
+ ### How It Works
79
+
80
+ 1. **`tools`**: We give the agent a list of tools (e.g., `[calculator]`).
81
+ 2. **The Loop**:
82
+ * The agent sees the prompt: "Solve... {{ context.problem }}"
83
+ * It decides to call `calculator(5, 3, 'add')`.
84
+ * The tool returns `8`.
85
+ * The agent sees the result and decides to **Finish**.
86
+ 3. **Output**: The final answer is stored in `context.result`.
87
+
88
+ ## Advanced Configuration: Multiple Tools
89
+
90
+ Agents can handle complex tasks with multiple tools.
91
+
92
+ ### The Workflow
93
+
94
+ ```yaml
95
+ example_workflow:
96
+ ResearchAgent:
97
+ node_type: agent
98
+ event_triggers: [START]
99
+ prompt: "Research the topic: {{ context.topic }}"
100
+ tools: [search_web, summarize_text]
101
+ output_field: report
102
+ event_emissions:
103
+ - signal_name: REPORT_READY
104
+ ```
105
+
106
+ ### Robustness Features
107
+
108
+ - **Tool Selection**: The agent intelligently chooses between `search_web` and `summarize_text` based on the goal.
109
+ - **Error Recovery**: If a tool fails (throws an exception), the agent sees the error and can try again or try a different strategy.
110
+
111
+ ## Agent Signal Selection
112
+
113
+ Like LLM nodes, Agent nodes can select which signal to emit based on their analysis:
114
+
115
+ ### The Workflow
116
+
117
+ ```yaml
118
+ example_workflow:
119
+ AnalysisAgent:
120
+ node_type: agent
121
+ event_triggers: [START]
122
+ prompt: "Analyze the data and determine if it needs human review: {{ context.data }}"
123
+ tools: [analyze_data]
124
+ output_field: analysis
125
+ event_emissions:
126
+ - signal_name: AUTO_APPROVE
127
+ condition: "The analysis shows the data is clearly valid"
128
+ - signal_name: NEEDS_REVIEW
129
+ condition: "The analysis shows the data requires human review"
130
+ - signal_name: REJECT
131
+ condition: "The analysis shows the data is clearly invalid"
132
+ ```
133
+
134
+ The agent completes its task, then the signal selection works the same as LLM nodes:
135
+ - **Plain text conditions**: LLM chooses semantically
136
+ - **Jinja conditions**: Evaluated programmatically
137
+
138
+ ## When to Choose Agent vs Custom Workflow
139
+
140
+ ### Use Agent Node For:
141
+
142
+ - **Quick prototyping**: Get a tool-using agent running fast
143
+ - **Standard tasks**: Research, calculation, data gathering
144
+ - **Simple tool orchestration**: When tool selection is the main complexity
145
+
146
+ ### Build Custom Workflows For:
147
+
148
+ - **Chain-of-thought reasoning**: Explicit step-by-step thinking
149
+ - **Metacognition**: Self-review and refinement loops
150
+ - **Parallel/voting patterns**: Multiple analyses combined
151
+ - **Hybrid logic**: Mixing deterministic gates with AI
152
+ - **Audit requirements**: When you need to log/inspect each decision
153
+ - **Custom termination conditions**: Beyond simple "finish" detection
154
+
155
+ Remember: The Agent Node is **syntactic sugar** for a common pattern. Everything it does, you can build yourself with the three core node types.
156
+
157
+ ## Next Steps
158
+
159
+ Now that you understand both custom workflows and the Agent Node, let's explore [Schemas](guide_06_schema.md) for structured LLM output →