Backend: thread on_fallback callback through analysis chain (gemini_service → agents → analysis_service → handlers). The handler sends a 'model_fallback' WebSocket message exactly once per analysis when the primary model is unavailable. Frontend: handle 'model_fallback' WS message and show a dismissible yellow toast at the bottom of the screen with an 8-second auto-dismiss. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
171 lines
9.1 KiB
Python
171 lines
9.1 KiB
Python
from typing import List, Optional, Tuple
|
|
|
|
from app.agents.base_agent import BaseAgent
|
|
from app.models.schemas import PreviousReviewContext, SubReview
|
|
from app.services.gemini_service import GeminiService
|
|
from app.services.reference_docs import ReferenceDocsService
|
|
|
|
|
|
class ChannelBestPracticesAgent(BaseAgent):
|
|
"""Channel Best Practices Agent - analyzes proofs for creative best practices and content strategy using Gemini."""
|
|
|
|
name = "Channel Best Practices Agent"
|
|
|
|
def __init__(self, gemini_service: GeminiService, reference_docs: ReferenceDocsService):
|
|
"""
|
|
Initialize the Channel Best Practices Agent.
|
|
|
|
Args:
|
|
gemini_service: Service for making Gemini API calls
|
|
reference_docs: Service for loading reference documents
|
|
"""
|
|
self.gemini = gemini_service
|
|
self.reference_docs = reference_docs
|
|
|
|
def _build_revision_context(self, previous_review: PreviousReviewContext) -> str:
|
|
"""Build prompt section for revision-aware analysis."""
|
|
issues_list = "\n".join(f" - {issue}" for issue in previous_review.issues) if previous_review.issues else " (No issues)"
|
|
return f"""
|
|
---
|
|
|
|
**REVISION CONTEXT**
|
|
|
|
This is a revision of a previously reviewed proof. The previous version (Version {previous_review.version}) had the following channel best practices review:
|
|
|
|
- RAG Status: {previous_review.ragStatus}
|
|
- Feedback: {previous_review.feedback}
|
|
- Issues identified:
|
|
{issues_list}
|
|
|
|
When analyzing this revision, you MUST:
|
|
1. Compare against the previous issues and determine which have been RESOLVED
|
|
2. Identify which previous issues are still OUTSTANDING (not fixed)
|
|
3. Identify any NEW issues introduced in this revision
|
|
|
|
Your response MUST include:
|
|
- resolvedIssues: Array of issues from the previous version that have been fixed
|
|
- outstandingIssues: Array of issues from the previous version that remain unfixed
|
|
- newIssues: Array of new issues not present in the previous version
|
|
|
|
---
|
|
"""
|
|
|
|
async def analyze(
|
|
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,
|
|
on_fallback=None,
|
|
) -> SubReview:
|
|
"""
|
|
Analyze the proof for channel best practices and content strategy.
|
|
|
|
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
|
|
"""
|
|
# Get the channel best practices specification
|
|
best_practices_context = self.reference_docs.get_channel_best_practices_spec()
|
|
|
|
# Build revision context if available
|
|
revision_context = ""
|
|
if previous_review:
|
|
revision_context = self._build_revision_context(previous_review)
|
|
|
|
prompt = f"""You are a digital channel best practices specialist for Barclays Bank. Your role is to analyze marketing proofs for creative best practices, content strategy, and platform optimization.
|
|
|
|
Here are the channel best practices guidelines to use for your analysis:
|
|
|
|
{best_practices_context}
|
|
{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**:
|
|
- Is the messaging clear and appropriate for the target platform?
|
|
- Does the content follow platform-specific engagement best practices?
|
|
- Is the call-to-action clear and effective?
|
|
|
|
2. **Creative Best Practices**:
|
|
- Is the visual hierarchy optimized for the channel?
|
|
- Does the layout follow proven engagement patterns?
|
|
- Are key messages prominently displayed?
|
|
|
|
3. **Platform Optimization**:
|
|
- Is the content optimized for the intended platform's algorithm?
|
|
- Does it follow safe zone guidelines for interactive elements?
|
|
- Are text-to-image ratios appropriate for the platform?
|
|
|
|
4. **Engagement Considerations**:
|
|
- Does the design encourage user interaction?
|
|
- Are social-specific elements (hashtags, mentions) used appropriately?
|
|
- Is the tone suitable for the platform's audience expectations?
|
|
|
|
5. **Mobile-First Design**:
|
|
- Is the content legible on mobile devices?
|
|
- Are touch targets appropriately sized?
|
|
- Does it account for thumb-zone navigation?
|
|
|
|
Provide your analysis as a JSON object. Be specific about any issues found.
|
|
|
|
IMPORTANT: Do NOT include page numbers, document names, or source citations in your feedback (e.g., no "Page 10", "per Page 12", "Social Media Guidelines Page 8"). All feedback must be self-contained and directly actionable without requiring users to look up external references.
|
|
|
|
RAG Status Guidelines:
|
|
- **Green**: Fully aligned with best practices, optimized for the channel
|
|
- **Amber**: Minor improvements could enhance performance but content is acceptable
|
|
- **Red**: Significant best practice issues that will impact content effectiveness
|
|
|
|
If the proof is nonsensical, not a marketing material, or cannot be analyzed, set analysisStatus to 'low_confidence'.
|
|
|
|
**Response Format:**
|
|
- Keep feedback brief and scannable
|
|
- Use bullet points for each finding
|
|
- Structure each feedback bullet as two clearly labelled parts separated by a line break:
|
|
**Issue:** [Clear description of what's wrong]
|
|
**Recommendation:** [Actionable fix — what to do and how]
|
|
- Always capitalise "Issue:" and "Recommendation:" and bold them with double asterisks (**)
|
|
- Always place the Recommendation on a new line after the Issue (not on the same line)
|
|
- Example:
|
|
"• **Issue:** The Call-to-Action (CTA) is placed below the fold, reducing visibility.
|
|
**Recommendation:** Move the CTA above the fold so users see it without scrolling."
|
|
- IMPORTANT: Use British English spelling throughout all output (e.g. "authorised" not "authorized", "colour" not "color", "capitalise" not "capitalize", "organised" not "organized", "centre" not "center", "analysed" not "analyzed").
|
|
- IMPORTANT: Never use the words "violation", "violates", or "violated" in your output. Use constructive alternatives such as "issue", "doesn't align with", "doesn't meet", or "conflicts with".
|
|
- IMPORTANT: Use Plain English throughout. Choose simple, clear words over complex vocabulary. Prefer: "add" over "incorporate/integrate", "about" over "regarding", "qualifies as" over "constitutes", "use" over "utilise", "before" over "prior to", "to" over "in order to", "try" over "endeavour", "then" over "subsequently", "put in place" over "implement", "keep/contain" over "constrain", "standard interest rate" over "reversion rate". Avoid unnecessary jargon (e.g. use "exaggerated claim" instead of "puffery"). Feedback should be easy to understand for all users.
|
|
- IMPORTANT: Apply consistent punctuation and capitalisation throughout:
|
|
(a) Always capitalise the first word after a full stop, including labels like "Recommendation:" and "Issue:".
|
|
(b) End every bullet point with a full stop if it is a complete sentence. If bullets are short fragments, omit the full stop — but be consistent within the same output.
|
|
(c) Write "e.g." with no comma after it (e.g. "Apply rotation" not "e.g., Apply rotation").
|
|
- IMPORTANT: When providing example corrections in recommendations, always show the example in the format you are recommending. If recommending sentence case, write the example in sentence case (e.g. "Apply now" not "Apply Now"). If quoting the user's original error, show it first, then the corrected version: "Change 'Apply Now' to 'Apply now' (sentence case)."
|
|
- IMPORTANT: Always spell out acronyms in full on first use, with the abbreviation in parentheses. Use the short form only for subsequent mentions within the same output. Common acronyms to expand include: WCAG (Web Content Accessibility Guidelines), FSCS (Financial Services Compensation Scheme), GDE (Global Digital Expression), APR (Annual Percentage Rate), CTA (Call-to-Action), FCA (Financial Conduct Authority), PRA (Prudential Regulation Authority), T&Cs (Terms and Conditions). Apply this rule to any acronym, not only those listed here.
|
|
"""
|
|
|
|
# Determine if revision fields should be included
|
|
include_revision_fields = previous_review is not None
|
|
|
|
# Use single-image or multi-image analysis depending on input
|
|
if len(images) == 1:
|
|
file_data, file_type = images[0]
|
|
return await self.gemini.analyze_with_image(
|
|
prompt, file_data, file_type, include_revision_fields=include_revision_fields, on_fallback=on_fallback
|
|
)
|
|
else:
|
|
return await self.gemini.analyze_with_images(
|
|
prompt, images, include_revision_fields=include_revision_fields, on_fallback=on_fallback
|
|
)
|