Menlo TechReal Estate
Remax Case

Algorithm Tour

How artificial intelligence is woven into everything Menlo Tech does.

Scroll to start the tour

At Menlo Tech, we turn a business’s data into pages that people find — and that AI engines cite. Our clients have thousands of legitimate topics to cover (cities, neighborhoods, products, integrations, rules), but no editorial team can write, review and maintain all of it by hand.

That is where algorithms come in. They decide which topics deserve a page of their own, how it should be assembled, when it goes live, whether it stays live, and what the platform learns from each result. AI agents do the repetitive work; people do the work that requires judgment. The algorithms choose who does what.

4,200
raw queries collected
38
intent clusters
78%
pages without human review
480
pages in the fourth batch

Illustrative numbers from the example followed in this tour.

  • Intent clustering
  • Viability scoring
  • Assignment by optimization
  • Agent orchestration
  • Duplicate detection
  • Gated launch
  • Markov chains
  • Demand forecasting
  • Economic order quantity
  • Data infrastructure

Let’s follow the path of a single set of pages — from the idea to being cited by an AI — to see some of the algorithms involved. Then we will step back and look at the whole system.

Chapter 1

Demand discovery

It all starts with a simple question: what are people — and AIs — really asking about?

We collect search signals from several sources: the client’s Search Console queries, autocomplete suggestions, questions asked to AI assistants, and what competitors already cover. Each point is a raw query — thousands of them, noisy and redundant.

Queries are turned into vectors with a language model. Different phrases with the same intent (“apartment in Botafogo”, “living in Botafogo”, “is Botafogo worth it?”) end up close together in the vector space.

sim(q, p) = cos θ = (eq · ep) / (‖eq‖ ‖ep‖)

Proximity is measured by the cosine similarity between the vectors.

A clustering algorithm groups the queries into intents. Each group gets an aggregate volume, a difficulty level and an intent type: informational, comparative, transactional.

vol(Ck) = Σq ∈ Ck vq  ·  Ck = { q : argmaxj sim(q, μj) = k }

The groups are cross-checked against the data the client already has. An intent group with supporting data becomes a candidate page type; a group without data becomes a gap to fill — or a discarded idea.

coverage(k) = |usable columns(k)| / |required columns(k)|  ·  build ⇔ coverage ≥ θ
Test the similarity interactive
q p
28°
0.80
0.88cos θ
simsame intent?
+0.08margin over τ
cos θ ≥ τ ⇒ q and p join the same group
“living in botafogo” “bilingual school south zone” “botafogo metro schedule” “2-bedroom rental rj” dim 1 dim 2 cos θ living in the neighborhood 4.8k/mo · informational schools 1.1k/mo · comparative transport 2.3k/mo · informational rentals 9.6k/mo · transactional Intent Client data Decision living in the neighborhood page type rentals page type transport data gap schools discarded Each bar is a usable data column for that intent. Without data of its own, a page would be generic text — and it is not built.
Chapter 2

Page-by-page viability

An approved page type does not mean every row in the table deserves a page. This is the most important decision in the whole system.

Imagine the type “Living in [neighborhood]” for a real estate network. The raw material is a table: one row per neighborhood, one column per fact — average price per m², number of properties, time to downtown, schools, parks, stations.

First, we classify each column. Key columns identify the row; distinctive columns genuinely vary between rows; cosmetic columns change little or not at all. Only distinctive ones can support a page.

Then we score each row: how many distinctive facts does it have that differ from the column’s most common value? A row with many values of its own becomes a rich page; a row that just repeats the mode would become a thin page.

score(r) = Σc ∈ D [ vrc ≠ mode(c) ]

Rows with identical facts are grouped into a single page, instead of generating copies.

The result is a three-level verdict: build all, build some (with the exact list of those that pass), or build just a hub page. It is a conservative filter by design — one page fewer costs little; a thousand thin pages can cost the whole domain.

