GuidesOneMux

Build a Bounded LangGraph Agent for API Incident Diagnosis

Build a LangGraph incident-diagnosis agent that calls read-only diagnostic tools, limits execution, surfaces failures, and pauses before risky actions.

AI

Summary · TL;DR

A useful incident agent should gather evidence, not take uncontrolled action. This tutorial outlines a LangGraph workflow with read-only diagnostic tools, explicit failure signals, bounded execution, and human review before remediation.

Key Takeaways

  • Keep diagnosis tools read-only and return structured evidence, including failures.
  • Set an execution limit and treat it as a safety boundary, not a diagnosis of the incident.
  • Require human approval before tools that change production state.
  • Persist graph state when using interrupts so a paused run can resume.
  • Report tool failures and incomplete evidence instead of presenting guesses as findings.
On this page (8)

The decision: diagnose automatically, remediate deliberately

An API incident agent can save time by collecting evidence from logs, health checks, and recent deploy metadata. It should not silently restart services, roll back releases, or change configuration. A safer design separates read-only diagnosis from side-effecting remediation, bounds the agent’s work, and makes failed or missing evidence visible to the operator.

This is a practical design, not a tested integration or production-ready service. The tool bodies below are illustrative: replace them with your own authenticated, permission-limited observability clients.

Prerequisites and workflow

You need Python, a LangGraph-compatible chat model configured in your application, and client functions for the diagnostic systems you already use. This example assumes those client functions return data or raise exceptions. It does not assume a particular model provider, endpoint, or tool integration.

A useful incident run follows a constrained loop:

  1. Collect a small, predefined set of signals for the affected service and time window.
  2. Label each result as successful, failed, or unavailable.
  3. Ask the model to summarize evidence and identify what remains unknown.
  4. Stop after a bounded number of graph steps; request human review before any state-changing action.

The point of the status field is operational: a failed log query must not look like a clean log search with no matching events.

Define diagnostic tools with explicit failure signals

Keep the tool contract narrow. Require a service name and bounded time window; avoid giving the model arbitrary query access where a fixed query will do. Return structured results so downstream nodes and the incident report can distinguish empty evidence from tool failure.

from typing import Literal, TypedDict
from langchain.tools import tool

class Signal(TypedDict):
    status: Literal["ok", "error", "unavailable"]
    source: str
    data: str
    error: str | None


def query_logs(service: str, minutes: int) -> Signal:
    if not 1 <= minutes <= 60:
        return {
            "status": "error", "source": "logs", "data": "",
            "error": "minutes must be between 1 and 60",
        }
    try:
        # Replace with a read-only, scoped observability client call.
        rows = logs_client.search(service=service, minutes=minutes)
        return {
            "status": "ok", "source": "logs",
            "data": str(rows), "error": None,
        }
    except TimeoutError as exc:
        return {
            "status": "unavailable", "source": "logs", "data": "",
            "error": f"timeout: {exc}",
        }

@tool
def inspect_api(service: str, minutes: int = 15) -> dict:
    """Collect scoped, read-only API incident signals."""
    return query_logs(service, minutes)

Add separate tools for health checks or deployment metadata, using the same result shape. Do not catch every exception and return an empty string: retain enough error context to diagnose a broken integration, while avoiding secrets or sensitive payloads in logs and model context.

Bound execution and expose incomplete runs

For an agent built with LangGraph’s create_agent, set a graph recursion limit on invocation. The limit bounds graph steps; it does not guarantee a useful answer or replace tool-level timeouts. Handle the limit being reached in the surrounding application and return a clear “incomplete” status rather than treating the run as a completed diagnosis.

from langchain.agents import create_agent

agent = create_agent(
    model=your_configured_chat_model,
    tools=[inspect_api],
    system_prompt=(
        "Diagnose API incidents using available evidence. "
        "Report each source as ok, error, or unavailable. "
        "Do not claim a cause without supporting evidence. "
        "Do not perform remediation."
    ),
)

try:
    result = agent.invoke(
        {"messages": [{"role": "user", "content":
            "Investigate elevated errors for payments-api over the last 15 minutes."}]},
        config={"recursion_limit": 12},
    )
except Exception as exc:
    # Map known graph-limit and provider/tool failures to your app's
    # incident-run status; do not expose credentials or raw secrets.
    result = {"status": "incomplete", "reason": type(exc).__name__}

The limit value is an example, not a universal recommendation. Choose one based on the number of tools and model turns you permit, and add timeouts and request-size limits at the tool and application layers. Record the run ID, selected service and time window, tools attempted, tool status, and termination reason. Those signals let an operator tell the difference between “no errors found” and “the log backend timed out.”

Add human review before remediation

If you later add tools that alter production state, gate those tools with LangChain’s human-in-the-loop middleware and a checkpointer. The documented middleware can pause matching tool calls for approval, editing, rejection, or a direct response. Its state is saved so execution can resume after a decision; production use requires a persistent checkpointer rather than the in-memory option shown for prototyping. See the LangChain human-in-the-loop documentation.

A practical policy is to allow read-only diagnostics without interruption and require approval for restart, rollback, or configuration-change tools. Reject a proposed action when denying it; the documentation cautions that “respond” is for acting as the tool, not denying a side-effecting call. Validate edited arguments in your own tool implementation: the documented middleware does not validate edits against the displayed schema.

Failure cases to design for

  • Empty results: Report “no matching events returned,” not “the service is healthy.”
  • Tool timeout or authorization failure: Preserve the failed source and error category; do not silently retry forever.
  • Repeated tool calls: Bound graph execution and constrain tool inputs. If you add retries, set a small explicit retry policy and expose the attempt count.
  • Interrupt cannot resume: Check that a checkpointer is configured and that the same thread identifier is used when resuming. The source documentation requires checkpointing for human review.
  • Misleading final summary: Require claims to cite collected signals in the report, and list unavailable sources and unresolved questions.

Incident-run checklist

Before deploying, verify that diagnostic tools are read-only and scoped; time windows and query volume are limited; tool errors remain observable; graph execution has a limit; and each side-effecting action pauses for a human decision. Test timeout, empty-result, rejected-action, and interrupted-resume paths. A good agent does not merely produce a plausible root cause—it shows what evidence it actually obtained and where its diagnosis stops.

Sources

FAQ

How do I limit a LangGraph agent’s execution?

Set a recursion limit in the invocation configuration and handle limit or runtime failures in the surrounding application. Also use tool-level timeouts and input bounds; the graph limit does not guarantee a complete diagnosis.

How should an incident agent report a failed diagnostic tool?

Return a structured status such as error or unavailable, with a safe error category and the source name. Keep that distinct from a successful query that returned no matching events.

How do I require approval before a LangGraph tool changes production?

Use human-in-the-loop middleware to interrupt selected tool calls and configure a checkpointer so the graph can pause and resume. Allow read-only tools through while requiring review for side-effecting tools.

Does the human-in-the-loop middleware validate edited tool arguments?

The supplied documentation says it does not check edits against the displayed response schema. Validate arguments in the tool or application before performing the action.

Related articles