Agent Playbook, build tutorial

How to Build an AI Agent for Content Production

A walkthrough of how to build a content agent that takes a brief to a researched, on-brand first draft: it gathers real sources and checks each link resolves, writes in your voice with inline citations, and adds internal links from your own sitemap, while a person edits and approves before anything is published. Every code step is shown in three frameworks, Google ADK, the Claude SDK, and LangGraph, so you can build it on the stack your team already knows. The code here is the agent's core logic; standing it up in production also means adding the setup, integrations, and hardening around it.

Why the blank page is the bottleneck

Most of the time a piece of content takes is not the writing, it is everything around it: finding sources, checking they are still live, building an outline, remembering to link to your own pages, and keeping the voice consistent. Do that by hand for every brief and the queue backs up, citations get skipped, and the draft reads like whoever was free that day.

A content agent does the heavy first pass. It researches with real sources it has checked resolve, drafts on-brand with the citations in place, and suggests internal links from your sitemap, so a writer starts from a strong, sourced draft rather than a blank page. The one thing it never does is publish on its own: a person always edits and approves. For getting that draft seen and shared, this pairs with the social-media playbook.

Pick a framework: ADK, Claude SDK, or LangGraph

You do not build the agent loop from scratch. A framework gives you the moving parts, and you supply the logic: a step is a model plus an instruction plus a tool, a search tool for research, a typed schema for the outline. The code tabs on the build steps show the same research, outline, and draft flow three ways.

Google ADK composes the steps with a SequentialAgent and a Runner, the Claude SDK drives a tool-use loop, and LangGraph builds an explicit graph. The source verification, the internal linking, the SEO checks, and the approval guardrail are deterministic and identical in all three; only the orchestration and the model calls differ.

The architecture

Every brief flows through one loop: read in, researched with sources verified, outlined, then drafted on-brand with citations. Internal links and SEO checks are added, and the draft goes to a person to edit and approve before it reaches the CMS. Guardrails for source verification, idempotency, and audit wrap the whole thing.

1. Take the brief topic, style guide 2 to 3. Research and outline verified sources, headings 4. Draft on-brand inline citations 5 to 6. Links and SEO sitemap, structure checks 7. Edit and approve a person edits, then CMS Sources verify each URL resolves 8. Run and monitor scheduled, audited Guardrails real sources never auto-publish audit log draft check sources verified feedback loop

Before you build: prerequisites

Inputs and access

A brief format (topic, audience, angle, keyword, length), your style guide, and your sitemap so internal links point at real pages.

Systems to connect

A model API key (Gemini, Claude, or both), a search or retrieval tool for research, and your CMS for the human-approved draft to land in.

Governance

Who edits and approves, your originality and citation rules, your AI-assistance disclosure policy, and DPDP Act 2023 handling for any personal data in briefs.

Setup: install and keys

Install the packages and set your Gemini key. That is enough to run the runnable file below, which researches a sample brief with live web search, verifies the sources, and drafts an outline locally. For production you also connect your search or retrieval tool, your sitemap, and the CMS the approved draft lands in.

Shell (install and key)
# Python 3.10 or newer
python -m venv venv && source venv/bin/activate

# The runnable path below uses Google ADK + Gemini:
pip install google-adk google-genai httpx pydantic

# Your Gemini key from aistudio.google.com/apikey:
export GEMINI_API_KEY="your-key-here"

The stack, and why each piece

Layer Pick Why
Brief and style-guide intake A typed brief plus your style guide (tone, banned words, reading level) Every draft starts from the same inputs: the topic, the audience, the angle, and the rules your brand writes by. Deterministic, so two runs of the same brief begin the same way.
Research and sources A model with a real search or retrieval tool (Google Search grounding, Claude web search, Tavily) Gathers facts from the live web and your own documents, and keeps the source URL with every claim. The code tabs show this three ways. The agent later verifies each URL actually resolves.
Outline A model over the brief and the gathered research Turns the brief and the verified sources into a heading structure a writer would recognise, so the draft has a spine instead of wandering.
Drafting A model with your voice, the outline, and inline citations Writes the draft on-brand, section by section, citing the source next to each claim that needs one. The draft is for a person to edit, never to publish on its own.
Internal linking and SEO Deterministic Python over your sitemap and a structure checker Matches keywords to your own URLs to add internal links, then checks headings, meta, length, and readability against your rules. Both are rules, not guesses.
Human review and data An approval step, a draft store, and an audit log Holds the brief, the sources, the draft, and the originality check, and records every run, so a person edits and approves before anything is published.

