ANTIGRAVITY LABJP
Articles/Agents & Manager
Agents & Manager/2026-03-14Advanced

LangChain and Antigravity Pipelines, Designed Backwards from Permissions, Failures, and Cost

Design pipelines starting from where they resume after a crash, not from how the chains connect. With permission tiers, checkpointing, and measured before-and-after numbers from my own runs.

LangChain3AI PipelinesArchitecture5Production10Chain CompositionMemory Management

Premium Article

The first time I ran an asset-classification pipeline overnight for one of my wallpaper apps, I woke up to a log that simply stopped. Roughly half of a few thousand images had been processed. Rate limiting was the trigger, but that wasn't the real problem. The real problem was that nothing had recorded how far it got.

So I ran the whole thing again, and paid twice for the same images.

Since then, I decide one thing before I decide how the chains connect: where does this pipeline resume when it dies halfway through. LangChain supplies the composable components. Antigravity supplies the orchestration layer that runs them alongside a human. What sits between them are three decisions this article is really about — the permission boundary, the record of failure, and the visibility of cost.

We'll walk through composable chain design, memory management, routing, and resilience, then finish in the territory the official docs tend to skip: permission tiers and cost measured from real runs.

Understanding AI Pipeline Architecture

The Pipeline Abstraction

A pipeline isn't simply a chain of LLM calls. It's a composition of specialized components working in concert:

Input → Preprocessing → Routing → Chain Selection → Execution → Post-processing → Output
           ↓                ↓              ↓              ↓              ↓
      Data Validation   Context        Multi-branch   Logging      Response
      Transformation    Retrieval      Fallbacks      Monitoring   Formatting
                        Caching        Error Handler  Metrics

Why this matters: Generic pipeline designs break under production load. Custom architectures distribute complexity intelligently—moving expensive operations (like retrieval) outside the critical path, caching aggressively, and gracefully degrading when components fail.

Five Pipeline Patterns

LangChain enables five distinct patterns, each suited to different problems:

  1. Sequential Pipelines — Linear chains of operations (document Q&A, summarization)
  2. Branching Pipelines — Conditional routing based on input characteristics
  3. Recursive Pipelines — Chains that call themselves for refinement or decomposition
  4. Parallel Pipelines — Multiple chains executing simultaneously, results merged
  5. Adaptive Pipelines — Runtime behavior determined by metrics, performance data, or state

Understanding when to apply each pattern dramatically improves system efficiency and reliability.

Building Sequential Pipelines with Composable Components

Core Architecture

A production sequential pipeline separates concerns into discrete, testable layers:

# config/pipeline_config.py
from dataclasses import dataclass
from typing import Optional, Dict, Any
from enum import Enum
 
class ChainStage(Enum):
    """Pipeline execution stages"""
    INPUT_VALIDATION = "input_validation"
    CONTEXT_RETRIEVAL = "context_retrieval"
    PROMPT_CONSTRUCTION = "prompt_construction"
    LLM_EXECUTION = "llm_execution"
    OUTPUT_PARSING = "output_parsing"
    RESPONSE_VALIDATION = "response_validation"
 
@dataclass
class PipelineConfig:
    """Centralized pipeline configuration"""
    # Model configuration
    model_name: str = "gpt-4"
    temperature: float = 0.7
    max_tokens: int = 2048
 
    # Chain behavior
    max_retries: int = 3
    retry_backoff: float = 2.0
    timeout_seconds: int = 30
 
    # Memory management
    memory_type: str = "buffer"  # buffer, summary, entity
    max_memory_tokens: int = 4000
 
    # Context retrieval
    enable_rag: bool = True
    retrieval_strategy: str = "similarity"  # similarity, bm25, hybrid
    top_k_docs: int = 5
 
    # Logging and monitoring
    log_level: str = "INFO"
    enable_metrics: bool = True
    enable_tracing: bool = True
 
@dataclass
class StageContext:
    """Shared context across pipeline stages"""
    input_text: str
    metadata: Dict[str, Any]
    retrieved_docs: list = None
    prompt_template_used: str = None
    llm_response: str = None
    execution_time: Dict[str, float] = None
    errors: list = None
💡
**Configuration-First Design:** Store all pipeline parameters in centralized configs. This enables experimentation—swap temperature, memory types, or retrieval strategies without touching code.

Component Composition

Each stage is an independent, composable unit:

# pipelines/components/base.py
from abc import ABC, abstractmethod
from typing import Any, Dict
import time
from datetime import datetime
 
