Using Guidance with AutoGen
This notebook shows how Guidance can be used to enable structured responses from AutoGen agents. In particular, this notebook focuses on creating agents that always output a valid code block or valid json object.
import re
from guidance import assistant, gen, models, system, user
from pydantic import BaseModel
from autogen import Agent, AssistantAgent, UserProxyAgent, config_list_from_json
llm_config = config_list_from_json("OAI_CONFIG_LIST")[0] # use the first config
gpt = models.OpenAI("gpt-4", api_key=llm_config.get("api_key"))
The example below uses guidance to create a guidance_coder
agent that
only responds with valid code blocks.
def is_valid_code_block(code):
pattern = r"```[\w\s]*\n([\s\S]*?)\n```"
match = re.search(pattern, code)
if match:
return True
else:
return False
def generate_structured_response(recipient, messages, sender, config):
gpt = models.OpenAI("gpt-4", api_key=llm_config.get("api_key"), echo=False)
# populate the recipient with the messages from the history
with system():
lm = gpt + recipient.system_message
for message in messages:
if message.get("role") == "user":
with user():
lm += message.get("content")
else:
with assistant():
lm += message.get("content")
# generate a new response and store it
with assistant():
lm += gen(name="initial_response")
# ask the agent to reflect on the nature of the response and store it
with user():
lm += "Does the very last response from you contain code? Respond with yes or no."
with assistant():
lm += gen(name="contains_code")
# if the response contains code, ask the agent to generate a proper code block
if "yes" in lm["contains_code"].lower():
with user():
lm += "Respond with a single blocks containing the valid code. Valid code blocks start with ```"
with assistant():
lm += "```" + gen(name="code")
response = "```" + lm["code"]
is_valid = is_valid_code_block(response)
if not is_valid:
raise ValueError(f"Failed to generate a valid code block\n {response}")
# otherwise, just use the initial response
else:
response = lm["initial_response"]
return True, response
guidance_agent = AssistantAgent("guidance_coder", llm_config=llm_config)
guidance_agent.register_reply(Agent, generate_structured_response, 1)
user_proxy = UserProxyAgent(
"user",
human_input_mode="TERMINATE",
code_execution_config={
"work_dir": "coding",
"use_docker": False,
}, # Please set use_docker=True if docker is available to run the generated code. Using docker is safer than running the generated code directly.
is_termination_msg=lambda msg: "TERMINATE" in msg.get("content"),
)
user_proxy.initiate_chat(guidance_agent, message="Plot and save a chart of nvidia and tsla stock price change YTD.")
Plot and save a chart of nvidia and tsla stock price change YTD.
--------------------------------------------------------------------------------
```python
# filename: stock_price_change.py
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
from datetime import datetime
# Get today's date
today = datetime.today().strftime('%Y-%m-%d')
# Download stock data
nvda = yf.download('NVDA', start='2022-01-01', end=today)
tsla = yf.download('TSLA', start='2022-01-01', end=today)
# Calculate percentage change in closing price
nvda['Pct Change'] = nvda['Close'].pct_change()
tsla['Pct Change'] = tsla['Close'].pct_change()
# Plot percentage change
plt.figure(figsize=(14,7))
plt.plot(nvda['Pct Change'], label='NVDA')
plt.plot(tsla['Pct Change'], label='TSLA')
plt.title('Nvidia and Tesla Stock Price Change YTD')
plt.xlabel('Date')
plt.ylabel('Percentage Change')
plt.legend()
plt.grid(True)
# Save the plot as a PNG file
plt.savefig('stock_price_change.png')
```
--------------------------------------------------------------------------------
>>>>>>>> USING AUTO REPLY...
>>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)...
exitcode: 0 (execution succeeded)
Code output:
[*********************100%%**********************] 1 of 1 completed
[*********************100%%**********************] 1 of 1 completed
--------------------------------------------------------------------------------
Great! The code executed successfully and the chart of Nvidia and Tesla stock price change Year-To-Date (YTD) has been saved as 'stock_price_change.png' in the current directory. You can open this file to view the chart.
TERMINATE
--------------------------------------------------------------------------------
execute_code was called without specifying a value for use_docker. Since the python docker package is not available, code will be run natively. Note: this fallback behavior is subject to change
The example below uses Guidance to enable a guidance_labeler
agent
that only responds with a valid JSON that labels a given comment/joke.
class Response(BaseModel):
label: str
explanation: str
response_prompt_instructions = """The label must be a JSON of the format:
{
"label": str,
"explanation": str
}"""
def generate_structured_response(recipient, messages, sender, config):
gpt = models.OpenAI("gpt-4", api_key=llm_config.get("api_key"), echo=False)
# populate the recipient with the messages from the history
with system():
lm = gpt + recipient.system_message
for message in messages:
if message.get("role") == "user":
with user():
lm += message.get("content")
else:
with assistant():
lm += message.get("content")
# generate a new response and store it
with assistant():
lm += gen(name="initial_response")
# ask the agent to reflect on the nature of the response and store it
with user():
lm += "Does the very last response from you contain JSON object? Respond with yes or no."
with assistant():
lm += gen(name="contains_json")
# if the response contains code, ask the agent to generate a proper code block
if "yes" in lm["contains_json"].lower():
with user():
lm += (
"What was that JSON object? Only respond with that valid JSON string. A valid JSON string starts with {"
)
with assistant():
lm += "{" + gen(name="json")
response = "{" + lm["json"]
# verify that the response is valid json
try:
response_obj = Response.model_validate_json(response)
response = response_obj.model_dump_json()
except Exception as e:
response = str(e)
# otherwise, just use the initial response
else:
response = lm["initial_response"]
return True, response
guidance_agent = AssistantAgent("guidance_labeler", llm_config=llm_config, system_message="You are a helpful assistant")
guidance_agent.register_reply(Agent, generate_structured_response, 1)
user_proxy = UserProxyAgent("user", human_input_mode="ALWAYS", code_execution_config=False)
user_proxy.initiate_chat(
guidance_agent,
message=f"""
Label the TEXT via the following instructions:
{response_prompt_instructions}
TEXT: what did the fish say when it bumped into a wall? Dam!
""",
)
Label the TEXT via the following instructions:
The label must be a JSON of the format:
{
"label": str,
"explanation": str
}
TEXT: what did the fish say when it bumped into a wall? Dam!
--------------------------------------------------------------------------------
{"label":"Joke","explanation":"The text is a joke, using a play on words where the fish says 'Dam!' after bumping into a wall, which is a pun on the word 'damn' and a 'dam' which is a barrier that stops or restricts the flow of water, often creating a reservoir, and is something a fish might encounter."}
--------------------------------------------------------------------------------