create_goap_agent quickstart#
Turn ordinary LangChain tools plus a goal into a compiled LangGraph graph in one call. This primer covers the Layer A on-ramp: pass @tool-decorated functions and explicit preconditions/effects, get back a CompiledStateGraph.
We use a GoalSpec rather than a natural-language goal so the notebook runs without an LLM. The natural-language path (goal="Publish an article ...", llm=ChatOpenAI(...)) is covered separately by nl_goal_interpreter.ipynb.
1. Define three LangChain tools#
Each tool takes a topic argument. At execution time the GOAP executor reads topic from world_state and passes it to the tool — tool arguments are state, not LLM-inferred parameters.
from langchain_core.tools import tool
@tool
def research_topic(topic: str) -> str:
"""Produce a short research brief for a topic."""
return f"Brief on {topic}"
@tool
def write_article(topic: str) -> str:
"""Turn a research brief into an article draft."""
return f"Draft about {topic}"
@tool
def publish_article(topic: str) -> str:
"""Publish an article draft."""
return f"Published: {topic}"
[research_topic.name, write_article.name, publish_article.name]
['research_topic', 'write_article', 'publish_article']
2. Compile the agent#
Preconditions and effects are passed explicitly, keyed by tool name. The factory never asks an LLM to guess them — the planner needs them as deterministic ground truth.
from langgoap import create_goap_agent
from langgoap.goals import GoalSpec
goal = GoalSpec(conditions={"published": True})
agent = create_goap_agent(
tools=[research_topic, write_article, publish_article],
goal=goal,
preconditions={
"write_article": {"have_brief": True},
"publish_article": {"have_draft": True},
},
effects={
"research_topic": {"have_brief": True},
"write_article": {"have_draft": True},
"publish_article": {"published": True},
},
)
type(agent).__name__
/Users/brian.sam-bodden/Code/langgoap/.venv/lib/python3.12/site-packages/langgraph/checkpoint/serde/encrypted.py:5: LangChainPendingDeprecationWarning: The default value of `allowed_objects` will change in a future version. Pass an explicit value (e.g., allowed_objects='messages' or allowed_objects='core') to suppress this warning.
from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
'CompiledStateGraph'
3. Invoke the compiled graph#
agent is a real CompiledStateGraph. Streaming, checkpointing, interrupt(), and LangSmith tracing all work out of the box.
initial_state = {"topic": "GOAP for LangGraph"}
result = agent.invoke({"world_state": initial_state, "goal": goal})
result["status"]
'goal_achieved'
executed = [r.action_name for r in result["execution_history"]]
executed
['research_topic', 'write_article', 'publish_article']
4. The planner picked the order — you didn’t#
The actions ran in the only order that satisfies every precondition: research_topic → write_article → publish_article. The same factory call would have picked a different ordering if the dependency graph allowed one. No routing edges, no conditional logic in the agent definition.
Next steps#
Use a natural-language goal: pass
goal="..."andllm=ChatOpenAI(...); seenl_goal_interpreter.ipynb.Hand-author
ActionSpecobjects when you need richer semantics (resources, effect validators, retries); seeexamples/tutorials/directory_handler.ipynb.Drop a GOAP loop into an existing
StateGraphas one node viaGoapSubgraph/add_goap_subgraph.