<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Evgenii Slepinin</title>
    <description>The latest articles on DEV Community by Evgenii Slepinin (@evgenii-slepinin).</description>
    <link>https://dev.to/evgenii-slepinin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4034006%2F0a81c463-98ad-4065-ad33-a8bb19bc7153.jpg</url>
      <title>DEV Community: Evgenii Slepinin</title>
      <link>https://dev.to/evgenii-slepinin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/evgenii-slepinin"/>
    <language>en</language>
    <item>
      <title>RAG is for finding. Full context is for deciding.</title>
      <dc:creator>Evgenii Slepinin</dc:creator>
      <pubDate>Fri, 17 Jul 2026 20:39:39 +0000</pubDate>
      <link>https://dev.to/evgenii-slepinin/rag-is-for-finding-full-context-is-for-deciding-315c</link>
      <guid>https://dev.to/evgenii-slepinin/rag-is-for-finding-full-context-is-for-deciding-315c</guid>
      <description>&lt;p&gt;The advice in 2026 is settled: chunk your documents, embed them, retrieve top-k, feed those to the model. Don't waste context. Don't waste tokens.&lt;/p&gt;

&lt;p&gt;I built a system that deliberately does none of that. Every query gets the expert's entire CV — 69 projects, publications, education, languages. Roughly 34,000 characters, in full, every time.&lt;/p&gt;

&lt;p&gt;Here's the reasoning, because it wasn't laziness.&lt;/p&gt;

&lt;h2&gt;
  
  
  The task decides the architecture
&lt;/h2&gt;

&lt;p&gt;The system finds UN tenders for one specific expert. Every day it crawls 28 sources, normalises and deduplicates thousands of procurement notices, then answers one question per tender: does this person fit?&lt;/p&gt;

&lt;p&gt;That looks like retrieval. It isn't. It's exclusion.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval&lt;/strong&gt; asks: what in this corpus is most similar to the query?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exclusion&lt;/strong&gt; asks: is there anything here that disqualifies this?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are opposite operations. The first wants the strongest matches. The second needs the weakest link — and by definition, the weakest link is the thing least similar to everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why similarity would have failed
&lt;/h2&gt;

&lt;p&gt;A real pattern from the data.&lt;/p&gt;

&lt;p&gt;A tender is titled &lt;code&gt;Public Financial Management&lt;/code&gt;. The expert has a dozen public-finance projects. Cosine similarity between tender and profile: very high.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# what a RAG pipeline does here
&lt;/span&gt;&lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;corpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cv_chunks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# returns: finance_2019, finance_2021, education_economics
# verdict: strong match
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tender is for an environmental officer. It says so once, on page five, in the responsibilities section.&lt;/p&gt;

&lt;p&gt;Or: the role fits perfectly, but requires a national consultant with a mandatory local language. One sentence, buried in eligibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The disqualifying detail is never the semantically closest passage.&lt;/strong&gt; That's exactly what makes it disqualifying — it's the part that doesn't match the pattern. A retrieval system is optimised to not surface it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What chunking destroys
&lt;/h2&gt;

&lt;p&gt;Split a CV into chunks and you keep the facts but lose the shape.&lt;/p&gt;

&lt;p&gt;Retrieve three chunks about public finance and the model sees an expert in public finance. Send all 69 projects and the model sees which countries he's actually worked in, which languages he actually has, what seniority he operates at, and — critically — what he has &lt;strong&gt;never&lt;/strong&gt; done.&lt;/p&gt;

&lt;p&gt;Absence is information. And absence cannot be retrieved. There is no chunk that says "this person has no environmental background". You only learn it by reading everything and noticing nothing is there.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;RAG can tell you what's in a document. It can't tell you what isn't.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The economics I was told to fear
&lt;/h2&gt;

&lt;p&gt;34,000 characters is roughly 9,000 tokens of context per judgement. Multiplied by hundreds of tenders a day, this is supposed to be the expensive mistake.&lt;/p&gt;

&lt;p&gt;It isn't. The AI and the proxies together run on a few dollars a month.&lt;/p&gt;

&lt;p&gt;Two reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The prefilter does the cheap work first.&lt;/strong&gt; Stop-words, theme, territory, language, deadline, expired — all in code, no model calls. The AI only ever sees candidates that survived the rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# rules run first, model runs last
&lt;/span&gt;&lt;span class="n"&gt;candidates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tenders&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;passes_rules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="c1"&gt;# cheap: no model calls
&lt;/span&gt;
&lt;span class="n"&gt;verdicts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;judge_with_full_cv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# expensive, but few
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. The CV is byte-identical on every call.&lt;/strong&gt; Same prefix, over and over — the ideal shape for prompt caching.&lt;/p&gt;

