DEV Community

Cover image for Building a Brand Monitoring Agent: Search Is the Easy Part
Marcus ma
Marcus ma

Posted on Originally published at cloudsway.ai

Building a Brand Monitoring Agent: Search Is the Easy Part

A brand monitoring agent sounds like a small weekend project.

Search for the company name, ask an LLM whether each result is positive or negative, and send an alert when something interesting appears.

The first version might even fit into a few lines:

for result in search_web(brand_name):
    sentiment = classify_sentiment(result.snippet)

    if sentiment == "negative":
        send_alert(result)
Enter fullscreen mode Exit fullscreen mode

It will also fail in several predictable ways.

The search may return another company with the same name. A snippet may omit the sentence that changes the meaning of an article. The same press release may appear on ten domains. A review published six months ago may be discovered today and reported as breaking news.

By the end of the week, the team is not monitoring the brand. It is monitoring the monitoring system.

Search turns out to be the easy part. The real engineering begins after the URLs arrive.

TL;DR

  • Define the brand as an entity with products, domains, aliases, and contextual clues instead of monitoring one keyword.
  • Treat search results as discovery candidates until the underlying pages have been retrieved and checked.
  • Store publication time, first-seen time, and retrieval time separately.
  • Deduplicate individual URLs while grouping related coverage into events.
  • Keep workflow state, permissions, and alert decisions in application code rather than delegating everything to an LLM.

The first problem is identity

Brand monitoring generally involves tracking references to a company, its products, and relevant people across news sites, blogs, forums, reviews, social platforms, and other channels.

A company name alone is rarely a reliable identifier.

Suppose the product is called Harbour. Searching for that word could surface shipping terminals, property developments, restaurants, and local government projects. Adding software to every query reduces some noise, but it may also exclude a genuine customer review that never mentions the category.

The agent needs a brand profile that describes what it is looking for:

{
  "name": "Harbour",
  "domain": "harbour.example",
  "industry": "project management software",
  "products": [
    "Harbour Projects",
    "Harbour Teams"
  ],
  "aliases": [
    "Harbour PM"
  ],
  "common_confusions": [
    "shipping port",
    "property development",
    "restaurant"
  ]
}
Enter fullscreen mode Exit fullscreen mode

This profile can support both query generation and later relevance classification.

The common_confusions field should provide context rather than act as a hard blocklist. An article could mention shipping as a metaphor and still discuss the software product. Entity matching is usually a weighted judgement based on several signals: product names, industry language, official domains, named executives, and the surrounding passage.

The classifier should also be allowed to return uncertain. Forcing it to choose between relevant and irrelevant will create false confidence when the page contains too little evidence.

Search results are leads, not source material

The second mistake is analysing search snippets as though they were the original page.

Snippets are designed to help a person decide which result to open. They may be shortened, assembled from different parts of a page, or missing the qualification that determines what a sentence actually means.

A better pipeline separates discovery from extraction:

Query generation
      ↓
Web search
      ↓
Candidate URLs
      ↓
Page retrieval
      ↓
Evidence extraction
      ↓
Classification
Enter fullscreen mode Exit fullscreen mode

This is also a useful way to divide API responsibilities. Cloudsway Search, for example, provides SmartSearch for discovering fresh, source-backed web information and Reader for extracting structured content from pages and documents.

Regardless of the provider, the principle is the same: search finds possible sources; retrieved content provides the evidence used to evaluate them.

If a page cannot be retrieved, store that as a limitation. Do not quietly promote its snippet into a verified mention.

For an accepted result, retain the passage that supports the classification. An LLM-generated summary may make the report easier to scan, but the recipient should still be able to inspect what the source actually said.

A minimal record could look like this:

{
  "url": "https://example.com/harbour-review",
  "title": "Harbour Projects Review",
  "query": "\"Harbour Projects\" review",
  "decision": "relevant",
  "confidence": 0.92,
  "supporting_passage": "The text that supports the decision",
  "topic": "pricing",
  "sentiment": "mixed"
}
Enter fullscreen mode Exit fullscreen mode

A confidence score alone is not evidence. It becomes useful only when it accompanies a decision explanation and a source passage.

One page needs three timestamps

Time creates another subtle problem.

A page can be:

  • published on one date;
  • discovered by the agent on another;
  • retrieved and analysed later.

These events should be stored separately.

{
  "publication_time": "2026-02-04T09:00:00Z",
  "first_seen_time": "2026-09-03T02:10:00Z",
  "retrieved_time": "2026-09-03T02:11:14Z"
}
Enter fullscreen mode Exit fullscreen mode

If an old review becomes discoverable today, it is a new result for the system but not new coverage of the company.

Replacing a missing publication date with the current time makes the record look complete, but it creates a false fact. Unknown should remain unknown.

This distinction also affects scheduled searches. Search indexes may expose pages later than expected, while some sources update existing URLs rather than publishing new ones. Overlapping time windows can reduce missed results, but they do not provide complete coverage.

An occasional broader search is still useful for finding older pages that have only recently become visible.

URL deduplication is not enough

The first kind of duplication is straightforward: the same page may appear with tracking parameters, fragments, or alternate URL formats.

Normalising known tracking parameters can eliminate those copies. Parameters that select a different article, language, or product must be preserved, so blindly removing everything after ? is dangerous.

The second kind is content duplication. Press releases are routinely republished across several websites with only minor formatting changes. Comparing titles and extracted text can identify these near-identical copies.

The third kind is event duplication, and it requires more judgement.

An official launch announcement, five syndicated copies, an independent review, and a customer discussion may all describe the same product launch. They belong to one broad event, but they do not contain the same evidence.

The data model should preserve both levels:

Launch event
├── Official announcement
├── Syndicated copy A
├── Syndicated copy B
├── Independent review
└── Customer discussion
Enter fullscreen mode Exit fullscreen mode

The event prevents the digest from presenting one announcement as several independent developments. The mention records preserve attribution and allow independent reporting to remain visible.

If every result is deduplicated only by URL, syndicated coverage will inflate the report. If every result about the same launch is collapsed into one document, valuable independent evidence will disappear.

A monitoring agent needs memory, but not conversational memory

The system must know what it has already seen.

This does not require the model to remember earlier conversations. It requires durable application state: processed URLs, content fingerprints, event identifiers, previous classifications, and the last time an event triggered a notification.

A simplified processing loop might look like this:

for query in build_queries(brand_profile):
    results = search_web(query)

    for result in results:
        page = retrieve_page(result.url)
        mention = analyse(page, brand_profile)

        if mention.decision == "irrelevant":
            store_rejection(mention)
            continue

        event = match_event(mention)
        change = compare_with_previous_state(event, mention)

        save_mention(event, mention)

        if change.is_material:
            queue_alert(event, change)
Enter fullscreen mode Exit fullscreen mode

Notice that send_alert() is not called immediately after classification.

The new mention is first compared with stored state. The system asks whether it adds evidence, corrects an earlier claim, changes the severity of an event, or simply repeats information that the team has already received.

Without that state, running the same query more frequently only produces the same alert more frequently.

Sentiment is a label, not a priority system

It is tempting to send an immediate notification whenever the model assigns negative sentiment.

That rule confuses tone with impact.

A mildly written review containing an incorrect price may affect purchasing decisions and deserve attention. An angry post from an unrelated account may not. A positive article can also quote a serious customer complaint, while a negative sentence may describe a competitor.

Priority should combine several signals:

priority =
    novelty
    + potential impact
    + source credibility
    + quality of evidence
    + relevance to an owner
Enter fullscreen mode Exit fullscreen mode

Sentiment can be one signal, but it should not control the workflow on its own.

Claims must also remain attributed. If a customer says that an onboarding process deleted their work, the alert can report that a customer made the claim. It should not state that the product deletes customer data unless additional evidence establishes that conclusion.

This becomes particularly important when alerts go to support, communications, legal, or security teams.

Alerts should recommend a decision

“Negative mention detected” does not tell anyone what to do.

A better alert explains the change, evidence, uncertainty, and likely owner:

Pricing information may be outdated

A newly discovered review describes a plan limit that differs from the current product documentation. The statement has not yet been verified. Compare the attached passage with the pricing page before deciding whether to contact the publisher.

The alert record should include the source URL, supporting passage, publication date when available, first-seen time, and the query that discovered it.

Routine coverage can enter a daily digest. A repeated onboarding complaint may go to support. A factual product discrepancy may belong with marketing. A possible vulnerability requires a controlled security workflow.

The agent can recommend the route. External communication should still require human approval.

Finding a public comment is not permission to answer it on the company’s behalf.

Retrieved pages are untrusted input

Any agent that reads the public web has to consider prompt injection.

A retrieved page may contain sentences instructing the model to ignore previous rules, reveal information, or call another tool. Those sentences are part of the source being analysed. They must not become workflow instructions.

The application should keep data and authority separate.

The page can provide evidence for a classification. It should not gain access to notification systems, internal databases, or external actions merely because its text entered the model’s context.

Important controls belong outside the prompt:

  • validate tool arguments;
  • restrict tool permissions;
  • require approval for external actions;
  • escape or isolate retrieved content;
  • log the evidence used for every decision;
  • limit retries and repeated searches.

Prompts help the model follow the workflow. Application code enforces the boundaries.

Open-web monitoring is not complete social listening

A web search API can discover public pages available to a search system. It does not guarantee access to closed groups, private communities, platform feeds, or every newly published social post.

Some sources require platform-specific APIs, authentication, or commercial data access. Open-web monitoring can complement those sources, but it cannot replace them.

This is one reason social listening is usually treated as a broader analysis of conversations and trends, while a brand monitoring workflow may focus on individual mentions that require review.

Your product should describe this limitation accurately.

“No relevant public-web results were found” is a defensible output. “Nobody is talking about the brand” is not.

Build the smallest report people trust

The first version does not need real-time alerts across every channel.

Start with:

One brand profile
One balanced query set
One persistent mention store
One event model
One daily digest
Enter fullscreen mode Exit fullscreen mode

Review samples of accepted and rejected results. Track duplicate-alert rates. Keep a small set of known pages to check whether query or classifier changes break previously working cases.

The useful metric is not how many mentions the agent collects. It is how often the report helps someone understand what changed without reopening every link.

Search supplies the candidates. The rest of the application turns those candidates into evidence, events, and decisions.

That is where a brand monitoring agent becomes more than an automated keyword alert.


If you have built a monitoring or research agent, where did most of the complexity appear: retrieval, entity matching, deduplication, or deciding when to alert?

Top comments (0)