Compare
Architecture and routing
PicoFlow makes the active conversation stage the runtime cursor; LangGraph makes state, nodes, and routes explicit graph concerns.
The supplied architecture brief is right about the central distinction, after updating its
older API examples: PicoFlow authors a multi-turn conversation around Flow and Step; LangGraph
authors a state graph around nodes and routing.
The two authoring shapes
| Architecture Component | PicoFlow Implementation | Direct LangGraph Hotel Example |
|---|---|---|
| Top-Level Coordinator | Flow owning registered steps and model policies |
StateGraph(state) declaring channels and nodes |
| Active Milestone Cursor | flow.currentStep managed by runtime |
Application-managed phase and route state fields |
| Stage Execution Unit | Cohesive Step class combining prompt, tools, and state |
Split pairs: agent node (LLM prompt) + tool node (execution) |
| Transition Outcome | Explicit return: go(), stay(), or direct() |
Conditional edge functions inspecting message arrays |
HotelFlow registers ExploreStep, PresentStep, and CompareStep. The active step name is
persisted in flow.currentStep; a tool handler returns go(TargetStep), stay(...), or
direct(...). The framework applies the transition, state, feedback, or direct message.
The direct hotel graph defines three agent nodes, three tool nodes, and conditional edges after
each. Its state carries both business data and runtime-routing fields such as phase, route,
and inputConsumed. This is transparent and flexible, but those fields and every edge are
application responsibilities.
The same state machine, different decomposition
| Responsibility | PicoFlow module | Direct LangGraph location |
|---|---|---|
| Default and per-stage models | hotel-flow.ts |
constructor model factory and createOpenAiModel() |
| Criteria prompt and search | ExploreStep |
exploreAgent plus exploreTools |
| Results, booking, and branch selection | PresentStep |
presentAgent plus presentTools |
| Comparison and return | CompareStep |
compareAgent plus compareTools |
| Terminal behavior | shared TerminateSessionStep |
repeated terminate_session branches plus terminateUpdate() |
| Cursor | framework flow.currentStep |
application phase and route |
| Agent/tool loop | PicoFlow LlmRunner |
graph nodes and conditional edges |
| Session lifecycle | FlowEngine and FlowSession |
HotelLanggraph.run() and HotelSessionStore |
PicoFlow decomposes by business stage and inherits infrastructure. The direct implementation
decomposes some concerns into helper files, but its 1,374-line HotelLanggraph class remains
the integration point for topology, prompts, tools, models, validation, domain operations,
response creation, and session lifecycle.
That monolith is not a LangGraph requirement. Agent nodes, tool nodes, validators, and state updates could be split by stage. The architectural question is what shared convention replaces the current class when the application adds its second and third graphs.
Where routing lives
PicoFlow has no declarative edge table. Routing is ordinary TypeScript colocated with the tool or response handler that knows the business rule:
if (!hotel) return stay("No hotel found; adjust the criteria.");
return go(PresentStep).withState({ hotelFound: hotel });
In LangGraph, a conditional edge selects the next named node. A current alternative is to return
a Command, which combines a state update with goto. Neither representation is inherently
more expressive: the trade-off is whether topology should be a first-class graph artifact or a
local part of a stage’s domain code.
The direct graph currently combines both approaches indirectly. Tool nodes return a route
field, then the shared routeAfterTools() conditional edge reads it. A Command({ update, goto }) could remove that transient routing channel, but would couple each tool node directly
to destination node names. The existing form centralizes the edge map at the cost of persisting
route in the custom session document.
One HTTP turn versus one graph execution
PicoFlow restores one session, starts at currentStep, and may recursively call models and
tools until it has a user-facing response. The internal call stack—not a public graph
scheduler—owns those within-turn transitions.
The direct implementation manually restores its document, passes the hydrated state into
compiledGraph.invoke(), and always enters at START. A conditional entry edge derives the
first agent from phase. The graph may traverse several agent/tool nodes in one HTTP turn and
stops at END when it has a response.
This has an important consequence: END does not mean that the hotel conversation is
complete. It usually means only that this HTTP turn is complete. Durable conversation
completion is the separate completed flag plus phase: "terminal". PicoFlow has a similar
distinction between returning a turn response and entering its terminal step, but the framework
encapsulates it.
Static topology and dynamic behavior
The direct graph’s six nodes and conditional edges can be inspected or visualized as topology. That is useful when reviewing loops and possible destinations. However, important behavior still lives outside the edge diagram:
routeFromPhase()decides the entry node;hasToolCall()decides whether an agent ends the turn or enters a tool node;- each tool node mutates both
phaseandroute; inputConsumedcontrols whether the original user input appears in the next model call; andcompletedoverrides normal phase routing.
PicoFlow’s application topology is less directly visualizable because transitions are returned from handlers. In exchange, the route, forwarded message, target state, and exact response can be read together at the business decision point.
Extension cost: add a “review booking” stage
In PicoFlow the normal change is a new ReviewStep, registration in defineSteps(), and
go(ReviewStep) outcomes from relevant handlers. The shared engine already knows how to run,
persist, log, and restore it.
In the direct implementation the change normally touches the phase and route unions, state defaults, tool declarations, stage tool map, model record and factory, graph nodes, conditional edge destinations, one or more tool dispatchers, prompts, message arrays, serialization, and tests. LangGraph permits a cleaner modular design, but the application must establish it.
Practical choice
Choose PicoFlow’s shape when a flow benefits from named responsibilities—collect, validate, present, compare, and finish—and from composing nested, sequential, parallel, and tool-driven work through ordinary steps. Choose direct LangGraph when explicit graph topology, custom reducers, or scheduler-level control are the primary design objects.
The strongest use case for PicoFlow is a portfolio of similarly shaped multi-turn assistants whose teams benefit from one lifecycle, while its stack-based step composition also serves one-turn and background orchestration. Direct LangGraph is a good fit when the team wants its graph semantics—parallel branches, checkpointed tasks, interrupts, subgraphs, or unusual state reduction—to remain the primary application model.
Do not copy the reference HTML’s constructor or model-configuration snippets verbatim. The
current PicoFlow API uses defineSteps()/initialStep() to choose the cursor and
configModel() for a flow default; useModel() is a Step API.