Learning loop
Give the agents you already run access to what prior runs learned, with a resolve before the work and an observe after it.
In one line
Two calls wrap the work your agent already does: resolve before it starts, observe when it finishes. Everything else is Hyperstruck's job.
This is the path for agents you already run, on your own framework, in your own process. Hyperstruck sits underneath and never takes over the loop. If you want Hyperstruck to own planning and execution instead, that is the Engine, and it is a different starting point.
The pairing
| Call | When | What it does |
|---|---|---|
resolve | Before the agent acts, with the goal | Returns the learnings prior runs earned that fit this goal, plus the identifiers of what was offered. |
observe | After the run finishes, with the same run_id | Submits the finished episode so the outcome credits what helped and new learnings are extracted from what happened. |
The run_id is yours. It is a correlation key you invent, not a hosted resource identifier, and reusing the same value across both calls is what lets Hyperstruck attribute the outcome back to the learnings it offered.
Extraction is asynchronous
observe returns 202 Accepted. Learnings are extracted in the background, so a lesson from the run you just submitted is not retrievable the instant the call returns. By the time a similar task comes around, it is in place.
LangGraph
The shortest path. Register one middleware and your existing agent keeps its model, its tools, and its graph.
from hyperstruck.langgraph import HyperstruckLearningMiddleware
async with HyperstruckLearningMiddleware(
api_key="<YOUR_HYPERSTRUCK_API_KEY>",
agent_id="support-agent",
) as learning:
agent = create_agent(model, tools=tools, middleware=[learning])
result = await agent.ainvoke({"messages": [("user", "Triage ticket SUP-2048")]})Install it with pip install hyperstruck[langgraph]. The middleware resolves at run start and observes at run end, so neither call appears in your code. Other frameworks are covered in framework integrations.
REST
Any language, no SDK. The two calls below are the whole loop.
curl -X POST "https://api.hyperstruck.com/resolve" \
-H "Authorization: Bearer <YOUR_HYPERSTRUCK_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "support-agent",
"run_id": "support-agent:ticket-2048:attempt-1",
"goal": "Triage ticket SUP-2048 and recommend the next action."
}'
curl -X POST "https://api.hyperstruck.com/observe" \
-H "Authorization: Bearer <YOUR_HYPERSTRUCK_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "support-agent",
"episode": {
"run_id": "support-agent:ticket-2048:attempt-1",
"goal": "Triage ticket SUP-2048 and recommend the next action.",
"steps": [{ "id": "step-1", "name": "ticket_lookup" }],
"outcome": { "is_success": true }
}
}'resolve returns injected_text, ready to place in front of your model, and offered_learning_ids for the entries it drew on. Put injected_text where your agent will actually read it, then run the work as normal.
Resolve fields
Only agent_id, run_id, and goal are required.
| Field | Default | Notes |
|---|---|---|
agent_id | required | Your own identifier for the agent, not a hosted UUID. |
run_id | required | Your correlation key. Reuse it with observe. |
goal | required | What the agent is about to attempt. |
available_tools | [] | Tool names and descriptions, so lessons about a tool reach the runs that can use it. |
max_learnings | 8 | Between 1 and 50. |
retrieval | fast | full trades latency for richer context. |
source_framework | "" | Attributes the per-host funnel, for example mcp:cursor. |
Observe fields
The episode wants a real execution trace. Steps default to completed, so a failed step needs "status": "failed" set explicitly, and an honest failure is worth more to the corpus than a tidy success.
| Field | Default | Notes |
|---|---|---|
agent_id | required | Matches the value used at resolve. |
episode.run_id | required | The same key you resolved with. Also the idempotency key. |
episode.goal | required | The goal that was attempted. |
episode.steps | required | Each step needs id and name. args, result, and error are optional. |
episode.outcome.is_success | required | Whether the episode achieved its goal. |
Send the trace, not documents
observe is for what your agent actually did. To contribute reference material rather than execution evidence, use distill instead, which is built for corpus content.
Other surfaces
The same loop reaches Hyperstruck through several front doors, and learnings captured one way are available to every other.
- MCP exposes
resolveandcomplete_runas tools to Claude Code, Cursor, and any other MCP host. - Claude Code and Cursor skills drive it from a slash command inside your IDE.
- Framework integrations cover LangGraph, the OpenAI Agents SDK, and CrewAI.
- Learnings API is the manual control surface for storing, searching, and reinforcing specific lessons by hand.