add multi-agent tutorial
This commit is contained in:
parent
eb1c721e00
commit
7473d4a017
|
|
@ -70,11 +70,13 @@ From there, it's easy to implement popular design patterns like ([Multi-](https:
|
|||
| [Map-Reduce](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-map-reduce) | ☆☆☆ <br> *Dummy* | A resume qualification processor using map-reduce pattern for batch evaluation |
|
||||
| [Agent](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-agent) | ☆☆☆ <br> *Dummy* | A research agent that can search the web and answer questions |
|
||||
| [Streaming](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-llm-streaming) | ☆☆☆ <br> *Dummy* | A real-time LLM streaming demo with user interrupt capability |
|
||||
| [Parallel](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-parallel-batch) | ★☆☆ <br> *Beginner* | A parallel execution demo that shows 3x speedup |
|
||||
| [Multi-Agent](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-multi-agent) | ★☆☆ <br> *Beginner* | A Taboo word game for asynchronous communication between two agents |
|
||||
| [Supervisor](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-supervisor) | ★☆☆ <br> *Beginner* | Research agent is getting unreliable... Let's build a supervision process|
|
||||
| [Parallel](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-parallel-batch) | ★☆☆ <br> *Beginner* | A parallel execution demo that shows 3x speedup |
|
||||
| [Thinking](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-thinking) | ★☆☆ <br> *Beginner* | Solve complex reasoning problems through Chain-of-Thought |
|
||||
| [Memory](https://github.com/The-Pocket/PocketFlow/tree/main/cookbook/pocketflow-chat-memory) | ★☆☆ <br> *Beginner* | A chat bot with short-term and long-term memory |
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
👀 Want to see other tutorials for dummies? [Create an issue!](https://github.com/The-Pocket/PocketFlow/issues/new)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
# Multi-Agent Taboo Game
|
||||
|
||||
A PocketFlow example that demonstrates how to implement asynchronous multi-agent communication using the Taboo word guessing game.
|
||||
|
||||
## Features
|
||||
|
||||
- Implement asynchronous communication between two AI agents (Hinter and Guesser)
|
||||
- Use AsyncNode for non-blocking agent interactions
|
||||
- Create dynamic conversation flow through asyncio message queues
|
||||
- Demonstrate complex turn-based game mechanics with LLMs
|
||||
- Automatically terminate the game when the correct word is guessed
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Install the required dependencies:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. Set your OpenAI API key as an environment variable:
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_api_key_here
|
||||
```
|
||||
|
||||
3. Run the application:
|
||||
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
The workflow follows an asynchronous multi-agent communication pattern:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
AsyncHinter[AsyncHinter Node] <--> MessageQueue{Message Queue}
|
||||
MessageQueue <--> AsyncGuesser[AsyncGuesser Node]
|
||||
```
|
||||
|
||||
Here's what each component does:
|
||||
|
||||
1. **AsyncHinter Node**: Generates hints about the target word while avoiding forbidden words
|
||||
2. **AsyncGuesser Node**: Makes guesses based on the hints received from the Hinter
|
||||
3. **Message Queue**: Facilitates asynchronous communication between the agents
|
||||
|
||||
## Files
|
||||
|
||||
- [`main.py`](./main.py): Main entry point implementing the AsyncHinter and AsyncGuesser nodes and game flow
|
||||
- [`utils.py`](./utils.py): Utility functions including LLM wrappers for generating hints and guesses
|
||||
- [`requirements.txt`](./requirements.txt): Lists the required dependencies
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
=========== Taboo Game Starting! ===========
|
||||
Target word: nostalgic
|
||||
Forbidden words: ['memory', 'past', 'remember', 'feeling', 'longing']
|
||||
============================================
|
||||
|
||||
Hinter: Here's your hint - Sentiment for earlier times.
|
||||
Guesser: I guess it's - Nostalgia
|
||||
|
||||
Hinter: Here's your hint - Sentiment for earlier times.
|
||||
Guesser: I guess it's - Reminiscence
|
||||
|
||||
Hinter: Here's your hint - Yearning for days gone by.
|
||||
Guesser: I guess it's - Sentimentality
|
||||
|
||||
Hinter: Here's your hint - Reliving cherished moments or experiences.
|
||||
Guesser: I guess it's - Memories
|
||||
|
||||
Hinter: Here's your hint - Recollection of cherished experiences.
|
||||
Guesser: I guess it's - Reflection
|
||||
|
||||
Hinter: Here's your hint - Yearning for earlier times.
|
||||
Guesser: I guess it's - Longing
|
||||
|
||||
Hinter: Here's your hint - Sentiment for earlier times.
|
||||
Guesser: I guess it's - Nostalgic
|
||||
Game Over - Correct guess!
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
import asyncio
|
||||
from pocketflow import AsyncNode, AsyncFlow
|
||||
from utils import call_llm
|
||||
|
||||
class AsyncHinter(AsyncNode):
|
||||
async def prep_async(self, shared):
|
||||
# Wait for message from guesser (or empty string at start)
|
||||
guess = await shared["hinter_queue"].get()
|
||||
if guess == "GAME_OVER":
|
||||
return None
|
||||
return shared["target_word"], shared["forbidden_words"], shared.get("past_guesses", [])
|
||||
|
||||
async def exec_async(self, inputs):
|
||||
if inputs is None:
|
||||
return None
|
||||
target, forbidden, past_guesses = inputs
|
||||
prompt = f"Generate hint for '{target}'\nForbidden words: {forbidden}"
|
||||
if past_guesses:
|
||||
prompt += f"\nPrevious wrong guesses: {past_guesses}\nMake hint more specific."
|
||||
prompt += "\nUse at most 5 words."
|
||||
|
||||
hint = call_llm(prompt)
|
||||
print(f"\nHinter: Here's your hint - {hint}")
|
||||
return hint
|
||||
|
||||
async def post_async(self, shared, prep_res, exec_res):
|
||||
if exec_res is None:
|
||||
return "end"
|
||||
# Send hint to guesser
|
||||
await shared["guesser_queue"].put(exec_res)
|
||||
return "continue"
|
||||
|
||||
class AsyncGuesser(AsyncNode):
|
||||
async def prep_async(self, shared):
|
||||
# Wait for hint from hinter
|
||||
hint = await shared["guesser_queue"].get()
|
||||
return hint, shared.get("past_guesses", [])
|
||||
|
||||
async def exec_async(self, inputs):
|
||||
hint, past_guesses = inputs
|
||||
prompt = f"Given hint: {hint}, past wrong guesses: {past_guesses}, make a new guess. Directly reply a single word:"
|
||||
guess = call_llm(prompt)
|
||||
print(f"Guesser: I guess it's - {guess}")
|
||||
return guess
|
||||
|
||||
async def post_async(self, shared, prep_res, exec_res):
|
||||
# Check if guess is correct
|
||||
if exec_res.lower() == shared["target_word"].lower():
|
||||
print("Game Over - Correct guess!")
|
||||
await shared["hinter_queue"].put("GAME_OVER")
|
||||
return "end"
|
||||
|
||||
# Store the guess in shared state
|
||||
if "past_guesses" not in shared:
|
||||
shared["past_guesses"] = []
|
||||
shared["past_guesses"].append(exec_res)
|
||||
|
||||
# Send guess to hinter
|
||||
await shared["hinter_queue"].put(exec_res)
|
||||
return "continue"
|
||||
|
||||
async def main():
|
||||
# Set up game
|
||||
shared = {
|
||||
"target_word": "nostalgic",
|
||||
"forbidden_words": ["memory", "past", "remember", "feeling", "longing"],
|
||||
"hinter_queue": asyncio.Queue(),
|
||||
"guesser_queue": asyncio.Queue()
|
||||
}
|
||||
|
||||
print("=========== Taboo Game Starting! ===========")
|
||||
print(f"Target word: {shared['target_word']}")
|
||||
print(f"Forbidden words: {shared['forbidden_words']}")
|
||||
print("============================================")
|
||||
|
||||
# Initialize by sending empty guess to hinter
|
||||
await shared["hinter_queue"].put("")
|
||||
|
||||
# Create nodes and flows
|
||||
hinter = AsyncHinter()
|
||||
guesser = AsyncGuesser()
|
||||
|
||||
# Set up flows
|
||||
hinter_flow = AsyncFlow(start=hinter)
|
||||
guesser_flow = AsyncFlow(start=guesser)
|
||||
|
||||
# Connect nodes to themselves for looping
|
||||
hinter - "continue" >> hinter
|
||||
guesser - "continue" >> guesser
|
||||
|
||||
# Run both agents concurrently
|
||||
await asyncio.gather(
|
||||
hinter_flow.run_async(shared),
|
||||
guesser_flow.run_async(shared)
|
||||
)
|
||||
|
||||
print("=========== Game Complete! ===========")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import os
|
||||
from openai import OpenAI
|
||||
|
||||
def call_llm(prompt):
|
||||
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "your-api-key"))
|
||||
r = client.chat.completions.create(
|
||||
model="gpt-4o-mini",
|
||||
messages=[{"role": "user", "content": prompt}]
|
||||
)
|
||||
return r.choices[0].message.content
|
||||
|
||||
# Example usage
|
||||
if __name__ == "__main__":
|
||||
print(call_llm("Tell me a short joke"))
|
||||
Loading…
Reference in New Issue