Skip to main content

doubleoh

Your agents learn from every fix. Stop failing the same way twice.

When your agent gets stuck on a real website, one of your people fixes it once in a live browser. That fix compiles into a skill. The next time any agent in your fleet hits the same wall, it follows the skill instead of asking a human.

pip install doubleoh

Zero dependencies — it is urllib and dataclasses.

The loop, in code

from doubleoh import DoubleOh

doubleoh = DoubleOh(api_key=os.environ["DOUBLEOH_API_KEY"])

# 1. Before your agent tries something it has failed at before, ask what has been learned.
skills = doubleoh.skills_for("check out on the supplier portal")
if skills and skills[0].times_worked > skills[0].times_failed:
    prompt += f"\n\nA colleague has done this before:\n{skills[0].instructions}"

# 2. Your agent runs. When it gets stuck, ask for a human.
fix = doubleoh.request_fix(url=page.url, task="Check out on the supplier portal")
if fix.deflected:
    # Nobody was paged: the fleet already knows this wall. Retry with the skill.
    prompt += fix.skill.instructions
elif fix.fix_url:
    slack.send(f"An agent needs a hand: {fix.fix_url}")
# fix.duplicate_of set? The wall is already waiting on a human — fix.blocked_runs says
# how many runs are stuck on it. Your retry loop needs no change; nobody gets re-paged.

# 3. Close the loop: say whether the skill worked. This is what makes the counts mean
#    something, and a skill followed three times that never works retires itself.
doubleoh.report_skill(skills[0].name, worked=True)

You decide what "stuck" means — a timeout, a retry count, a model that says it cannot proceed. Only you know your agent.

API

  • skills_for(task) -> list[LearnedSkill] — what has been learned; put instructions in your agent's context. times_worked / times_failed are raw counts on purpose: 5 for 5 is worth following, 1 for 1 is a guess that happened to work once.
  • request_fix(url, task) -> Intervention — ask for help. Three possible shapes: a fix_url (a human was paged), deflected=True with skill (nobody was paged — you already know the answer), or duplicate_of with blocked_runs (already waiting; triage by the count). url must be a public page; private and loopback addresses are refused.
  • report_skill(name, worked) — one boolean; wire it to whatever already tells you a run succeeded.
  • intervention(id) / interventions() — status; skill_name is the receipt.
  • retire_skill(name, reason) — stop serving a skill immediately.

Errors raise DoubleOhError carrying the server's own message and the HTTP status.

What is recorded

Fix sessions keep screenshots and the shape of each action. Typed text is never recorded — a fix session is exactly where a password gets typed, so keystrokes stay off the record entirely.

Framework adapters

Every adapter serves the same three tools; pick your framework's line.

from doubleoh import DoubleOh
client = DoubleOh(api_key=os.environ["DOUBLEOH_API_KEY"])

# LangChain / LangGraph
from doubleoh.langchain import doubleoh_tools
graph = create_react_agent(model, tools=doubleoh_tools(client))

# CrewAI
from doubleoh.crewai import doubleoh_tools
agent = Agent(role="operator", tools=doubleoh_tools(client))

# LlamaIndex
from doubleoh.llamaindex import doubleoh_tools
agent = ReActAgent.from_tools(doubleoh_tools(client), llm=llm)

# OpenAI Agents SDK, AutoGen, Pydantic AI — all accept plain functions:
from doubleoh.tools import build_tools
skills_for, request_fix, report_skill = build_tools(client)
agent = Agent(tools=[function_tool(skills_for), function_tool(request_fix), function_tool(report_skill)])

No framework is a dependency of this package: each adapter imports its framework lazily and, if it is missing, raises one sentence naming what to pip install.

Using Claude or another MCP-capable agent? Skip the SDK entirely — doubleoh-mcp serves the same three tools over MCP.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

doubleoh-0.1.0.tar.gz (10.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

doubleoh-0.1.0-py3-none-any.whl (10.0 kB view details)

Uploaded Python 3

File details

Details for the file doubleoh-0.1.0.tar.gz.

File metadata

  • Download URL: doubleoh-0.1.0.tar.gz
  • Upload date:
  • Size: 10.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.4

File hashes

Hashes for doubleoh-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e6604763de45d59c37194c2eb4381ff004bac9fbd2e260ac3ae783df6650f781
MD5 9697889db52abc9d518fc4809dbc2e1a
BLAKE2b-256 ffde79e6d84686a75770e6857600418b96c6c5af1742658ea94959591293c9fb

See more details on using hashes here.

File details

Details for the file doubleoh-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: doubleoh-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 10.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.4

File hashes

Hashes for doubleoh-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c43064e6911ae3916c48382a3d4ad4f95d08db93efea2e18a0ebda6914528996
MD5 149ad85c9a2660f3249e8a96877fb590
BLAKE2b-256 1d527d1f78e2272005dd959532f09641d67a1e0c8fb876b61ab239ce74702579

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page