Build it, step by step

The research, draft, and assembly steps show the same logic in Google ADK, the Claude SDK, and LangGraph. Use the tabs to switch. The brief intake, the internal linking, the SEO checks, and the approval step are deterministic Python and identical everywhere. These are the building blocks, and they call your own systems (the search tool, your sitemap, the CMS) by name; the complete file you can run today is in "Put it together" below.

1

Take the brief and the style guide

Start with the inputs, not the cleverness. The agent reads a typed brief, the topic, the audience, the angle, the target keyword, and a desired length, alongside your style guide: tone, banned words, and reading level. It keys the run on the brief id so the pipeline is idempotent, the same brief is processed once even if the job re-runs. Deterministic, and the same in every framework.

Python (brief intake)
from dataclasses import dataclass, field

@dataclass
class Brief:
    id: str
    topic: str
    audience: str
    angle: str
    keyword: str
    words: int = 1200
    style: dict = field(default_factory=dict)   # tone, banned words, reading level

def load_brief(raw: dict, seen: set) -> Brief | None:
    if raw["id"] in seen:                        # already drafted
        return None
    seen.add(raw["id"])
    return Brief(**raw)
# The brief id makes it idempotent: each brief is drafted once, even on a re-run.
2

Research the topic and gather real sources

A draft is only as good as what it is built on, so before any writing the agent researches the topic with a real search or retrieval tool and keeps the source URL with every fact it gathers. It then verifies each URL actually resolves with a plain HTTP check, because a citation that 404s is worse than none. Nothing is invented here: a claim with no source that resolves does not make it into the brief. The tabs show research three ways, Gemini with Google Search grounding, Claude with its web search tool, and LangChain with a search tool.

from google import genai
from google.genai import types
import httpx
client = genai.Client()

def research(topic: str) -> dict:
    # Google Search grounding: the model searches the live web for real sources.
    res = client.models.generate_content(
        model="gemini-2.5-flash",
        contents=[f"Research '{topic}'. Return key facts with the source URL for each."],
        config=types.GenerateContentConfig(
            tools=[types.Tool(google_search=types.GoogleSearch())]))
    urls = [c.web.uri for c in
            res.candidates[0].grounding_metadata.grounding_chunks if c.web]
    return {"notes": res.text, "sources": verify(urls)}

def verify(urls: list[str]) -> list[str]:
    # A cited URL that does not resolve is dropped, never kept.
    return [u for u in urls if _ok(u)]

def _ok(u: str) -> bool:
    try:    return httpx.head(u, follow_redirects=True, timeout=8).status_code < 400
    except Exception: return False
from anthropic import Anthropic
import httpx
client = Anthropic()

def research(topic: str) -> dict:
    # The server-side web_search tool grounds the model in real, live results.
    res = client.messages.create(model="claude-opus-4-8", max_tokens=2048,
        tools=[{"type": "web_search_20250305", "name": "web_search",
                "max_uses": 5}],
        messages=[{"role": "user", "content":
            f"Research '{topic}'. List key facts, each with its source URL."}])
    text = "".join(b.text for b in res.content if b.type == "text")
    urls = [b.url for b in res.content
            if getattr(b, "type", "") == "web_search_result"]
    return {"notes": text, "sources": [u for u in urls if _ok(u)]}

def _ok(u: str) -> bool:
    try:    return httpx.head(u, follow_redirects=True, timeout=8).status_code < 400
    except Exception: return False
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_community.tools.tavily_search import TavilySearchResults
import httpx

search = TavilySearchResults(max_results=6)          # a real web-search tool
llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash")

