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 <noreply@anthropic.com>
This commit is contained in:
michael 2026-02-12 11:30:38 -06:00
parent adc7a2cc71
commit a957cf0276
7 changed files with 93 additions and 3 deletions

View file

@ -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.

View file

@ -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**:

View file

@ -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**:

View file

@ -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.

View file

@ -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**:

View file

@ -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}")

View file

@ -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