| name | ai-agent-builder |
| description | Principal AI Architect. LangChain/LangGraph (chains, agents, tools, memory), CrewAI (orchestration, roles), Claude Agent SDK, RAG pipelines (chunking, embeddings, vector stores), context management, prompt engineering (CoT, few-shot), guardrails, cost optimization. Use when this capability is needed. |
| metadata | {"author":"AniruddhaPKawarase"} |
Principal AI Agent Architecture & Development
Overview
A 30-year architect treats AI agents as distributed systems: they have state, dependencies, failure modes, and scaling limits. Building production-grade agents requires understanding token economics, context windows, tool integration patterns, and graceful degradation.
Agent Architecture Patterns
Pattern 1: Linear Chain (Deterministic)
Use case: Form filling, sequential data extraction, predictable workflows.
from langchain.llms import ChatOpenAI
from langchain.chains import SequentialChain
llm = ChatOpenAI(model='gpt-4', temperature=0)
entity_chain = LLMChain(
llm=llm,
prompt=PromptTemplate(
input_variables=['text'],
template="Extract all people names from: {text}"
),
output_key='entities'
)
sentiment_chain = LLMChain(
llm=llm,
prompt=PromptTemplate(
input_variables=['text'],
template="Rate sentiment of: {text} as positive/negative/neutral"
),
output_key='sentiment'
)
chain = SequentialChain(
chains=[entity_chain, sentiment_chain],
input_variables=['text'],
output_variables=['entities', 'sentiment']
)
result = chain({'text': "Alice and Bob are great friends!"})
Advantage: Predictable, easy to debug, cheap token usage.
Disadvantage: No adaptive behavior, no tool use, rigid structure.
Pattern 2: ReAct (Reasoning + Acting)
Use case: Research, API integration, multi-step problem solving.
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
tools = [
Tool(
name='web_search',
func=search_wikipedia,
description='Search Wikipedia for information'
),
Tool(
name='calculator',
func=eval_math,
description='Evaluate math expressions'
),
Tool(
name='web_scraper',
func=scrape_url,
description='Fetch and parse URL content'
)
]
agent = initialize_agent(
tools,
llm,
agent=AgentType.REACT_DOCSTRING,
verbose=True,
max_iterations=15,
early_stopping_method='generate'
)
result = agent.run("What is the population of France? Multiply by 2 and verify.")
Advantage: Adaptive, uses tools dynamically, handles emergent problems.
Disadvantage: Unpredictable token usage, hallucination risks, latency.
Pattern 3: Planning + Execution
Use case: Large projects, multi-stage orchestration, resource constraints.
from langchain.agents import initialize_agent
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
planner = LLMChain(
llm=llm,
prompt=PromptTemplate(
input_variables=['objective'],
template="""Create a 5-step plan to achieve: {objective}
Format:
1. [Step 1]
2. [Step 2]
... etc
"""
)
)
executor = initialize_agent(
tools,
llm,
agent=AgentType.OPENAI_FUNCTIONS,
verbose=True,
max_iterations=15
)
objective = "Build a customer support chatbot for a SaaS product"
plan = planner.run(objective)
result = executor.run(f"Execute this plan: {plan}")
Advantage: Cost-efficient (plan once, execute multiple times), transparent (explicit steps).
Disadvantage: Less adaptive, requires manual plan intervention.
LangGraph (Agentic Control Flow)
LangGraph is a state machine framework for agents. Unlike LangChain chains, LangGraph handles conditional branching, loops, and persistence.
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
from typing import TypedDict, Annotated
class AgentState(TypedDict):
messages: Annotated[list, 'Append-only message history']
user_id: str
session_id: str
context: dict
def query_node(state: AgentState) -> AgentState:
"""Process user query."""
response = llm.invoke(state['messages'])
state['messages'].append({'role': 'assistant', 'content': response})
return state
def tool_node(state: AgentState) -> AgentState:
"""Execute tool calls from LLM response."""
last_message = state['messages'][-1]
for tool_call in last_message['tool_calls']:
result = execute_tool(tool_call['name'], tool_call['args'])
state['messages'].append({'role': 'tool', 'content': result})
return state
def decision_node() -> :
last_message = state[][-]
last_message:
:
END
workflow = StateGraph(AgentState)
workflow.add_node(, query_node)
workflow.add_node(, tool_node)
workflow.add_edge(START, )
workflow.add_conditional_edges(, decision_node)
workflow.add_edge(, )
checkpointer = MemorySaver()
app = workflow.(checkpointer=checkpointer)
state = {
: [{: , : }],
: ,
: ,
: {}
}
result = app.invoke(state, config={: {: }})
Key Features:
- Checkpointing: Resume interrupted conversations (human-in-the-loop).
- Conditional routing: Branches based on agent state.
- Memory persistence: Thread-based conversation history.
RAG (Retrieval-Augmented Generation) Pipelines
RAG is the pattern for grounding LLM responses in external knowledge. Prevents hallucination by retrieving relevant documents before generation.
RAG Architecture
User Query
↓
1. Embedding (Text → Vector)
↓
2. Retrieve (Vector Similarity Search)
↓
3. Rerank (Score Relevance)
↓
4. Augment (Inject into Prompt)
↓
5. Generate (LLM Produces Response)
Step 1: Data Ingestion & Chunking
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import PyPDFLoader, DirectoryLoader
loader = DirectoryLoader('./docs', glob='*.pdf', loader_cls=PyPDFLoader)
documents = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=['\n\n', '\n', '.', ' ']
)
chunks = splitter.split_documents(documents)
Chunking Strategy Considerations:
- Chunk Size: 500-2000 chars typical. Larger = more context, fewer chunks. Smaller = more precise, more chunks.
- Overlap: 200-500 chars prevents information split across chunks.
- Separators: Hierarchical (paragraphs → sentences → words) preserves semantic boundaries.
Step 2: Embedding & Vector Storage
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Pinecone
import pinecone
embeddings = OpenAIEmbeddings(model='text-embedding-3-large')
pinecone.init(api_key=os.environ['PINECONE_API_KEY'])
index = pinecone.Index('documents')
vector_store = Pinecone.from_documents(
chunks,
embeddings,
index_name='documents'
)
from langchain.vectorstores.pgvector import PGVector
connection_string = 'postgresql://user:pass@localhost/vectordb'
vector_store = PGVector.from_documents(
chunks,
embeddings,
connection_string=connection_string,
table_name='documents'
)
Step 3: Retrieval & Reranking
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import CohereReranker
from langchain.retrievers.multi_query import MultiQueryRetriever
retriever = vector_store.as_retriever(
search_type='similarity',
search_kwargs={'k': 5}
)
compressor = CohereReranker(model='rerank-english-v2.0', top_n=3)
compressed_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=retriever
)
multi_query = MultiQueryRetriever.from_llm(
retriever=retriever,
llm=llm,
prompt=MULTI_QUERY_PROMPT
)
query = "How do I handle authentication in microservices?"
docs = compressed_retriever.get_relevant_documents(query)
Step 4: Augmented Generation
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
template = """Use the following context to answer the question.
Context:
{context}
Question: {question}
Answer:"""
prompt = PromptTemplate(
input_variables=['context', 'question'],
template=template
)
rag_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type='stuff',
retriever=compressed_retriever,
chain_type_kwargs={'prompt': prompt},
return_source_documents=True
)
result = rag_chain({'query': 'How do I handle authentication in microservices?'})
Context Window Management
LLM context windows are finite. Every token costs money and latency.
Token Counting & Optimization
from tiktoken import encoding_for_model
def estimate_tokens(text, model='gpt-4'):
"""Count tokens in text."""
encoding = encoding_for_model(model)
return len(encoding.encode(text))
class ContextManager:
def __init__(self, max_tokens=8000):
self.max_tokens = max_tokens
self.token_count = 0
def add_message(self, role: str, content: str) -> bool:
"""Add message if it fits in context window."""
tokens = estimate_tokens(content)
if self.token_count + tokens > self.max_tokens:
self.evict_oldest()
self.token_count += tokens
return True
def evict_oldest(self):
"""Remove oldest message to make room."""
oldest = self.messages.pop(0)
self.token_count -= estimate_tokens(oldest['content'])
manager = ContextManager(max_tokens=8000)
manager.add_message(, )
manager.add_message(, )
Prompt Engineering (Advanced Patterns)
Chain-of-Thought (CoT)
Principle: Ask LLM to explain reasoning before answering.
Prompt:
"Let's think step by step. If Sally has 5 apples and gives 2 to Bob, how many does she have left?"
vs.
Standard prompt:
"Sally has 5 apples, gives 2 to Bob. How many left?"
Result: CoT improves accuracy on complex reasoning by 15-30%.
Few-Shot Learning
Principle: Provide examples before the actual query.
from langchain.prompts import FewShotPromptTemplate, PromptTemplate
examples = [
{'input': 'The food was delicious and the staff was friendly', 'output': 'Positive'},
{'input': 'The service was slow and the food was cold', 'output': 'Negative'},
{'input': 'The ambiance was nice, but pricey', 'output': 'Mixed'}
]
example_template = PromptTemplate(
input_variables=['input', 'output'],
template="Input: {input}\nOutput: {output}"
)
few_shot_prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_template,
suffix="Input: {input}\nOutput:",
input_variables=['input']
)
Structured Output (JSON Schema)
Principle: Force LLM to output structured JSON.
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel
class Person(BaseModel):
name: str
age: int
email: str
parser = PydanticOutputParser(pydantic_object=Person)
prompt_template = PromptTemplate(
template="Extract person info from: {text}\n{format_instructions}",
input_variables=['text'],
partial_variables={'format_instructions': parser.get_format_instructions()}
)
Guardrails & Safety
Input Validation
from langchain.chains import GuardrailsChain
from langchain.prompts import PromptTemplate
class InputGuardrail:
def __init__(self):
self.blocked_keywords = ['execute', 'rm -rf', 'drop database']
def check(self, user_input: str) -> bool:
"""Return False if input contains dangerous keywords."""
for keyword in self.blocked_keywords:
if keyword.lower() in user_input.lower():
return False
return True
guardrail = InputGuardrail()
if not guardrail.check(user_input):
raise ValueError("Input contains blocked content")
Output Validation
def validate_output(llm_output: str, constraints: dict) -> bool:
"""
Validate LLM output against constraints.
constraints = {
'max_length': 200,
'contains_citation': True,
'no_profanity': True
}
"""
if len(llm_output) > constraints.get('max_length', float('inf')):
return False
if constraints.get('contains_citation') and '[' not in llm_output:
return False
return True
Cost Optimization
class CostOptimizer:
MODELS = {
'gpt-4-turbo': {'input': 0.01, 'output': 0.03},
'gpt-4': {'input': 0.03, 'output': 0.06},
'gpt-3.5-turbo': {'input': 0.0005, 'output': 0.0015},
'claude-3-opus': {'input': 0.015, 'output': 0.075},
'claude-3-sonnet': {'input': 0.003, 'output': 0.015}
}
def estimate_cost(self, model: str, input_tokens: int, output_tokens: int) -> float:
rates = self.MODELS[model]
return (input_tokens * rates['input'] + output_tokens * rates['output']) / 1000
def select_model(self, task: str, budget: float) -> str:
"""Select cheapest model that meets requirements."""
if task == 'simple_classification':
return
task == :
task == :
Output Format
# 🤖 AI Agent Architecture Review
## I. Agent Pattern Assessment
- **Current Pattern**: [Linear / ReAct / Planning+Execution]
- **Recommendation**: [Pattern recommendation with rationale]
## II. RAG Pipeline Evaluation
- Chunking: Size [X] chars, Overlap [Y] chars
- Embedding Model: [OpenAI / Cohere / Local]
- Vector DB: [Pinecone / Weaviate / pgvector]
- Retrieval: [Cosine similarity / Hybrid search]
- Reranking: [Enabled / Disabled] - Recommend: [Enabled for production]
### RAG Quality Metrics
- Retrieval Precision@5: [X%]
- Answer Relevance: [Y%]
- Token Efficiency: [Z tokens/query]
## III. Context Management
- Max Context Window: [tokens]
- Average Query Token Count: [tokens]
- Headroom: [% of context window available]
## IV. Prompt Engineering Assessment
- [ ] Chain-of-Thought enabled for complex reasoning
- [ ] Few-shot examples provided (if needed)
- [ ] Structured output validation enabled
- [ ] Temperature tuned ([X] - 0=deterministic, 1=creative)
## V. Safety & Guardrails
- Input validation: [Implemented / Missing]
- Output validation: [Implemented / Missing]
- Rate limiting: [Implemented / Missing]
- Token limit enforcement: [Implemented / Missing]
## VI. Cost Analysis
- Est. Monthly Cost: $[X] at current usage
- Recommended Model: [Model] (balance quality/cost)
- Optimization Opportunities: [List any]
## VII. Skill Chaining
- Chain to `saas-architecture` for deployment patterns.
Chain to for training data pipelines.
Source: AniruddhaPKawarase/ai-agent-skills-toolkit — distributed by TomeVault.