def research(topic: str) -> dict:
    hits = search.invoke(topic)                      # [{content, url}, ...]
    sources = [h["url"] for h in hits if _ok(h["url"])]
    notes = llm.invoke(
        f"Summarise key facts about '{topic}' from: {hits}").content
    return {"notes": notes, "sources": sources}

def _ok(u: str) -> bool:
    try:    return httpx.head(u, follow_redirects=True, timeout=8).status_code < 400
    except Exception: return False
3

Build an outline from the brief and the research

With verified research in hand, the next move is structure. The agent calls the model once to turn the brief and the gathered facts into a heading outline, an introduction, a handful of sections that each map to something the research supports, and a conclusion. A structured outline keeps the draft from wandering and gives the writer a spine to edit against. This is a single, plain model call, the same in every framework.

Python (outline, one model call)
from google import genai
from google.genai import types
from pydantic import BaseModel
client = genai.Client()

class Outline(BaseModel):
    title: str
    sections: list[str]          # ordered H2 headings

def outline(brief, research: dict) -> Outline:
    res = client.models.generate_content(
        model="gemini-2.5-flash",
        contents=[f"Topic: {brief.topic}. Angle: {brief.angle}. "
                  f"Audience: {brief.audience}. Research notes:\n{research['notes']}\n"
                  f"Return a title and ordered H2 headings for a {brief.words}-word piece."],
        config=types.GenerateContentConfig(
            response_mime_type="application/json", response_schema=Outline))
    return res.parsed            # a typed Outline the drafter writes against
4

Draft on-brand with inline citations

Now the writing. The model drafts section by section against the outline, in your voice and within your style guide, and it cites a source next to each claim that needs one, drawing only from the verified sources gathered earlier. This is the one place to be clear: the agent drafts, it does not publish. Every draft goes to a person to edit and approve. The tabs show the same on-brand draft three ways.

from google import genai
client = genai.Client()

def draft(brief, outline, sources: list[str]) -> str:
    return client.models.generate_content(
        model="gemini-2.5-flash",
        contents=[f"Write a {brief.words}-word draft for the outline below in this "
                  f"voice: {brief.style}. Cite a source inline, as [n], next to each "
                  f"claim, using ONLY these sources: {sources}. Do not invent sources. "
                  f"This is a draft for human review, not for publishing.\n{outline}"]).text
# The return value is a draft a person edits and approves; the agent never publishes.
from anthropic import Anthropic
client = Anthropic()

def draft(brief, outline, sources: list[str]) -> str:
    res = client.messages.create(model="claude-opus-4-8", max_tokens=4096,
        messages=[{"role": "user", "content":
            f"Write a {brief.words}-word draft for this outline in the voice {brief.style}. "
            f"Add an inline [n] citation next to each claim, using ONLY these verified "
            f"sources: {sources}. Never invent a source. Draft for review, not publishing."
            f"\n{outline}"}])
    return res.content[0].text
from langchain_google_genai import ChatGoogleGenerativeAI
llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash")   # or ChatAnthropic(...)

def draft(brief, outline, sources: list[str]) -> str:
    return llm.invoke(
        f"Write a {brief.words}-word on-brand draft ({brief.style}) for the outline "
        f"below. Cite a source inline as [n] beside each claim, using ONLY these "
        f"verified sources: {sources}. Do not invent sources. Draft for review only."
        f"\n{outline}").content
5

Insert internal links from your sitemap

A draft that links only outward leaks the reader away, so the agent adds links to your own pages. This is deterministic: it loads the URLs from your sitemap with their target keywords, and where a keyword appears in the draft it wraps the first mention in a link to that URL. Matching against your own sitemap means it can only ever link to pages that exist, never to a made-up path. Identical in every framework.

Python (deterministic)
import re

def load_sitemap_keywords() -> dict[str, str]:
    # keyword -> a real URL on your site, parsed from sitemap.xml
    return {"demand forecasting": "/services/demand-forecasting/",
            "computer vision": "/services/computer-vision/"}

def add_internal_links(markdown: str, kw_to_url: dict[str, str]) -> str:
    for kw, url in kw_to_url.items():
        # link only the FIRST mention, and only whole words
        markdown = re.sub(rf"\b({re.escape(kw)})\b",
                          rf"[\1]({url})", markdown, count=1, flags=re.I)
    return markdown
# Links only ever point at URLs already in your sitemap, never an invented path.
6

Check SEO and structure

Before a person ever opens the draft, the agent runs it past your rules. This is deterministic: it checks the heading hierarchy, the meta title and description lengths, the word count against the brief, the presence of the target keyword, and a readability score. It does not block, it produces a checklist of pass and fail items so the editor sees what needs attention at a glance. Rules, not a model opinion, so the result is the same every time.

Python (deterministic)
import re, textstat

def seo_checks(brief, markdown: str, meta_title: str, meta_desc: str) -> list[dict]:
    words = len(re.findall(r"\w+", markdown))
    checks = [
      {"name": "word count", "ok": words >= brief.words * 0.8},
      {"name": "has H2", "ok": markdown.count("## ") >= 2},
      {"name": "keyword present", "ok": brief.keyword.lower() in markdown.lower()},
      {"name": "meta title <= 60", "ok": len(meta_title) <= 60},
      {"name": "meta desc 50-160", "ok": 50 <= len(meta_desc) <= 160},
      {"name": "readable (grade <= 10)", "ok": textstat.flesch_kincaid_grade(markdown) <= 10},
    ]
    return checks      # a pass/fail checklist for the editor, never an auto-block
7

Human edit and approve before publish

This is the guardrail that makes the whole thing trustworthy. The draft, its sources, the link suggestions, and the SEO checklist go into a review queue, where a person edits the copy, verifies the cited facts, keeps or cuts the suggested links, and approves. Only on approval does anything move toward publishing, and even then through your normal CMS step, not the agent. Nothing your readers see was published without a human behind it. Deterministic, and the same in every framework.

Python (queue, never auto-publish)
def queue_for_review(brief, draft: str, sources: list, checks: list) -> None:
    review_queue.add(brief_id=brief.id, draft=draft,
                     sources=sources, checks=checks)      # awaits a human

def on_approval(brief, edited_markdown: str) -> None:
    # A person has edited and approved; hand to the CMS as a DRAFT, not live.
    cms.create_draft(title=brief.topic, body=edited_markdown)
    audit.log("approved_to_cms", brief.id)        # only after human approval
# The agent drafts and queues; publishing is a human action in your CMS.
8

Assemble the run, with source verification and publish guardrails

The pieces run on each new brief. ADK composes research, outline, and draft under a SequentialAgent and a Runner, the Claude SDK runs them in a tool-use loop, and LangGraph builds an explicit research-outline-draft graph. Whichever you pick, the same guardrails apply: every cited URL is checked to be real and resolving so no source is fabricated, an originality check flags anything too close to a source, internal links only point at your sitemap, the brief id makes every run idempotent, a person edits and approves before publish, and every run is written to an immutable audit log.

from google.adk.agents import SequentialAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService

content_pipeline = SequentialAgent(name="content_pipeline",
    sub_agents=[researcher, outliner, drafter])   # each an LlmAgent with the tools

runner = Runner(agent=content_pipeline, app_name="content",
                session_service=InMemorySessionService())

def guard(brief, draft: dict) -> str | None:
    if store.seen(brief.id):               return "already_drafted"  # idempotent
    if any(not _ok(u) for u in draft["sources"]): return "bad_source"  # must resolve
    if originality(draft["text"]) < 0.8:   return "too_similar"      # plagiarism check
    return None
# Run on each new brief via cron / Cloud Scheduler. Publishing always needs a human.
from anthropic import Anthropic
client = Anthropic()
TOOLS = [research_tool, outline_tool, draft_tool, link_tool]   # JSON schemas

def run_content(messages: list):
    while True:
        resp = client.messages.create(model="claude-opus-4-8", max_tokens=2048,
                                      tools=TOOLS, messages=messages)
        if resp.stop_reason != "tool_use":
            return resp                       # no publish tool: drafts only
        messages.append({"role": "assistant", "content": resp.content})
        for b in resp.content:
            if b.type == "tool_use":
                out = dispatch(b.name, b.input)
                messages.append({"role": "user", "content": [
                    {"type": "tool_result", "tool_use_id": b.id, "content": str(out)}]})
