Skip to content

Commit ad16fb7

Browse files
committed
add TAO example
1 parent 02ff1e7 commit ad16fb7

5 files changed

Lines changed: 380 additions & 0 deletions

File tree

cookbook/pocketflow-tao/README.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# PocketFlow TAO (Thought-Action-Observation)
2+
3+
A powerful pattern that enables AI agents to solve complex problems through structured thinking, action execution, and result observation. This example demonstrates how to implement the TAO pattern using PocketFlow.
4+
5+
## Project Structure
6+
7+
```
8+
.
9+
├── flow.py # PocketFlow implementation of TAO pattern
10+
├── main.py # Main application entry point
11+
├── nodes.py # TAO node definitions
12+
├── requirements.txt # Project dependencies
13+
└── README.md # Project documentation
14+
```
15+
16+
## Overview
17+
18+
The TAO pattern consists of three key steps:
19+
1. **Thought**: The agent deeply analyzes the problem and forms a solution strategy
20+
2. **Action**: Concrete actions are executed based on the thinking
21+
3. **Observation**: Results are evaluated and feedback is gathered
22+
23+
This cycle continues until the problem is solved or termination conditions are met.
24+
25+
## Setup
26+
27+
1. Create a virtual environment:
28+
```bash
29+
python -m venv venv
30+
source venv/bin/activate # On Windows: venv\Scripts\activate
31+
```
32+
33+
2. Install dependencies:
34+
```bash
35+
pip install -r requirements.txt
36+
```
37+
38+
3. Set API key (if using specific LLM services):
39+
```bash
40+
export OPENAI_API_KEY="your-api-key-here"
41+
# Or set in code
42+
```
43+
44+
## How to Run
45+
46+
Execute the example:
47+
```bash
48+
python main.py
49+
```
50+
51+
## How It Works
52+
53+
The TAO pattern is implemented as a flow in PocketFlow, with each step handled by specialized nodes:
54+
55+
```mermaid
56+
graph TD
57+
Problem[Problem Input] --> ThoughtNode
58+
ThoughtNode[Thought Node] --> ActionNode[Action Node]
59+
ActionNode --> ObservationNode[Observation Node]
60+
ObservationNode --> DecisionNode{Problem Solved?}
61+
DecisionNode -->|Yes| Solution[Solution]
62+
DecisionNode -->|No| ThoughtNode
63+
```
64+
65+
Each TAO cycle generates new insights for the problem-solving process, allowing the AI to iteratively approach an optimal solution.
66+
67+
## Use Cases
68+
69+
- Complex problem solving
70+
- Multi-step reasoning tasks
71+
- Projects requiring iterative improvement
72+
- Reinforcement learning-style AI applications
73+
74+
## Example Output
75+
76+
```
77+
Query: I need to understand the latest developments in artificial intelligence
78+
79+
🤔 Thought 1: Decided to execute search
80+
🚀 Executing action: search, input: latest developments in artificial intelligence 2023
81+
✅ Action completed, result obtained
82+
👁️ Observation: The search result indicates that information was r...
83+
🎯 Final Answer: As of October 2023, some of the latest developments in artificial intelligence include advances in large language models like GPT-4, increased focus on AI alignment and safety, improvements in reinforcement learning, and the integration of AI into more industries such as healthcare, finance, and autonomous vehicles. Researchers are also exploring ethical considerations and regulatory frameworks to ensure responsible AI deployment. For the most current updates beyond this date, I recommend checking recent publications, official AI research organization releases, or news sources specializing in technology.
84+
85+
Flow ended, thank you for using!
86+
87+
Final Answer:
88+
As of October 2023, some of the latest developments in artificial intelligence include advances in large language models like GPT-4, increased focus on AI alignment and safety, improvements in reinforcement learning, and the integration of AI into more industries such as healthcare, finance, and autonomous vehicles. Researchers are also exploring ethical considerations and regulatory frameworks to ensure responsible AI deployment. For the most current updates beyond this date, I recommend checking recent publications, official AI research organization releases, or news sources specializing in technology.
89+
```
90+
91+
## Advanced Usage
92+
93+
The TAO pattern can be extended by:
94+
- Adding memory components to store past thoughts and observations.
95+
- Implementing adaptive action selection strategies.
96+
- Integrating external tools and APIs.
97+
- Adding human feedback loops.
98+
- Adding max attempt to control the iteration.
99+
100+
## Additional Resources
101+
102+
- [PocketFlow Documentation](https://the-pocket.github.io/PocketFlow/)
103+
- [Understanding AI Agents through the Thought-Action-Observation Cycle](https://huggingface.co/learn/agents-course/en/unit1/agent-steps-and-structure)

cookbook/pocketflow-tao/flow.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# flow.py
2+
3+
from pocketflow import Flow
4+
from nodes import ThinkNode, ActionNode, ObserveNode, EndNode
5+
6+
def create_tao_flow():
7+
"""
8+
Create a Thought-Action-Observation loop flow
9+
10+
How the flow works:
11+
1. ThinkNode decides the next action
12+
2. ActionNode executes the action
13+
3. ObserveNode observes the action result
14+
4. Return to ThinkNode to continue thinking, or end the flow
15+
16+
Returns:
17+
Flow: Complete TAO loop flow
18+
"""
19+
# Create node instances
20+
think = ThinkNode()
21+
action = ActionNode()
22+
observe = ObserveNode()
23+
end = EndNode()
24+
25+
# Connect nodes
26+
# If ThinkNode returns "action", go to ActionNode
27+
think - "action" >> action
28+
29+
# If ThinkNode returns "end", end the flow
30+
think - "end" >> end
31+
32+
# After ActionNode completes, go to ObserveNode
33+
action - "observe" >> observe
34+
35+
# After ObserveNode completes, return to ThinkNode
36+
observe - "think" >> think
37+
38+
# Create and return flow, starting from ThinkNode
39+
return Flow(start=think)

cookbook/pocketflow-tao/main.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# main.py
2+
3+
from flow import create_tao_flow
4+
5+
def main():
6+
7+
query = """I need to understand the latest developments in artificial intelligence"""
8+
9+
# Create shared data
10+
shared = {
11+
"query": query,
12+
"thoughts": [],
13+
"observations": [],
14+
"current_thought_number": 0
15+
}
16+
17+
# Create and run flow
18+
tao_flow = create_tao_flow()
19+
tao_flow.run(shared)
20+
21+
# Print final result
22+
if "final_answer" in shared:
23+
print("\nFinal Answer:")
24+
print(shared["final_answer"])
25+
else:
26+
print("\nFlow did not produce a final answer")
27+
28+
if __name__ == "__main__":
29+
main()

cookbook/pocketflow-tao/nodes.py

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# nodes.py
2+
3+
from pocketflow import Node
4+
import yaml
5+
from utils import call_llm
6+
7+
class ThinkNode(Node):
8+
def prep(self, shared):
9+
"""Prepare the context needed for thinking"""
10+
query = shared.get("query", "")
11+
observations = shared.get("observations", [])
12+
thoughts = shared.get("thoughts", [])
13+
current_thought_number = shared.get("current_thought_number", 0)
14+
15+
# Update thought count
16+
shared["current_thought_number"] = current_thought_number + 1
17+
18+
# Format previous observations
19+
observations_text = "\n".join([f"Observation {i+1}: {obs}" for i, obs in enumerate(observations)])
20+
if not observations_text:
21+
observations_text = "No observations yet."
22+
23+
return {
24+
"query": query,
25+
"observations_text": observations_text,
26+
"thoughts": thoughts,
27+
"current_thought_number": current_thought_number + 1
28+
}
29+
30+
def exec(self, prep_res):
31+
"""Execute the thinking process, decide the next action"""
32+
query = prep_res["query"]
33+
observations_text = prep_res["observations_text"]
34+
current_thought_number = prep_res["current_thought_number"]
35+
36+
# Build the prompt
37+
prompt = f"""
38+
You are an AI assistant solving a problem. Based on the user's query and previous observations, think about what action to take next.
39+
40+
User query: {query}
41+
42+
Previous observations:
43+
{observations_text}
44+
45+
Please think about the next action and return your thinking process and decision in YAML format:
46+
```yaml
47+
thinking: |
48+
<detailed thinking process>
49+
action: <action name, such as 'search' or 'answer'>
50+
action_input: <input parameters for the action>
51+
is_final: <set to true if this is the final answer, otherwise false>
52+
```
53+
"""
54+
55+
# Call LLM to get thinking result
56+
response = call_llm(prompt)
57+
58+
# Parse YAML response
59+
yaml_str = response.split("```yaml")[1].split("```")[0].strip()
60+
thought_data = yaml.safe_load(yaml_str)
61+
62+
# Add thought number
63+
thought_data["thought_number"] = current_thought_number
64+
65+
return thought_data
66+
67+
def post(self, shared, prep_res, exec_res):
68+
"""Save the thinking result and decide the next step in the flow"""
69+
# Save thinking result
70+
if "thoughts" not in shared:
71+
shared["thoughts"] = []
72+
shared["thoughts"].append(exec_res)
73+
74+
# Save action information
75+
shared["current_action"] = exec_res["action"]
76+
shared["current_action_input"] = exec_res["action_input"]
77+
78+
# If it's the final answer, end the flow
79+
if exec_res.get("is_final", False):
80+
shared["final_answer"] = exec_res["action_input"]
81+
print(f"🎯 Final Answer: {exec_res['action_input']}")
82+
return "end"
83+
84+
# Otherwise continue with the action
85+
print(f"🤔 Thought {exec_res['thought_number']}: Decided to execute {exec_res['action']}")
86+
return "action"
87+
88+
class ActionNode(Node):
89+
def prep(self, shared):
90+
"""Prepare to execute action"""
91+
action = shared["current_action"]
92+
action_input = shared["current_action_input"]
93+
return action, action_input
94+
95+
def exec(self, inputs):
96+
"""Execute action and return result"""
97+
action, action_input = inputs
98+
99+
print(f"🚀 Executing action: {action}, input: {action_input}")
100+
101+
# Execute different operations based on action type
102+
if action == "search":
103+
# Simulate search operation
104+
result = self.search_web(action_input)
105+
elif action == "calculate":
106+
# Simulate calculation operation
107+
result = self.calculate(action_input)
108+
elif action == "answer":
109+
# Direct return answer
110+
result = action_input
111+
else:
112+
# Unknown action type
113+
result = f"Unknown action type: {action}"
114+
115+
return result
116+
117+
def post(self, shared, prep_res, exec_res):
118+
"""Save action result"""
119+
# Save the current action result
120+
shared["current_action_result"] = exec_res
121+
print(f"✅ Action completed, result obtained")
122+
123+
# Continue to observation node
124+
return "observe"
125+
126+
# Simulated tool functions
127+
def search_web(self, query):
128+
# This should be actual search logic
129+
return f"Search results: Information about '{query}'..."
130+
131+
def calculate(self, expression):
132+
# This should be actual calculation logic
133+
try:
134+
return f"Calculation result: {eval(expression)}"
135+
except:
136+
return f"Unable to calculate expression: {expression}"
137+
138+
class ObserveNode(Node):
139+
def prep(self, shared):
140+
"""Prepare observation data"""
141+
action = shared["current_action"]
142+
action_input = shared["current_action_input"]
143+
action_result = shared["current_action_result"]
144+
return action, action_input, action_result
145+
146+
def exec(self, inputs):
147+
"""Analyze action results, generate observation"""
148+
action, action_input, action_result = inputs
149+
150+
# Build prompt
151+
prompt = f"""
152+
You are an observer, needing to analyze action results and provide objective observations.
153+
154+
Action: {action}
155+
Action input: {action_input}
156+
Action result: {action_result}
157+
158+
Please provide a concise observation of this result. Don't make decisions, just describe what you see.
159+
"""
160+
161+
# Call LLM to get observation result
162+
observation = call_llm(prompt)
163+
164+
print(f"👁️ Observation: {observation[:50]}...")
165+
return observation
166+
167+
def post(self, shared, prep_res, exec_res):
168+
"""Save observation result and decide next flow step"""
169+
# Save observation result
170+
if "observations" not in shared:
171+
shared["observations"] = []
172+
shared["observations"].append(exec_res)
173+
174+
# Continue thinking
175+
return "think"
176+
177+
178+
179+
class EndNode(Node):
180+
def prep(self, shared):
181+
"""Prepare end node"""
182+
183+
return {}
184+
def exec(self, prep_res):
185+
"""Execute end operation"""
186+
print("Flow ended, thank you for using!")
187+
return None
188+
def post(self, shared, prep_res, exec_res):
189+
"""End flow"""
190+
return None

cookbook/pocketflow-tao/utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# utils.py
2+
3+
from openai import OpenAI
4+
import os
5+
6+
def call_llm(prompt):
7+
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "Your Key Here"),base_url=os.environ.get("OPENAI_API_BASE", "Your API Base Here"))
8+
r = client.chat.completions.create(
9+
model=os.environ.get("OPENAI_MODEL", "openai/gpt-4.1-nano"),
10+
messages=[{"role": "user", "content": prompt}]
11+
)
12+
return r.choices[0].message.content
13+
14+
if __name__ == "__main__":
15+
print("## Testing call_llm")
16+
prompt = "In a few words, what is the meaning of life?"
17+
print(f"## Prompt: {prompt}")
18+
response = call_llm(prompt)
19+
print(f"## Response: {response}")

0 commit comments

Comments
 (0)