File size: 14,712 Bytes
66dbebd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
"""
Response Synthesis Agent
Specialized in integrating multiple agent outputs into coherent responses
"""

import logging
from typing import Dict, Any, List
import re

logger = logging.getLogger(__name__)

class ResponseSynthesisAgent:
    def __init__(self, llm_router=None):
        self.llm_router = llm_router
        self.agent_id = "RESP_SYNTH_001"
        self.specialization = "Multi-source information integration and coherent response generation"
        
        # Response templates for different intent types
        self.response_templates = {
            "information_request": {
                "structure": "introduction β†’ key_points β†’ conclusion",
                "tone": "informative, clear, authoritative"
            },
            "task_execution": {
                "structure": "confirmation β†’ steps β†’ expected_outcome",
                "tone": "action-oriented, precise, reassuring"
            },
            "creative_generation": {
                "structure": "concept β†’ development β†’ refinement",
                "tone": "creative, engaging, expressive"
            },
            "analysis_research": {
                "structure": "hypothesis β†’ analysis β†’ insights",
                "tone": "analytical, evidence-based, objective"
            },
            "casual_conversation": {
                "structure": "engagement β†’ response β†’ follow_up",
                "tone": "friendly, conversational, natural"
            }
        }
    
    async def execute(self, agent_outputs: List[Dict[str, Any]], user_input: str, 
                     context: Dict[str, Any] = None, **kwargs) -> Dict[str, Any]:
        """
        Synthesize responses from multiple agent outputs
        """
        try:
            logger.info(f"{self.agent_id} synthesizing {len(agent_outputs)} agent outputs")
            
            # Extract intent information
            intent_info = self._extract_intent_info(agent_outputs)
            primary_intent = intent_info.get('primary_intent', 'casual_conversation')
            
            # Structure the synthesis process
            synthesis_result = await self._synthesize_response(
                agent_outputs, user_input, context, primary_intent
            )
            
            # Add quality metrics
            synthesis_result.update({
                "agent_id": self.agent_id,
                "synthesis_quality_metrics": self._calculate_quality_metrics(synthesis_result),
                "intent_alignment": self._check_intent_alignment(synthesis_result, intent_info)
            })
            
            logger.info(f"{self.agent_id} completed synthesis")
            return synthesis_result
            
        except Exception as e:
            logger.error(f"{self.agent_id} synthesis error: {str(e)}")
            return self._get_fallback_response(user_input, agent_outputs)
    
    async def _synthesize_response(self, agent_outputs: List[Dict[str, Any]], 
                                 user_input: str, context: Dict[str, Any], 
                                 primary_intent: str) -> Dict[str, Any]:
        """Synthesize responses using appropriate method based on intent"""
        
        if self.llm_router:
            # Use LLM for sophisticated synthesis
            return await self._llm_based_synthesis(agent_outputs, user_input, context, primary_intent)
        else:
            # Use template-based synthesis
            return await self._template_based_synthesis(agent_outputs, user_input, primary_intent)
    
    async def _llm_based_synthesis(self, agent_outputs: List[Dict[str, Any]], 
                                  user_input: str, context: Dict[str, Any], 
                                  primary_intent: str) -> Dict[str, Any]:
        """Use LLM for sophisticated response synthesis"""
        
        synthesis_prompt = self._build_synthesis_prompt(agent_outputs, user_input, context, primary_intent)
        
        # Simulate LLM synthesis (replace with actual LLM call)
        synthesized_response = await self._template_based_synthesis(agent_outputs, user_input, primary_intent)
        
        # Enhance with simulated LLM improvements
        draft_response = synthesized_response["final_response"]
        enhanced_response = self._enhance_response_quality(draft_response, primary_intent)
        
        return {
            "draft_response": draft_response,
            "final_response": enhanced_response,
            "source_references": self._extract_source_references(agent_outputs),
            "coherence_score": 0.85,
            "improvement_opportunities": self._identify_improvements(enhanced_response),
            "synthesis_method": "llm_enhanced"
        }
    
    async def _template_based_synthesis(self, agent_outputs: List[Dict[str, Any]], 
                                     user_input: str, primary_intent: str) -> Dict[str, Any]:
        """Template-based response synthesis"""
        
        template = self.response_templates.get(primary_intent, self.response_templates["casual_conversation"])
        
        # Extract relevant content from agent outputs
        content_blocks = self._extract_content_blocks(agent_outputs)
        
        # Apply template structure
        structured_response = self._apply_response_template(content_blocks, template, primary_intent)
        
        return {
            "draft_response": structured_response,
            "final_response": structured_response,  # No enhancement in template mode
            "source_references": self._extract_source_references(agent_outputs),
            "coherence_score": 0.75,
            "improvement_opportunities": ["Consider adding more specific details"],
            "synthesis_method": "template_based"
        }
    
    def _build_synthesis_prompt(self, agent_outputs: List[Dict[str, Any]], 
                              user_input: str, context: Dict[str, Any], 
                              primary_intent: str) -> str:
        """Build prompt for LLM-based synthesis"""
        
        return f"""
        Synthesize a coherent response from multiple AI agent outputs:
        
        User Question: "{user_input}"
        Primary Intent: {primary_intent}
        
        Agent Outputs to Integrate:
        {self._format_agent_outputs_for_synthesis(agent_outputs)}
        
        Conversation Context: {context.get('conversation_history', [])[-3:] if context else 'No context'}
        
        Requirements:
        - Maintain accuracy from source materials
        - Ensure logical flow and coherence
        - Match the {primary_intent} intent style
        - Keep response concise but comprehensive
        - Include relevant details from agent outputs
        
        Provide a well-structured, natural-sounding response.
        """
    
    def _extract_intent_info(self, agent_outputs: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Extract intent information from agent outputs"""
        for output in agent_outputs:
            if 'primary_intent' in output:
                return {
                    'primary_intent': output['primary_intent'],
                    'confidence': output.get('confidence_scores', {}).get(output['primary_intent'], 0.5),
                    'source_agent': output.get('agent_id', 'unknown')
                }
        return {'primary_intent': 'casual_conversation', 'confidence': 0.5}
    
    def _extract_content_blocks(self, agent_outputs: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Extract content blocks from agent outputs for synthesis"""
        content_blocks = []
        
        for output in agent_outputs:
            if 'result' in output:
                content_blocks.append({
                    'content': output['result'],
                    'source': output.get('agent_id', 'unknown'),
                    'confidence': output.get('confidence', 0.5)
                })
            elif 'primary_intent' in output:
                content_blocks.append({
                    'content': f"Intent analysis: {output['primary_intent']}",
                    'source': output.get('agent_id', 'intent_agent'),
                    'confidence': output.get('confidence_scores', {}).get(output['primary_intent'], 0.5)
                })
            elif 'final_response' in output:
                content_blocks.append({
                    'content': output['final_response'],
                    'source': output.get('agent_id', 'unknown'),
                    'confidence': output.get('confidence_score', 0.7)
                })
        
        return content_blocks
    
    def _apply_response_template(self, content_blocks: List[Dict[str, Any]], 
                               template: Dict[str, str], intent: str) -> str:
        """Apply response template to structure the content"""
        
        if intent == "information_request":
            return self._structure_informative_response(content_blocks)
        elif intent == "task_execution":
            return self._structure_actionable_response(content_blocks)
        else:
            return self._structure_conversational_response(content_blocks)
    
    def _structure_informative_response(self, content_blocks: List[Dict[str, Any]]) -> str:
        """Structure an informative response (intro β†’ key_points β†’ conclusion)"""
        if not content_blocks:
            return "I'm here to help! Could you provide more details about what you're looking for?"
        
        intro = f"Based on the information available"
        key_points = "\n".join([f"β€’ {block['content']}" for block in content_blocks[:3]])
        conclusion = "I hope this helps! Let me know if you need any clarification."
        
        return f"{intro}:\n\n{key_points}\n\n{conclusion}"
    
    def _structure_actionable_response(self, content_blocks: List[Dict[str, Any]]) -> str:
        """Structure an actionable response (confirmation β†’ steps β†’ outcome)"""
        if not content_blocks:
            return "I understand you'd like some help. What specific task would you like to accomplish?"
        
        confirmation = "I can help with that!"
        steps = "\n".join([f"{i+1}. {block['content']}" for i, block in enumerate(content_blocks[:5])])
        outcome = "This should help you get started. Feel free to ask if you need further assistance."
        
        return f"{confirmation}\n\n{steps}\n\n{outcome}"
    
    def _structure_conversational_response(self, content_blocks: List[Dict[str, Any]]) -> str:
        """Structure a conversational response"""
        if not content_blocks:
            return "Thanks for chatting! How can I assist you today?"
        
        # Combine content naturally
        combined_content = " ".join([block['content'] for block in content_blocks])
        return combined_content[:500] + "..." if len(combined_content) > 500 else combined_content
    
    def _enhance_response_quality(self, response: str, intent: str) -> str:
        """Enhance response quality based on intent"""
        # Add simple enhancements
        enhanced = response
        
        # Check for clarity
        if len(response.split()) < 5:
            enhanced += "\n\nWould you like me to expand on this?"
        
        # Add intent-specific enhancements
        if intent == "information_request" and "?" not in response:
            enhanced += "\n\nIs there anything specific you'd like to know more about?"
        
        return enhanced
    
    def _extract_source_references(self, agent_outputs: List[Dict[str, Any]]) -> List[str]:
        """Extract source references from agent outputs"""
        sources = []
        for output in agent_outputs:
            agent_id = output.get('agent_id', 'unknown')
            sources.append(agent_id)
        return list(set(sources))  # Remove duplicates
    
    def _format_agent_outputs_for_synthesis(self, agent_outputs: List[Dict[str, Any]]) -> str:
        """Format agent outputs for LLM synthesis prompt"""
        formatted = []
        for i, output in enumerate(agent_outputs, 1):
            agent_id = output.get('agent_id', 'unknown')
            content = output.get('result', output.get('final_response', str(output)))
            formatted.append(f"Agent {i} ({agent_id}): {content[:100]}...")
        return "\n".join(formatted)
    
    def _calculate_quality_metrics(self, synthesis_result: Dict[str, Any]) -> Dict[str, Any]:
        """Calculate quality metrics for synthesis"""
        response = synthesis_result.get('final_response', '')
        
        return {
            "length": len(response),
            "word_count": len(response.split()),
            "coherence_score": synthesis_result.get('coherence_score', 0.7),
            "source_count": len(synthesis_result.get('source_references', [])),
            "has_structured_elements": bool(re.search(r'[β€’\d+\.]', response))
        }
    
    def _check_intent_alignment(self, synthesis_result: Dict[str, Any], intent_info: Dict[str, Any]) -> Dict[str, Any]:
        """Check if synthesis aligns with detected intent"""
        alignment_score = 0.8  # Placeholder
        
        return {
            "intent_detected": intent_info.get('primary_intent'),
            "alignment_score": alignment_score,
            "alignment_verified": alignment_score > 0.7
        }
    
    def _identify_improvements(self, response: str) -> List[str]:
        """Identify opportunities to improve the response"""
        improvements = []
        
        if len(response) < 50:
            improvements.append("Could be more detailed")
        
        if "?" not in response and len(response.split()) < 100:
            improvements.append("Consider adding examples")
        
        return improvements
    
    def _get_fallback_response(self, user_input: str, agent_outputs: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Provide fallback response when synthesis fails"""
        return {
            "final_response": f"I apologize, but I'm having trouble generating a response. Your question was: {user_input[:100]}...",
            "draft_response": "",
            "source_references": [],
            "coherence_score": 0.3,
            "improvement_opportunities": ["System had synthesis error"],
            "synthesis_method": "fallback",
            "agent_id": self.agent_id,
            "synthesis_quality_metrics": {"error": "synthesis_failed"},
            "intent_alignment": {"error": "not_available"},
            "error_handled": True
        }

# Factory function for easy instantiation
def create_synthesis_agent(llm_router=None):
    return ResponseSynthesisAgent(llm_router)