Field Notes Blog cover Agentic AI

Building Agentic AI: From Chatbot to Autonomous Research System

How I built a 35-tool AI agent that autonomously queries databases, builds knowledge graphs, and generates research reports — running on Kubernetes.

AP
Ashutosh Upadhyay
Platform Engineer | AI/ML Infrastructure | AWS & Kubernetes

The Problem with "AI Chatbots"

Most enterprise AI deployments today are glorified search boxes. You ask a question, it fetches from a vector database, and returns a summary. That's RAG. It's useful — but it's not agentic.

An agentic system doesn't just retrieve — it reasons, plans, executes, and iterates. It decides which tools to use, what order to call them in, whether the results are sufficient, and when to try a different approach.

Over the past year, I've built and deployed an agentic AI system with 35 specialized tools, a knowledge graph, automated data ingestion pipelines, and multi-agent collaboration — all running on EKS. Here's what I learned.

What Makes an Agent "Agentic"?

Key insight: The difference between a chatbot and an agent is not the model — it's the tool loop. An agent has a reasoning cycle: observe → think → act → observe again. The model is just the brain; tools are the hands.

The core architecture has three layers:

Agentic AI — Three-Layer Architecture REASONING LAYER — LLM (Claude / GPT) System Prompt Tool Selection Logic Iterative Planning Self-Correction TOOL LAYER — 35 Specialized Functions Data Query S3 / DynamoDB Ingestion APIs / ETL Knowledge Graph CRUD Visualization Charts / SVG Skills Domain Rules Freshness SLA Checks DATA LAYER — Persistent State S3 Data Lake Parquet / Partitioned Knowledge Graph DynamoDB / 90d TTL Ontology Store Permanent Facts Conversation Memory Session State
Figure 1: Three-layer agentic architecture — reasoning drives tool selection, tools interact with persistent data stores

The Tool Loop: Where the Magic Happens

Here's what a single agentic "turn" looks like in practice:

# Simplified agent loop (using Strands Agents SDK)
from strands import Agent, tool

@tool
def query_data_lake(source: str, filters: dict) -> dict:
    """Query partitioned Parquet data from S3."""
    path = f"s3://data-lake/{source}/"
    df = read_parquet_with_filters(path, filters)
    return {"rows": len(df), "summary": df.describe().to_dict()}

@tool
def write_knowledge_graph(nodes: list, edges: list) -> dict:
    """Persist discovered relationships to the knowledge graph."""
    store.batch_write(nodes, edges, ttl_days=90)
    return {"written": len(nodes) + len(edges)}

agent = Agent(
    model="us.anthropic.claude-sonnet-4-6",
    tools=[query_data_lake, write_knowledge_graph, ...],  # 35 tools
    system_prompt=RESEARCH_PROMPT
)

response = agent("What are the key pathways affected by BRCA1 mutations?")
# Agent autonomously: queries → analyzes → graphs → summarizes

The agent doesn't just call one tool. In a typical research query, it:

  1. Calls get_skills to load domain-specific rules for the topic
  2. Queries the knowledge graph for existing relationships
  3. Fetches fresh data from the data lake
  4. Cross-references with ontology databases
  5. Generates a visualization of discovered pathways
  6. Writes new nodes/edges back to the knowledge graph for future queries

That's 6+ tool calls per user message — each one chosen by the model based on what it learned from the previous call.

The Knowledge Graph: Memory That Compounds

The most powerful pattern I discovered is query-derived knowledge persistence. Every time the agent answers a research question, it extracts entities and relationships and writes them back to a graph database.

Knowledge Graph — Compounding Intelligence Query 1 (Day 1) "BRCA1 interactions?" Knowledge Graph (DynamoDB) BRCA1 TP53 RAD51 HR DDR PARP Query 2 (Day 15) "DNA damage repair targets?" Result Query 2 is FASTER because BRCA1→HR relationship already exists from Query 1 Fewer tool calls
Figure 2: Knowledge compounding — later queries benefit from relationships discovered by earlier queries. Note: retrieval is exact-match and prefix-based; a paraphrased query ("DNA damage repair") does not automatically surface the BRCA1→HR edge without explicit entity normalization (e.g. HGNC synonym resolution).

The key design decisions:

The Skills System: Teaching Domain Expertise

Raw tools aren't enough. The agent also needs to know when to use each tool and how to interpret results. I built a dynamic skills system that injects domain knowledge at query time:

SKILLS = {
    "network_analysis": {
        "knowledge": "Interaction databases contain experimentally validated edges...",
        "key_concepts": ["confidence_score > 0.7 for high-confidence", ...],
        "common_mistakes": ["Don't confuse gene-level vs protein-level IDs", ...],
        "example_prompts": ["Find interactions for TP53 with score > 0.9"]
    },
    "dose_response": {
        "knowledge": "IC50 curves use 4-parameter logistic fit...",
        "key_concepts": ["Log-scale concentration", "Hill slope interpretation"],
        "common_mistakes": ["Always verify identifier type matches the data schema"],
        ...
    }
}

The agent calls get_skills(topic) as its first action, loading relevant domain rules before it starts reasoning. This prevents the most common failure mode: the model "hallucinating" domain knowledge instead of following validated rules.

System Metrics

35
Specialized Tools
6.2
Avg Tool Calls / Query
90d
Knowledge TTL

What I Got Wrong (And What Worked)

Mistake: Starting with RAG

My first version embedded documents into a vector DB. The problem? Scientific data is structured — tables, relationships, ontologies. Embeddings flatten structure. I ripped out the vector store and replaced it with direct Parquet queries + a graph database. Accuracy jumped immediately.

Mistake: One giant system prompt

A 10,000-token system prompt made the agent confused about priorities. The dynamic skills system — loading only relevant knowledge per query — was the fix. Smaller, focused context = better tool selection.

What worked: Automated data ingestion

A daily CronJob refreshes external data sources automatically. The agent always has fresh data without human intervention. This is the "boring infrastructure" that makes the AI actually useful.

Key Takeaways

Mistake: No LLM observability early on

For the first 3 months, I was flying blind — no traces, no per-tool latency, no cost breakdown. When the agent gave a bad answer, I had no way to reconstruct why. Adding Langfuse tracing was transformative: suddenly I could see exactly which tool returned bad data and which reasoning step went wrong. If you're building agentic systems, instrument with LLM-specific observability from day one.

#AgenticAI #LLM #KnowledgeGraphs #AWS #CloudArchitecture #AIEngineering #AIArchitecture #PlatformEngineering
Share

Comments & Discussion