from langgraph.graph import StateGraph, START, END

g = StateGraph(dict)
g.add_node("research", lambda s: {"research": research(s["topic"])})
g.add_node("outline",  lambda s: {"outline": outline(s["brief"], s["research"])})
g.add_node("draft",    lambda s: {"draft": draft(s["brief"], s["outline"],
                                                 s["research"]["sources"])})
g.add_edge(START, "research")
g.add_edge("research", "outline")
g.add_edge("outline", "draft")
g.add_edge("draft", END)
content_pipeline = g.compile()    # invoke per brief; drafts queue for human review

Put it together: run it end to end

Here is a runnable file. It researches a sample brief with live web search, verifies that each cited URL resolves, and drafts a titled outline with inline citations, and it does not publish anything. The brief is inline text; production reads it from your queue, as in the build steps. Save it as main.py and run python main.py.

Python (full runnable file, ADK)
# main.py  --  run:  python main.py
# A runnable reference: researches a sample brief with live web search, verifies
# each source URL resolves, and drafts an on-brand outline. It does NOT publish
# (publishing is always a human action in your CMS).
# The brief is inline; production reads it from your queue, as in the build steps.
import asyncio
import httpx
from google import genai
from google.genai import types
from google.adk.agents import LlmAgent
from google.adk.runners import InMemoryRunner

client = genai.Client()                       # reads GEMINI_API_KEY

# ---- a sample brief (replace with a real brief from your queue) ----
SAMPLE_BRIEF = """Topic: demand forecasting for Indian retail SMEs
Audience: small retail owners. Angle: practical, not technical.
Keyword: demand forecasting. Target length: about 1000 words.
Voice: warm, plain English, no jargon, no banned words."""
# --------------------------------------------------------------------

def research(topic: str) -> str:
    """Research the topic with live Google Search and keep only URLs that resolve."""
    res = client.models.generate_content(
        model="gemini-2.5-flash",
        contents=[f"Research '{topic}'. List key facts, each with its source URL."],
        config=types.GenerateContentConfig(
            tools=[types.Tool(google_search=types.GoogleSearch())]))
    meta = res.candidates[0].grounding_metadata
    urls = [c.web.uri for c in (meta.grounding_chunks or []) if c.web]
    good = [u for u in urls if _resolves(u)]      # drop any URL that 404s
    return res.text + "\n\nVerified sources: " + ", ".join(good)

def _resolves(u: str) -> bool:
    """A cited URL that does not resolve is dropped, never kept."""
    try:    return httpx.head(u, follow_redirects=True, timeout=8).status_code < 400
    except Exception: return False

def outline_and_draft(brief: str, research_notes: str) -> str:
    """Build an outline and an on-brand draft that cites only verified sources."""
    return client.models.generate_content(
        model="gemini-2.5-flash",
        contents=["Using ONLY the verified sources in the notes, write a title, H2 "
                  "outline, and a first draft for this brief. Cite each claim inline "
                  "as [n]. Do not invent sources. This is a draft for human review, "
                  "not for publishing.\n\nBrief:\n" + brief +
                  "\n\nResearch notes:\n" + research_notes]).text

agent = LlmAgent(
    name="content_agent", model="gemini-2.5-flash",
    instruction=("Call research on the brief's topic, then call outline_and_draft "
                 "with the brief and the research notes. Report the verified sources "
                 "and the draft. Make clear the draft is queued for human editing and "
                 "approval, and is not published."),
    tools=[research, outline_and_draft])

async def main():
    runner = InMemoryRunner(agent=agent, app_name="content")
    session = await runner.session_service.create_session(app_name="content", user_id="demo")
    msg = types.Content(role="user",
                        parts=[types.Part(text=f"Draft from this brief:\n{SAMPLE_BRIEF}")])
    async for event in runner.run_async(user_id="demo", session_id=session.id, new_message=msg):
        if event.is_final_response():
            print(event.content.parts[0].text)

