Tuesday, May 27, 2025

How to create AI Agent using Pocketflow framework.

The project Pocket Flow is a minimalist, open-source framework designed for building LLM (Large Language Model) applications. It’s extremely lightweight—just 100 lines of core code—yet powerful enough to support complex agentic workflows.

πŸ”§ What Is Pocket Flow?
Pocket Flow provides a graph-based abstraction to design AI systems using a series of “nodes” and “flows”—each node represents a step in the pipeline (like calling an LLM, summarizing text, or validating output), and the flow connects them in sequence or branching logic.

Key Features:
Ultra-lightweight: 100-line core with no bloat, no dependencies.

Modular: Nodes, utilities, and flows can be mixed and matched.

Design Patterns: Built-in support for common LLM structures like:

Agents

RAG (Retrieval Augmented Generation)

Workflows

Map-Reduce

Parallel & Async processing

Language-agnostic Ports: Available in Python, TypeScript, Java, C++, Go.

🧠 What Problem Does It Solve?
Most LLM frameworks (e.g., LangChain, AutoGen) are large, bloated, and heavily vendor-locked. Pocket Flow aims to simplify development and increase developer control, allowing rapid prototyping and scaling of intelligent applications without depending on a specific provider or library.

πŸš€ Real-World Use Case: AI Resume Screener with Map-Reduce
Problem:
A company wants to automatically screen 1,000 resumes and extract whether candidates meet job requirements.

Solution:
Use Pocket Flow’s Map-Reduce pattern:

Map Step: Split all resumes and analyze them one by one using an LLM.

Reduce Step: Aggregate the outputs to generate a final shortlist.

Live Example from Project:

The folder cookbook/pocketflow-map-reduce/ has:

resume1.txt to resume5.txt — input data.

flow.py, nodes.py, main.py — defines the flow logic and execution.

1- Explain the pocketflow frame work
2- example in python reference with CV screening example

High-Level Flow:

flowchart TD
LoadResumes –> MapStep[Analyze Each Resume]
MapStep –> ReduceStep[Summarize and Rank Candidates]
ReduceStep –> Output[Final Shortlist]
Why Pocket Flow here?
Batch nodes allow scalable parallel analysis.

Shared store makes it easy to keep track of resume data and results.

No external dependency hell—you can run this with just Python and OpenAI API if needed.

✅ Summary
Aspect Description
Project Name Pocket Flow
Core Use Build intelligent agentic flows and pipelines with LLMs
Tech Stack Python (core), but also available in TS, Java, Go, etc.
Use Case Resume screener, chatbot with memory, text summarizer, SQL generator, etc.
Ideal For Developers who want minimal, fast, and modular LLM solutions

Project Goal
Input: Multiple resume text files
Process:

Use LLM to assess each resume against job requirements (Map)

Combine results and select the most qualified candidates (Reduce)
Output: A shortlist of top candidates

πŸ“ Project Structure
resume_screener/
├── main.py
├── flow.py
├── nodes.py
├── utils.py
├── requirements.txt
└── data/
├── resume1.txt
├── resume2.txt
├── …

  1. utils.py – LLM Helper

utils.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# utils.py
# from openai import OpenAI
# import os
 
# #OPENAI_API_KEY = "<YOIUR_API_KEY>";
# client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
# #client = OpenAI(OPENAI_API_KEY)
 
# def call_llm(prompt):
#     response = client.chat.completions.create(
#         model="gpt-4o",
#         messages=[{"role": "user", "content": prompt}]
#     )
#     return response.choices[0].message.content
 
# import requests
# import json
 
# def call_llm(prompt):
#     response = requests.post(
#   headers={
#     "Authorization": "Bearer <YOUR_API_KEY>",
#     "Content-Type": "application/json",
#     "HTTP-Referer": "<YOUR_SITE_URL>", # Optional. Site URL for rankings on openrouter.ai.
#     "X-Title": "<YOUR_SITE_NAME>", # Optional. Site title for rankings on openrouter.ai.
#   },
#   data=json.dumps({
#     "model": "meta-llama/llama-3.3-8b-instruct:free",
#     "messages": [{"role": "user", "content": prompt}],
     
#   })
# )
#     print("response.text", response.text)
#     print("response.text", response.content)
     
#     return response.content
 
import google.generativeai as genai
import google.generativeai.types as types
import os
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
 
 
# Initialize the model
model = genai.GenerativeModel('gemini-2.5-flash-preview-05-20')
 
 
# Generate content
# chat = model.start_chat()
# response = chat.send_message("Write a short story about a cat.")
# print(response.text)
 
def call_llm(prompt):
     
    response = model.generate_content([
       {"role": "user", "parts": [prompt]}
   ], stream=False)
    print(response.text)
    return response.text


🧠 2. nodes.py – Custom Nodes

nodes.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# nodes.py
from pocketflow import Node, BatchNode
from utils import call_llm
import os
 