class PipelineComponent(ABC):
    """Base class for all pipeline components"""
 
    def __init__(self, name: str, config: Dict[str, Any] = None):
        self.name = name
        self.config = config or {}
        self.execution_metrics = {
            "total_calls": 0,
            "successful_calls": 0,
            "failed_calls": 0,
            "total_time": 0.0,
            "avg_time": 0.0,
            "errors": []
        }
 
    @abstractmethod
    async def execute(self, context: StageContext) -> StageContext:
        """Execute component logic. Must be idempotent."""
        pass
 
    async def run(self, context: StageContext) -> StageContext:
        """Wrapper that handles timing, error tracking, metrics"""
        start_time = time.time()
 
        try:
            result = await self.execute(context)
            elapsed = time.time() - start_time
 
            # Update metrics
            self.execution_metrics["total_calls"] += 1
            self.execution_metrics["successful_calls"] += 1
            self.execution_metrics["total_time"] += elapsed
            self.execution_metrics["avg_time"] = (
                self.execution_metrics["total_time"] /
                self.execution_metrics["successful_calls"]
            )
 
            if context.execution_time is None:
                context.execution_time = {}
            context.execution_time[self.name] = elapsed
 
            return result
 
        except Exception as e:
            self.execution_metrics["failed_calls"] += 1
            self.execution_metrics["errors"].append({
                "timestamp": datetime.utcnow().isoformat(),
                "error": str(e),
                "component": self.name
            })
            raise
 
# pipelines/components/validators.py
import re
from typing import List, Optional
 
class InputValidator(PipelineComponent):
    """Validates input before pipeline execution"""
 
    def __init__(self, config: Dict[str, Any] = None):
        super().__init__("InputValidator", config)
        self.min_length = self.config.get("min_length", 10)
        self.max_length = self.config.get("max_length", 50000)
        self.forbidden_patterns = self.config.get("forbidden_patterns", [
            r"<script>",
            r"eval\(",
            r"__import__"
        ])
 
    async def execute(self, context: StageContext) -> StageContext:
        text = context.input_text.strip()
 
        # Length validation
        if len(text) < self.min_length:
            raise ValueError(f"Input too short: {len(text)} chars, minimum {self.min_length}")
        if len(text) > self.max_length:
            raise ValueError(f"Input too long: {len(text)} chars, maximum {self.max_length}")
 
        # Security checks
        for pattern in self.forbidden_patterns:
            if re.search(pattern, text, re.IGNORECASE):
                raise ValueError(f"Forbidden pattern detected: {pattern}")
 
        # Normalize text
        context.input_text = self._normalize_text(text)
        context.metadata["validation_timestamp"] = datetime.utcnow().isoformat()
 
        return context
 
    def _normalize_text(self, text: str) -> str:
        """Normalize whitespace and encoding"""
        # Remove extra whitespace
        text = " ".join(text.split())
        # Normalize unicode
        return text.encode('utf-8', 'replace').decode('utf-8')
 
# pipelines/components/retrieval.py
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from typing import List
 
class ContextRetriever(PipelineComponent):
    """Retrieves relevant context using RAG"""
 
    def __init__(self, vector_store: Chroma, config: Dict[str, Any] = None):
        super().__init__("ContextRetriever", config)
        self.vector_store = vector_store
        self.top_k = self.config.get("top_k", 5)
        self.min_relevance_score = self.config.get("min_relevance_score", 0.5)
 
    async def execute(self, context: StageContext) -> StageContext:
        # Retrieve relevant documents
        docs_with_scores = self.vector_store.similarity_search_with_score(
            context.input_text,
            k=self.top_k * 2  # Over-retrieve to filter by relevance
        )
 
        # Filter by minimum relevance score
        relevant_docs = [
            doc for doc, score in docs_with_scores
            if (1 - score) >= self.min_relevance_score  # Convert distance to similarity
        ][:self.top_k]
 
        if not relevant_docs:
            context.retrieved_docs = []
            context.metadata["retrieval_status"] = "no_relevant_documents"
        else:
            context.retrieved_docs = relevant_docs
            context.metadata["retrieval_status"] = "success"
            context.metadata["retrieved_doc_count"] = len(relevant_docs)
 
        return context
⚠️
**Memory and Cost Implications:** Each RAG retrieval adds latency and costs. Use relevance thresholds aggressively, and implement caching for frequently retrieved contexts. Never over-retrieve.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A permission model that splits agent authority by whether the side effect can be undone, not by how scary it feels
Measured before-and-after numbers for reprocessed items and API calls once checkpointing and caching were added
A complete resumable pipeline built on nothing but SQLite
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Agents & Manager2026-04-27
Designing a Context Compression Sub-Agent in AgentKit 2.0 — A Production Pipeline That Stops Long-Running Tasks From Drowning in Their Own History
When you run an AgentKit 2.0 agent for hours inside Antigravity, response time and accuracy degrade together as the conversation history balloons. This premium guide walks through a production-grade dynamic compression sub-agent — schema, prompt, integration hooks, and an A/B evaluation harness — so you can decide rollout based on numbers, not gut feel.
Agents & Manager2026-04-23
Production Multi-Agent Systems with Antigravity AgentKit 2.0: Patterns, Failure Modes, and What the Demos Don't Show
AgentKit 2.0 makes multi-agent systems look effortless in demos, but running them in production is a different problem. This guide covers Planning vs. Fast mode, three real orchestration patterns, and the failure modes — infinite loops, cost blowouts, prompt injection — that bite on day one.
Agents & Manager2026-04-07
Antigravity × LangGraph: Complete Masterguide for Stateful AI Agent Development
A comprehensive guide to building stateful AI agents by combining LangGraph with Antigravity. Covers graph-based workflow design, state management, checkpointing, and human-in-the-loop patterns for production-ready systems.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →