RFC: Production Reliability Improvements for ReAct Agents #1101

Closed
opened 2026-02-20 17:43:04 -05:00 by yindo · 1 comment
Owner

Originally created by @fede-kamel on GitHub (Dec 22, 2025).

RFC: Production Reliability Improvements for ReAct Agents

Summary

This RFC proposes adding optional, backwards-compatible parameters to create_react_agent that address common production failures: context overflow, silent failures, stuck loops, and unvalidated outputs.

Motivation

The Problem

Users building production agents with create_react_agent are hitting predictable failure modes:

Issue Description GitHub Reference
Empty responses Agent returns nothing instead of expected output #6235
Silent termination Agent stops on errors without retry or notification #6574
Context overflow Message history exceeds LLM context window Docs: Managing Message History
Stuck in loops Agent repeatedly calls same tool without progress #5548
Unpredictable termination Agent doesn't stop when it should #6578
Tool errors mishandled Error handling disabled by default #6486

These aren't edge cases - they're fundamental reliability gaps that make ReAct agents unsuitable for production without significant custom work.

Current Workarounds

Users must currently:

  1. Implement custom pre_model_hook for message trimming (every user reinvents this)
  2. Build custom error handling and retry logic
  3. Add loop detection manually
  4. Create validation layers before returning output
  5. Implement their own early termination logic

The Opportunity

We can solve these common problems with optional parameters that:

  • Default to current behavior (100% backwards compatible)
  • Are opt-in and incrementally adoptable
  • Follow existing patterns (pre_model_hook, post_model_hook)
  • Address documented pain points

Proposed API

New Optional Parameters

def create_react_agent(
    model: LanguageModelLike,
    tools: Sequence[BaseTool] | ToolNode,
    *,
    # === EXISTING PARAMETERS (unchanged) ===
    prompt: Prompt | None = None,
    response_format: StructuredResponseSchema | None = None,
    pre_model_hook: RunnableLike | None = None,
    post_model_hook: RunnableLike | None = None,
    state_schema: StateSchemaType | None = None,
    checkpointer: Checkpointer | None = None,
    store: BaseStore | None = None,
    interrupt_before: list[str] | None = None,
    interrupt_after: list[str] | None = None,
    debug: bool = False,
    version: Literal["v1", "v2"] = "v2",
    name: str = "react",

    # === NEW OPTIONAL PARAMETERS ===

    # Token management: Prevent context overflow
    token_budget: TokenBudgetConfig | int | None = None,

    # Reflection: Detect stuck loops and assess progress
    reflection: ReflectionConfig | bool | None = None,

    # Grounding: Validate conclusions before returning
    grounding: GroundingConfig | bool | None = None,

    # Reasoning trace: Capture chain of thought for debugging
    reasoning_trace: bool = False,

    # Error handling: Classify errors and retry appropriately
    error_handling: ErrorHandlingConfig | bool | None = None,

) -> CompiledStateGraph:

Usage Examples

Backwards Compatible (no changes required)

# Existing code continues to work exactly as before
agent = create_react_agent(model, tools)
agent = create_react_agent(model, tools, prompt="You are helpful")

Opt-in to Reliability Features

# Simple: Pass True for sensible defaults
agent = create_react_agent(
    model,
    tools,
    token_budget=8000,        # Trim messages to fit context
    reflection=True,          # Detect stuck loops
    error_handling=True,      # Retry transient errors
)

# Advanced: Configure behavior
agent = create_react_agent(
    model,
    tools,
    token_budget=TokenBudgetConfig(
        max_tokens=8000,
        reserve_for_output=1000,
        strategy="trim_oldest",      # or "trim_middle", "summarize"
        preserve_system=True,
    ),
    reflection=ReflectionConfig(
        mode="heuristic",            # or "llm", "hybrid"
        detect_loops_after=3,        # Flag after 3 same-tool calls
        assess_progress_every=2,     # Check progress every 2 iterations
    ),
    grounding=GroundingConfig(
        threshold=0.7,               # Require 70% grounding score
        max_retries=2,               # Retry if below threshold
    ),
    reasoning_trace=True,            # Capture for debugging
)

