Skip to content

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 nodes
  • FunctionGraphNode: Node that encapsulates SK functions
  • ConditionalGraphNode: Node for conditional decisions
  • ReasoningGraphNode: Node for step-by-step reasoning
  • ReActLoopGraphNode: Node for ReAct loops
  • ObservationGraphNode: Node for observation and logging
  • SubgraphGraphNode: Node that encapsulates other graphs
  • ErrorHandlerGraphNode: Node for error handling
  • HumanApprovalGraphNode: Node for human interaction