if __name__ == "__main__":
    asyncio.run(main())

The agent reports the verified sources it kept (any URL that did not resolve is dropped) and a draft with inline citations queued for editing. The exact wording and the sources found are model and search generated, so they vary run to run; publishing is never automatic, and a person edits and approves before anything goes live.

The review console

An editor sees one screen: the brief, the on-brand draft with its inline citations, the verified sources and SEO checks, and a single approve action before anything reaches the CMS.

Content review IN REVIEW (3) Demand forecastingdrafted, sourced GST e-invoicingdrafting Inventory tipsresearching DRAFT Briefdemand forecasting Words1,040 of 1,000 Forecasting cuts stockoutsfor small retailers [1]. Itneeds only past sales [2] ... Internal links2 suggested SOURCES AND CHECKS Sources3 verified, resolve Originalitypasses Headings, metaall pass Readability grade 8, keyword ok Nothing publishes until you approve. Approve to CMS Edit

Build, buy, or no-code

Approach Best for Effort Cost Control
Custom agent (build in-house) You want your own voice, your sitemap for internal links, verified sources, and the drafts in-house High Build cost + API and model usage Full
Content SaaS (Jasper, Copy.ai, Surfer SEO) Fast drafts and SEO scoring out of the box, you accept their workflow and prompts Low Subscription per seat Medium
No-code (n8n or Make with a model node) A simple brief-to-draft chain, low volume, a quick pilot Low Cheap, but brittle as research and linking grow Low

Accuracy, sources, and originality

The defining safety choice is that the agent never publishes, and never cites a source it has not checked. Research comes from a real search or retrieval tool, every cited URL is verified to resolve so there are no hallucinated sources, and the drafter is told to cite only from that verified set. An originality check flags any passage that sits too close to a source so it can be rewritten before review, and you can add your own plagiarism tool as a final gate.

A person edits and approves every draft before it reaches the CMS, because a link that resolves is not the same as a claim being correct, and judgement about quality and angle stays human. Where you use AI assistance, disclose it in line with your own editorial policy. Any personal data in a brief is handled in line with the DPDP Act 2023, whose core obligations phase in through 2027, and every run is written to an immutable audit log.

The numbers

These are directional, not promises, and depend on your volume and how much research each piece needs. The point is the shape of the change: faster first drafts, sources that are checked rather than chased, internal links that are not forgotten, steadier voice.

Metric Manual Agent
Time to a first draft A writer researches, outlines, and drafts from scratch over hours A researched, on-brand draft with sources and links is ready to edit, so the writer starts from a strong first version
Source quality Citations are added by hand and sometimes skipped or go stale Every claim carries a source the agent has checked resolves, so editing is verifying rather than hunting
Internal linking Links to your own pages are forgotten in the rush to publish Relevant URLs from your sitemap are suggested in place, for the editor to keep or cut
On-brand consistency Voice drifts with whoever is writing that day Drafts follow one style guide every time, with a person editing before publish

Time saved depends on content volume and how much research each piece needs. Figures are ranges that vary by team, not guarantees, and quality still rests on the human edit.

What makes it fail

  • !Letting it auto-publish, so an unedited draft goes live with no human check.
  • !Skipping URL verification, so a citation that 404s or was invented slips through.
  • !Drafting without a style guide, so the voice drifts piece to piece.
  • !Linking to paths not in your sitemap, so internal links break.
  • !Treating the draft as final and skipping the originality and fact check.

A safe rollout

  1. Start with research and outlining only, no drafting, on a few briefs.
  2. Add on-brand drafting with inline citations, every draft edited by a person.
  3. Turn on internal linking and SEO checks, with approval still required.
  4. Scale to your full brief queue, with a turnaround and source-quality dashboard.

FAQs

General FAQs

Everything you need to know about the service and how it works. Can’t find an answer? Mail us at info@galific.com

  • Does it publish content on its own?
    No, and this is the most important design choice. The agent researches, outlines, drafts on-brand, adds internal links, and runs SEO checks, but the result goes into a review queue where a person edits the copy, verifies the facts, and approves. Only then does it move into your CMS, as a draft, and publishing stays a human action. Nothing your readers see was published without someone behind it.
  • Are the sources real, or does it make them up?
    The sources are real, and the agent is built so it cannot quietly invent them. Research comes from a live search or retrieval tool, the source URL is kept with every fact, and each URL is checked with an HTTP request so a citation that does not resolve is dropped before it reaches the draft. The drafter is told to cite only from that verified set, and the assembly step rejects a draft with any source that fails the check. Even so, a person verifies the cited facts on edit, because a URL that resolves is not the same as a claim being right.
  • How does it stay on-brand?
    It writes against your style guide every time: your tone, your reading level, words you do not use, and example pieces if you provide them. Because the same guide drives every run, the voice is consistent rather than drifting with whoever is writing that day. The human edit step is where any off-voice phrasing is caught and fixed, so the guide sets the baseline and the editor keeps it sharp.
  • How does internal linking work?
    Deterministically, from your own sitemap. The agent loads the URLs on your site with their target keywords, and where a keyword appears in the draft it links the first mention to that page. Because it matches against your sitemap, it can only ever suggest links to pages that actually exist, never an invented path, and the editor keeps or cuts each suggestion before publish.
  • Will it help the content rank, and how?
    It helps with the mechanics, not magic. The agent checks the heading structure, the meta title and description lengths, the word count against the brief, whether the target keyword is present, and a readability score, and it hands the editor a pass and fail checklist. That removes the easy SEO mistakes. Ranking still depends on genuinely useful content and your wider site, which is why a person edits for quality before anything goes live. For how we think about this, see the writing on our blog.
  • What about originality and plagiarism?
    The agent drafts from the facts in its research rather than copying passages, and the assembly step runs an originality check that flags any section too close to a source so it can be rewritten before review. It is still a draft a person edits, and you can run it through your own plagiarism tool as a final gate. The aim is original writing grounded in real sources, not paraphrased scraping.
  • Which framework should I use: ADK, the Claude SDK, or LangGraph?
    Whichever your team already runs, because the agent logic is identical in all three. The code tabs show the same research, outline, and draft flow in Google ADK, the Anthropic SDK, and LangGraph. ADK and LangGraph are model-agnostic, so you can run Gemini or Claude under either; the source verification, the internal linking, and the SEO checks are the same deterministic Python regardless.
  • What does it cost to run, and how fast is payback?
    Running cost is mostly model and search-tool usage plus hosting, which is small against the hours a writer spends researching and drafting from scratch. Because the agent delivers a researched, cited, on-brand first draft, the saving shows up as faster turnaround and fewer dropped citations, so for a team publishing regularly the build can pay for itself quickly. Output volume is up to you, and quality still rests on the human edit.
  • Can it write in Indian languages?
    The models draft in English and major Indian languages, so a brief can ask for Hindi or a bilingual piece. As always, quality varies with the language and the subject, and the human edit step before anything reaches the CMS means an awkward passage is caught and fixed rather than published. Source verification and internal linking work the same regardless of the draft language.
  • Should I build a custom agent or use a content tool like Jasper or Surfer?
    Use a content SaaS such as Jasper, Copy.ai, or Surfer SEO for fast drafts and SEO scoring with little setup, accepting their workflow. Build, or have it built, when you need your own voice, internal links from your sitemap, sources you have verified resolve, and the drafts in-house. Teams often start with a SaaS and build a custom agent once the content engine matters to the business. See the comparison table above.
  • What should this agent NOT do?
    It should not publish on its own, present an unverified or invented source, or be treated as a replacement for an editor. It is a drafting and research assistant: it can take a brief to a strong, cited first draft, but the judgement about whether a claim is true, whether the angle is right, and whether the piece is worth publishing stays with a person. Keeping it to that scope is what makes it safe to use on a public-facing blog.

Want this built and running in your stack?

Galific designs, builds, and runs content agents like this on ADK, the Claude SDK, or LangGraph, wired into your search tool, your sitemap, and your CMS, with a person editing and approving every draft. Or explore the ready-made versions in our agent suite.