Detailed Design

1. Token Budget (token_budget)

Problem: Message history grows unbounded, causing context overflow errors.

Solution: Automatically trim messages before each LLM call.

@dataclass
class TokenBudgetConfig:
    """Configuration for automatic message trimming."""
    max_tokens: int = 8000
    reserve_for_output: int = 1000
    strategy: Literal["trim_oldest", "trim_middle", "summarize"] = "trim_oldest"
    preserve_system: bool = True
    preserve_recent: int = 2  # Always keep last N messages

Implementation:

  • Integrates with pre_model_hook internally
  • Uses tiktoken for accurate counting (with fallback)
  • Validates message pairs after trimming (no orphaned tool calls)

Graph Impact: None - implemented as pre-processing.

2. Reflection (reflection)

Problem: Agents get stuck in loops, repeatedly calling the same tool or making no progress.

Solution: Assess progress periodically and inject guidance.

@dataclass
class ReflectionConfig:
    """Configuration for progress assessment."""
    enabled: bool = True
    mode: Literal["heuristic", "llm", "hybrid"] = "heuristic"
    detect_loops_after: int = 3         # Same tool called N times
    assess_progress_every: int = 2      # Every N iterations
    on_stuck: Literal["warn", "guidance", "terminate"] = "guidance"

Modes:

  • heuristic: Fast, rule-based (loop detection, failure counting) - no extra LLM calls
  • llm: Use LLM to assess progress (more accurate, costs tokens)
  • hybrid: Heuristics first, LLM for ambiguous cases

Implementation:

  • Adds reflect node after tools node
  • Returns progress assessment and optional guidance
  • Can inject corrective messages into state

Graph Impact:

# Without reflection
agent → tools → agent → ...

# With reflection
agent → tools → reflect → agent → ...

State Extension:

class ReflectionState(TypedDict):
    progress: NotRequired[Literal["on_track", "stuck", "blocked", "completed"]]
    loop_detected: NotRequired[bool]
    guidance: NotRequired[str]

3. Grounding (grounding)

Problem: Agents return hallucinated conclusions not supported by tool outputs.

Solution: Validate that conclusions are grounded in evidence before returning.

@dataclass
class GroundingConfig:
    """Configuration for output validation."""
    enabled: bool = True
    threshold: float = 0.7          # Minimum grounding score
    max_retries: int = 2            # Retry if below threshold
    strategy: GroundingStrategy | None = None  # Custom validation

Implementation:

  • Adds ground node before END
  • Extracts claims from agent response
  • Validates claims against tool outputs
  • Returns grounding score and ungrounded claims
  • Routes back to agent if below threshold (with guidance)

Graph Impact:

# Without grounding
agent → END (when no tool calls)

# With grounding
agent → ground → END (if grounded)
               → agent (if not grounded, with retry guidance)

State Extension:

class GroundingState(TypedDict):
    grounding_score: NotRequired[float]
    ungrounded_claims: NotRequired[list[str]]
    grounding_retries: NotRequired[int]

4. Reasoning Trace (reasoning_trace)

Problem: When agents fail, it's hard to understand why.

Solution: Capture structured chain of thought for debugging.

@dataclass
class ReasoningStep:
    """A single step in the reasoning chain."""
    type: Literal["thinking", "action", "observation", "reflection"]
    content: str
    timestamp: datetime
    iteration: int
    metadata: dict | None = None

Implementation:

  • Adds reasoning_steps to state with accumulating reducer
  • Each node appends its reasoning
  • Available via state["reasoning_steps"] after execution

State Extension:

def add_reasoning_steps(existing, new):
    """Reducer for accumulating reasoning trace."""
    return (existing or []) + (new or [])

class ReasoningTraceState(TypedDict):
    reasoning_steps: NotRequired[Annotated[list[ReasoningStep], add_reasoning_steps]]

5. Error Handling (error_handling)

Problem: Agents fail silently on transient errors or retry permanent errors forever.

