Skip to main content

g6-agent-langgraph

Use when a multi-step workflow should run as a LangGraph graph that saves its state after every step under G6's state folder, so a run that is stopped, killed or fails resumes where it was without repeating finished steps, with every tool call decided by G6's gate, llm steps only on a model the person passes in, and the result kept as a draft to check.

Source facts

Repository
basicisabc1994/g6solver
Last source activity
October 30, 2026 at 10:47
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
g6-agent-langgraph
description
Use when a multi-step workflow should run as a LangGraph graph that saves its state after every step under G6's state folder, so a run that is stopped, killed or fails resumes where it was without repeating finished steps, with every tool call decided by G6's gate, llm steps only on a model the person passes in, and the result kept as a draft to check.
# G6 LangGraph adapter Runs a LangGraph graph through the agents family's runtime. The graph is a typed spec (state, nodes, edges, entry, tools) or a `StateGraph` of your own. G6 compiles it with its own checkpoint store, one SQLite file under G6's state folder, and runs it with each super-step on disk before the next starts. G6's stop, the step limit and the time limit are checked between super-steps; a run that ends there, or whose process dies, resumes from its last checkpoint and finishes the rest. A finished graph is a qualified draft. ## When to use it - A workflow of several steps that must survive a stop, a crash or a failing step without starting again. - A workflow with branches or loops, bounded by a step limit that G6 enforces. - Steps that call tools (G6 components' ops, or Python functions of yours) that must each be decided before they run. ## When not to use it - For one step, or one agent run: call the component, or use `agent_codex` or `agent_claude`, directly. - To get a checked answer: the graph's state is what its nodes wrote. Check it with `g6.harness`. - For a step with side effects that cannot be repeated safely, unless you make it idempotent: a step that was running when a run crashed runs again on resume. - Over MCP with an llm step: no model is passed over MCP, so such a graph is refused. Run it from Python with a model. ## Ops - `run_graph`: start a thread with the graph (`spec`, `thread_id`, `input`, `max_steps`, `deadline_s`, `dry_run`), checkpointed after every super-step; outward tool calls are dry runs unless `dry_run` is false. - `resume`: continue the thread from its last checkpoint (`spec`, `thread_id`, `max_steps`, `deadline_s`, `dry_run`), with the graph that started it. - `list_checkpoints`: the thread's checkpoints, newest first (`thread_id`, `limit`). ## Reading the result `run_graph` and `resume` answer with the runtime's outcome in the library envelope. `qualified-draft` with `G6_E_AGENT_SELF_REPORTED` (or `G6_E_AGENT_DRY_RUN` when every outward call was a dry run): `output` is the final state as JSON, and `detail` holds `final_state`, `path` (the nodes that ran in this run), `thread_id`, `graph` (the spec's digest) and `checkpoint_id`; a resume of a finished thread says `already_finished`. A dry run of a graph with an llm node asks no model and runs nothing: `G6_E_AGENT_DRY_RUN` with `detail.plan` (its nodes, its llm nodes, its tools, its limits and the model a run would ask), and the thread is not started. A tool node writes what its call came to into its key: `verdict` (`run`, `dry_run` or `refused`), its `code`, and `result` when it ran. `blocked-escalated` codes: `G6_E_AGENT_LANGGRAPH_NOT_INSTALLED`, `G6_E_AGENT_LANGGRAPH_NO_MODEL`, `G6_E_LLM_DISABLED`, `G6_E_AGENT_LANGGRAPH_THREAD_EXISTS`, `G6_E_AGENT_LANGGRAPH_NO_THREAD`, `G6_E_AGENT_LANGGRAPH_GRAPH_CHANGED`, `G6_E_AGENT_LANGGRAPH_INPUT_UNDECLARED`, `G6_E_AGENT_LANGGRAPH_NODE_FAILED` and `G6_E_AGENT_LANGGRAPH_MODEL_FAILED` (with `resume_from`: the nodes a resume runs next), `G6_E_AGENT_LANGGRAPH_TOO_LARGE` (a value, a checkpoint or the thread would pass its cap; nothing past it was written), `G6_E_AGENT_STEP_LIMIT`, `G6_E_AGENT_DEADLINE` and `G6_E_STOPPED` (resume the thread to carry on). Sizes are capped: a filled value, a value a node writes, the starting state and the input hold at most 1 MiB (in characters); a node's own `value` and `arguments` at most 100,000; one checkpoint, or one batch of a node's writes, at most 1 MiB; a thread's checkpoints and writes together at most 32 MiB. ## Alone and composed Alone, over MCP: `g6_invoke("agent_langgraph", "run_graph", params_json)`, then `resume` with the same spec and thread. From Python: `run_agent(runtime, runtime.task())` with `LangGraphRuntime(spec, thread_id=..., llm=..., tools={...})`, or `LangGraphRuntime.from_builder(builder, graph_id=..., thread_id=...)` for a graph of your own, whose nodes call tools through `call_tool(name, arguments)`. Composed: - `agent_runtime`: the runtime this adapter runs on; its gate decides every tool call. - `g6.harness`: `run(standard, executor=as_executor(runtime, runtime.task()))` judges the final state against checks frozen first. - Any component whose ops are not sensitive, as a tool: declare `component.op` in the spec's tools. What the op does is the component's word, not the effect you declare: a dry run calls only the ops it declares read-only. ## Failure modes it catches - `unresumable_workflow` (a long workflow that cannot resume after a failure): every super-step's state, and the writes of the steps that finished within a super-step that failed, are in G6's checkpoint store before the next super-step starts, so a run stopped by G6, killed with its process or ended by a failing node resumes from its last checkpoint, in this process or another, and a node that had finished does not run again. A thread belongs to the graph that started it: it is never started twice, and never resumed with another graph. ## Blind spots - A node that was running when the run crashed or failed runs again on resume; a stop between super-steps never cuts one. Make steps with side effects safe to repeat. - G6's stop and time limit are checked between super-steps, not inside a node: a long node (a slow model, a slow tool) runs to its end first. Nodes run in G6's process. - An llm node reads what is in the state, and text a tool put there can carry instructions the model follows (`prompt_injection`); the gate still decides every tool call after it. - A node of your own graph is your code: a call it makes without `call_tool` is not gated, and a model it asks is asked on a dry run too. - The checkpoint file holds the graph's state as it is: keep secrets out of the state. Anyone who can write G6's state folder can change what a resume sees; G6 reads it back without pickle and rebuilds only the types LangGraph lists as safe and those you allow (`allowed_types`). - The graph's state is what its nodes wrote; nothing here checks that it is right. ## What it needs LangGraph, behind the extra: `pip install 'g6[agents-langgraph] @ git+https://github.com/basicisabc1994/g6solver'` (LangGraph and its checkpoint package, MIT, with their own permissive dependencies). Without it every op says so. From the library it uses `action_gating` and `safety_engine`, through the family's runtime. A model only for llm nodes, passed in from Python as a `g6.core.llm` caller. No outside program. ## Examples ```g6-example op=run_graph {"spec": {"state": {"notes": []}, "entry": "a", "nodes": [{"name": "a", "op": "append", "key": "notes", "value": "first"}, {"name": "b", "op": "append", "key": "notes", "value": "second"}], "edges": [{"source": "a", "target": "b"}, {"source": "b", "target": "END"}]}, "thread_id": "example-notes"} ``` A two-step graph: `qualified-draft` with `detail.final_state.notes` `["first", "second"]`, or, when the thread was started before, `G6_E_AGENT_LANGGRAPH_THREAD_EXISTS`. ```g6-example op=resume {"spec": {"state": {"notes": []}, "entry": "a", "nodes": [{"name": "a", "op": "append", "key": "notes", "value": "first"}, {"name": "b", "op": "append", "key": "notes", "value": "second"}], "edges": [{"source": "a", "target": "b"}, {"source": "b", "target": "END"}]}, "thread_id": "example-notes"} ``` Carries the thread on from its last checkpoint; a finished thread answers `already_finished`. ```g6-example op=list_checkpoints {"thread_id": "example-notes"} ``` The thread's checkpoints, newest first, each with its step. ```python from g6.agents.agent_langgraph import GraphSpec, LangGraphRuntime spec = GraphSpec.model_validate({ "state": {"n": 0}, "entry": "count", "nodes": [{"name": "count", "op": "increment", "key": "n"}, {"name": "enough", "op": "branch", "key": "n", "value": 3, "then": "END", "else": "count"}], "edges": [{"source": "count", "target": "enough"}]}) assert spec.digest().startswith("spec:") and not spec.has_llm task = LangGraphRuntime(spec, thread_id="count-to-three", max_steps=10).task() assert task.limits.max_steps == 10 and task.tools == () # the loop is bounded by G6's step limit ``` ## The four open problems - **Learning signals**: each run records the path it took, its steps and every tool decision; a resume records where it carried on from. Not yet: nothing learns from which nodes fail and are resumed. - **Evaluators the model cannot influence**: the checkpoint store, the step limit, the gate and the thread rules are fixed code; an llm node's text never changes the label. Judging the state is left to checks frozen before the run. - **Transfer by failure mode**: `unresumable_workflow` is handled by the store and the runtime, so any graph, a spec or your own, gets it. Not yet: measured only on the graphs of its tests. - **Enough held-out cases**: Not yet: its tests stop, kill and fail graphs of a few nodes; long graphs and parallel branches are not measured.
View on GitHub