update cursor rules
This commit is contained in:
parent
0252865d20
commit
ab2072a4dc
|
|
@ -19,6 +19,7 @@ A **Node** is the smallest building block. Each Node has 3 steps `prep->exec->po
|
||||||
- Examples: *(mostly) LLM calls, remote APIs, tool use*.
|
- Examples: *(mostly) LLM calls, remote APIs, tool use*.
|
||||||
- ⚠️ This shall be only for compute and **NOT** access `shared`.
|
- ⚠️ This shall be only for compute and **NOT** access `shared`.
|
||||||
- ⚠️ If retries enabled, ensure idempotent implementation.
|
- ⚠️ If retries enabled, ensure idempotent implementation.
|
||||||
|
- ⚠️ Defer exception handling to the Node's built-in retry mechanism.
|
||||||
- Return `exec_res`, which is passed to `post()`.
|
- Return `exec_res`, which is passed to `post()`.
|
||||||
|
|
||||||
3. `post(shared, prep_res, exec_res)`
|
3. `post(shared, prep_res, exec_res)`
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,11 @@ Agentic Coding should be a collaboration between Human System Design and Agent I
|
||||||
| 1. Requirements | ★★★ High | ★☆☆ Low | Humans understand the requirements and context. |
|
| 1. Requirements | ★★★ High | ★☆☆ Low | Humans understand the requirements and context. |
|
||||||
| 2. Flow | ★★☆ Medium | ★★☆ Medium | Humans specify the high-level design, and the AI fills in the details. |
|
| 2. Flow | ★★☆ Medium | ★★☆ Medium | Humans specify the high-level design, and the AI fills in the details. |
|
||||||
| 3. Utilities | ★★☆ Medium | ★★☆ Medium | Humans provide available external APIs and integrations, and the AI helps with implementation. |
|
| 3. Utilities | ★★☆ Medium | ★★☆ Medium | Humans provide available external APIs and integrations, and the AI helps with implementation. |
|
||||||
| 4. Node | ★☆☆ Low | ★★★ High | The AI helps design the node types and data handling based on the flow. |
|
| 4. Data | ★☆☆ Low | ★★★ High | AI designs the data schema, and humans verify. |
|
||||||
| 5. Implementation | ★☆☆ Low | ★★★ High | The AI implements the flow based on the design. |
|
| 5. Node | ★☆☆ Low | ★★★ High | The AI helps design the node based on the flow. |
|
||||||
| 6. Optimization | ★★☆ Medium | ★★☆ Medium | Humans evaluate the results, and the AI helps optimize. |
|
| 6. Implementation | ★☆☆ Low | ★★★ High | The AI implements the flow based on the design. |
|
||||||
| 7. Reliability | ★☆☆ Low | ★★★ High | The AI writes test cases and addresses corner cases. |
|
| 7. Optimization | ★★☆ Medium | ★★☆ Medium | Humans evaluate the results, and the AI helps optimize. |
|
||||||
|
| 8. Reliability | ★☆☆ Low | ★★★ High | The AI writes test cases and addresses corner cases. |
|
||||||
|
|
||||||
1. **Requirements**: Clarify the requirements for your project, and evaluate whether an AI system is a good fit.
|
1. **Requirements**: Clarify the requirements for your project, and evaluate whether an AI system is a good fit.
|
||||||
- Understand AI systems' strengths and limitations:
|
- Understand AI systems' strengths and limitations:
|
||||||
|
|
@ -97,9 +98,11 @@ Agentic Coding should be a collaboration between Human System Design and Agent I
|
||||||
```
|
```
|
||||||
- > **Sometimes, design Utilities before Flow:** For example, for an LLM project to automate a legacy system, the bottleneck will likely be the available interface to that system. Start by designing the hardest utilities for interfacing, and then build the flow around them.
|
- > **Sometimes, design Utilities before Flow:** For example, for an LLM project to automate a legacy system, the bottleneck will likely be the available interface to that system. Start by designing the hardest utilities for interfacing, and then build the flow around them.
|
||||||
{: .best-practice }
|
{: .best-practice }
|
||||||
|
- > **Avoid Exception Handling in Utilities**: If a utility function is called from a Node's `exec()` method, avoid using `try...except` blocks within the utility. Let the Node's built-in retry mechanism handle failures.
|
||||||
|
{: .warning }
|
||||||
|
|
||||||
4. **Node Design**: Plan how each node will read and write data, and use utility functions.
|
4. **Data Design**: Design the shared store that nodes will use to communicate.
|
||||||
- One core design principle for PocketFlow is to use a [shared store], so start with a shared store design:
|
- One core design principle for PocketFlow is to use a well-designed [shared store]—a data contract that all nodes agree upon to retrieve and store data.
|
||||||
- For simple systems, use an in-memory dictionary.
|
- For simple systems, use an in-memory dictionary.
|
||||||
- For more complex systems or when persistence is required, use a database.
|
- For more complex systems or when persistence is required, use a database.
|
||||||
- **Don't Repeat Yourself**: Use in-memory references or foreign keys.
|
- **Don't Repeat Yourself**: Use in-memory references or foreign keys.
|
||||||
|
|
@ -116,16 +119,18 @@ Agentic Coding should be a collaboration between Human System Design and Agent I
|
||||||
"results": {} # Empty dict to store outputs
|
"results": {} # Empty dict to store outputs
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
5. **Node Design**: Plan how each node will read and write data, and use utility functions.
|
||||||
- For each [Node], describe its type, how it reads and writes data, and which utility function it uses. Keep it specific but high-level without codes. For example:
|
- For each [Node], describe its type, how it reads and writes data, and which utility function it uses. Keep it specific but high-level without codes. For example:
|
||||||
- `type`: Regular (or Batch, or Async)
|
- `type`: Regular (or Batch, or Async)
|
||||||
- `prep`: Read "text" from the shared store
|
- `prep`: Read "text" from the shared store
|
||||||
- `exec`: Call the embedding utility function
|
- `exec`: Call the embedding utility function. **Avoid exception handling here**; let the Node's retry mechanism manage failures.
|
||||||
- `post`: Write "embedding" to the shared store
|
- `post`: Write "embedding" to the shared store
|
||||||
|
|
||||||
5. **Implementation**: Implement the initial nodes and flows based on the design.
|
6. **Implementation**: Implement the initial nodes and flows based on the design.
|
||||||
- 🎉 If you've reached this step, humans have finished the design. Now *Agentic Coding* begins!
|
- 🎉 If you've reached this step, humans have finished the design. Now *Agentic Coding* begins!
|
||||||
- **"Keep it simple, stupid!"** Avoid complex features and full-scale type checking.
|
- **"Keep it simple, stupid!"** Avoid complex features and full-scale type checking.
|
||||||
- **FAIL FAST**! Avoid `try` logic so you can quickly identify any weak points in the system.
|
- **FAIL FAST**! Leverage the built-in [Node] retry and fallback mechanisms to handle failures gracefully. This helps you quickly identify weak points in the system.
|
||||||
- Add logging throughout the code to facilitate debugging.
|
- Add logging throughout the code to facilitate debugging.
|
||||||
|
|
||||||
7. **Optimization**:
|
7. **Optimization**:
|
||||||
|
|
|
||||||
|
|
@ -43,13 +43,13 @@ Here, we provide some minimal example implementations:
|
||||||
3. Google (Generative AI Studio / PaLM API)
|
3. Google (Generative AI Studio / PaLM API)
|
||||||
```python
|
```python
|
||||||
def call_llm(prompt):
|
def call_llm(prompt):
|
||||||
import google.generativeai as genai
|
from google import genai
|
||||||
genai.configure(api_key="YOUR_API_KEY_HERE")
|
client = genai.Client(api_key='GEMINI_API_KEY')
|
||||||
r = genai.generate_text(
|
response = client.models.generate_content(
|
||||||
model="models/text-bison-001",
|
model='gemini-2.0-flash-001',
|
||||||
prompt=prompt
|
contents=prompt
|
||||||
)
|
)
|
||||||
return r.result
|
return response.text
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Azure (Azure OpenAI)
|
4. Azure (Azure OpenAI)
|
||||||
|
|
|
||||||
|
|
@ -87,146 +87,9 @@ graph LR
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
## 2. Interactive D3.js Visualization
|
For visualization based on d3.js, check out [the cookbook](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-visualization).
|
||||||
|
|
||||||
For more complex flows, a static diagram may not be sufficient. We provide a D3.js-based interactive visualization that allows for dragging nodes, showing group boundaries for flows, and connecting flows at their boundaries.
|
## 2. Call Stack Debugging
|
||||||
|
|
||||||
### Converting Flow to JSON
|
|
||||||
|
|
||||||
First, we convert the PocketFlow graph to JSON format suitable for D3.js:
|
|
||||||
|
|
||||||
```python
|
|
||||||
def flow_to_json(start):
|
|
||||||
"""Convert a flow to JSON format suitable for D3.js visualization.
|
|
||||||
|
|
||||||
This function walks through the flow graph and builds a structure with:
|
|
||||||
- nodes: All non-Flow nodes with their group memberships
|
|
||||||
- links: Connections between nodes within the same group
|
|
||||||
- group_links: Connections between different groups (for inter-flow connections)
|
|
||||||
- flows: Flow information for group labeling
|
|
||||||
"""
|
|
||||||
nodes = []
|
|
||||||
links = []
|
|
||||||
group_links = [] # For connections between groups (Flow to Flow)
|
|
||||||
ids = {}
|
|
||||||
node_types = {}
|
|
||||||
flow_nodes = {} # Keep track of flow nodes
|
|
||||||
ctr = 1
|
|
||||||
|
|
||||||
# Implementation details...
|
|
||||||
|
|
||||||
# Post-processing: Generate group links based on node connections between different groups
|
|
||||||
node_groups = {n["id"]: n["group"] for n in nodes}
|
|
||||||
filtered_links = []
|
|
||||||
|
|
||||||
# Filter out direct node-to-node connections between different groups
|
|
||||||
for link in links:
|
|
||||||
source_id = link["source"]
|
|
||||||
target_id = link["target"]
|
|
||||||
source_group = node_groups.get(source_id, 0)
|
|
||||||
target_group = node_groups.get(target_id, 0)
|
|
||||||
|
|
||||||
if source_group != target_group and source_group > 0 and target_group > 0:
|
|
||||||
# Create group-to-group links instead of node-to-node links across groups
|
|
||||||
if not any(gl["source"] == source_group and gl["target"] == target_group
|
|
||||||
for gl in group_links):
|
|
||||||
group_links.append({
|
|
||||||
"source": source_group,
|
|
||||||
"target": target_group,
|
|
||||||
"action": link["action"]
|
|
||||||
})
|
|
||||||
# Skip adding this link to filtered_links - we don't want direct node connections across groups
|
|
||||||
else:
|
|
||||||
# Keep links within the same group
|
|
||||||
filtered_links.append(link)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"nodes": nodes,
|
|
||||||
"links": filtered_links,
|
|
||||||
"group_links": group_links,
|
|
||||||
"flows": {str(k): v.__class__.__name__ for k, v in flow_nodes.items()},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Creating the Visualization
|
|
||||||
|
|
||||||
Then, we generate an HTML file with D3.js visualization:
|
|
||||||
|
|
||||||
```python
|
|
||||||
def create_d3_visualization(json_data, output_dir="./viz", filename="flow_viz"):
|
|
||||||
"""Create a D3.js visualization from JSON data."""
|
|
||||||
# Create output directory
|
|
||||||
os.makedirs(output_dir, exist_ok=True)
|
|
||||||
|
|
||||||
# Save JSON data to file
|
|
||||||
json_path = os.path.join(output_dir, f"{filename}.json")
|
|
||||||
with open(json_path, "w") as f:
|
|
||||||
json.dump(json_data, f, indent=2)
|
|
||||||
|
|
||||||
# Generate HTML with D3.js visualization
|
|
||||||
# ...HTML template with D3.js code...
|
|
||||||
|
|
||||||
# Key features implemented in the visualization:
|
|
||||||
# 1. Nodes can be dragged to reorganize the layout
|
|
||||||
# 2. Flows are shown as dashed rectangles (groups)
|
|
||||||
# 3. Inter-group connections shown as dashed lines connecting at group boundaries
|
|
||||||
# 4. Edge labels show transition actions
|
|
||||||
|
|
||||||
# Write HTML to file
|
|
||||||
html_path = os.path.join(output_dir, f"{filename}.html")
|
|
||||||
with open(html_path, "w") as f:
|
|
||||||
f.write(html_content)
|
|
||||||
|
|
||||||
print(f"Visualization created at {html_path}")
|
|
||||||
return html_path
|
|
||||||
```
|
|
||||||
|
|
||||||
### Convenience Function
|
|
||||||
|
|
||||||
A convenience function to visualize flows:
|
|
||||||
|
|
||||||
```python
|
|
||||||
def visualize_flow(flow, flow_name):
|
|
||||||
"""Helper function to visualize a flow with both mermaid and D3.js"""
|
|
||||||
print(f"\n--- {flow_name} Mermaid Diagram ---")
|
|
||||||
print(build_mermaid(start=flow))
|
|
||||||
|
|
||||||
print(f"\n--- {flow_name} D3.js Visualization ---")
|
|
||||||
json_data = flow_to_json(flow)
|
|
||||||
create_d3_visualization(
|
|
||||||
json_data, filename=f"{flow_name.lower().replace(' ', '_')}"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Usage Example
|
|
||||||
|
|
||||||
```python
|
|
||||||
from visualize import visualize_flow
|
|
||||||
|
|
||||||
# Create a complex flow with nested subflows
|
|
||||||
# ...flow definition...
|
|
||||||
|
|
||||||
# Generate visualization
|
|
||||||
visualize_flow(data_science_flow, "Data Science Flow")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Customizing the Visualization
|
|
||||||
|
|
||||||
You can customize the visualization by adjusting the force simulation parameters:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const simulation = d3.forceSimulation(data.nodes)
|
|
||||||
// Controls the distance between connected nodes
|
|
||||||
.force("link", d3.forceLink(data.links).id(d => d.id).distance(100))
|
|
||||||
// Controls how nodes repel each other - lower values bring nodes closer
|
|
||||||
.force("charge", d3.forceManyBody().strength(-30))
|
|
||||||
// Centers the entire graph in the SVG
|
|
||||||
.force("center", d3.forceCenter(width / 2, height / 2))
|
|
||||||
// Prevents nodes from overlapping - acts like a minimum distance
|
|
||||||
.force("collide", d3.forceCollide().radius(50));
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3. Call Stack Debugging
|
|
||||||
|
|
||||||
It would be useful to print the Node call stacks for debugging. This can be achieved by inspecting the runtime call stack:
|
It would be useful to print the Node call stacks for debugging. This can be achieved by inspecting the runtime call stack:
|
||||||
|
|
||||||
|
|
@ -275,3 +138,5 @@ data_science_flow.run({})
|
||||||
```
|
```
|
||||||
|
|
||||||
The output would be: `Call stack: ['EvaluateModelNode', 'ModelFlow', 'DataScienceFlow']`
|
The output would be: `Call stack: ['EvaluateModelNode', 'ModelFlow', 'DataScienceFlow']`
|
||||||
|
|
||||||
|
For a more complete implementation, check out [the cookbook](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-tracing).
|
||||||
Loading…
Reference in New Issue