Skip to content

Commit ad0249a

Browse files
ccenzuracezarymicpst
authored
refactor: review agent planning capabilites (#907)
Co-authored-by: cezary <cezary@ST60726.local> Co-authored-by: Michał Pstrąg <47692610+micpst@users.noreply.github.com>
1 parent df386d0 commit ad0249a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+600
-904
lines changed

examples/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ All necessary details are provided in the comments at the top of each script.
3939
| [File Explorer Agent](/examples/chat/file_explorer_agent.py) | [ragbits-chat](/packages/ragbits-chat) | Secure file management agent with path validation and confirmation for all file operations within a restricted directory. |
4040
| [Recontextualize Last Message](/examples/chat/recontextualize_message.py) | [ragbits-chat](/packages/ragbits-chat) | Example of how to use the `StandaloneMessageCompressor` compressor to recontextualize the last message in a conversation history. |
4141
| [Agents Tool Use](/examples/agents/tool_use.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use agent with tools. |
42+
| [Agents Planning Tools](/examples/agents/planning.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use planning tools for task breakdown and sequential execution. |
4243
| [Agents OpenAI Native Tool Use](/examples/agents/openai_native_tool_use.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use agent with OpenAI native tools. |
4344
| [Agents Post Processors](/examples/agents/post_processors.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use post-processors with agent.
44-
| [Agents Downstream Streaming](/examples/agents/downstream_agents_streaming.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to stream outputs from downstream agents in real time. | |
45+
| [Agents Downstream Streaming](/examples/agents/downstream_agents_streaming.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to stream outputs from downstream agents in real time. |
4546
| [Agents CLI](/examples/agents/cli_agent.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use agent in CLI. |
4647
| [MCP Local](/examples/agents/mcp/local.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use the `Agent` class to connect with a local MCP server. |
4748
| [MCP SSE](/examples/agents/mcp/sse.py) | [ragbits-agents](/packages/ragbits-agents) | Example of how to use the `Agent` class to connect with a remote MCP server via SSE. |
@@ -50,4 +51,4 @@ All necessary details are provided in the comments at the top of each script.
5051
| [Agent Scenarios](/examples/evaluate/agent-scenarios/duet_cli.py) | [ragbits-agents](/packages/ragbits-agents) | Example of agent-to-agent communication with a simulated user and goal checker for hotel booking scenarios. |
5152
| [Agent Benchmark: HotpotQA](/examples/evaluate/agent-benchmarking/run_hotpot.py) | [ragbits-evaluate](/packages/ragbits-evaluate) | Example of how to benchmark on HotpotQA comparing `Agent` and `TodoAgent`. |
5253
| [Agent Benchmark: GAIA](/examples/evaluate/agent-benchmarking/run_gaia.py) | [ragbits-evaluate](/packages/ragbits-evaluate) | Example of how to benchmark on GAIA with tools comparing `Agent` and `TodoAgent`. |
53-
| [Agent Benchmark: HumanEval](/examples/evaluate/agent-benchmarking/run_humaneval.py) | [ragbits-evaluate](/packages/ragbits-evaluate) | Example of how to benchmark on HumanEval comparing `Agent` and `TodoAgent`. |
54+
| [Agent Benchmark: HumanEval](/examples/evaluate/agent-benchmarking/run_humaneval.py) | [ragbits-evaluate](/packages/ragbits-evaluate) | Example of how to benchmark on HumanEval comparing `Agent` and `TodoAgent`. |

examples/agents/planning.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""
2+
Ragbits Agents Example: Planning Tools
3+
4+
This example demonstrates how to use planning tools with an agent.
5+
The agent breaks down complex requests into tasks, works through them sequentially,
6+
and builds context from completed tasks to generate comprehensive answers.
7+
8+
To run the script, execute the following command:
9+
10+
```bash
11+
uv run examples/agents/planning.py
12+
```
13+
"""
14+
15+
# /// script
16+
# requires-python = ">=3.10"
17+
# dependencies = [
18+
# "ragbits-core",
19+
# "ragbits-agents",
20+
# ]
21+
# ///
22+
import asyncio
23+
24+
from pydantic import BaseModel
25+
26+
from ragbits.agents import Agent, AgentOptions, ToolCall, ToolCallResult
27+
from ragbits.agents.tools.planning import PlanningState, create_planning_tools
28+
from ragbits.core.llms import LiteLLM
29+
from ragbits.core.prompt import Prompt
30+
31+
32+
class CodePlannerInput(BaseModel):
33+
"""
34+
Input format for the CodePlannerPrompt.
35+
"""
36+
37+
query: str
38+
39+
40+
class CodePlannerPrompt(Prompt[CodePlannerInput, str]):
41+
"""
42+
Prompt for a code planner agent.
43+
"""
44+
45+
system_prompt = """
46+
You are an expert software architect with planning capabilities.
47+
48+
For complex design requests:
49+
1. Use `create_plan` to break down the architecture task into focused subtasks
50+
2. Work through each task using `get_current_task` and `complete_task`
51+
3. Keep working on tasks util there is nothing left
52+
4. Build on context from completed tasks
53+
54+
Cover: technology stack, architecture patterns, scalability, and security.
55+
"""
56+
user_prompt = "{{ query }}"
57+
58+
59+
async def main() -> None:
60+
"""
61+
Run the example.
62+
"""
63+
planning_state = PlanningState()
64+
agent = Agent(
65+
llm=LiteLLM("gpt-4.1-mini"),
66+
prompt=CodePlannerPrompt,
67+
tools=create_planning_tools(planning_state),
68+
default_options=AgentOptions(max_turns=50),
69+
)
70+
query = (
71+
"Design a scalable e-commerce platform for 100k+ users with "
72+
"real-time inventory management and payment processing."
73+
)
74+
async for result in agent.run_streaming(CodePlannerInput(query=query)):
75+
match result:
76+
case str():
77+
print(result, end="", flush=True)
78+
case ToolCall():
79+
if result.arguments:
80+
print(
81+
f">>> TOOL CALL: {result.name}({', '.join(f'{k}={v!r}' for k, v in result.arguments.items())})"
82+
)
83+
else:
84+
print(f">>> TOOL CALL: {result.name}()")
85+
case ToolCallResult():
86+
print(f"<<< TOOL RESULT ({result.name}): {result.result}\n")
87+
88+
89+
if __name__ == "__main__":
90+
asyncio.run(main())

examples/agents/todo_tools_example.py

Lines changed: 0 additions & 123 deletions
This file was deleted.

examples/chat/code_planner.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
"""
2+
Ragbits Chat Example: Code Planner
3+
4+
This example demonstrates how to use the ChatInterface with planning tools.
5+
6+
To run the script, execute the following command:
7+
8+
```bash
9+
uv run ragbits api run examples.chat.code_planner:CodePlannerChat
10+
```
11+
"""
12+
13+
from collections.abc import AsyncGenerator
14+
15+
from pydantic import BaseModel
16+
17+
from ragbits.agents import Agent, AgentOptions, ToolCall, ToolCallResult
18+
from ragbits.agents.tools.planning import PlanningState, create_planning_tools
19+
from ragbits.chat.interface import ChatInterface
20+
from ragbits.chat.interface.types import ChatContext, ChatResponseUnion, LiveUpdateType
21+
from ragbits.chat.interface.ui_customization import HeaderCustomization, UICustomization
22+
from ragbits.core.llms import LiteLLM
23+
from ragbits.core.prompt import ChatFormat, Prompt
24+
25+
26+
class CodePlannerInput(BaseModel):
27+
"""
28+
Input format for the CodePlannerPrompt.
29+
"""
30+
31+
query: str
32+
33+
34+
class CodePlannerPrompt(Prompt[CodePlannerInput, str]):
35+
"""
36+
Prompt for a code planner agent.
37+
"""
38+
39+
system_prompt = """
40+
You are an expert software architect with planning capabilities.
41+
42+
For complex design requests:
43+
1. Use `create_plan` to break down the architecture task into focused subtasks
44+
2. Work through each task using `get_current_task` and `complete_task`
45+
3. Keep working on tasks util there is nothing left
46+
4. Build on context from completed tasks
47+
48+
Cover: technology stack, architecture patterns, scalability, and security.
49+
"""
50+
user_prompt = "{{ query }}"
51+
52+
53+
class CodePlannerChat(ChatInterface):
54+
"""
55+
Code planner agent with planning capabilities.
56+
"""
57+
58+
ui_customization = UICustomization(
59+
header=HeaderCustomization(title="Code Planner", subtitle="Spec driven development", logo="📋"),
60+
welcome_message="Code planner",
61+
)
62+
conversation_history = True
63+
show_usage = True
64+
65+
def __init__(self) -> None:
66+
self.planning_state = PlanningState()
67+
self.agent = Agent(
68+
llm=LiteLLM("gpt-4.1-mini"),
69+
prompt=CodePlannerPrompt,
70+
tools=create_planning_tools(self.planning_state),
71+
default_options=AgentOptions(max_turns=50),
72+
keep_history=True,
73+
)
74+
75+
async def chat(
76+
self,
77+
message: str,
78+
history: ChatFormat,
79+
context: ChatContext,
80+
) -> AsyncGenerator[ChatResponseUnion, None]:
81+
"""
82+
Chat implementation with planning capabilities.
83+
84+
Args:
85+
message: The current user message.
86+
history: Previous messages in the conversation.
87+
context: Additional context for the chat.
88+
89+
Yields:
90+
ChatResponse objects containing:
91+
- Text chunks for the response
92+
- Live updates showing planning progress
93+
- Plan items for planning tasks
94+
"""
95+
async for response in self.agent.run_streaming(CodePlannerInput(query=message)):
96+
match response:
97+
case str():
98+
yield self.create_text_response(response)
99+
100+
case ToolCall():
101+
if response.name == "create_plan":
102+
yield self.create_live_update(
103+
update_id=response.id,
104+
type=LiveUpdateType.START,
105+
label="Creating plan...",
106+
)
107+
108+
case ToolCallResult():
109+
if self.planning_state.plan:
110+
match response.name:
111+
case "create_plan":
112+
yield self.create_live_update(
113+
update_id=response.id,
114+
type=LiveUpdateType.FINISH,
115+
label="Plan created",
116+
description=f"{len(self.planning_state.plan.tasks)} tasks",
117+
)
118+
for task in self.planning_state.plan.tasks:
119+
yield self.create_plan_item_response(task)
120+
121+
case "get_current_task":
122+
if self.planning_state.plan.current_task:
123+
yield self.create_live_update(
124+
update_id=self.planning_state.plan.current_task.id,
125+
type=LiveUpdateType.START,
126+
label=self.planning_state.plan.current_task.description,
127+
)
128+
yield self.create_plan_item_response(self.planning_state.plan.current_task)
129+
130+
case "complete_task":
131+
if self.planning_state.plan.last_completed_task:
132+
yield self.create_live_update(
133+
update_id=self.planning_state.plan.last_completed_task.id,
134+
type=LiveUpdateType.FINISH,
135+
label=self.planning_state.plan.last_completed_task.description,
136+
description=self.planning_state.plan.last_completed_task.result,
137+
)
138+
yield self.create_plan_item_response(self.planning_state.plan.last_completed_task)

0 commit comments

Comments
 (0)