&lt;p&gt;The panel tracks cost per run and cost per &lt;em&gt;found&lt;/em&gt; tender, so this isn't a guess. I watch the number.&lt;/p&gt;

&lt;p&gt;The generalisable lesson: token cost is a real constraint, but it's one people apply from memory rather than measurement. Prices moved. Most RAG pipelines I see were designed for an economics that no longer exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the model returns
&lt;/h2&gt;

&lt;p&gt;Not prose. A structured verdict, logged in full:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"short_reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"national consultant required"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"long_reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Role matches PFM background, but
    eligibility restricts applicants to nationals
    with working Portuguese."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mozambique"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tor_attached"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It judges the substance of the role, not its title. That's the entire point.&lt;/p&gt;

&lt;h2&gt;
  
  
  When I would use RAG — and do
&lt;/h2&gt;

&lt;p&gt;This is not an argument against RAG. It's an argument against defaulting to it.&lt;/p&gt;

&lt;p&gt;RAG is right when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the corpus is &lt;strong&gt;unbounded&lt;/strong&gt; — a knowledge base that grows forever, docs, support history&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;latency&lt;/strong&gt; matters&lt;/li&gt;
&lt;li&gt;the task genuinely &lt;strong&gt;is&lt;/strong&gt; retrieval: find the passage, answer from it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I use RAG on other projects for exactly those reasons. Different task, different shape, different answer.&lt;/p&gt;

&lt;p&gt;The honest caveat on my side: long context isn't free of problems either. Models attend less reliably to the middle of a long prompt than to its start and end. 34k characters sits comfortably inside what current models handle well. At 300k I'd be having a different conversation, and I'd probably be building a hybrid.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule I ended up with
&lt;/h2&gt;

&lt;p&gt;Ask what the model has to &lt;strong&gt;do&lt;/strong&gt; with the document.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Find something&lt;/strong&gt; → retrieve. The corpus can be enormous, and finding is what embeddings are good at.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Judge something&lt;/strong&gt; → give it everything. Judgement needs the whole picture, including the parts that aren't relevant, because irrelevance is half the verdict.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most business tasks people are wiring RAG into are judgement tasks wearing a retrieval costume. &lt;em&gt;Does this candidate fit. Is this contract risky. Should we bid on this.&lt;/em&gt; In every one of those, the thing that decides the answer is the detail nobody would have retrieved.&lt;/p&gt;




&lt;p&gt;Related reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://seo7.es/en/proyecto/tender" rel="noopener noreferrer"&gt;The full tender system: 28 sources, 17 connectors, self-repairing parsers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://seo7.es/en/servicios/inteligencia-artificial" rel="noopener noreferrer"&gt;AI and automation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>python</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Your page ranks #1 on Google and is invisible to ChatGPT. Both are true.</title>
      <dc:creator>Evgenii Slepinin</dc:creator>
      <pubDate>Fri, 17 Jul 2026 19:59:08 +0000</pubDate>
      <link>https://dev.to/evgenii-slepinin/your-page-ranks-1-on-google-and-is-invisible-to-chatgpt-both-are-true-3lg4</link>
      <guid>https://dev.to/evgenii-slepinin/your-page-ranks-1-on-google-and-is-invisible-to-chatgpt-both-are-true-3lg4</guid>
      <description>&lt;p&gt;There's an assumption sitting quietly inside most engineering teams: if Google can crawl and render our site, AI crawlers can too.&lt;/p&gt;

&lt;p&gt;It's wrong. And it's the single most expensive wrong assumption in technical SEO right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap
&lt;/h2&gt;

&lt;p&gt;Googlebot renders JavaScript. It has done for years — the page goes into a render queue, headless Chromium executes the scripts, the result gets indexed. Deferred, but it works.&lt;/p&gt;

&lt;p&gt;AI crawlers don't render anything.&lt;/p&gt;

&lt;p&gt;A server-log study by &lt;strong&gt;Vercel and MERJ&lt;/strong&gt;, published 17 December 2024, found that none of the major AI crawlers execute JavaScript. That includes OpenAI's &lt;code&gt;GPTBot&lt;/code&gt;, &lt;code&gt;OAI-SearchBot&lt;/code&gt; and &lt;code&gt;ChatGPT-User&lt;/code&gt;, and Anthropic's &lt;code&gt;ClaudeBot&lt;/code&gt;. A separate analysis of over 500 million GPTBot fetches found zero evidence of JavaScript execution.&lt;/p&gt;

&lt;p&gt;The detail I find most telling: these crawlers &lt;strong&gt;do download&lt;/strong&gt; JavaScript files. GPTBot in roughly 11.5% of requests, ClaudeBot in roughly 23.84%. They fetch the scripts and never run them.&lt;/p&gt;

&lt;p&gt;There's no partial credit here. Either your content is in the initial HTML response, or it isn't.&lt;/p&gt;

&lt;p&gt;The one exception is Google Gemini, which rides on Googlebot's Web Rendering Service and inherits its ability to execute JS — along with all its queue delays.&lt;/p&gt;

&lt;h2&gt;
  
  
  So both things are true at once
&lt;/h2&gt;

&lt;p&gt;Your React site ranks on page one. Google rendered it, indexed it, ranks it.&lt;/p&gt;

&lt;p&gt;Then a customer asks ChatGPT the exact question your page answers, and you're not in the response. Nothing is wrong with your rankings. The crawler received an empty shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"root"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/assets/index-a1b2c3.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are simultaneously first and invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap inside the trap: structured data
&lt;/h2&gt;

&lt;p&gt;This is where it gets specific.&lt;/p&gt;

&lt;p&gt;You build a React site. You add &lt;code&gt;react-helmet&lt;/code&gt;. You inject JSON-LD through it — Organization, LocalBusiness, FAQPage, the full graph:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Helmet&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"application/ld+json"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Helmet&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You open DevTools, expand &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, and there it is. Beautiful markup. Job done.&lt;/p&gt;

&lt;p&gt;It isn't done. &lt;strong&gt;DevTools shows you the rendered DOM&lt;/strong&gt; — the page after JavaScript has run. That is not what a crawler receives.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;view-source:&lt;/code&gt; instead. Not the Elements panel. If your JSON-LD only exists in DevTools and not in view-source, then GPTBot, ClaudeBot and PerplexityBot never see it.&lt;/p&gt;

&lt;p&gt;Google will still find it, because Google renders. That's why this failure is so quiet — every tool you'd normally check with says you're fine.&lt;/p&gt;

&lt;p&gt;One nuance worth knowing: content doesn't have to be visible prose to count. Data embedded in the initial HTML as inline JSON or server-rendered payload &lt;strong&gt;is&lt;/strong&gt; in the raw response and readable. What's missed is specifically what the browser builds after load.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this compounds
&lt;/h2&gt;

&lt;p&gt;Around 92% of ChatGPT agent queries rely on Bing's index — and Bingbot's JavaScript rendering is limited. So a client-side rendered SPA risks being absent from Bing &lt;strong&gt;and&lt;/strong&gt; from every AI crawler that queries it. Two failures, one root cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test takes thirty seconds
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://you.com/page | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"ld+json"&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://you.com/page | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"your headline text"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;grep&lt;/code&gt; returns nothing, neither does the crawler.&lt;/p&gt;

&lt;p&gt;Do it on your pricing page, your service pages, your best article. Those are the pages with the most to lose.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix is not new
&lt;/h2&gt;

&lt;p&gt;Server-side rendering, static generation, or prerendering. Next.js, Nuxt, Angular Universal, Django SSR — the framework matters less than the principle: &lt;strong&gt;put the real content in the HTML the server sends.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On our own platform the JSON-LD is rendered directly into the DOM rather than injected through Helmet, and key pages are prerendered. Not because it's elegant — because it's the difference between having schema and having schema anyone can read.&lt;/p&gt;

&lt;p&gt;This is the same advice that's been correct for ten years. What changed is the cost of ignoring it. It used to mean waiting on Google's render queue. Now it means being absent from answers entirely, in the fastest-growing discovery channel there is.&lt;/p&gt;




&lt;p&gt;Related reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://seo7.es/en/proyecto/yoga-praktika" rel="noopener noreferrer"&gt;The Django SSR rebuild that took a site from 0 to 116 indexed pages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://seo7.es/en/servicios/ai_seo" rel="noopener noreferrer"&gt;AI visibility / GEO&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>ai</category>
      <category>seo</category>
    </item>
  </channel>
</rss>