P = { r : score(r) ≥ k }  ·  verdict = all if |P| = n; some if 0 < |P| < n; hub if |P| = 0
How many rows become a page? interactive
1 of 3
4 of 6rows pass
4final pages
build someverdict
P = { r : score(r) ≥ k }  ·  rows with identical facts count as one page
Living in [neighborhood] neighborhoodavg. m²propertiesmin downtownschoolsparks Botafogo14.2k81218113 Tijuca8.9k1,20425143 Leblon22.6k3403463 Vila Isabel8.9k3882563 Grajaú8.9k3882563 Barra11.1k2,95052223 key distinctive cosmetic “parks” is 3 in every row: it distinguishes nothing. “schools” varies little and depends on a fragile source. 3/3 2/3 3/3 0/3 0/3 3/3 Vila Isabel and Grajaú have identical facts: they become a single page. Score = distinctive facts that differ from the column mode (8.9k, 388, 25). Verdict: build some 4 pages pass · 1 grouped page (Vila Isabel + Grajaú) · 1 hub “Rio neighborhoods” Rows that do not pass only return when they gain new data.
Chapter 3

Agents + editors

Machines are great at repeated calculation. People are better at judgment, context and tone. Work is divided by an algorithm, not by an org chart.

Each approved page gets a specification: which blocks must come directly from the row’s data, what share of the text must vary between pages (our goal is above 40%), which filler blocks are forbidden, and what the title and heading pattern is.

var(p) = |T(p) ∖ Tcommon| / |T(p)|  ≥  0.40

A research agent gathers the row’s facts and the permitted external sources. A writing agent assembles the draft within the specification. A verification agent checks every number against its origin — a number without a source is flagged, never invented.

But not every page is equal. Some are routine; others touch sensitive subjects, have contradictory data or belong to a client with a strong editorial voice. A classifier estimates each draft’s risk and decides whether it goes straight through or passes by a human editor.

P(review) = σ(β₀ + β · x)  ·  review if P ≥ τ

Editors are not interchangeable either. We assign drafts to editors with an optimization problem similar to assigning customers to warehouses: a cost per (editor, page) pair that combines expertise, history with the client and the size of each person’s queue — recalculated several times a day.

min Σi Σj aij cij  s.t.  Σj aij = 1,  Σi aij ≤ qj

The editor works in an interface built to understand the page quickly: the row’s data on one side, the draft on the other, each claim linked to its source. We test variations of this interface all the time; what we learn about how editors decide flows back to improve the agents.

Size the human review interactive
go to review
pages reviewed
editor hours
editors (6 h/day)
P(risco ≥ τ) = 1 − τ0.49  ·  H = N · P · t / 60  (risk ~ Beta(0.49; 1), illustrative)
Specification for the type “Living in [neighborhood]” Required blocks from data price · inventory · commute · map Variable text per page goal ≥ 40% Forbidden “in this complete guide…” · generic history · invented FAQ Title Living in {neighborhood}: prices, properties and daily life in {year} Research agent Writing agent Verification agent “avg. m² 14.2k” ✓ source: client base, 10/2026 “18 min to downtown” ✓ source: route matrix “opened in 1890” ✗ no source → [DATA NEEDED] risk? low · 78% of pages publish high · 22% human review The classifier uses: contradictions between sources, sensitive subjects, the client’s edit history and how new the page type is. Cost (editor, page) p1p2p3p4 AnaBrunoCarla 0.2 0.7 0.9 0.5 0.6 0.1 0.4 0.3 0.8 0.5 0.2 0.9 queue: 3queue: 1queue: 5 min Σ a_ij · c_ij, each page to one editor, respecting the queues Row data avg_m2 14.2k properties 812 min_downtown 18 schools 11 Draft blue: linked to a data point · red: pending a source
Chapter 4

Quality control

Before any page goes live, a sample of the set is tested against the two failure modes that kill programmatic pages: near-duplicates and thin pages.

We take a sample of finished pages and replace each one’s key value with a placeholder. If “Living in Botafogo” and “Living in Tijuca” are identical after that, the variation was just the name — and that is exactly what search engines penalize.

