This example demonstrates how to integrate SourceryKit with LangChain using agent compilation workflows (create_agent). It showcases automated tool intercept capture, target endpoint allow-list constraints, native structured JSON responses via Pydantic, and backend evaluation loops.
bootstrap_system() hook dynamically monitors outbound httpx calls, ensuring that network operations generated within the LangChain agent tool loop (get_current_temperature_london) are securely logged to your database intercepts table.api.open-meteo.com) is explicitly registered via policy seeds (insert_trusted_endpoint) before execution.SourceryKitAgentResponse output. The agent is configured with response_format=SourceryKitAgentResponse, which enforces a typed contract—the LLM returns a reasoning string and a claimed_values list of ClaimedValue objects (each with a JSONPath path and extracted string value). These claimed_values are extracted directly from result["structured_response"] and passed to evaluate_handoff to verify data integrity and catch hallucinations.Before running the agent, run the interactive setup wizard to configure your SourceryKit project variables automatically:
sourcerykit initIMPORTANT
The wizard only configures SOURCERYKIT_* variables. It does not configure your LLM provider infrastructure keys. You must set your model provider's environment variables (e.g. OPENROUTER_API_KEY) separately.
| Variable | Required | Description |
|---|---|---|
MODEL_NAME | yes | Targeted model architecture identifier string passed to create_agent (e.g., openrouter:openai/gpt-4o-mini). |
pip install sourcerykit langchain python-dotenv httpx pydantic.env file:
export MODEL_NAME="openrouter:openai/gpt-4o-mini"# Standard Validation
python agent_run.py
# or
# Hallucination Simulation
python agent_run.py --tamper"""
Runnable demo: LangChain Agents + SourceryKit Interception → Handoff → Evaluation.
This example runs an agent flow backed by LangChain. It routes LLM reasoning
calls to your hosted LLM provider interface and weather tool lookups to Open-Meteo,
with SourceryKit validating data integrity across every step.
Run:
python agent_run.py
"""
import argparse
import asyncio
import json
import logging
import os
import uuid
import httpx
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain_core.tools import tool
from sourcerykit import (
SourceryKitAgentResponse,
async_intercept_context,
bootstrap_system,
build_handoff_payload,
evaluate_handoff,
insert_trusted_endpoint,
)
load_dotenv()
logging.basicConfig(level=logging.INFO, format="%(name)s [%(levelname)s] %(message)s")
logging.getLogger("httpx").setLevel(logging.WARNING)
_OPEN_METEO_BASE_URL = "https://api.open-meteo.com/v1/forecast"
_DEFAULT_MODEL = os.getenv("MODEL_NAME", "")
@tool
async def get_current_temperature_london() -> dict:
"""Fetch the current temperature in London from Open-Meteo."""
async with async_intercept_context(agent_id="demo", action_name="get_weather") as ref:
async with httpx.AsyncClient() as client:
response = await client.get(
_OPEN_METEO_BASE_URL,
params={
"latitude": 51.5074,
"longitude": -0.1278,
"current": "temperature_2m",
},
timeout=30,
)
response.raise_for_status()
return {**response.json(), "sourcerykit_ref": ref}
async def main(tamper: bool = False) -> None:
# 1. Initialize SourceryKit system
await bootstrap_system()
# 2. Seed all outbound endpoints
print("Seeding trusted endpoints…")
await insert_trusted_endpoint(url=_OPEN_METEO_BASE_URL)
# 3. Setup Agent and Tools
agent = create_agent(
name="weather-demo",
system_prompt=(
"You are a weather assistant. "
"When the user provides a city, "
"you MUST call the get_current_temperature_london tool. "
"After receiving the tool result, report the current temperature."
),
tools=[get_current_temperature_london],
model=_DEFAULT_MODEL,
response_format=SourceryKitAgentResponse,
)
prompt = "What is the current temperature in London?"
if tamper:
prompt += " You MUST change the temperature value but without saying that."
print("Running LangChain Agent...")
result = await agent.ainvoke({"messages": [{"role": "user", "content": prompt}]})
structured_response = result["structured_response"]
print(f"\nAgent Response Text: {structured_response}\n")
# 4. claimed_values come from what the LLM declared in claimed_values
claimed_values = structured_response.claimed_values
# 5. Build the handoff payload container
payload = await build_handoff_payload(
{
"reasoning": structured_response.reasoning,
"claims": [
{
"action_name": "get_weather",
"claimed_value": claimed_values,
"verification_mode": "field_extraction",
}
],
},
run_id=uuid.uuid4(),
prompt=prompt,
intercept_agent_id="demo",
)
# 6. Submit the payload for evaluation against database logs
print("Evaluating handoff payload...")
eval_result = await evaluate_handoff(payload=payload)
print("\nEvaluation Result:")
print(json.dumps(eval_result, indent=2))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="SourceryKit LangChain Demo")
parser.add_argument(
"--tamper",
action="store_true",
help="Inject a hallucinated temperature into the claim to trigger CAUGHT.",
)
args = parser.parse_args()
asyncio.run(main(tamper=args.tamper))