Public/Invoke-AzAIFanOut.ps1
|
function Invoke-AzAIFanOut { <# .SYNOPSIS Invokes multiple agents in parallel on isolated conversations (fan-out pattern). .DESCRIPTION Each agent gets its own conversation seeded with the provided message. Agents work independently — they do not see each other's outputs. Results are collected and returned as an array. Use Invoke-AzAISynthesize to combine the results on a shared conversation. .EXAMPLE # Fan-out to 3 named agents $results = @("DocsAgent", "IncidentAgent", "PolicyAgent") | Invoke-AzAIFanOut -Message "Analyze RBAC issue" .EXAMPLE # Fan-out with per-agent prompts $branches = @( @{ AgentName = "DocsAgent"; Message = "Search docs for RBAC configuration" } @{ AgentName = "IncidentAgent"; Message = "Known issues with RBAC in last 30 days" } ) $results = Invoke-AzAIFanOut -Branches $branches .EXAMPLE # Fan-out using direct mode — no pre-configured agents needed $branches = @( @{ Model = "gpt-5-mini"; Instructions = "You are a docs expert."; Message = "RBAC troubleshooting" } @{ Model = "gpt-5-mini"; Instructions = "You are an incident analyst."; Message = "Known RBAC issues" } ) $results = Invoke-AzAIFanOut -Branches $branches #> [CmdletBinding(DefaultParameterSetName = 'PipelineInput')] param( [Parameter(ValueFromPipeline, ParameterSetName = 'PipelineInput')] [string]$AgentName, [Parameter(ParameterSetName = 'PipelineInput')] [string]$Message, [Parameter(Mandatory, ParameterSetName = 'BranchList')] [hashtable[]]$Branches, [int]$MaxOutputTokens, [double]$Temperature, [int]$ThrottleLimit = 5 ) begin { $pipelineAgents = [System.Collections.Generic.List[string]]::new() } process { if ($AgentName) { $pipelineAgents.Add($AgentName) } } end { # Build branch list from pipeline input or -Branches parameter if ($pipelineAgents.Count -gt 0) { if (-not $Message) { throw 'When piping agent names, -Message is required.' } $branchList = $pipelineAgents | ForEach-Object { @{ AgentName = $_; Message = $Message } } } elseif ($Branches) { $branchList = $Branches } else { throw 'Provide agent names via pipeline or use -Branches parameter.' } Write-Verbose "Fan-out: $($branchList.Count) branches (throttle: $ThrottleLimit)" # Capture connection for parallel runspaces $connData = $script:Connection $modulePath = $PSScriptRoot # Execute in parallel using thread jobs for proper isolation $jobs = foreach ($branch in $branchList) { Start-ThreadJob -ScriptBlock { param($Branch, $ConnData, $ModulePath, $MaxTok, $Temp) # Load module and restore connection in this runspace Import-Module "$ModulePath/../Az.AI.Workbench.psd1" -Force & (Get-Module Az.AI.Workbench) { $script:Connection = $args[0] } $ConnData $params = @{ Message = $Branch.Message } if ($Branch.AgentName) { $params.AgentName = $Branch.AgentName } elseif ($Branch.Model) { $params.Model = $Branch.Model if ($Branch.Instructions) { $params.Instructions = $Branch.Instructions } if ($Branch.Tools) { $params.Tools = $Branch.Tools } } if ($MaxTok -gt 0) { $params.MaxOutputTokens = $MaxTok } try { $result = Invoke-AzAIAgent @params $result | Add-Member -NotePropertyName 'BranchStatus' -NotePropertyValue 'Success' -PassThru } catch { [PSCustomObject]@{ PSTypeName = 'AzAIAgentResponse' AgentName = $Branch.AgentName ?? $Branch.Model Response = "Error: $_" BranchStatus = 'Failed' ConversationId = $null ToolCalls = @() DurationMs = 0 } } } -ArgumentList $branch, $connData, $modulePath, $MaxOutputTokens, $Temperature -ThrottleLimit $ThrottleLimit } # Wait for all jobs and collect results $results = $jobs | Wait-Job | Receive-Job $jobs | Remove-Job -Force Write-Verbose "Fan-out complete: $($results.Count) results" return $results } } |