Solution: Classify errors and handle appropriately.

@dataclass
class ErrorHandlingConfig:
    """Configuration for error classification and retry."""
    enabled: bool = True
    retry_transient: bool = True
    max_retries: int = 3
    backoff: Literal["constant", "exponential"] = "exponential"
    initial_delay: float = 1.0
    max_delay: float = 60.0

Error Categories:

  • transient: Connection errors, 503s → Retry with backoff
  • rate_limit: 429s → Retry with longer delay
  • timeout: Request timeout → Retry with increased timeout
  • permanent: Auth errors, validation → Fail immediately

Implementation:

  • Wraps LLM calls with retry logic
  • Wraps tool calls with error classification
  • Surfaces classified errors in state for debugging

State Schema Composition

When features are enabled, the state schema is automatically extended:

# Base (always present)
class AgentState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], add_messages]
    remaining_steps: NotRequired[RemainingSteps]

# Extended based on enabled features
class ExtendedAgentState(AgentState):
    # When reflection=True
    progress: NotRequired[str]
    loop_detected: NotRequired[bool]

    # When grounding=True
    grounding_score: NotRequired[float]
    grounding_retries: NotRequired[int]

    # When reasoning_trace=True
    reasoning_steps: NotRequired[Annotated[list[ReasoningStep], add_reasoning_steps]]

Users providing custom state_schema get these fields merged automatically.

Backwards Compatibility

This proposal is 100% backwards compatible:

  1. All new parameters default to None or False

    • Existing code works without changes
    • No new behavior unless explicitly opted in
  2. State schema extension is additive

    • New fields use NotRequired
    • Custom state schemas are extended, not replaced
  3. Graph structure unchanged by default

    • New nodes only added when features enabled
    • Default graph: agent ⟷ tools → END
  4. No breaking changes to return types

    • Same output format
    • New state fields are optional

Implementation Plan

Phase 1: Core Utilities (Foundation)

  • Token counting utilities (with tiktoken fallback)
  • Message pair validation (detect orphaned tool calls)
  • Error classification utilities
  • Reducer functions for new state fields

Phase 2: Token Budget

  • Implement TokenBudgetConfig
  • Add trimming logic to pre_model_hook path
  • Preserve message pairs during trimming

Phase 3: Reflection

  • Implement ReflectionConfig
  • Add reflect node with heuristic mode
  • Add LLM mode as optional enhancement
  • Progress assessment and loop detection

Phase 4: Grounding

  • Implement GroundingConfig
  • Add ground node with validation logic
  • Implement retry routing

Phase 5: Integration

  • Wire all components together
  • Comprehensive testing
  • Documentation and examples

Alternatives Considered

1. New Function (create_production_react_agent)

  • Rejected: Proliferates API surface, fragments ecosystem
  • Chosen: Same function, optional parameters

2. Separate Package (langgraph-reliability)

  • Rejected: Adds dependency management burden
  • Chosen: Built into langgraph.prebuilt

3. Only Provide Building Blocks

  • Rejected: Every user would reinvent the same patterns
  • Chosen: Provide both building blocks AND integrated solution

4. Make Features Default-On

  • Rejected: Breaking change, unexpected behavior
  • Chosen: Opt-in only

Open Questions

  1. Should reflection and grounding share an LLM or use the agent's model?

    • Proposal: Use agent's model by default, allow override
  2. What should the default token_budget strategy be?

    • Proposal: trim_oldest (simplest, most predictable)
  3. Should we add a planning parameter in this RFC or defer?

    • Proposal: Defer to follow-up RFC (this one focuses on reliability)
  4. How should we handle conflicts with user-provided pre_model_hook?

    • Proposal: Compose them (token budget runs first, then user hook)

References

Appendix: Config Object Definitions

from dataclasses import dataclass
from typing import Literal, Protocol
from datetime import datetime

@dataclass
class TokenBudgetConfig:
    """Configuration for automatic message trimming."""
    max_tokens: int = 8000
    reserve_for_output: int = 1000
    strategy: Literal["trim_oldest", "trim_middle", "summarize"] = "trim_oldest"
    preserve_system: bool = True
    preserve_recent: int = 2


@dataclass
class ReflectionConfig:
    """Configuration for progress assessment during execution."""
    enabled: bool = True
    mode: Literal["heuristic", "llm", "hybrid"] = "heuristic"
    detect_loops_after: int = 3
    assess_progress_every: int = 2
    on_stuck: Literal["warn", "guidance", "terminate"] = "guidance"


@dataclass
class GroundingConfig:
    """Configuration for output validation."""
    enabled: bool = True
    threshold: float = 0.7
    max_retries: int = 2
    strategy: "GroundingStrategy | None" = None


@dataclass
class ErrorHandlingConfig:
    """Configuration for error classification and retry."""
    enabled: bool = True
    retry_transient: bool = True
    max_retries: int = 3
    backoff: Literal["constant", "exponential"] = "exponential"
    initial_delay: float = 1.0
    max_delay: float = 60.0


@dataclass
class ReasoningStep:
    """A single step in the reasoning chain."""
    type: Literal["thinking", "action", "observation", "reflection"]
    content: str
    timestamp: datetime
    iteration: int
    metadata: dict | None = None


class GroundingStrategy(Protocol):
    """Protocol for custom grounding validation."""

    async def validate(
        self,
        response: str,
        tool_outputs: list[dict],
    ) -> "GroundingResult":
        ...


@dataclass
class GroundingResult:
    """Result of grounding validation."""
    score: float
    grounded_claims: list[str]
    ungrounded_claims: list[str]
    should_retry: bool
Originally created by @fede-kamel on GitHub (Dec 22, 2025). # RFC: Production Reliability Improvements for ReAct Agents ## Summary This RFC proposes adding optional, backwards-compatible parameters to `create_react_agent` that address common production failures: context overflow, silent failures, stuck loops, and unvalidated outputs. ## Motivation ### The Problem Users building production agents with `create_react_agent` are hitting predictable failure modes: | Issue | Description | GitHub Reference | |-------|-------------|------------------| | Empty responses | Agent returns nothing instead of expected output | [#6235](https://github.com/langchain-ai/langgraph/issues/6235) | | Silent termination | Agent stops on errors without retry or notification | [#6574](https://github.com/langchain-ai/langgraph/issues/6574) | | Context overflow | Message history exceeds LLM context window | [Docs: Managing Message History](https://langchain-ai.github.io/langgraph/how-tos/create-react-agent-manage-message-history/) | | Stuck in loops | Agent repeatedly calls same tool without progress | [#5548](https://github.com/langchain-ai/langgraph/issues/5548) | | Unpredictable termination | Agent doesn't stop when it should | [#6578](https://github.com/langchain-ai/langgraph/issues/6578) | | Tool errors mishandled | Error handling disabled by default | [#6486](https://github.com/langchain-ai/langgraph/issues/6486) | These aren't edge cases - they're fundamental reliability gaps that make ReAct agents unsuitable for production without significant custom work. ### Current Workarounds Users must currently: 1. Implement custom `pre_model_hook` for message trimming (every user reinvents this) 2. Build custom error handling and retry logic 3. Add loop detection manually 4. Create validation layers before returning output 5. Implement their own early termination logic ### The Opportunity We can solve these common problems with **optional parameters** that: - Default to current behavior (100% backwards compatible) - Are opt-in and incrementally adoptable - Follow existing patterns (`pre_model_hook`, `post_model_hook`) - Address documented pain points ## Proposed API ### New Optional Parameters ```python def create_react_agent( model: LanguageModelLike, tools: Sequence[BaseTool] | ToolNode, *, # === EXISTING PARAMETERS (unchanged) === prompt: Prompt | None = None, response_format: StructuredResponseSchema | None = None, pre_model_hook: RunnableLike | None = None, post_model_hook: RunnableLike | None = None, state_schema: StateSchemaType | None = None, checkpointer: Checkpointer | None = None, store: BaseStore | None = None, interrupt_before: list[str] | None = None, interrupt_after: list[str] | None = None, debug: bool = False, version: Literal["v1", "v2"] = "v2", name: str = "react", # === NEW OPTIONAL PARAMETERS === # Token management: Prevent context overflow token_budget: TokenBudgetConfig | int | None = None, # Reflection: Detect stuck loops and assess progress reflection: ReflectionConfig | bool | None = None, # Grounding: Validate conclusions before returning grounding: GroundingConfig | bool | None = None, # Reasoning trace: Capture chain of thought for debugging reasoning_trace: bool = False, # Error handling: Classify errors and retry appropriately error_handling: ErrorHandlingConfig | bool | None = None, ) -> CompiledStateGraph: ``` ### Usage Examples #### Backwards Compatible (no changes required) ```python # Existing code continues to work exactly as before agent = create_react_agent(model, tools) agent = create_react_agent(model, tools, prompt="You are helpful") ``` #### Opt-in to Reliability Features ```python # Simple: Pass True for sensible defaults agent = create_react_agent( model, tools, token_budget=8000, # Trim messages to fit context reflection=True, # Detect stuck loops error_handling=True, # Retry transient errors ) # Advanced: Configure behavior agent = create_react_agent( model, tools, token_budget=TokenBudgetConfig( max_tokens=8000, reserve_for_output=1000, strategy="trim_oldest", # or "trim_middle", "summarize" preserve_system=True, ), reflection=ReflectionConfig( mode="heuristic", # or "llm", "hybrid" detect_loops_after=3, # Flag after 3 same-tool calls assess_progress_every=2, # Check progress every 2 iterations ), grounding=GroundingConfig( threshold=0.7, # Require 70% grounding score max_retries=2, # Retry if below threshold ), reasoning_trace=True, # Capture for debugging ) ``` ## Detailed Design ### 1. Token Budget (`token_budget`) **Problem**: Message history grows unbounded, causing context overflow errors. **Solution**: Automatically trim messages before each LLM call. ```python @dataclass class TokenBudgetConfig: """Configuration for automatic message trimming.""" max_tokens: int = 8000 reserve_for_output: int = 1000 strategy: Literal["trim_oldest", "trim_middle", "summarize"] = "trim_oldest" preserve_system: bool = True preserve_recent: int = 2 # Always keep last N messages ``` **Implementation**: - Integrates with `pre_model_hook` internally - Uses tiktoken for accurate counting (with fallback) - Validates message pairs after trimming (no orphaned tool calls) **Graph Impact**: None - implemented as pre-processing. ### 2. Reflection (`reflection`) **Problem**: Agents get stuck in loops, repeatedly calling the same tool or making no progress. **Solution**: Assess progress periodically and inject guidance. ```python @dataclass class ReflectionConfig: """Configuration for progress assessment.""" enabled: bool = True mode: Literal["heuristic", "llm", "hybrid"] = "heuristic" detect_loops_after: int = 3 # Same tool called N times assess_progress_every: int = 2 # Every N iterations on_stuck: Literal["warn", "guidance", "terminate"] = "guidance" ``` **Modes**: - `heuristic`: Fast, rule-based (loop detection, failure counting) - no extra LLM calls - `llm`: Use LLM to assess progress (more accurate, costs tokens) - `hybrid`: Heuristics first, LLM for ambiguous cases **Implementation**: - Adds `reflect` node after `tools` node - Returns progress assessment and optional guidance - Can inject corrective messages into state **Graph Impact**: ``` # Without reflection agent → tools → agent → ... # With reflection agent → tools → reflect → agent → ... ``` **State Extension**: ```python class ReflectionState(TypedDict): progress: NotRequired[Literal["on_track", "stuck", "blocked", "completed"]] loop_detected: NotRequired[bool] guidance: NotRequired[str] ``` ### 3. Grounding (`grounding`) **Problem**: Agents return hallucinated conclusions not supported by tool outputs. **Solution**: Validate that conclusions are grounded in evidence before returning. ```python @dataclass class GroundingConfig: """Configuration for output validation.""" enabled: bool = True threshold: float = 0.7 # Minimum grounding score max_retries: int = 2 # Retry if below threshold strategy: GroundingStrategy | None = None # Custom validation ``` **Implementation**: - Adds `ground` node before `END` - Extracts claims from agent response - Validates claims against tool outputs - Returns grounding score and ungrounded claims - Routes back to `agent` if below threshold (with guidance) **Graph Impact**: ``` # Without grounding agent → END (when no tool calls) # With grounding agent → ground → END (if grounded) → agent (if not grounded, with retry guidance) ``` **State Extension**: ```python class GroundingState(TypedDict): grounding_score: NotRequired[float] ungrounded_claims: NotRequired[list[str]] grounding_retries: NotRequired[int] ``` ### 4. Reasoning Trace (`reasoning_trace`) **Problem**: When agents fail, it's hard to understand why. **Solution**: Capture structured chain of thought for debugging. ```python @dataclass class ReasoningStep: """A single step in the reasoning chain.""" type: Literal["thinking", "action", "observation", "reflection"] content: str timestamp: datetime iteration: int metadata: dict | None = None ``` **Implementation**: - Adds `reasoning_steps` to state with accumulating reducer - Each node appends its reasoning - Available via `state["reasoning_steps"]` after execution **State Extension**: ```python def add_reasoning_steps(existing, new): """Reducer for accumulating reasoning trace.""" return (existing or []) + (new or []) class ReasoningTraceState(TypedDict): reasoning_steps: NotRequired[Annotated[list[ReasoningStep], add_reasoning_steps]] ``` ### 5. Error Handling (`error_handling`) **Problem**: Agents fail silently on transient errors or retry permanent errors forever. **Solution**: Classify errors and handle appropriately. ```python @dataclass class ErrorHandlingConfig: """Configuration for error classification and retry.""" enabled: bool = True retry_transient: bool = True max_retries: int = 3 backoff: Literal["constant", "exponential"] = "exponential" initial_delay: float = 1.0 max_delay: float = 60.0 ``` **Error Categories**: - `transient`: Connection errors, 503s → Retry with backoff - `rate_limit`: 429s → Retry with longer delay - `timeout`: Request timeout → Retry with increased timeout - `permanent`: Auth errors, validation → Fail immediately **Implementation**: - Wraps LLM calls with retry logic - Wraps tool calls with error classification - Surfaces classified errors in state for debugging ## State Schema Composition When features are enabled, the state schema is automatically extended: ```python # Base (always present) class AgentState(TypedDict): messages: Annotated[Sequence[BaseMessage], add_messages] remaining_steps: NotRequired[RemainingSteps] # Extended based on enabled features class ExtendedAgentState(AgentState): # When reflection=True progress: NotRequired[str] loop_detected: NotRequired[bool] # When grounding=True grounding_score: NotRequired[float] grounding_retries: NotRequired[int] # When reasoning_trace=True reasoning_steps: NotRequired[Annotated[list[ReasoningStep], add_reasoning_steps]] ``` Users providing custom `state_schema` get these fields merged automatically. ## Backwards Compatibility This proposal is **100% backwards compatible**: 1. **All new parameters default to `None` or `False`** - Existing code works without changes - No new behavior unless explicitly opted in 2. **State schema extension is additive** - New fields use `NotRequired` - Custom state schemas are extended, not replaced 3. **Graph structure unchanged by default** - New nodes only added when features enabled - Default graph: `agent ⟷ tools → END` 4. **No breaking changes to return types** - Same output format - New state fields are optional ## Implementation Plan ### Phase 1: Core Utilities (Foundation) - Token counting utilities (with tiktoken fallback) - Message pair validation (detect orphaned tool calls) - Error classification utilities - Reducer functions for new state fields ### Phase 2: Token Budget - Implement `TokenBudgetConfig` - Add trimming logic to `pre_model_hook` path - Preserve message pairs during trimming ### Phase 3: Reflection - Implement `ReflectionConfig` - Add `reflect` node with heuristic mode - Add LLM mode as optional enhancement - Progress assessment and loop detection ### Phase 4: Grounding - Implement `GroundingConfig` - Add `ground` node with validation logic - Implement retry routing ### Phase 5: Integration - Wire all components together - Comprehensive testing - Documentation and examples ## Alternatives Considered ### 1. New Function (`create_production_react_agent`) - **Rejected**: Proliferates API surface, fragments ecosystem - **Chosen**: Same function, optional parameters ### 2. Separate Package (`langgraph-reliability`) - **Rejected**: Adds dependency management burden - **Chosen**: Built into `langgraph.prebuilt` ### 3. Only Provide Building Blocks - **Rejected**: Every user would reinvent the same patterns - **Chosen**: Provide both building blocks AND integrated solution ### 4. Make Features Default-On - **Rejected**: Breaking change, unexpected behavior - **Chosen**: Opt-in only ## Open Questions 1. **Should `reflection` and `grounding` share an LLM or use the agent's model?** - Proposal: Use agent's model by default, allow override 2. **What should the default `token_budget` strategy be?** - Proposal: `trim_oldest` (simplest, most predictable) 3. **Should we add a `planning` parameter in this RFC or defer?** - Proposal: Defer to follow-up RFC (this one focuses on reliability) 4. **How should we handle conflicts with user-provided `pre_model_hook`?** - Proposal: Compose them (token budget runs first, then user hook) ## References - [Managing Message History](https://langchain-ai.github.io/langgraph/how-tos/create-react-agent-manage-message-history/) - [ReAct Agent from Scratch](https://langchain-ai.github.io/langgraph/how-tos/react-agent-from-scratch/) - [Reflexion Paper](https://arxiv.org/abs/2303.11366) (Shinn et al. 2023) - Related Issues: #6235, #6574, #5548, #6578, #6486 ## Appendix: Config Object Definitions ```python from dataclasses import dataclass from typing import Literal, Protocol from datetime import datetime @dataclass class TokenBudgetConfig: """Configuration for automatic message trimming.""" max_tokens: int = 8000 reserve_for_output: int = 1000 strategy: Literal["trim_oldest", "trim_middle", "summarize"] = "trim_oldest" preserve_system: bool = True preserve_recent: int = 2 @dataclass class ReflectionConfig: """Configuration for progress assessment during execution.""" enabled: bool = True mode: Literal["heuristic", "llm", "hybrid"] = "heuristic" detect_loops_after: int = 3 assess_progress_every: int = 2 on_stuck: Literal["warn", "guidance", "terminate"] = "guidance" @dataclass class GroundingConfig: """Configuration for output validation.""" enabled: bool = True threshold: float = 0.7 max_retries: int = 2 strategy: "GroundingStrategy | None" = None @dataclass class ErrorHandlingConfig: """Configuration for error classification and retry.""" enabled: bool = True retry_transient: bool = True max_retries: int = 3 backoff: Literal["constant", "exponential"] = "exponential" initial_delay: float = 1.0 max_delay: float = 60.0 @dataclass class ReasoningStep: """A single step in the reasoning chain.""" type: Literal["thinking", "action", "observation", "reflection"] content: str timestamp: datetime iteration: int metadata: dict | None = None class GroundingStrategy(Protocol): """Protocol for custom grounding validation.""" async def validate( self, response: str, tool_outputs: list[dict], ) -> "GroundingResult": ... @dataclass class GroundingResult: """Result of grounding validation.""" score: float grounded_claims: list[str] ungrounded_claims: list[str] should_retry: bool ```
yindo closed this issue 2026-02-20 17:43:04 -05:00
Author
Owner

@fede-kamel commented on GitHub (Dec 22, 2025):

Closing this RFC after further analysis. The proposed features are utilities with opinions rather than true primitives, and many overlap with existing functionality in langchain-core. Will reconsider approach.

@fede-kamel commented on GitHub (Dec 22, 2025): Closing this RFC after further analysis. The proposed features are utilities with opinions rather than true primitives, and many overlap with existing functionality in langchain-core. Will reconsider approach.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#1101