Then we remove the sentences shared by most of the samples. What is left is the text that is truly each page’s own. We measure the variable share against the 40% goal and compare the pages with each other using five-word sequences.

J(A, B) = |G5(A) ∩ G5(B)| / |G5(A) ∪ G5(B)|

Each page gets one of five labels — ok, near-duplicate, variation only by key, low variable share or thin — and an action: keep, merge with another, or publish with noindex until it gains content. None of this depends on someone reading a thousand pages.

Adjust the quality rules interactive
keep
merge
noindex
merge if maxj J(i, j) ≥ τJ; otherwise noindex if var(i) < vmin; otherwise keep
Before Living in Botafogo Botafogo is a charming neighborhood… With an average m² of 14.2k, Botafogo… Living in Botafogo is ideal for… Living in Tijuca Tijuca is a charming neighborhood… With an average m² of 8.9k, Tijuca… Living in Tijuca is ideal for… After swapping the key for [X] Living in [X] [X] is a charming neighborhood… With an average m² of [N], [X]… Living in [X] is ideal for… Living in [X] [X] is a charming neighborhood… With an average m² of [N], [X]… Living in [X] is ideal for… Identical: variation only by key. Similarity between samples (5-grams) ABCDE ABCDE — .21 .18 .24 .17 .21 — .86 .20 .25 .18 .86 — .19 .22 .24 .20 .19 — .49 .17 .25 .22 .49 — Variable share A 58%B 31%C 29%D 44%E 12% goal 40% Green: distinct. Red: near-duplicate. Amber: review. Label → action A ok → keep B near-dup. → merge with C C near-dup. → merge with B D review → keep E thin → noindex The rule is applied to each batch before publishing, with no manual reading.
Chapter 5

Staged launch

A thousand pages never go live at once. The launch is an experiment with gates, and the algorithm decides when to open the next one.

First, the hierarchy: a hub, categories and leaf pages, each level linked to the one above. Sitemaps are split by type and by batch, within Google’s file limits, so we can observe each wave separately.

A small pilot goes live — large enough to be statistically readable, small enough not to hurt if it goes wrong. From there we track three counts per batch: pages published, pages indexed, pages indexed with no impressions at all.

The stop criteria are explicit rules on those ratios. If the indexing rate falls below the threshold, or the share of indexed pages with no impressions rises above it, the next wave does not open. Each cause has its own rollback action: strengthen internal links, enrich data, apply noindex or merge pages.

open batch t+1 ⇔ idxt/pubt ≥ θ₁  ∧  zerot/idxt ≤ θ₂

When a batch passes, the next one opens larger. The same mechanism that accelerates a healthy set brakes a sick one — and the decision is recorded, batch by batch, for anyone who wants to audit.

nt+1 = 2 · nt  if the gate opens;  pause otherwise
Open or close the gate interactive
pages published
next batch
open ⇔ idx/pub ≥ θ₁ ∧ zero/idx ≤ θ₂  ·  nt+1 = 2 nt
Hub: Rio neighborhoods South Zone North Zone West Zone … leaf pages sitemap-bairros-zs-lote1.xml sitemap-bairros-zn-lote1.xml sitemap-bairros-zo-lote1.xml ≤ 50,000 URLs · ≤ 50 MB per file Batches pages pilot 60 published60 indexed51 indexed with no impressions6 idx/pub = 0.85 · zero/idx = 0.12 60 120 240 gate closed batch 3: idx/pub = 0.61 < 0.75 zero/idx = 0.31 > 0.20 Rollback by cause low indexing →internal links + sitemap no impressions →enrich data or noindex 60 120 240 480 next ✓✓✓✓ Each ✓ is a recorded decision with the counts that justified it.
Chapter 6

Learning from the market

Publishing is the beginning, not the end. Each page starts generating signals — and those signals are the raw material of every previous chapter.

Once live, each page has a state: published, indexed, with impressions, with clicks, cited by an AI. We record every state change, together with everything that happened between one and the next — an edit, an updated data point, a new link.

With thousands of pages, the transitions become a matrix. It tells us, for example, the probability that an indexed page will be cited by an AI assistant within 30 days — and how that probability changes when the page has a data table, a direct answer at the top or a cited source.

Mij = N(i → j) / Σk N(i → k)

This is a simple Markov chain model, and it is surprisingly useful: it lets us simulate the whole set months ahead, compare page types with each other and identify which page features move the citation probability most.

πt+1 = πt M  ·  πt+k = πt Mk

What the model learns flows back to the writing agents as specification rules, to the risk classifier as new variables and to viability as column weights. The loop closes — and the next wave already starts better than the previous one.

What moves the chance of AI citation? interactive
chance in 30 days (indexed → AI)
cited per 1,000 indexed
odds ratio
logit(p) = logit(0.07) + Σ βi xi  ·  βi = logit(pi) − logit(0.07)
publishedindexedimpressionsclickscited by AI /morar-em-botafogo Oct 02 published Oct 05 indexed Oct 11 1st impression Oct 19 price table updated Every transition and every event between transitions is recorded. Transitions in 30 days idximprclickAI pubidximpr .82.05.00.00 —.64.11.07 ——.38.19 with data table: .07 → .13 with direct answer at the top: .07 → .16 with cited source: .07 → .11 πt+1 = πt · M type “Living in” type “Properties for sale” month 0month 6 Simulation of the share of pages cited by AI, by type, from the observed transition matrix. → specification rules (ch. 3) → risk classifier variables (ch. 3) → column weights in viability (ch. 2) The loop closes What the market answers becomes a rule for the next wave.
Chapter 7

Forecast and priority

Stepping back: with dozens of clients and hundreds of thousands of pages, the scarce resource stops being writing and becomes attention — from crawlers, editors and compute.

Pages age at different rates. A neighborhood guide changes slowly; a properties-for-sale page changes every day. Each page has a simple “freshness” model: how much its data changed, how much traffic it gets and how long ago it was revalidated.

freshness(p) = w₁ Δdata + w₂ traffic + w₃ age

This model decides the order of regeneration. Instead of rebuilding everything every hour, we regenerate first the pages where the expected gain is greatest — and the same signal feeds the crawl budget we ask search engines for.

Looking even further ahead, search demand is seasonal and predictable. With historical series by intent, we anticipate which sets of pages need to be ready — and reviewed by people — months before the peak, so editorial capacity is where the traffic will be.

yt = Tt + St + εt  (trend + seasonality + noise)
Build the freshness score interactive
freshness score
when to regenerate
Regeneration queue pageΔ datatrafficage /imoveis-a-venda/botafogohighhigh6 h /morar-em-botafogolowhigh3 d /morar-em-grajauzerolow9 d /escolas/tijucamediummedium2 d freshness(p) = f(Δ_data, traffic, age) Three cheap-to-measure signals, combined into a single score. /imoveis-a-venda/botafogo0.92 /escolas/tijuca0.61 /morar-em-botafogo0.34 /morar-em-grajau0.08 Priority Regenerated first: the top two. The last one can wait days. The same score orders the sitemap and the lastmod sent to crawlers. Demand by intent: “living in” (Rio) forecast peak human review JanAprJulOctDec Editorial capacity is reserved before the peak, not after.
Chapter 8

Inventory management

Each sale takes a property out of the portfolio. The challenge is to replenish at the right time and in the right quantity.

In each neighborhood, the property portfolio works like an inventory: each sale or rental removes a unit, and the acquisition of new properties replenishes it. Without replenishment, the “properties for sale” pages empty out and brokers are left with nothing to offer.

The hard part is the timing and size of acquisition. Acquiring too much leaves properties sitting, listings aging and capital tied up; acquiring too little leaves the neighborhood with no supply. We want to minimize the sum of the acquisition cost and the cost of carrying inventory — the latter proportional to the area under the curve.

CT(Q) = K · D / Q + h · Q / 2

Adding the two costs, a batch size appears that balances them. Acquiring in larger batches dilutes the fixed cost of each campaign but inflates inventory; smaller batches do the opposite. The minimum of the curve is the economic order quantity.

Q* = √(2 K D / h)  ·  CT* = √(2 K D h)

Use the controls at the end of this chapter to see the minimum shift.

The real question is bigger: how much to acquire of each type, in which neighborhood, and which old listings to take down to make room. We answer with a system dynamics model, fitted to history and used in robust optimization, with quantified uncertainty in the demand forecasts.

∂x/∂t = f(xt, ut, wt)  ·  months of inventory = x / d

x = inventory levels per neighborhood · u = acquisition, reallocation and removal · w = demand forecast. In practice we look at each property type in each neighborhood; here we stay with the simple case.

Find the economic order quantity interactive
K·D/Q h·Q/2 total cost batch Q Q*
optimal batch Q* (properties)
minimum cost CT* per month
optimal acquisition cycle
extra cost of your Q
Q* = √(2KD/h)  ·  T* = Q*/D  ·  values in R$, illustrative
time properties in inventory x(t) ↘ each sale removes a property ↑ acquisition replenishes K K K area under the curve = carrying cost (h) each acquisition pays a fixed cost K batch Q cost per month acquisition K·D/Q carrying h·Q/2 total cost minimum: Q* Months of inventory by neighborhood target: 3 to 6 Botafogo1.8 · acquire Tijuca4.7 · hold Leblon9.1 · take down Barra3.2 · hold months of inventory = properties in portfolio ÷ sales per month ∂x/∂t = f(xt, ut, wt) x: inventory by neighborhood · u: acquire, reallocate, take down · w: forecast demand
Chapter 9

Data platform

None of this works without a foundation that lets each algorithm team work end to end without asking anyone’s permission.

Client data comes in through connectors and becomes versioned tables: each row carries its source and origin date. That lineage is what lets the verification agent say “this number comes from here” and the editor trust it.

On top of that, a layer of internal services: viability, generation, quality control, launch and learning are independent services, each with its own queue and records. The algorithm teams write the logic; the platform takes care of scaling, repeating and observing.

At the edge, pages are served statically and regenerated incrementally, page by page, in the order the freshness model decides. For the visitor — and for the crawler — the page always exists and always responds fast; all the complexity sits behind it.

R/day = N · q · 24 / T
How much does regenerating cost? interactive
regenerations per day
if everything were regenerated
CPU·h per day (~0.4 s/page)
R = N · q · 24 / T  ·  savings = 1 − q
Versioned data neighborhoods@2026-10-19 · source: client CRM routes@2026-10-12 · source: commute matrix CRM Spreadsheets APIs Connectors Every row knows where it came from and when. tables · lineage · versions Internal services viability generation quality launch learning Each service: its own queue, its own records, its own scale. Algorithm teams write the logic; the platform takes care of the rest. queue → execution → record Delivery Static pages · incremental regeneration per page revalidate(p) = f(freshness) · response in milliseconds

A lot is going on

To recap the path of a single set of pages: we discovered an intent with real demand, verified row by row whether there is data to support it, let agents assemble drafts within a specification, routed to editors only what requires judgment, tested the sample against duplication and thinness, launched in waves with explicit gates, sized property acquisition against the cost of holding inventory — and used what the market answers to start the next wave better.

This work is divided into four teams, three aligned to a business stage and one cutting across all of them.

  • Demand AlgorithmsIntent discovery, page-by-page viability, seasonal forecasting and property inventory management. They decide what is worth building and what to acquire.
  • Content AlgorithmsSpecification, agent orchestration, risk classification, editor assignment. They decide how to build and who reviews.
  • Distribution AlgorithmsQuality control, staged launch, state and freshness models. They decide when to publish, when to stop and what to regenerate.
  • Data PlatformConnectors, lineage, internal services and delivery. They carry no business logic: they build the foundation so the other three can work end to end.

The question that runs through all of this is always the same: between what a machine does well and what a person does better, where is the line — and how do we measure whether it is in the right place?