""" Synthesis Engine - Combines all agent perspectives into a unified multi-perspective response. Takes the concept, all agent analyses, and critic feedback, then produces a synthesized explanation that highlights how different perspectives complement each other. Includes a Final Integrated Understanding section. """ import random import re class SynthesisEngine: """Combines multi-agent analyses into coherent synthesized responses.""" # Opening templates that set up the multi-perspective frame _opening_templates = [ ( "To understand '{concept}' with genuine depth, we must examine it through " "multiple lenses, each revealing structure that the others miss." ), ( "'{concept}' resists single-framework analysis. Its full meaning emerges " "only at the intersection of several distinct modes of reasoning." ), ( "A comprehensive understanding of '{concept}' requires weaving together " "insights from fundamentally different ways of thinking." ), ( "No single perspective captures '{concept}' adequately. What follows is " "an integrated analysis drawing on physics, philosophy, ethics, creativity, " "and human experience." ), ( "The richness of '{concept}' becomes apparent only when we hold multiple " "analytical frameworks simultaneously and let them inform each other." ), ] # Bridge templates connecting one perspective to another _bridge_templates = [ "Where {agent_a} reveals {insight_a}, {agent_b} adds the crucial dimension of {insight_b}.", "The {agent_a} analysis and the {agent_b} analysis converge on a shared insight: {shared}.", "What appears as {aspect_a} from the {agent_a} perspective is revealed as {aspect_b} when viewed through {agent_b}.", "The tension between {agent_a}'s emphasis on {focus_a} and {agent_b}'s emphasis on {focus_b} is productive, not contradictory.", "{agent_a} identifies the mechanism; {agent_b} identifies the meaning.", "Combining {agent_a}'s structural analysis with {agent_b}'s human-centered analysis yields a fuller picture.", ] # Closing templates for the Final Integrated Understanding _closing_templates = [ ( "**Final Integrated Understanding:** {concept} is simultaneously a " "{physical_desc}, a {philosophical_desc}, a {ethical_desc}, a " "{creative_desc}, and a {human_desc}. These are not competing descriptions " "but complementary facets of a single complex reality. The most robust " "understanding holds all five in view, using each to compensate for the " "blind spots of the others." ), ( "**Final Integrated Understanding:** The multi-perspective analysis reveals " "that {concept} cannot be reduced to any single framework without distortion. " "The physical analysis provides causal grounding, the philosophical analysis " "excavates hidden assumptions, the ethical analysis maps the stakes, the " "creative analysis opens new solution spaces, and the empathic analysis " "anchors everything in lived human experience. Together they constitute " "not a list of separate views but an integrated understanding richer than " "any view alone." ), ( "**Final Integrated Understanding:** What emerges from this multi-lens " "examination of {concept} is not a single 'correct' interpretation but a " "structured understanding of how different valid interpretations relate to " "each other. The causal structure identified by physics, the meaning " "structure identified by philosophy, the value structure identified by " "ethics, the possibility structure identified by creative reasoning, and " "the experience structure identified by empathy are all real and all " "essential. Wisdom lies in knowing which lens to apply in which context " "and how to translate insights between them." ), ] def synthesize( self, concept: str, analyses: dict[str, str], critique: dict, ) -> str: """Produce a synthesized multi-perspective response. Args: concept: The original concept. analyses: Dict mapping agent_name -> analysis_text. critique: Output from CriticAgent.evaluate_ensemble(). Returns: A synthesized text of 200-400 words. """ sections = [] # 1. Opening opening = random.choice(self._opening_templates).replace("{concept}", concept) sections.append(opening) # 2. Per-perspective summaries (compressed) perspective_summaries = self._extract_perspective_summaries(analyses) for agent_name, summary in perspective_summaries.items(): sections.append(f"**{agent_name} perspective:** {summary}") # 3. Cross-perspective bridges (pick 2-3) bridges = self._generate_bridges(analyses, perspective_summaries) if bridges: sections.append("") # blank line for readability for bridge in bridges[:2]: sections.append(bridge) # 4. Incorporate critic insights critic_section = self._incorporate_critique(critique) if critic_section: sections.append("") sections.append(critic_section) # 5. Final Integrated Understanding closing = self._generate_closing(concept, perspective_summaries) sections.append("") sections.append(closing) raw_synthesis = "\n\n".join(sections) # Trim to 200-400 words if needed return self._trim_to_target(raw_synthesis, min_words=200, max_words=400) def _extract_perspective_summaries( self, analyses: dict[str, str] ) -> dict[str, str]: """Extract a 1-2 sentence summary from each agent's analysis.""" summaries = {} for agent_name, text in analyses.items(): sentences = [s.strip() for s in re.split(r'(?<=[.!?])\s+', text) if s.strip()] if len(sentences) >= 3: # Take the 2nd and 3rd sentences (skip the opening framing) summary = " ".join(sentences[1:3]) elif len(sentences) >= 1: summary = sentences[0] else: summary = text[:200] # Trim to ~40 words words = summary.split() if len(words) > 45: summary = " ".join(words[:40]) + "..." summaries[agent_name] = summary return summaries def _generate_bridges( self, analyses: dict[str, str], summaries: dict[str, str], ) -> list[str]: """Generate cross-perspective bridge statements.""" bridges = [] agent_names = list(analyses.keys()) # Define perspective focus areas for bridge generation focus_map = { "Newton": "causal mechanisms and measurable dynamics", "Quantum": "uncertainty, probability, and the limits of definite knowledge", "Ethics": "moral stakes, fairness, and human impact", "Philosophy": "foundational assumptions and the structure of meaning", "DaVinci": "creative possibilities and cross-domain innovation", "Empathy": "emotional reality and lived human experience", } # Generate a few meaningful bridges if len(agent_names) >= 2: pairs = [] for i in range(len(agent_names)): for j in range(i + 1, len(agent_names)): pairs.append((agent_names[i], agent_names[j])) random.shuffle(pairs) for name_a, name_b in pairs[:3]: focus_a = focus_map.get(name_a, "its analytical focus") focus_b = focus_map.get(name_b, "its analytical focus") template = random.choice(self._bridge_templates) bridge = template.format( agent_a=name_a, agent_b=name_b, insight_a=focus_a, insight_b=focus_b, shared="the importance of understanding the full system rather than isolated parts", aspect_a="a structural feature", aspect_b="a deeply human concern", focus_a=focus_a, focus_b=focus_b, ) bridges.append(bridge) return bridges def _incorporate_critique(self, critique: dict) -> str: """Turn critic feedback into a synthesis-relevant observation.""" parts = [] if critique.get("missing_perspectives"): gap = critique["missing_perspectives"][0] # Extract just the perspective name parts.append( f"A notable gap in the analysis is the limited attention to " f"{gap.split('lacks a ')[1].split(' perspective')[0] if 'lacks a ' in gap else 'additional'} " f"dimensions, which future analysis should address." ) if critique.get("improvement_suggestions"): suggestion = critique["improvement_suggestions"][0] # Compress the suggestion words = suggestion.split() if len(words) > 25: suggestion = " ".join(words[:25]) + "..." parts.append(f"The critic notes: {suggestion}") overall = critique.get("overall_quality", 0) if overall >= 0.75: parts.append( "Overall, the multi-perspective ensemble achieves strong analytical " "coverage with good complementarity between viewpoints." ) elif overall >= 0.5: parts.append( "The ensemble provides reasonable coverage but would benefit from " "deeper engagement between perspectives." ) return " ".join(parts) if parts else "" def _generate_closing( self, concept: str, summaries: dict[str, str] ) -> str: """Generate the Final Integrated Understanding section.""" template = random.choice(self._closing_templates) # Build descriptors from available perspectives descriptors = { "physical_desc": "system governed by causal dynamics and conservation principles", "philosophical_desc": "concept whose meaning depends on the framework from which it is examined", "ethical_desc": "domain of genuine moral stakes affecting real people", "creative_desc": "space of untapped possibilities waiting for cross-domain insight", "human_desc": "lived experience with emotional texture that abstract analysis alone cannot capture", } result = template result = result.replace("{concept}", concept) for key, value in descriptors.items(): result = result.replace("{" + key + "}", value) return result def _trim_to_target( self, text: str, min_words: int = 200, max_words: int = 400 ) -> str: """Trim or pad text to fall within the target word range.""" words = text.split() if len(words) > max_words: # Trim from the middle sections, preserving opening and closing lines = text.split("\n\n") while len(" ".join(lines).split()) > max_words and len(lines) > 3: # Remove the longest middle section middle_indices = list(range(1, len(lines) - 1)) if not middle_indices: break longest_idx = max(middle_indices, key=lambda i: len(lines[i].split())) lines.pop(longest_idx) return "\n\n".join(lines) return text