Private/Invoke-AISecurityAnalysis.ps1
|
function Invoke-AISecurityAnalysis { <# .SYNOPSIS Perform AI analysis on Conditional Access policies with multiple specialist agents .DESCRIPTION Uses multiple specialist AI agents to analyze policies, find gaps, provide recommendations, and analyze per-policy improvements. Logs all results in separate files. Supports chunking for large policy sets to avoid rate limits. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [array]$Policies, [Parameter(Mandatory = $true)] [PSCustomObject]$AIConfig, [Parameter(Mandatory = $false)] [string]$StoragePath, [Parameter(Mandatory = $false)] [int]$ChunkSize = 10 ) # Helper function to split policies into chunks function Split-PoliciesIntoChunks { param( [array]$Policies, [int]$ChunkSize ) $chunks = @() for ($i = 0; $i -lt $Policies.Count; $i += $ChunkSize) { $chunk = $Policies[$i..([Math]::Min($i + $ChunkSize - 1, $Policies.Count - 1))] $chunks += ,$chunk } return $chunks } # Track if chunking was used (use script scope for nested function access) $script:usedChunking = $false # Helper function to call AI with chunking support and smart rate limit handling function Invoke-AIWithChunking { param( [string]$PromptTemplate, [array]$Policies, [PSCustomObject]$AIConfig, [string]$AgentName, [int]$ChunkSize = 10, [int]$MaxRetries = 3 ) $allResults = @() $chunks = Split-PoliciesIntoChunks -Policies $Policies -ChunkSize $ChunkSize if ($chunks.Count -eq 1) { # Single chunk - no need for chunking $policiesJson = ($Policies | ForEach-Object { @{ Name = $_.displayName State = $_.state Id = $_.id Users = if ($_.conditions.users) { @{ Include = $_.conditions.users.includeUsers Exclude = $_.conditions.users.excludeUsers IncludeGroups = $_.conditions.users.includeGroups ExcludeGroups = $_.conditions.users.excludeGroups } } else { $null } Applications = if ($_.conditions.applications) { @{ Include = $_.conditions.applications.includeApplications Exclude = $_.conditions.applications.excludeApplications } } else { $null } GrantControls = if ($_.grantControls) { $_.grantControls.builtInControls } else { $null } } }) | ConvertTo-Json -Depth 10 $prompt = $PromptTemplate -replace '\$policiesJson', $policiesJson $response = Invoke-OpenAICall -Prompt $prompt -AIConfig $AIConfig if ($response) { return $response } return $null } else { # Multiple chunks - process with smart queue and rate limit handling $script:usedChunking = $true Write-Host " Processing $($chunks.Count) chunks (${ChunkSize} policies each) to avoid rate limits..." -ForegroundColor Gray $chunkResults = @() $failedChunks = @() # Process chunks with smart delays and retry logic for ($i = 0; $i -lt $chunks.Count; $i++) { $chunk = $chunks[$i] $chunkSuccess = $false $chunkAttempts = 0 $maxChunkAttempts = 3 while (-not $chunkSuccess -and $chunkAttempts -lt $maxChunkAttempts) { $chunkAttempts++ Write-Host " Chunk $($i + 1)/$($chunks.Count) ($($chunk.Count) policies) - Attempt $chunkAttempts/$maxChunkAttempts..." -ForegroundColor DarkGray $chunkPoliciesJson = ($chunk | ForEach-Object { @{ Name = $_.displayName State = $_.state Id = $_.id Users = if ($_.conditions.users) { @{ Include = $_.conditions.users.includeUsers Exclude = $_.conditions.users.excludeUsers IncludeGroups = $_.conditions.users.includeGroups ExcludeGroups = $_.conditions.users.excludeGroups } } else { $null } Applications = if ($_.conditions.applications) { @{ Include = $_.conditions.applications.includeApplications Exclude = $_.conditions.applications.excludeApplications } } else { $null } GrantControls = if ($_.grantControls) { $_.grantControls.builtInControls } else { $null } } }) | ConvertTo-Json -Depth 10 $chunkPrompt = $PromptTemplate -replace '\$policiesJson', $chunkPoliciesJson # Use longer delays and more retries for chunk processing $chunkResponse = Invoke-OpenAICall -Prompt $chunkPrompt -AIConfig $AIConfig -MaxRetries 8 -BaseDelaySeconds 5 if ($chunkResponse) { $chunkResults += @{ ChunkNumber = $i + 1 TotalChunks = $chunks.Count PoliciesInChunk = $chunk.Count Analysis = $chunkResponse } $chunkSuccess = $true Write-Host " Chunk $($i + 1) completed successfully" -ForegroundColor Green } else { if ($chunkAttempts -lt $maxChunkAttempts) { # Exponential backoff: 15, 30, 60 seconds $backoffDelay = 15 * [Math]::Pow(2, $chunkAttempts - 1) Write-Host " Chunk $($i + 1) failed, waiting $backoffDelay seconds before retry..." -ForegroundColor Yellow Start-Sleep -Seconds $backoffDelay } else { Write-Host " Chunk $($i + 1) failed after $maxChunkAttempts attempts, will retry later" -ForegroundColor Red $failedChunks += @{ Index = $i Chunk = $chunk Attempts = $chunkAttempts } } } } # Smart delay between chunks: longer delays as we progress if ($i -lt $chunks.Count - 1) { # Base delay increases with chunk number to prevent rate limits $baseDelay = 10 $progressiveDelay = [Math]::Min($baseDelay + ($i * 2), 30) Write-Host " Waiting $progressiveDelay seconds before next chunk..." -ForegroundColor DarkGray Start-Sleep -Seconds $progressiveDelay } } # Retry failed chunks with longer delays if ($failedChunks.Count -gt 0) { Write-Host " Retrying $($failedChunks.Count) failed chunk(s) with extended delays..." -ForegroundColor Yellow Start-Sleep -Seconds 60 # Wait 1 minute before retrying failed chunks foreach ($failedChunk in $failedChunks) { $i = $failedChunk.Index $chunk = $failedChunk.Chunk Write-Host " Retrying Chunk $($i + 1)/$($chunks.Count) ($($chunk.Count) policies)..." -ForegroundColor Yellow $chunkPoliciesJson = ($chunk | ForEach-Object { @{ Name = $_.displayName State = $_.state Id = $_.id Users = if ($_.conditions.users) { @{ Include = $_.conditions.users.includeUsers Exclude = $_.conditions.users.excludeUsers IncludeGroups = $_.conditions.users.includeGroups ExcludeGroups = $_.conditions.users.excludeGroups } } else { $null } Applications = if ($_.conditions.applications) { @{ Include = $_.conditions.applications.includeApplications Exclude = $_.conditions.applications.excludeApplications } } else { $null } GrantControls = if ($_.grantControls) { $_.grantControls.builtInControls } else { $null } } }) | ConvertTo-Json -Depth 10 $chunkPrompt = $PromptTemplate -replace '\$policiesJson', $chunkPoliciesJson $chunkResponse = Invoke-OpenAICall -Prompt $chunkPrompt -AIConfig $AIConfig -MaxRetries 10 -BaseDelaySeconds 10 if ($chunkResponse) { $chunkResults += @{ ChunkNumber = $i + 1 TotalChunks = $chunks.Count PoliciesInChunk = $chunk.Count Analysis = $chunkResponse Retried = $true } Write-Host " Chunk $($i + 1) retry successful" -ForegroundColor Green } else { Write-Host " Chunk $($i + 1) retry failed - will continue without this chunk" -ForegroundColor Red } Start-Sleep -Seconds 20 # Delay between retry attempts } } # Bundle all chunk results if ($chunkResults.Count -gt 0) { Write-Host " Bundling results from $($chunkResults.Count)/$($chunks.Count) successful chunks..." -ForegroundColor Gray $bundlePrompt = @" You are a Bundling Specialist for the Baseline Secure Cloud. You have received analysis results from multiple chunks of Conditional Access policies. Combine and consolidate these results into a single comprehensive analysis. Chunk Results: $($chunkResults | ConvertTo-Json -Depth 10) Your task: 1. Combine all identified gaps from all chunks (remove duplicates, prioritize critical ones) 2. Consolidate recommendations (merge similar ones, prioritize by impact) 3. Create a unified assessment that covers all policies 4. Ensure no information is lost in the consolidation 5. Maintain the structure and quality of the original analyses 6. Note: Some chunks may have failed, work with available data Return the consolidated result in the same format as the original analysis. "@ $bundledResponse = Invoke-OpenAICall -Prompt $bundlePrompt -AIConfig $AIConfig -MaxRetries 8 -BaseDelaySeconds 5 return $bundledResponse } else { Write-Warning "All chunks failed. Cannot bundle results." return $null } } } try { # Create logs directory $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" $logsPath = $null if ($StoragePath) { $logsPath = Join-Path $StoragePath "SecurityAnalysis" if (-not (Test-Path $logsPath)) { New-Item -Path $logsPath -ItemType Directory -Force | Out-Null } $logsPath = Join-Path $logsPath $timestamp New-Item -Path $logsPath -ItemType Directory -Force | Out-Null } # Prepare detailed policy summary for AI analysis $policySummary = @() foreach ($policy in $Policies) { $summary = @{ Name = $policy.displayName State = $policy.state Id = $policy.id Users = if ($policy.conditions.users) { @{ Include = $policy.conditions.users.includeUsers Exclude = $policy.conditions.users.excludeUsers IncludeGroups = $policy.conditions.users.includeGroups ExcludeGroups = $policy.conditions.users.excludeGroups IncludeGuests = $policy.conditions.users.includeGuestsOrExternalUsers ExcludeGuests = $policy.conditions.users.excludeGuestsOrExternalUsers } } else { $null } Applications = if ($policy.conditions.applications) { @{ Include = $policy.conditions.applications.includeApplications Exclude = $policy.conditions.applications.excludeApplications } } else { $null } Locations = if ($policy.conditions.locations) { @{ Include = $policy.conditions.locations.includeLocations Exclude = $policy.conditions.locations.excludeLocations } } else { $null } Platforms = if ($policy.conditions.platforms) { @{ Include = $policy.conditions.platforms.includePlatforms Exclude = $policy.conditions.platforms.excludePlatforms } } else { $null } ClientAppTypes = $policy.conditions.clientAppTypes UserRiskLevels = $policy.conditions.userRiskLevels SignInRiskLevels = $policy.conditions.signInRiskLevels GrantControls = if ($policy.grantControls) { @{ Operator = $policy.grantControls.operator BuiltInControls = $policy.grantControls.builtInControls AuthenticationStrength = $policy.grantControls.authenticationStrength } } else { $null } SessionControls = if ($policy.sessionControls) { $policy.sessionControls } else { $null } } $policySummary += $summary } $policiesJson = $policySummary | ConvertTo-Json -Depth 10 # Load baseline policies for coverage analysis $baselinePolicies = @() $moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) $baselinePath = Join-Path $moduleRoot "Baseline" if (Test-Path $baselinePath) { $baselineFiles = Get-ChildItem -Path $baselinePath -Filter "*.json" -File -ErrorAction SilentlyContinue foreach ($file in $baselineFiles) { try { $baseline = Get-Content $file.FullName | ConvertFrom-Json $baselinePolicies += @{ Name = $baseline.displayName FileName = $file.Name } } catch { # Skip invalid files } } } $baselineJson = $baselinePolicies | ConvertTo-Json -Depth 5 # Initialize comprehensive results $results = @{ AnalysisDate = (Get-Date).ToString("o") TotalPolicies = $Policies.Count AgentResults = @{} PolicyAnalysis = @() BaselineCoverage = @{} OverallGaps = @() OverallRecommendations = @() OverallAssessment = "" RiskScore = 0 QualityScore = 0 UsedChunking = $false } # Add delay between agents to prevent rate limiting (longer delays for better rate limit handling) $agentDelay = 5 # Agent 1: Gap Analysis Specialist Write-Host " Agent 1: Gap Analysis Specialist..." -ForegroundColor Cyan Start-Sleep -Seconds $agentDelay $gapPromptTemplate = @" You are a Gap Analysis Specialist for the Baseline Secure Cloud. Analyze these Conditional Access policies and identify SECURITY GAPS: $policiesJson Focus on identifying: - Missing MFA requirements for critical users/groups/applications - Missing device compliance requirements - Missing risk-based policies (user risk, sign-in risk) - Missing session controls (sign-in frequency, persistent browser, etc.) - Missing guest user restrictions - Missing location-based restrictions - Missing platform restrictions - Missing application-specific protections - Policies that are disabled when they should be enabled - Missing break-glass exclusions For each gap, provide: 1. The specific gap description 2. Why it's a security concern 3. Which user groups/applications are affected Return as a structured list, one gap per line with clear description. "@ $gapResponse = Invoke-AIWithChunking -PromptTemplate $gapPromptTemplate -Policies $Policies -AIConfig $AIConfig -AgentName "Gap Analysis" -ChunkSize $ChunkSize $gaps = @() if ($gapResponse) { $gaps = ($gapResponse -split "`n" | Where-Object { $_.Trim() -ne "" }) | ForEach-Object { $_.Trim() } } $results.OverallGaps = $gaps $results.AgentResults.GapAnalysis = $gaps if ($logsPath) { $gapLog = Join-Path $logsPath "01-GapAnalysis.json" @{ Agent = "Gap Analysis Specialist" Timestamp = (Get-Date).ToString("o") Gaps = $gaps RawResponse = $gapResponse } | ConvertTo-Json -Depth 10 | Out-File -FilePath $gapLog -Encoding UTF8 } # Agent 2: Policy Compliance Specialist Write-Host " Agent 2: Policy Compliance Specialist..." -ForegroundColor Cyan Start-Sleep -Seconds $agentDelay $compliancePromptTemplate = @" You are a Policy Compliance Specialist for the Baseline Secure Cloud. Analyze these Conditional Access policies for compliance with security best practices: $policiesJson Evaluate each policy for: - Compliance with Security Baseline requirements - Microsoft security best practices - Zero Trust principles - NIST Cybersecurity Framework alignment - GDPR compliance considerations For each policy, identify: 1. Compliance status (Compliant / Partially Compliant / Non-Compliant) 2. Specific compliance issues 3. Required improvements to achieve compliance Return a structured assessment per policy. "@ $complianceResponse = Invoke-AIWithChunking -PromptTemplate $compliancePromptTemplate -Policies $Policies -AIConfig $AIConfig -AgentName "Policy Compliance" -ChunkSize $ChunkSize $results.AgentResults.PolicyCompliance = $complianceResponse if ($logsPath) { $complianceLog = Join-Path $logsPath "02-PolicyCompliance.json" @{ Agent = "Policy Compliance Specialist" Timestamp = (Get-Date).ToString("o") Assessment = $complianceResponse } | ConvertTo-Json -Depth 10 | Out-File -FilePath $complianceLog -Encoding UTF8 } # Agent 3: Baseline Coverage Specialist Write-Host " Agent 3: Baseline Coverage Specialist..." -ForegroundColor Cyan Start-Sleep -Seconds $agentDelay # Baseline coverage doesn't need chunking as it compares against all baselines $coveragePrompt = @" You are a Baseline Coverage Specialist for the Baseline Secure Cloud. Available baseline policies: $baselineJson Current deployed policies: $policiesJson Analyze which baseline policies are MISSING from the current deployment. For each missing baseline: 1. Identify the baseline policy code and name 2. Explain what security control it provides 3. Assess the risk of not having this policy 4. Recommend priority for implementation (High/Medium/Low) Also identify: - Which baseline policies are partially implemented - Which baseline policies are fully implemented - Coverage percentage Return structured analysis. "@ $coverageResponse = Invoke-OpenAICall -Prompt $coveragePrompt -AIConfig $AIConfig $results.AgentResults.BaselineCoverage = $coverageResponse if ($logsPath) { $coverageLog = Join-Path $logsPath "03-BaselineCoverage.json" @{ Agent = "Baseline Coverage Specialist" Timestamp = (Get-Date).ToString("o") Analysis = $coverageResponse AvailableBaselines = $baselinePolicies.Count } | ConvertTo-Json -Depth 10 | Out-File -FilePath $coverageLog -Encoding UTF8 } # Agent 4: Security Best Practices Specialist Write-Host " Agent 4: Security Best Practices Specialist..." -ForegroundColor Cyan Start-Sleep -Seconds $agentDelay $bestPracticesPromptTemplate = @" You are a Security Best Practices Specialist for the Baseline Secure Cloud. Analyze these Conditional Access policies for security best practices: $policiesJson Provide specific recommendations for: - Implementing Zero Trust principles - Defense in depth strategies - Least privilege access - Continuous monitoring and evaluation - Incident response readiness - User experience optimization while maintaining security - Automation opportunities For each recommendation, provide: 1. The specific improvement 2. Why it's important 3. Implementation priority 4. Expected security benefit Return structured recommendations. "@ $bestPracticesResponse = Invoke-AIWithChunking -PromptTemplate $bestPracticesPromptTemplate -Policies $Policies -AIConfig $AIConfig -AgentName "Security Best Practices" -ChunkSize $ChunkSize $results.AgentResults.SecurityBestPractices = $bestPracticesResponse if ($logsPath) { $bestPracticesLog = Join-Path $logsPath "04-SecurityBestPractices.json" @{ Agent = "Security Best Practices Specialist" Timestamp = (Get-Date).ToString("o") Recommendations = $bestPracticesResponse } | ConvertTo-Json -Depth 10 | Out-File -FilePath $bestPracticesLog -Encoding UTF8 } # Agent 5: Risk Assessment Specialist Write-Host " Agent 5: Risk Assessment Specialist..." -ForegroundColor Cyan Start-Sleep -Seconds $agentDelay $riskPromptTemplate = @" You are a Risk Assessment Specialist for the Baseline Secure Cloud. Analyze these Conditional Access policies for security risks: $policiesJson Assess: - Overall risk score (0-100, where 0 is no risk, 100 is critical risk) - High-risk areas that need immediate attention - Medium-risk areas for improvement - Low-risk areas that are acceptable - Attack surface exposure - Potential attack vectors - Business impact of security gaps Provide: 1. Overall risk score with justification 2. Top 5 highest risk areas 3. Risk mitigation priorities 4. Timeline recommendations for addressing risks Return structured risk assessment. "@ $riskResponse = Invoke-AIWithChunking -PromptTemplate $riskPromptTemplate -Policies $Policies -AIConfig $AIConfig -AgentName "Risk Assessment" -ChunkSize $ChunkSize $results.AgentResults.RiskAssessment = $riskResponse # Extract risk score if mentioned if ($riskResponse -match "(?i)(?:risk\s*score|score)[:\s]*(\d+)") { $results.RiskScore = [int]$matches[1] } if ($logsPath) { $riskLog = Join-Path $logsPath "05-RiskAssessment.json" @{ Agent = "Risk Assessment Specialist" Timestamp = (Get-Date).ToString("o") Assessment = $riskResponse RiskScore = $results.RiskScore } | ConvertTo-Json -Depth 10 | Out-File -FilePath $riskLog -Encoding UTF8 } # Per-Policy Analysis Write-Host " Analyzing individual policies..." -ForegroundColor Cyan $policyAnalysis = @() $policyCount = 0 foreach ($policy in $Policies) { $policyCount++ # Add delay between policy analyses to prevent rate limiting (longer for better handling) if ($policyCount -gt 1) { Start-Sleep -Seconds ($agentDelay * 2) } $policyJson = $policy | ConvertTo-Json -Depth 10 $policyPrompt = @" You are analyzing a single Conditional Access policy for the Baseline Secure Cloud. Policy details: $policyJson For this specific policy, provide: 1. Policy strengths (what it does well) 2. Security gaps specific to this policy 3. Recommended improvements 4. Compliance status with Security Baseline 5. Priority for improvement (High/Medium/Low) Be specific and actionable. "@ $policyResponse = Invoke-OpenAICall -Prompt $policyPrompt -AIConfig $AIConfig $policyAnalysis += @{ PolicyName = $policy.displayName PolicyId = $policy.id State = $policy.state Analysis = $policyResponse AnalysisDate = (Get-Date).ToString("o") } if ($logsPath) { $policyLogName = ($policy.displayName -replace '[^\w\s-]', '' -replace '\s+', '_') + ".json" $policyLog = Join-Path $logsPath "Policy-$policyLogName" $policyAnalysis[-1] | ConvertTo-Json -Depth 10 | Out-File -FilePath $policyLog -Encoding UTF8 } } $results.PolicyAnalysis = $policyAnalysis # Agent 6: Overall Assessment and Recommendations Write-Host " Agent 6: Overall Assessment..." -ForegroundColor Cyan Start-Sleep -Seconds $agentDelay $assessmentPromptTemplate = @" You are a Senior Security Architect for the Baseline Secure Cloud. Based on the analysis of these Conditional Access policies: $policiesJson Provide a comprehensive overall assessment (max 300 words) covering: - Overall security posture summary - Key strengths - Critical weaknesses - Compliance status with Security Baseline - Top 5 priority actions - Long-term security strategy recommendations Be concise but comprehensive. "@ $assessmentResponse = Invoke-AIWithChunking -PromptTemplate $assessmentPromptTemplate -Policies $Policies -AIConfig $AIConfig -AgentName "Overall Assessment" -ChunkSize $ChunkSize $results.OverallAssessment = if ($assessmentResponse) { $assessmentResponse.Trim() } else { "" } # Extract overall recommendations $recommendationsPrompt = @" Based on this security analysis, provide the top 10 actionable recommendations in priority order. Each recommendation should be one line, specific and actionable. "@ $recommendationsResponse = Invoke-OpenAICall -Prompt $recommendationsPrompt -AIConfig $AIConfig if ($recommendationsResponse) { $results.OverallRecommendations = ($recommendationsResponse -split "`n" | Where-Object { $_.Trim() -ne "" }) | ForEach-Object { $_.Trim() } } # Agent 7: Reviewer Agent - Validates and improves all results Write-Host " Agent 7: Reviewer Agent (Validating and improving results)..." -ForegroundColor Cyan Start-Sleep -Seconds $agentDelay $reviewerPrompt = @" You are a Senior Security Reviewer for the Baseline Secure Cloud. Review and validate the following security analysis results. Your task is to: 1. Validate the accuracy and completeness of all findings 2. Identify any missing critical security gaps 3. Improve the quality and clarity of recommendations 4. Ensure consistency across all agent analyses 5. Verify compliance assessments are accurate 6. Enhance the overall assessment with additional insights 7. Prioritize recommendations based on risk and impact Analysis Results to Review: - Gap Analysis: $($results.OverallGaps -join '; ') - Policy Compliance: $($results.AgentResults.PolicyCompliance) - Baseline Coverage: $($results.AgentResults.BaselineCoverage) - Security Best Practices: $($results.AgentResults.SecurityBestPractices) - Risk Assessment: $($results.AgentResults.RiskAssessment) - Overall Assessment: $($results.OverallAssessment) - Recommendations: $($results.OverallRecommendations -join '; ') Provide: 1. Validated and improved gap list (add any missing critical gaps) 2. Enhanced recommendations (improve clarity, add missing ones, prioritize) 3. Improved overall assessment (more comprehensive, actionable) 4. Quality score (1-10) for the analysis 5. Any corrections or improvements needed Return structured review results. "@ $reviewerResponse = Invoke-OpenAICall -Prompt $reviewerPrompt -AIConfig $AIConfig $results.AgentResults.Reviewer = $reviewerResponse # Extract improvements from reviewer if ($reviewerResponse) { # Try to extract improved gaps (using multiline regex with [\s\S] instead of .) if ($reviewerResponse -match "(?smi)(?:improved gaps|validated gaps|gaps:)(.*?)(?:\n\n|\nRecommendations|$)" -or $reviewerResponse -match "(?smi)(?:gap.*?:)(.*?)(?:\n\n|Recommendations|$)") { $improvedGaps = ($matches[1] -split "`n" | Where-Object { $_.Trim() -ne "" -and $_.Trim() -notmatch "^\d+\.\s*$" }) | ForEach-Object { $_.Trim() } if ($improvedGaps.Count -gt 0) { $results.OverallGaps = $improvedGaps } } # Try to extract improved recommendations if ($reviewerResponse -match "(?smi)(?:recommendations|improved recommendations):(.*?)(?:\n\n|Assessment|$)") { $improvedRecs = ($matches[1] -split "`n" | Where-Object { $_.Trim() -ne "" -and $_.Trim() -notmatch "^\d+\.\s*$" }) | ForEach-Object { $_.Trim() } if ($improvedRecs.Count -gt 0) { $results.OverallRecommendations = $improvedRecs } } # Try to extract improved assessment if ($reviewerResponse -match "(?smi)(?:improved assessment|enhanced assessment|assessment):(.*?)(?:\n\n|Quality|$)") { $improvedAssessment = $matches[1].Trim() if ($improvedAssessment) { $results.OverallAssessment = $improvedAssessment } } # Extract quality score if ($reviewerResponse -match "(?i)(?:quality score|score)[:\s]*(\d+)") { $results.QualityScore = [int]$matches[1] } } if ($logsPath) { $assessmentLog = Join-Path $logsPath "06-OverallAssessment.json" @{ Agent = "Overall Assessment" Timestamp = (Get-Date).ToString("o") Assessment = $results.OverallAssessment Recommendations = $results.OverallRecommendations } | ConvertTo-Json -Depth 10 | Out-File -FilePath $assessmentLog -Encoding UTF8 # Save reviewer results $reviewerLog = Join-Path $logsPath "07-ReviewerValidation.json" @{ Agent = "Reviewer Agent" Timestamp = (Get-Date).ToString("o") Review = $reviewerResponse QualityScore = $results.QualityScore } | ConvertTo-Json -Depth 10 | Out-File -FilePath $reviewerLog -Encoding UTF8 # Save complete summary $summaryLog = Join-Path $logsPath "00-CompleteAnalysis.json" $results | ConvertTo-Json -Depth 10 | Out-File -FilePath $summaryLog -Encoding UTF8 } # Mark if chunking was used $results.UsedChunking = $script:usedChunking # Backward compatibility - keep old structure $results.Gaps = $results.OverallGaps $results.Recommendations = $results.OverallRecommendations $results.Assessment = $results.OverallAssessment return $results } catch { Write-Error "Error in AI analysis: $_" return @{ Gaps = @("Error analyzing: $_") Recommendations = @() Assessment = "Analysis failed" AnalysisDate = (Get-Date).ToString("o") AgentResults = @{} PolicyAnalysis = @() } } } |