Agents that think the way the job requires.
A support bot, a researcher and a code reviewer should not reason the same way, yet most frameworks give every agent one generic loop. With the Reasoning Engine you design the process each agent follows (how it plans, checks and decides), so its behaviour fits the job instead of fighting it.
Most agents get one generic loop for every task.
The default agent pattern is: the model calls tools, reads the results, calls more tools and answers. It works, and it is the only shape on offer.
One size fits all
Data analysis, research, coding and planning all get the same reasoning pattern, whatever the task needs.
No self-correction
When the agent makes a mistake it keeps going. There is no reflection, no re-planning and no quality gate.
No production safety
No retry on failure, no budget enforcement, no way to abort a critical step that went wrong.
Assemble reasoning from proven building blocks.
Pick the nodes you need. There are twenty built-in node types, several prebuilt patterns, and you can build your own. Choose a pattern to see which nodes it uses.
The agent builds its own path from a pool of nodes.
Break the task into subgoals and self-evaluate the plan.
Analyze gaps, rate confidence, recommend the next step.
Execute tools from your MCP servers.
Self-evaluate, spot mistakes, suggest corrections.
Interpret tool results and extract entities and facts.
Challenge the answer with counter-arguments.
Combine every finding into the final answer.
A quality gate with pass and fail routing.
The pool the agent chooses from. It decides the order at runtime.
Different jobs need different thinking.
A research agent should not think like a code reviewer, and neither should reason like a support bot. Design the process each one uses. Pick a use case to see its pattern and code.
Plan, search, verify, synthesize.
A research agent that plans its investigation, searches several sources, cross-checks what it finds and writes a cited report. A generic tool loop would just keep searching until it has enough text.Plan → Act → Observe → Validate → Synthesize
- PlanNode sets a research strategy up front
- ValidateNode catches contradictions between sources and loops back to search
- SynthesizeNode produces a structured report
agent = await build_agent( model="openai:gpt-5-mini", servers={"search": search_server}, agent_pattern=PromptGraph("researcher", nodes=[ PlanNode("plan", is_entry=True), PromptNode("search", inject_tools=True), ObserveNode("observe"), ValidateNode("verify", criteria=["Sources agree", "No contradictions"], on_fail="search"), # loop back if invalid SynthesizeNode("report", is_terminal=True), ]),)Let the agent choose its next step, from the steps you allow.
In autonomous mode, the default, you do not wire edges. You give the agent a pool of nodes and it picks the next one at each step, from their descriptions.
Depth that fits the question
The same pool handles different questions differently. A simple one might go Act then Synthesize. A hard one might run Plan, Act, Think, Act, Reflect, Act, Synthesize. The reasoning depth adapts to the task.
Or wire it yourself
For predictable pipelines, switch to static mode and wire every edge. You can also mix both: some paths fixed, some decided at runtime.
PromptGraph("agent", nodes=[ PlanNode("plan", is_entry=True), PromptNode("act", inject_tools=True), ThinkNode("think"), SynthesizeNode("answer", is_terminal=True),])# the agent decides: plan -> act -> think -> act -> answergraph = PromptGraph("pipeline", mode="static", nodes=[ PromptNode("extract", is_entry=True), PromptNode("analyze"), PromptNode("report", is_terminal=True),])graph.sequential("extract", "analyze", "report")# always: extract -> analyze -> reportProduction behaviour on any step, in one line.
Retries, caching and error recovery are flags on a node, not code you write and maintain. There are eighteen, and each is one line.
CRITICAL
Abort the graph if this node fails.
RETRYABLE
Retry with exponential backoff.
CACHEABLE
Cache the result by its input keys.
SKIP_ON_ERROR
Skip this node if the previous one failed.
NO_HISTORY
Give the node a clean context.
LIGHTWEIGHT
Run it on a smaller model.
from promptise.engine import NodeFlag # one flag, one production behaviorPromptNode("fetch", flags={NodeFlag.CRITICAL, NodeFlag.RETRYABLE, NodeFlag.CACHEABLE},)Encode your own expertise as reusable steps.
The twenty built-in nodes are a starting point, not a ceiling. Custom nodes can carry their own logic, instructions, tools, models, data flow and guards.
Turn a function into a node.
The simplest way to add custom logic: decorate an async function and return a NodeResult.
from promptise.engine import node, NodeResult @node("fetch_data")async def fetch_data(state): data = await api.get(...) state.context["data"] = data return NodeResult(node_name="fetch_data", output=data)The engine stays out of the way, so you pay for the model, not the framework.
Precomputed edge indexes, cached system prompts and parallel tool execution keep the engine out of the way. Your agent is only as slow as the LLM provider.
20
Built-in node types18
Typed node flags10
Prebuilt patterns0.009ms
Per-node engine overheadBuild your first custom reasoning pattern.
Define the nodes, pick a pattern, and let the engine handle the rest.