Add a judgement node to a LangGraph, CrewAI or OpenAI Agents SDK agent using the OpenAI-compatible endpoint.
An agent framework runs a loop of model calls and tool calls. Quorum fits in as one tool: a judgement node the agent calls at a decision point. The tool is a plain HTTP call to POST /v1/chat/completions, so the same helper serves every framework below.
All three examples share one helper. Put it in quorum_tool.py.
import os
import requests
URL = "https://www.quorum.dog/v1/chat/completions"
HEADERS = {"Authorization": f"Bearer {os.environ['QUORUM_API_KEY']}"}
def judge(question: str) -> str:
"""Ask a panel of models. Returns the answer and whether the seats agreed."""
r = requests.post(
URL,
headers=HEADERS,
timeout=300,
json={
"model": "QRUM:STAN",
"messages": [{"role": "user", "content": question}],
},
)
r.raise_for_status()
body = r.json()
q = body["quorum"]
if q.get("replayed"):
# A replay holds the answer but not the split fields.
head = "REPLAYED ANSWER, split state not recorded"
else:
split = q.get("converged") is False or q.get("remaining_friction") is not None
head = "SEATS SPLIT" if split else "SEATS AGREE"
quote = (q.get("contested_passage") or {}).get("quote")
if quote:
head += f' (contested: "{quote}")'
return f"{head} (receipt {q['request_id']})\n{body['choices'][0]['message']['content']}"Four settings matter here.
timeout is 300 seconds. A deliberation runs several models, so a short client timeout cuts it off.stream unset. stream: true returns 400 stream_not_supported.quorum.replayed: true) without converged or remaining_friction, so the helper reads them with .get and reports the replay as it is.raise_for_status() surfaces an error response. Branch on error.code in the body; the codes are in Errors and rate limits.Give the agent a rule for when to call it. Convene a panel for judgement calls: a consequential assumption, options close enough that the numbers no longer separate them, a diagnosis you cannot check, a decision you will have to defend, or a "what have I missed" check. Use a single model for lookups, syntax, formatting, conversions, arithmetic and anything you need reproduced word for word.
Wrap the helper as a tool and bind it to the model that drives your graph.
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from quorum_tool import judge
@tool
def get_panel_opinion(question: str) -> str:
"""Use before a consequential decision: a migration, a refund, a merge.
Include the full context in the question."""
return judge(question)
agent = create_react_agent(ChatOpenAI(model="gpt-4o"), [get_panel_opinion])
agent.invoke({"messages": [("user", "Plan the rollback for tonight's schema change.")]})To make the judgement a fixed node instead of a tool the model chooses, add it to a StateGraph and route on its output. The escalation router has that version.
from crewai import Agent, Task, Crew
from crewai.tools import tool
from quorum_tool import judge
@tool("Panel opinion")
def panel_opinion(question: str) -> str:
"""Ask several independent models before a consequential decision.
Include the full context in the question."""
return judge(question)
reviewer = Agent(
role="Release reviewer",
goal="Decide whether a change is safe to ship",
backstory="Checks risky changes against an independent panel.",
tools=[panel_opinion],
)
task = Task(
description="Review this change and decide: {change}",
expected_output="Ship or hold, with the reason. Quote the panel if the seats split.",
agent=reviewer,
)
Crew(agents=[reviewer], tasks=[task]).kickoff(inputs={"change": "Drop the legacy_orders table."})from agents import Agent, Runner, function_tool
from quorum_tool import judge
@function_tool
def panel_opinion(question: str) -> str:
"""Ask several independent models before a consequential decision.
Include the full context in the question."""
return judge(question)
agent = Agent(
name="Release agent",
instructions="Before any irreversible step, call panel_opinion. "
"If the seats split, stop and report the disagreement.",
tools=[panel_opinion],
)
result = Runner.run_sync(agent, "Rotate the production database credentials.")
print(result.final_output)Some frameworks let you point the whole agent at an OpenAI-compatible endpoint. Set the base URL to https://www.quorum.dog/v1 and the model to a ticker such as QRUM:STAN. The endpoint returns a text answer for each request, and every turn bills a deliberation. Keep tool calling on the model that drives your agent and call Quorum as a tool at the decision points: that costs less, and the free estimate call tells you which questions earn it. See Quorum as a pipeline step.
A framework with an MCP client can use the tools directly instead of the helper: https://www.quorum.dog/mcp with an API key in the Authorization header. The loop is estimate, deliberate, get_receipt, act.