From a957cf0276f564abaaae66db4aaf49e75a0c6153 Mon Sep 17 00:00:00 2001 From: michael Date: Thu, 12 Feb 2026 11:30:38 -0600 Subject: [PATCH] Pass proof metadata (channel, sub-channel, proof type) to AI agents during analysis Previously, proof metadata collected during upload was only used for database persistence. Now it flows through the entire analysis pipeline so agents can tailor their feedback to the specific channel and format being reviewed. Co-Authored-By: Claude Opus 4.6 --- backend/app/agents/brand_agent.py | 14 ++++++++++++++ .../app/agents/channel_best_practices_agent.py | 14 ++++++++++++++ backend/app/agents/channel_tech_specs_agent.py | 14 ++++++++++++++ backend/app/agents/lead_agent.py | 16 ++++++++++++++++ backend/app/agents/legal_agent.py | 14 ++++++++++++++ backend/app/services/analysis_service.py | 16 +++++++++++++--- backend/app/websocket/handlers.py | 8 ++++++++ 7 files changed, 93 insertions(+), 3 deletions(-) diff --git a/backend/app/agents/brand_agent.py b/backend/app/agents/brand_agent.py index 0827e05..df32eb0 100755 --- a/backend/app/agents/brand_agent.py +++ b/backend/app/agents/brand_agent.py @@ -71,6 +71,9 @@ Your response MUST include: images: List[Tuple[bytes, str]], previous_review: Optional[PreviousReviewContext] = None, brand: str = "Barclaycard", + channel: Optional[str] = None, + sub_channel: Optional[str] = None, + proof_type: Optional[str] = None, ) -> SubReview: """ Analyze the proof for brand guideline adherence. @@ -79,6 +82,9 @@ Your response MUST include: images: List of (file_data, mime_type) tuples representing the proof previous_review: Optional context from previous version for revision-aware analysis brand: Brand to analyze against ('Barclays' or 'Barclaycard') + channel: Target channel (e.g. "Social", "Digital") + sub_channel: Target sub-channel (e.g. "Meta", "Google") + proof_type: Proof format type (e.g. "In-feed 1x1", "Banner") Returns: SubReview with brand compliance assessment @@ -99,6 +105,14 @@ Here is the {brand} brand specification to use for your analysis: {revision_context} --- +**PROOF METADATA** +- Channel: {channel or "Not specified"} +- Sub-Channel: {sub_channel or "Not specified"} +- Proof Type: {proof_type or "Not specified"} + +Use this metadata to focus your analysis on the specific brand requirements for this channel and format. +--- + Analyze the uploaded proof against the {brand} brand specification above, checking for: 1. **Logo Usage**: Is the {brand} logo used correctly? Check minimum size, clear space, placement, and that it hasn't been altered. Verify correct logo colors per guidelines. diff --git a/backend/app/agents/channel_best_practices_agent.py b/backend/app/agents/channel_best_practices_agent.py index 67237de..0417d53 100644 --- a/backend/app/agents/channel_best_practices_agent.py +++ b/backend/app/agents/channel_best_practices_agent.py @@ -54,6 +54,9 @@ Your response MUST include: self, images: List[Tuple[bytes, str]], previous_review: Optional[PreviousReviewContext] = None, + channel: Optional[str] = None, + sub_channel: Optional[str] = None, + proof_type: Optional[str] = None, ) -> SubReview: """ Analyze the proof for channel best practices and content strategy. @@ -61,6 +64,9 @@ Your response MUST include: Args: images: List of (file_data, mime_type) tuples representing the proof previous_review: Optional context from previous version for revision-aware analysis + channel: Target channel (e.g. "Social", "Digital") + sub_channel: Target sub-channel (e.g. "Meta", "Google") + proof_type: Proof format type (e.g. "In-feed 1x1", "Banner") Returns: SubReview with channel best practices assessment @@ -81,6 +87,14 @@ Here are the channel best practices guidelines to use for your analysis: {revision_context} --- +**PROOF METADATA** +- Channel: {channel or "Not specified"} +- Sub-Channel: {sub_channel or "Not specified"} +- Proof Type: {proof_type or "Not specified"} + +Use this metadata to focus your analysis on the specific best practices for this channel and format. +--- + Analyze the uploaded proof for adherence to channel best practices, checking: 1. **Content Strategy**: diff --git a/backend/app/agents/channel_tech_specs_agent.py b/backend/app/agents/channel_tech_specs_agent.py index 4da7b29..ca1372d 100644 --- a/backend/app/agents/channel_tech_specs_agent.py +++ b/backend/app/agents/channel_tech_specs_agent.py @@ -54,6 +54,9 @@ Your response MUST include: self, images: List[Tuple[bytes, str]], previous_review: Optional[PreviousReviewContext] = None, + channel: Optional[str] = None, + sub_channel: Optional[str] = None, + proof_type: Optional[str] = None, ) -> SubReview: """ Analyze the proof for technical specifications compliance. @@ -61,6 +64,9 @@ Your response MUST include: Args: images: List of (file_data, mime_type) tuples representing the proof previous_review: Optional context from previous version for revision-aware analysis + channel: Target channel (e.g. "Social", "Digital") + sub_channel: Target sub-channel (e.g. "Meta", "Google") + proof_type: Proof format type (e.g. "In-feed 1x1", "Banner") Returns: SubReview with technical specifications assessment @@ -81,6 +87,14 @@ Here are the channel technical specifications to use for your analysis: {revision_context} --- +**PROOF METADATA** +- Channel: {channel or "Not specified"} +- Sub-Channel: {sub_channel or "Not specified"} +- Proof Type: {proof_type or "Not specified"} + +Use this metadata to focus your analysis on the specific technical specifications for this channel and format. +--- + Analyze the uploaded proof for technical specification compliance, checking: 1. **Dimensions & Resolution**: diff --git a/backend/app/agents/lead_agent.py b/backend/app/agents/lead_agent.py index c317de1..df51e61 100755 --- a/backend/app/agents/lead_agent.py +++ b/backend/app/agents/lead_agent.py @@ -76,6 +76,9 @@ In your summary: self, reviews: dict[str, SubReview], previous_analysis: Optional[dict] = None, + channel: Optional[str] = None, + sub_channel: Optional[str] = None, + proof_type: Optional[str] = None, ) -> tuple[OverallStatus, str, str | None]: """ Synthesize specialist reviews into final verdict and summary. @@ -84,6 +87,9 @@ In your summary: reviews: Dictionary mapping agent names to their SubReview results previous_analysis: Optional dict containing the previous version's analysis results. When provided, enables revision-aware summary. + channel: Target channel (e.g. "Social", "Digital") + sub_channel: Target sub-channel (e.g. "Meta", "Google") + proof_type: Proof format type (e.g. "In-feed 1x1", "Banner") Returns: Tuple of (overall_status, summary, financial_promotion_reason) @@ -106,11 +112,21 @@ In your summary: if previous_analysis and previous_analysis.get("version"): revision_context = self._build_revision_context(previous_analysis, reviews) + # Build proof metadata context + metadata_context = f""" +**PROOF METADATA** +- Channel: {channel or "Not specified"} +- Sub-Channel: {sub_channel or "Not specified"} +- Proof Type: {proof_type or "Not specified"} +""" + # Build the prompt for Gemini to generate summary prompt = f""" You are a Lead Agent responsible for auditing a marketing proof. You have received feedback from specialist AI agents. Your task is to provide a final verdict and write a concise, professional summary to the user. +{metadata_context} + Here is the logic you must follow: 1. The Legal Agent has determined if this is a financial promotion: {is_financial_promotion}. 2. If it IS a financial promotion, the final verdict MUST be 'Requires Manual Legal Review'. Your summary should state this clearly, explain that a separate manual legal review is required, and then summarize any other issues found by the other agents. diff --git a/backend/app/agents/legal_agent.py b/backend/app/agents/legal_agent.py index 65f46e5..d34cbc5 100755 --- a/backend/app/agents/legal_agent.py +++ b/backend/app/agents/legal_agent.py @@ -54,6 +54,9 @@ Your response MUST include: self, images: List[Tuple[bytes, str]], previous_review: Optional[PreviousReviewContext] = None, + channel: Optional[str] = None, + sub_channel: Optional[str] = None, + proof_type: Optional[str] = None, ) -> SubReview: """ Analyze the proof for legal compliance. @@ -61,6 +64,9 @@ Your response MUST include: Args: images: List of (file_data, mime_type) tuples representing the proof previous_review: Optional context from previous version for revision-aware analysis + channel: Target channel (e.g. "Social", "Digital") + sub_channel: Target sub-channel (e.g. "Meta", "Google") + proof_type: Proof format type (e.g. "In-feed 1x1", "Banner") Returns: SubReview with legal compliance assessment @@ -81,6 +87,14 @@ Here are the legal guidelines to use for your analysis: {revision_context} --- +**PROOF METADATA** +- Channel: {channel or "Not specified"} +- Sub-Channel: {sub_channel or "Not specified"} +- Proof Type: {proof_type or "Not specified"} + +Use this metadata to focus your analysis on the specific legal requirements for this channel and format. +--- + Analyze the uploaded proof for legal compliance, checking: 1. **Financial Promotion Detection**: diff --git a/backend/app/services/analysis_service.py b/backend/app/services/analysis_service.py index 95d2ddf..6a6e595 100755 --- a/backend/app/services/analysis_service.py +++ b/backend/app/services/analysis_service.py @@ -105,6 +105,9 @@ class AnalysisService: brand: str, on_agent_update: AgentCallback | None, previous_review: Optional[PreviousReviewContext] = None, + channel: Optional[str] = None, + sub_channel: Optional[str] = None, + proof_type: Optional[str] = None, ) -> Tuple[str, SubReview]: """Run a single agent with callback notifications.""" agent = self.agents[agent_name] @@ -114,9 +117,9 @@ class AnalysisService: await on_agent_update(agent_name, None) if agent_name == "Brand Agent": - review = await agent.analyze(images, previous_review=previous_review, brand=brand) + review = await agent.analyze(images, previous_review=previous_review, brand=brand, channel=channel, sub_channel=sub_channel, proof_type=proof_type) else: - review = await agent.analyze(images, previous_review=previous_review) + review = await agent.analyze(images, previous_review=previous_review, channel=channel, sub_channel=sub_channel, proof_type=proof_type) logger.info(f"[ANALYSIS] Agent completed: {agent_name} - ragStatus: {review.ragStatus}") if on_agent_update: @@ -132,6 +135,9 @@ class AnalysisService: is_wip: bool = False, brand: str = "Barclaycard", previous_analysis: Optional[dict] = None, + channel: Optional[str] = None, + sub_channel: Optional[str] = None, + proof_type: Optional[str] = None, ) -> Tuple[AgentReview, Optional[List[Tuple[bytes, int, int]]]]: """ Analyze a proof using all agents in parallel. @@ -198,6 +204,9 @@ class AnalysisService: brand, on_agent_update, previous_review=self._extract_previous_review_context(agent_name, previous_analysis), + channel=channel, + sub_channel=sub_channel, + proof_type=proof_type, ) for agent_name in self.AGENT_ORDER ] @@ -210,7 +219,8 @@ class AnalysisService: await on_agent_update("Summary", None) overall_status, summary, financial_promotion_reason = await self.lead_agent.synthesize( - reviews, previous_analysis=previous_analysis + reviews, previous_analysis=previous_analysis, + channel=channel, sub_channel=sub_channel, proof_type=proof_type, ) logger.info(f"[ANALYSIS] Analysis complete - overallStatus: {overall_status}") diff --git a/backend/app/websocket/handlers.py b/backend/app/websocket/handlers.py index bb9ddb2..8464108 100755 --- a/backend/app/websocket/handlers.py +++ b/backend/app/websocket/handlers.py @@ -127,6 +127,11 @@ async def handle_analyze_message( logger.warning(f"[WEBSOCKET] Failed to fetch previous analysis: {str(e)}") # Continue without previous analysis - still run the current analysis + # Extract proof metadata for agent context + channel = data.get("channel") + sub_channel = data.get("sub_channel") + proof_type = data.get("proof_type") + # Run the analysis logger.info("[WEBSOCKET] Starting analysis...") result, pdf_pages = await analysis_service.analyze_proof( @@ -136,6 +141,9 @@ async def handle_analyze_message( is_wip=is_wip, brand=brand, previous_analysis=previous_analysis, + channel=channel, + sub_channel=sub_channel, + proof_type=proof_type, ) # Build the result dict