Back to Docs
Integration

LangChain / LangGraph

Add a safety layer to your LangChain and LangGraph agents. Every external action your agent takes is verified against your knowledge base and evaluated by your policies before it reaches the outside world.

How the integration works

  1. Install the groundtruth-ai package with the LangChain extra.
  2. Call create_tools() to get configured @tool functions, then pass them to your agent.
  3. Your agents call the tools before any external action. GroundTruth returns approved, blocked, or escalated.

Installation

bash
pip install groundtruth-ai[langchain] langchain-openai langgraph

Set your API key as an environment variable:

bash
export GROUNDTRUTH_API_KEY="hg_sk_your_api_key"
export OPENAI_API_KEY="sk-your_openai_key"

Quick Start

python
from groundtruth.langchain import create_tools

# Create all GroundTruth tools, configured with your settings
tools = create_tools(
    agent_name="support-agent",
    session_id="sess_123",
    user_id="user_jane",
)

# Pass tools to your LangChain agent
# tools is a list of @tool-decorated functions compatible with any LangChain agent

create_tools() returns four @tool functions:

groundtruth_execute

Submit an action for safety verification. Verifies content, evaluates policies, and executes the action. Returns approved, blocked, or escalated.

groundtruth_verify

Fact-check AI-generated text against your knowledge base. Use for display-only text (no external action).

groundtruth_await_approval

Poll for a human decision on an escalated action. Uses exponential backoff (2s → 30s) and connection reuse to handle long review times efficiently.

groundtruth_session_history

Retrieve past execution results for the current session.

When to Use Verify vs Execute

A common question is when to use groundtruth_verify vs groundtruth_execute. They serve different purposes:

Execute

Verify + policies + action, all in one.

Use when the agent is performing an external action — sending an email, replying to a ticket, calling a webhook, issuing a refund.

Execute already verifies the content internally, so there is no need to call verify separately before execute.

Verify

Fact-check only. No policies, no action execution.

Use when the agent is showing text to a user — a chatbot response on screen, a draft preview, or comparing multiple options for accuracy.

No action is taken. You just want to know if the text is factually correct.

ScenarioToolWhy
Agent replies to a support ticketexecuteExternal action — needs policies + verification
Agent sends an emailexecuteExternal action
Chatbot shows answer in UIverifyDisplay only — no external action
Agent compares 3 draft optionsverifyPicking the most accurate draft
Agent issues a refundexecuteExternal action — needs approval flow

Avoid calling verify before execute. Since execute already verifies content internally, calling verify first is redundant and can cause confusion — the agent may verify a short draft (risk 0) but then send a longer, different version to execute (risk 1). Use execute directly for external actions.

Configuration

ParamDescription
api_keyAPI key. Falls back to GROUNDTRUTH_API_KEY env var.
agent_nameIdentifies this agent in the execution log.
session_idSession tracking ID for grouping executions.
user_idWho the agent acts on behalf of.
base_urlDefaults to https://app.groundtruth.dev.

Polling & Timeouts

When an action is escalated, the groundtruth_await_approval tool polls for a human decision. Since reviews can take minutes or hours, the tool uses exponential backoff and connection reuse to avoid exhausting HTTP connections.

ParamDefaultDescription
timeout3600Maximum seconds to wait (1 hour).
poll_interval2Initial seconds between polls. Responsive for quick approvals.
max_poll_interval30Upper bound for the backoff. Interval grows by 1.5x each poll until capped.

Backoff curve (defaults)

2s → 3s → 4.5s → 6.7s → 10s → 15s → 22s → 30s (capped)

~7 polls in the first minute, ~125 over a full hour — vs 720 at a constant 5-second interval. The underlying HTTP client reuses the same TCP connection across all polls, so no new connections are opened during the wait.

python
# Override defaults for a specific call
result = agent.invoke({
    "messages": [{
        "role": "user",
        "content": "Reply to ticket #4521 with a 2-hour approval window.",
    }]
})

# Or configure globally via the tool:
# groundtruth_await_approval(execution_id, timeout=7200, max_poll_interval=60)
Case Study

Customer Support Agent with LangGraph

A LangGraph ReAct agent that handles customer support tickets. The agent drafts replies, routes them through GroundTruth for verification, retries with safe rewrites when blocked, and waits for human approval when escalated.

Code

python
from dotenv import load_dotenv
load_dotenv()

from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from groundtruth.langchain import create_tools

# Create GroundTruth tools
gt_tools = create_tools(
    agent_name="support-agent",
    session_id="sess_demo_001",
    user_id="demo-user",
)

# Create agent
llm = ChatOpenAI(model="gpt-4o")
agent = create_react_agent(
    llm,
    tools=gt_tools,
    prompt=(
        "You are a customer support agent. When replying to customers:\n"
        "1. Draft your reply\n"
        "2. Use groundtruth_execute with action='reply_ticket' to send it.\n"
        "   Pass params as a JSON string, e.g. "
        "'{\"ticket_id\": \"4521\", \"body\": \"Your reply\"}\n"
        "   Always include the full reply text as the 'content' parameter.\n"
        "   Execute already verifies the content against the knowledge base.\n"
        "3. If ESCALATED, use groundtruth_await_approval to wait\n"
        "4. If BLOCKED, take the Suggested Rewrite from the response, then\n"
        "   call groundtruth_execute again with the rewritten text."
    ),
)

# Run
result = agent.invoke({
    "messages": [
        {
            "role": "user",
            "content": "Reply to ticket #4521: customer asks about "
                       "our cancellation policy.",
        }
    ]
})

Agent conversation flow

Agent drafts reply

Execute verifies & decides

If escalated, await approval

What happens at runtime

Real output from running the agent above against a GroundTruth instance with a product pricing knowledge base and safety policies.

1

Agent drafts a reply and calls execute

The agent drafts a reply about the cancellation policy and submits it via groundtruth_execute with action='reply_ticket'.

2

BLOCKED — 0/3 claims supported

Risk: 100%

GroundTruth verifies the reply and finds 3 claims, none supported by the knowledge base. Policies "Block high-risk responses" and "Block AI responses contradicting knowledge base facts" trigger. The action is blocked and a suggested rewrite is returned.

Decision: BLOCKED

Matched Policies: Block high-risk responses, Block AI responses contradicting knowledge base facts

3

Agent retries with rewrite — ESCALATED

Risk: 50%

The agent takes the suggested rewrite and calls groundtruth_execute again. This time 1/2 claims are supported, but 1 claim is flagged as "Needs Review." Policy "Escalate medium-risk responses" triggers.

Decision: ESCALATED

Verification: 1/2 claims supported — queued for human review

4

Agent calls await_approval — polls for decision

The agent calls groundtruth_await_approval with the execution ID. The tool polls with exponential backoff (starting at 2s, scaling to 30s) while a human reviews the action in the GroundTruth dashboard. Connections are reused across polls to avoid exhaustion.

5

APPROVED — human approves in dashboard

Approved

A reviewer approves the action. The await tool returns APPROVED. The agent confirms the reply was sent to the customer.

Approval APPROVED

The action has been approved. You may proceed.

6

Agent responds to user

"I've successfully replied to ticket #4521. Here's the response sent to the customer..."

The agent went through blocked → retried → escalated → approved in a single conversation. GroundTruth prevented an inaccurate reply from reaching the customer, and a human reviewer approved the corrected version.

Using the Core Functions Directly

If you need programmatic access without the LangChain @tool decorator, import the core functions directly:

python
from groundtruth import execute, verify

# Verify text (for display-only, no external action)
result = verify(answer="You can cancel anytime with a full refund.")
print(result)

# Execute an action (verify + policies + action in one call)
result = execute(
    action="send_email",
    params={"to": "customer@acme.com", "body": "..."},
    content="Your email text here.",
    channel="email",
    agent="my-script",
)
print(result)

Related Docs