The Agent¶
ArchitectAgent is a single planner with a toolbelt. It replaces the fixed eight-node pipeline: it reads its self-knowledge, builds the graph incrementally, checks itself with the validator, authors missing tools through a sandbox, and either registers a valid workflow or declares the request blocked with a clear reason.
from neurosurfer.architect import ArchitectAgent, WorkflowInfeasible
agent = ArchitectAgent(provider)
path = await agent.build("Summarise a CSV and write the result to a file")
How a build ends¶
Every build ends as a workflow or a reason. There is no third shape to handle.
| Outcome | You get |
|---|---|
| It registered | the package path |
| It refused | WorkflowInfeasible, with .requirements — the servers and credentials that would unblock it |
| It stopped short with a design that passes every gate | the package path — registered on its behalf |
| It stopped short with a design that does not | WorkflowInfeasible, naming the gate that refused it |
| It built nothing at all | RuntimeError |
from neurosurfer.architect import ArchitectAgent, WorkflowInfeasible
try:
path = await agent.build(intent)
except WorkflowInfeasible as e:
print(e) # why, in a sentence a person can act on
print(e.requirements) # what would have to be supplied
The ways a run can stop short are model-shaped — one narrates past its nudges, another spends its turns fiddling with an output node, a third writes a paragraph asking you which fix to apply. None of that should change the kind of answer you get, so it does not. RuntimeError is reserved for the agent having done nothing at all, which is a fault in the framework rather than a statement about your request.
A design that stopped short is not a claim that the request is impossible — the message says so, and says where the build stopped instead.
The terminal contract¶
Enforced after the loop ends, so a build cannot finish ambiguously:
| Outcome | Result |
|---|---|
| A registered path | build() returns it. |
| A blocked reason | build() raises WorkflowInfeasible. |
| Neither | RuntimeError carrying the agent's last text. |
There is no fourth case where a build "finishes" having produced nothing.
build()¶
async def build(
intent: str,
*,
answers: dict[str, str] | None = None,
refines: str | None = None,
plan: Any = None,
) -> str
| Argument | Effect |
|---|---|
intent | The plain-English request. |
answers | Answers to clarifying questions asked on a previous attempt. |
refines | Start from an existing registered workflow, reading the intent as a change to it rather than a fresh build. |
plan | Skip the planning call and build this plan — how a plan reviewed in one request gets built by the next without being re-invented in between. |
Construction¶
verify¶
| Value | Meaning |
|---|---|
"required" | Default. The build must pass verification before it registers. |
"encouraged" | Prompted to verify, not gated on it. |
"off" | No verification. |
The default is required because encouraged is prompt-only, and prompt-only is exactly what a weaker model ignores: two bug-report transcripts went validate → ok → register with no test_workflow call at all.
The earlier reason for not defaulting to required was that a 9B model stalled under it. The grounding gates and prescriptive errors exist to fix the stalling, so it is now affordable. Lowering it is an explicit caller decision.
review¶
"off" · "warn" (default) · "required" — whether a design review pass runs and whether it blocks.
plan¶
Plan-first is on by default. It costs one call and grounds every external step before a node exists, which is worth far more than it costs. It is switchable because a caller driving the agent with a scripted provider is testing the agent, not the planner.
Callbacks¶
Every interactive decision is a callback, so the same agent drives a CLI, a server, or a test:
| Callback | Asked for |
|---|---|
approve_tool | Approval before an authored tool is registered. |
approve_mcp | Approval before an MCP server is installed. |
approve_plan | Approval of the plan before it is built. |
request_authorization | An API key or account authorization. |
request_secrets | Values a workflow will need supplied. |
ask_question | A clarifying question. |
node_event | Progress, per node. |
notify | Free-text progress for a human. |
A build that parks on one of these and resumes when answered is what the Architect API exposes over HTTP.
The toolbelt¶
What the agent can actually do. Grouped by what each is for:
Build the graph
| Tool | Does |
|---|---|
set_workflow | Name, description, declared inputs. Called first. |
add_node / update_node / remove_node | One node at a time. |
set_outputs | Declare the result node(s). |
view_workflow | Read back the staged graph. |
Ground it
| Tool | Does |
|---|---|
find_capability | Find a tool for a need, by declared tag. |
describe_capability | How a construct works — node kinds, fields, constraints. |
neurosurfer_docs | Retrieve from these docs. See Self-knowledge. |
acknowledge_capability | Record that a flagged gap was considered. |
author_tool | Write a tool that does not exist, sandboxed and approved. |
install_mcp_server / list_mcp_tools | Import a server that provides the gap. |
Prove it
| Tool | Does |
|---|---|
validate_workflow | Structural check. Fix every issue, repeat until VALID. |
test_workflow | Actually runs it on derived inputs and judges the output. |
register_workflow | Register, once valid and tested. |
Stop
| Tool | Does |
|---|---|
declare_blocked | The request is impossible as described — with a precise reason. |
drop_plan_step | Explicitly abandon a planned step rather than silently skipping it. |
Every planned step must be built or explicitly dropped. Silence is not an outcome.
When a build stalls¶
Small models narrate mid-build and stop without calling a tool. The agent issues up to six continuation nudges after a premature text-only stop before giving up, which recovers the loop rather than failing a build that was one turn from finishing.
Fixing a failed verification¶
The operating procedure the agent follows is deliberately conservative: fix the smallest thing first. Sharpen the failing node's instructions, correct its depends_on wiring, or swap a tool.
Only add a node or a control-flow construct if the intent truly needs a step that is missing — do not escalate a working router into a loop, or bolt on extra nodes, to patch what is really a prompt bug.
Next¶
- Grounding & Refusal — how capability checks gate a build.
- Verification — what
test_workflowactually does. - Self-knowledge — where
find_capabilityandneurosurfer_docsread from.