Nodes¶
Nodes are the fundamental components of a graph, each encapsulating a specific unit of work or control logic.
Concepts and Techniques¶
Graph Node: Fundamental processing unit that encapsulates work, control logic or specific operations.
Node Lifecycle: Sequence of events that occurs during execution: Before → Execute → After.
IGraphNode Interface: Base contract that all nodes must implement for system integration.
Node Types¶
Function Nodes¶
// Encapsulates a Semantic Kernel function using a lightweight in-memory function
var processFunction = kernel.CreateFunctionFromMethod(
(string input) => $"Processed: {input}",
functionName: "process_input"
);
// Wrap the kernel function and store the result in graph state
var functionNode = new FunctionGraphNode(processFunction, "process_node")
.StoreResultAs("processed_result");
Characteristics:
* Encapsulation: Wraps KernelFunction
* Synchronous Execution: Direct function processing
* Shared State: Access to global GraphState
* Metrics: Automatic performance collection
Conditional Nodes¶
// Node that makes decisions based on conditions using graph state
var conditionalNode = new ConditionalGraphNode(
condition: state => state.ContainsValue("processed_result") &&
!string.IsNullOrEmpty(state.GetValue<string>("processed_result")),
name: "quality_check"
);
Characteristics: * Condition Evaluation: Boolean expressions over state * Dynamic Routing: Runtime decisions * SK Templates: Use of Semantic Kernel functions for decisions * Fallbacks: Fallback strategies for unmet conditions
Reasoning Nodes¶
// Node that implements reasoning patterns (simplified example)
var reasoningNode = new ReasoningGraphNode(
reasoningPrompt: "Analyze the processed result and decide next steps.",
name: "reasoning_node"
);
Characteristics: * Chain of Thought: Step-by-step reasoning * Few-shot Learning: Examples to guide reasoning * Result Validation: Quality verification of responses * Controlled Iteration: Limits to avoid infinite loops
Loop Nodes¶
// Node that implements controlled iterations; compose reasoning, actions and observations
var reactLoopNode = new ReActLoopGraphNode(nodeId: "react_loop", name: "react_loop");
reactLoopNode.ConfigureNodes(reasoningNode, actionNode, observationNode);
Characteristics: * ReAct Pattern: Observation → Thinking → Action * Clear Objectives: Specific goals for each iteration * Iteration Control: Limits to avoid infinite loops * Persistent State: Context maintenance between iterations
Observation Nodes¶
// Node that observes and records information (prompt-driven in examples)
var observationNode = new ObservationGraphNode(
observationPrompt: "Analyze the action result and say if the goal was achieved.",
name: "observation_node"
);
Characteristics: * Passive Observation: Does not modify state * Logging: Recording of execution information * Metrics: Performance data collection * Debug: Troubleshooting support
Subgraph Nodes¶
// Node that encapsulates another graph
var subgraphNode = new SubgraphGraphNode(
subgraph: documentAnalysisGraph,
name: "document_analysis",
description: "Complete document analysis pipeline"
);
Characteristics: * Composition: Reuse of existing graphs * Encapsulation: Clean interface for complex graphs * Isolated State: Variable scope control * Reusability: Reusable modules in different contexts
Error Handler Nodes¶
// Node that handles exceptions and failures
var errorHandlerNode = new ErrorHandlerGraphNode(
errorPolicy: new RetryPolicy(maxRetries: 3),
name: "error_handler",
description: "Handles errors and implements retry policies"
);
Characteristics: * Error Policies: Retry, backoff, circuit breaker * Recovery: Strategies for handling failures * Logging: Detailed error recording * Fallbacks: Alternatives when main operation fails
Human Approval Nodes¶
// Node that pauses for human interaction
var approvalNode = new HumanApprovalGraphNode(
channel: new ConsoleHumanInteractionChannel(),
timeout: TimeSpan.FromMinutes(30),
name: "human_approval",
description: "Waits for human approval to continue"
);
Characteristics: * Human Interaction: Pause for human input * Timeouts: Time limits for response * Multiple Channels: Console, web, email * Audit: Recording of human decisions
Node Lifecycle¶
Before Phase¶
public override async Task BeforeExecutionAsync(GraphExecutionContext context)
{
// Input validation
await ValidateInputAsync(context.State);
// Resource initialization
await InitializeResourcesAsync();
// Start logging
_logger.LogInformation($"Starting execution of node {Id}");
}
Execute Phase¶
public override async Task<GraphExecutionResult> ExecuteAsync(GraphExecutionContext context)
{
try
{
// Main execution
var result = await ProcessAsync(context.State);
// State update
context.State.SetValue("output", result);
return GraphExecutionResult.Success(result);
}
catch (Exception ex)
{
return GraphExecutionResult.Failure(ex);
}
}
After Phase¶
public override async Task AfterExecutionAsync(GraphExecutionContext context)
{
// Resource cleanup
await CleanupResourcesAsync();
// Performance logging
_logger.LogInformation($"Node {Id} completed in {context.ExecutionTime}");
// Counter updates
UpdateExecutionMetrics(context);
}
Configuration and Options¶
Node Options¶
var nodeOptions = new GraphNodeOptions
{
EnableMetrics = true,
EnableLogging = true,
MaxExecutionTime = TimeSpan.FromMinutes(5),
RetryPolicy = new ExponentialBackoffRetryPolicy(maxRetries: 3),
CircuitBreaker = new CircuitBreakerOptions
{
FailureThreshold = 5,
RecoveryTimeout = TimeSpan.FromMinutes(1)
}
};
Input/Output Validation¶
var inputSchema = new GraphNodeInputSchema
{
Required = new[] { "document", "language" },
Optional = new[] { "confidence_threshold" },
Types = new Dictionary<string, Type>
{
["document"] = typeof(string),
["language"] = typeof(string),
["confidence_threshold"] = typeof(double)
}
};
var outputSchema = new GraphNodeOutputSchema
{
Properties = new[] { "analysis_result", "confidence_score" },
Types = new Dictionary<string, Type>
{
["analysis_result"] = typeof(string),
["confidence_score"] = typeof(double)
}
};
Monitoring and Observability¶
Node Metrics¶
var nodeMetrics = new NodeExecutionMetrics
{
ExecutionCount = 150,
AverageExecutionTime = TimeSpan.FromMilliseconds(250),
SuccessRate = 0.98,
LastExecutionTime = DateTime.UtcNow,
ErrorCount = 3,
ResourceUsage = new ResourceUsageMetrics()
};
Structured Logging¶
_logger.LogInformation("Node execution started", new
{
NodeId = Id,
NodeType = GetType().Name,
InputKeys = context.State.GetKeys(),
ExecutionId = context.ExecutionId,
Timestamp = DateTime.UtcNow
});
See Also¶
References¶
IGraphNode
: Base interface for all nodesFunctionGraphNode
: Node that encapsulates SK functionsConditionalGraphNode
: Node for conditional decisionsReasoningGraphNode
: Node for step-by-step reasoningReActLoopGraphNode
: Node for ReAct loopsObservationGraphNode
: Node for observation and loggingSubgraphGraphNode
: Node that encapsulates other graphsErrorHandlerGraphNode
: Node for error handlingHumanApprovalGraphNode
: Node for human interaction