Plan before acting¶
The model writes a plan in the first turn, without tools. From the second turn it carries out the plan.
from pathlib import Path
from alpineagents import Agent, Message, State, loop, tool, waiting_for_user
@tool
def read_file(path: str) -> str:
"""Read a text file"""
return Path(path).read_text()
@tool
def list_files() -> list[str]:
"""List the files in this folder"""
return sorted(p.name for p in Path(".").iterdir())
@loop(until=waiting_for_user, limit=30)
def plan_then_act(agent: Agent, state: State):
if state.turn == 0:
agent.think(state, tools=[])
state.add_message(Message.notice("Now carry out the plan."))
return
agent.think(state)
if state.pending_calls:
agent.use_tools(state)
agent = Agent(model="claude-sonnet-5", tools=[read_file, list_files], loop=plan_then_act)
print(agent.run("Find out what this folder is for"))
- In the first turn,
agent.think(state, tools=[])shows the model no tools, so the reply is text: the plan. - A reply without tool calls makes
waiting_for_usertrue.state.add_message(Message.notice(...))adds a message after it, so the loop continues. - From the second turn,
agent.think(state)shows every tool.
Other tool sets per turn¶
think(state, tools=[...]) accepts any subset of the Agent's tools. For example, only reading tools for the first
five turns. Inside the loop body above:
tools = [read_file, list_files] if state.turn < 5 else None
agent.think(state, tools=tools)
tools=None shows all of the Agent's tools.
To pick them by what they do rather than by name, use the tools' hints. This also picks the read-only tools of MCP servers:
readers = [t for t in agent.tool_map.values() if t.read_only]
agent.think(state, tools=readers if state.turn < 5 else None)
Related¶
- Agent: think and use_tools
- Check the work before finishing: the same notice technique