class LoadResumes(Node):
    def post(self, shared, *_):
        data_dir = "data"
        shared["resumes"] = {}
        for file in os.listdir(data_dir):
            with open(os.path.join(data_dir, file), "r") as f:
                shared["resumes"][file] = f.read()
        return "default"
 
class AnalyzeResume(BatchNode):
    def prep(self, shared):
        return list(shared["resumes"].items())  # [(filename, content), ...]
 
    def exec(self, item):
        filename, content = item
        prompt = f"""
You are a recruiter. Evaluate this resume and score it from 0 to 10 for the following job:
 
Job: Data Scientist
Requirements:
- Python and SQL skills
- Experience with ML or Data Analysis
- Clear communication
 
Resume:
{content}
 
Return ONLY a score (0-10) and a one-line explanation.
"""
        return filename, call_llm(prompt)
 
    def post(self, shared, prep_res, exec_res_list):
        shared["results"] = {filename: result for filename, result in exec_res_list}
        return "default"
 
class GenerateShortlist(Node):
    def prep(self, shared):
        return shared["results"]
 
    def exec(self, results):
        # Sort by score in the response
        scored = []
        for fname, result in results.items():
            try:
                score = float(result.split()[0])
                scored.append((score, fname, result))
            except:
                pass
        scored.sort(reverse=True)
        return scored[:3]  # top 3 resumes
 
    def post(self, shared, _, exec_res):
        shared["shortlist"] = exec_res
        return "default"

πŸ” 3. flow.py – Connecting Nodes

flow.py

1
2
3
4
5
6
7
8
9
10
11
# flow.py
from pocketflow import Flow
from nodes import LoadResumes, AnalyzeResume, GenerateShortlist
 
def create_resume_flow():
    load = LoadResumes()
    analyze = AnalyzeResume()
    shortlist = GenerateShortlist()
 
    load >> analyze >> shortlist
    return Flow(start=load)

πŸš€ 4. main.py – Run the Flow

main.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# main.py
from flow import create_resume_flow
 
def main():
    shared = {}
    flow = create_resume_flow()
    flow.run(shared)
 
    print("\n--- SHORTLIST ---")
    for score, fname, explanation in shared["shortlist"]:
        print(f"{fname}: {score}/10 – {explanation}")
 
if __name__ == "__main__":
    main()


πŸ“¦ 5. requirements.txt

openai
pocketflow
πŸ§ͺ 6. data/ – Resume Files
Add a few .txt files here with sample resumes. Example:

resume1.txt

User One
Skills: Python, SQL, Tableau
Experience: 3 years in data analytics…

resume2.txt

User Two
Skills: JAVA
Experience: 8 years in data analytics…

resume3.txt

User Three with Python and SQL with 8 years of Experience in back end development and using python and djago. She has worked scalable systems and cloud deployment.

✅ How It Works
LoadResumes node reads all .txt files into memory.

AnalyzeResume is a batch node. It loops over resumes, uses OpenAI to score them.

GenerateShortlist picks the top 3 based on scores.

Output is printed in the terminal.

After creating the above project in vscode in side folder PocketFlow-Resume-Example follow below step to set the venv

C:\vscode-python-workspace\PocketFlow-Resume-Example>python -m venv env

C:\vscode-python-workspace\PocketFlow-Resume-Example>.\env\Scripts\activate

(env) C:\vscode-python-workspace\PocketFlow-Resume-Example>python.exe -m pip install –upgrade pip
Requirement already satisfied: pip in c:\vscode-python-workspace\pocketflow-resume-example\env\lib\site-packages (24.3.1)
Collecting pip
Downloading pip-25.1.1-py3-none-any.whl.metadata (3.6 kB)
Downloading pip-25.1.1-py3-none-any.whl (1.8 MB)
—————————————- 1.8/1.8 MB 18.6 MB/s eta 0:00:00
Installing collected packages: pip
Attempting uninstall: pip
Found existing installation: pip 24.3.1
Uninstalling pip-24.3.1:
Successfully uninstalled pip-24.3.1
Successfully installed pip-25.1.1

(env) C:\vscode-python-workspace\PocketFlow-Resume-Example>pip install -r requirements.txt

Soure code :- https://github.com/shdhumale/PocketFlow-Resume-Example/tree/main

Output:-

C:\vscode-python-workspace\PocketFlow-Resume-Example>python main.py

8 - Meets core technical and experience requirements directly, but communication skills cannot be assessed from this snippet.

10 - All stated requirements are directly met, with significant experience indicated.

8 - The resume perfectly matches technical skills and experience, but lacks the detail to demonstrate clear communication.


--- SHORTLIST ---

resume2.txt: 10.0/10 – 10 - All stated requirements are directly met, with significant experience indicated.

resume3.txt: 8.0/10 – 8 - The resume perfectly matches technical skills and experience, but lacks the detail to demonstrate clear communication.

resume1.txt: 8.0/10 – 8 - Meets core technical and experience requirements directly, but communication skills cannot be assessed from this snippet.

No comments: