Skip to content

Commit

Permalink
Merge pull request #302 from PrefectHQ/docs
Browse files Browse the repository at this point in the history
Add new agentic examples
  • Loading branch information
jlowin authored Sep 11, 2024
2 parents 3478c29 + 04e583c commit ea8923c
Show file tree
Hide file tree
Showing 12 changed files with 391 additions and 167 deletions.
106 changes: 58 additions & 48 deletions docs/examples/agent-engineer.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
---
title: Software Engineer
description: Create an AI agent that acts as a software engineer, taking user input and generating code based on the requirements.
icon: file
---

Who doesn't want an AI software engineer?
Expand All @@ -8,87 +10,78 @@ This example demonstrates how to create an AI agent that acts as a software engi


<Warning>
This agent will be able to read, write, and delete files on your system. Make sure you understand the code before running it.
This agent will be able to read, write, and delete files on your system. Make sure you understand the code before running it!
</Warning>

<CodeGroup>
```python engineer.py
from pathlib import Path

import controlflow as cf
import controlflow.tools.code
import controlflow.tools.filesystem
from controlflow.tools import filesystem, code
from pydantic import BaseModel

# load the instructions
instructions = open(Path(__file__).parent / "instructions.md").read()

# create the agent
agent = cf.Agent(
"Engineer",
instructions=instructions,
tools=[
*controlflow.tools.filesystem.ALL_TOOLS,
controlflow.tools.code.python,
controlflow.tools.code.shell,
],
)


class DesignDoc(BaseModel):
goals: str
design: str
implementation_details: str
criteria: str

# Load the instructions
# instructions = Path(__file__).parent.joinpath("instructions.md").read_text()
instructions = Path('/tmp/instructions.md').read_text()

@cf.flow
def run_engineer():
# Create the agent
engineer = cf.Agent(
name="Engineer",
instructions=instructions,
tools=[
*filesystem.ALL_TOOLS,
code.python,
code.shell,
],
)

# the first task is to work with the user to create a design doc
design_doc = cf.Task(
@cf.flow(default_agent=engineer, instructions='Do not give up until the software works.')
def software_engineer_flow():
# Task 1: Create design document
design_doc = cf.run(
"Learn about the software the user wants to build",
instructions="""
Interact with the user to understand the software they want to
build. What is its purpose? What language should you use? What does
it need to do? Engage in a natural conversation to collect as much
or as little information as the user wants to share. Once you have
enough, write out a design document to complete the task.
""",
Interact with the user to understand the software they want to build.
What is its purpose? What language should you use? What does it need to do?
Engage in a natural conversation to collect information.
Once you have enough, write out a design document to complete the task.
""",
interactive=True,
result_type=DesignDoc,
)

# next we create a directory for any files
mkdir = cf.Task(
# Task 2: Create project directory
project_dir = cf.run(
"Create a directory for the software",
instructions="""
Create a directory to store the software and any related files. The
directory should be named after the software. Return the path.
Create a directory to store the software and related files.
The directory should be named after the software. Return the path.
""",
result_type=str,
tools=[controlflow.tools.filesystem.mkdir],
agents=[agent],
tools=[filesystem.mkdir],
)

# the final task is to write the software
software = cf.Task(
"Finish the software",
# Task 3: Implement the software
cf.run(
"Implement the software",
instructions="""
Mark this task complete when the software runs as expected and the
user can invoke it. Until then, continue to build the software.
All files must be written to the provided root directory.
Implement the software based on the design document.
All files must be written to the provided project directory.
Continue building and refining until the software runs as expected and meets all requirements.
Update the user on your progress regularly.
""",
context=dict(design_doc=design_doc, project_dir=project_dir),
result_type=None,
context=dict(design_doc=design_doc, root_dir=mkdir),
agents=[agent],
)
return software


if __name__ == "__main__":
run_engineer()
software_engineer_flow()
```
```markdown instructions.md
# Software Engineer Agent
Expand Down Expand Up @@ -129,4 +122,21 @@ You are a software engineer specialized in leveraging large language models (LLM

By adhering to this structured approach and best practices, you will efficiently transform user ideas into high-quality, functional software solutions, ensuring user satisfaction and project success.
```
</CodeGroup>
</CodeGroup>

















123 changes: 82 additions & 41 deletions docs/examples/call-routing.mdx
Original file line number Diff line number Diff line change
@@ -1,73 +1,114 @@
---
title: Customer Call Routing
description: Train an agent to route customer calls to the correct department.
icon: headset
---

In this example, two agents interact in a call routing scenario. One agent plays the role of a customer calling into a call center, while the other agent is a trainee customer service representative. The trainee must listen to the customer's story and route them to the correct department based on the information provided.
In this example, you'll witness a roleplay between two AI agents:

1. A "customer" agent, who has been assigned a random department they need to reach but is instructed not to directly state it.
2. A "trainee" customer service representative, who must figure out the correct department based on the customer's story.

The conversation will continue back and forth until the trainee feels confident enough to route the call. This example showcases how ControlFlow can be used to create dynamic, multi-turn interactions between agents, with one agent (the trainee) ultimately making a decision that determines the outcome of the task.

As you run this example, you'll see the conversation unfold in real-time, culminating in the trainee's decision to route the call. The success of the interaction depends on whether the trainee correctly identifies the department the customer needs.

## Code

```python
import random
from enum import Enum
import controlflow as cf


class Department(Enum):
SALES = "sales"
SUPPORT = "support"
BILLING = "billing"
TECHNICAL = "technical"
RETURNS = "returns"


DEPARTMENTS = [
"Sales",
"Support",
"Billing",
"Returns",
]

@cf.flow
def routing_flow():
department = random.choice(list(Department))
target_department = random.choice(DEPARTMENTS)

print(f"\n---\nThe target department is: {target_department}\n---\n")

# create an agent to be our "customer"
customer = cf.Agent(
"Customer",
name="Customer",
instructions=f"""
You are training customer reps by pretending to be a customer
calling into a call center. You need to be routed to the
{department} department. Come up with a good backstory.""",
{target_department} department. Come up with a good backstory.
""",
)

trainee = cf.Agent(
"Trainee",
instructions="""
name="Trainee",
instructions=""",
You are a trainee customer service representative. You need to
listen to the customer's story and route them to the correct
department. Note that the customer is another agent training you.""",
department. Note that the customer is another agent training you.
""",
)

task = cf.Task(
"""
In this task, the customer agent and the trainee agent will speak to
each other. The customer is trying to be routed to the correct
department. The trainee will listen and ask questions to determine the
correct department.
""",
instructions="""
Only the trainee can mark the task successful by routing the customer to
the correct department. The trainee must begin the conversation by
greeting the customer. Agents speak to each other by posting messages
directly to the thread. Do not use the `end_turn` tool or try to talk
to a user.
""",
agents=[trainee, customer],
result_type=Department,
)
with cf.Task(
"Route the customer to the correct department.",
agents=[trainee],
result_type=DEPARTMENTS,
) as main_task:

while main_task.is_incomplete():

cf.run(
"Talk to the trainee.",
instructions=(
"Post a message to talk. In order to help the trainee "
"learn, don't be direct about the department you want. "
"Instead, share a story that will let them practice. "
"After you speak, mark this task as complete."
),
agents=[customer],
result_type=None
)

routed_dapartment = task.run()
if routed_dapartment == department:
cf.run(
"Talk to the customer.",
instructions=(
"Post a message to talk. Ask questions to learn more "
"about the customer. After you speak, mark this task as "
"complete. When you have enough information, use the main "
"task tool to route the customer to the correct department."
),
agents=[trainee],
result_type=None,
tools=[main_task.create_success_tool()]
)

if main_task.result == target_department:
print("Success! The customer was routed to the correct department.")
else:
print(
"Failed. The customer was routed to the wrong department. "
f"The correct department was {department}."
)
print(f"Failed. The customer was routed to the wrong department. "
f"The correct department was {target_department}.")

if __name__ == "__main__":
routing_flow()
````
```

## Key points

1. **Multi-agent interaction**: This example showcases how to orchestrate a conversation between two AI agents, each with distinct roles and objectives.

2. **Parent task as control flow**: The `main_task` serves dual purposes - it represents the overall objective and acts as a control mechanism for the conversation loop. The `while main_task.is_incomplete()` construct creates a flexible, AI-driven loop that continues until the trainee decides to route the call.

3. **Explicit turn-taking**: Instead of using ControlFlow's built-in turn strategies, this example manually alternates between the customer and trainee agents. This provides fine-grained control over the conversation flow and allows for specific instructions to be given to each agent on each turn.

4. **Task-specific tools**: The trainee is given access to the `main_task`'s success tool, allowing them to mark the overall task as complete when they're ready to route the call, even though that task isn't currently active. This demonstrates how tools can be used to give agents control over task state.


## Further reading

- For more details on creating and managing tasks, see the [Tasks documentation](/concepts/tasks).
- To learn more about agents and their capabilities, check out the [Agents guide](/concepts/agents).
- For information on how ControlFlow manages conversations and context, refer to the [Message History guide](/patterns/history).

This example effectively demonstrates how to create a complex, interactive scenario in ControlFlow, with fine-grained control over agent interactions and task flow. It showcases the flexibility of the framework in handling multi-turn conversations and decision-making processes, making it an excellent template for building sophisticated AI-powered applications.
Loading

0 comments on commit ea8923c

Please sign in to comment.