What if after ending a gathering with a colleague you’d have already got all of your mentioned objects in your project-management software? No want for writing something down in the course of the assembly, nor to manually create corresponding tickets! That was the considered this brief experimental venture.
On this step-by-step information we are going to create the Python utility “TaskPilot” utilizing OpenAI’s Agents SDK to robotically create Jira points given a gathering transcript.
The Problem: From Dialog to Actionable Duties
Given the transcript of a gathering, create points in a Jira venture robotically and similar to what was mentioned within the assembly.
The Answer: Automating with OpenAI Brokers
Utilizing the OpenAI Agents SDK we are going to implement an brokers workflow that:
- Receives and reads a gathering transcript.
- Makes use of an AI agent to extract motion objects from the dialog.
- Makes use of one other AI agent to create Jira points from these motion objects.
The OpenAI Brokers SDK
The OpenAI Agents SDK is a Python library to create AI brokers programmatically that may work together with instruments, use MCP-Servers or hand off duties to different brokers.
Listed here are among the key options of the SDK:
- Agent Loop: A built-in agent loop that handles the back-and-forth communication with the LLM till the agent is finished with its job.
- Operate Instruments: Turns any Python operate right into a software, with automated schema era and Pydantic-powered validation.
- MCP Help: Permits brokers to make use of MCP servers to increase its capabilities of interacting with the skin world.
- Handoffs: Permits brokers to delegate duties to different brokers relying on their experience/position.
- Guardrails: Validates the inputs and outputs of the brokers. Aborts execution early if the agent receives invalid enter.
- Periods: Routinely manages the dialog historical past. Ensures that the brokers have the context they should carry out their duties.
- Tracing: Gives a tracing context supervisor which permits to visualise your complete execution circulate of the brokers, making it simple to debug and perceive what’s occurring underneath the hood.
Now, let’s dive into the implementation!
Implementation
We are going to implement our venture in 8 easy steps:
- Setting up the project structure
- The TaskPilotRunner
- Defining our data models
- Creating the agents
- Providing tools
- Configuring the application
- Bringing it all together in
main.py
- Monitoring our runs in the OpenAI Dev Platform
Let’s get arms on!
Step 1: Setting Up the Venture Construction
First, let’s create the fundamental construction of our venture:
- The
taskpilot
listing: will comprise our essential utility logic. - The
local_agents
listing: will comprise the place we outline the brokers we are going to use on this venture (“local_agents” in order that there isn’t any interference with the OpenAI librarybrokers
) - The
utils
listing: for helper features, a config parser and knowledge fashions.
taskpilot_repo/
├── config.yml
├── .env
├── README.md
├── taskpilot/
│ ├── essential.py
│ ├── taskpilot_runner.py
│ ├── local_agents/
│ │ ├── __init__.py
│ │ ├── action_items_extractor.py
│ │ └── tickets_creator.py
│ └── utils/
│ ├── __init__.py
│ ├── agents_tools.py
│ ├── config_parser.py
│ ├── jira_interface_functions.py
│ └── fashions.py
Step 2: The TaskPilotRunner
The TaskPilotRunner
class in taskpilot/taskpilot_runner.py
would be the coronary heart of our utility. It is going to orchestrate your complete workflow, extracting motion objects from the assembly transcript after which creating the Jira tickets from the motion objects. On the identical time it’s going to activate the built-in tracing from the Brokers SDK to gather a file of occasions in the course of the brokers run that can assist for debugging and monitoring the agent workflows.
Let’s begin with the implementation:
- Within the
__init__()
technique we are going to create the 2 brokers used for this workflow. - The
run()
technique will probably be an important of theTaskPilotRunner
class, which is able to obtain the assembly transcript and cross it to the brokers to create the Jira points. The brokers will probably be began and run inside a hint context supervisor i.e.with hint("TaskPilot run", trace_id):
. A hint from the Brokers SDK represents a single end-to-end operation of a “workflow”. - The
_extract_action_items()
and_create_tickets()
strategies will begin and run every of the brokers respectively. Inside these strategies theRunner.run()
technique from the OpenAI Brokers SDK will probably be used to set off the brokers. It takes an agent and an enter, and it returns the ultimate output of the agent’s execution. Lastly, the results of every agent will probably be parsed to its outlined output sort.
# taskpilot/taskpilot_runner.py
from brokers import Runner, hint, gen_trace_id
from local_agents import create_action_items_agent, create_tickets_creator_agent
from utils.fashions import ActionItemsList, CreateIssuesResponse
class TaskPilotRunner:
def __init__(self):
self.action_items_extractor = create_action_items_agent()
self.tickets_creator = create_tickets_creator_agent()
async def run(self, meeting_transcript: str) -> None:
trace_id = gen_trace_id()
print(f"Beginning TaskPilot run... (Hint ID: {trace_id})")
print(
f"View hint: https://platform.openai.com/traces/hint?trace_id={trace_id}"
)
with hint("TaskPilot run", trace_id=trace_id):
# 1. Extract motion objects from assembly transcript
action_items = await self._extract_action_items(meeting_transcript)
# 2. Create tickets from motion objects
tickets_creation_response = await self._create_tickets(action_items)
# 3. Return the outcomes
print(tickets_creation_response.textual content)
async def _extract_action_items(self, meeting_transcript: str) -> ActionItemsList:
consequence = await Runner.run(
self.action_items_extractor, enter=meeting_transcript
)
final_output = consequence.final_output_as(ActionItemsList)
return final_output
async def _create_tickets(self, action_items: ActionItemsList) -> CreateIssuesResponse:
consequence = await Runner.run(
self.tickets_creator, enter=str(action_items)
)
final_output = consequence.final_output_as(CreateIssuesResponse)
return final_output
The three strategies are outlined as asynchronous features. The rationale for that is that the Runner.run()
technique from the OpenAI Brokers SDK is outlined itself as an async
coroutine. This enables a number of brokers, software calls, or streaming endpoints to run in parallel with out blocking.
Step 3: Defining Our Information Fashions
With out particular configuration brokers return textual content in str
as output. To make sure that our brokers present structured and predictable responses, the library helps the usage of Pydantic fashions for outlining the output_type
of the brokers (it actually supports any type that can be wrapped in a Pydantic TypeAdapter — dataclasses, lists, TypedDict, etc.). The info-models we outline would be the knowledge constructions that our brokers will work with.
For our usecase we are going to outline three fashions in taskpilot/utils/fashions.py
:
ActionItem
: This mannequin represents a single motion merchandise that’s extracted from the assembly transcript.ActionItemsList
: This mannequin is an inventory ofActionItem
objects.CreateIssuesResponse
: This mannequin defines the construction of the response from the agent that can create the problems/tickets.
# taskpilot/utils/fashions.py
from typing import Non-compulsory
from pydantic import BaseModel
class ActionItem(BaseModel):
title: str
description: str
assignee: str
standing: str
issuetype: str
venture: Non-compulsory[str] = None
due_date: Non-compulsory[str] = None
start_date: Non-compulsory[str] = None
precedence: Non-compulsory[str] = None
guardian: Non-compulsory[str] = None
youngsters: Non-compulsory[list[str]] = None
class ActionItemsList(BaseModel):
action_items: checklist[ActionItem]
class CreateIssuesResponse(BaseModel):
action_items: checklist[ActionItem]
error_messages: checklist[str]
success_messages: checklist[str]
textual content: str
Step 4: Creating the Brokers
The brokers are the core of our utility. Brokers are mainly an LLM configured with directions (the AGENT_PROMPT
) and entry to instruments for them to behave by itself on outlined duties. An agent from the OpenAI Brokers SDK is outlined by the next parameters:
identify
: The identify of the agent for identification.directions
: The immediate to inform the agent its position or job it shall execute (aka. system immediate).mannequin
: Which LLM to make use of for the agent. The SDK supplies out-of-the-box help for OpenAI fashions, nevertheless you can even use non-OpenAI fashions (see Agents SDK: Models).output_type
: Python object that the agent shall returned, as talked about beforehand.instruments
: A listing of python callables, that would be the instruments that the agent can use to carry out its duties.
Based mostly on this info, let’s create our two brokers: the ActionItemsExtractor
and the TicketsCreator
.
Motion Objects Extractor
This agent’s job is to learn the assembly transcript and extract the motion objects. We’ll create it in taskpilot/local_agents/action_items_extractor.py
.
# taskpilot/local_agents/action_items_extractor.py
from brokers import Agent
from utils.config_parser import Config
from utils.fashions import ActionItemsList
AGENT_PROMPT = """
Your are an assistant to extract motion objects from a gathering transcript.
You can be given a gathering transcript and you should extract the motion objects in order that they are often transformed into tickets by one other assistant.
The motion objects ought to comprise the next info:
- title: The title of the motion merchandise. It must be a brief description of the motion merchandise. It must be brief and concise. That is necessary.
- description: The outline of the motion merchandise. It must be a extra prolonged description of the motion merchandise. That is necessary.
- assignee: The identify of the one who will probably be answerable for the motion merchandise. You shall infer from the dialog the identify of the assignee and never use "Speaker 1" or "Speaker 2" or another speaker identifier. That is necessary.
- standing: The standing of the motion merchandise. It may be "To Do", "In Progress", "In Evaluation" or "Accomplished". You shall extract from the transcript through which state the motion merchandise is. If it's a new motion merchandise, you shall set it to "To Do".
- due_date: The due date of the motion merchandise. It shall be within the format "YYYY-MM-DD". You shall extract this from the transcript, nevertheless if it's not explicitly talked about, you shall set it to None. If relative dates are talked about (eg. by tomorrow, in every week,...), you shall convert them to absolute dates within the format "YYYY-MM-DD".
- start_date: The beginning date of the motion merchandise. It shall be within the format "YYYY-MM-DD". You shall extract this from the transcript, nevertheless if it's not explicitly talked about, you shall set it to None.
- precedence: The precedence of the motion merchandise. It may be "Lowest", "Low", "Medium", "Excessive" or "Highest". You shall interpret the precedence of the motion merchandise from the transcript, nevertheless if it's not clear, you shall set it to None.
- issuetype: The kind of the motion merchandise. It may be "Epic", "Bug", "Job", "Story", "Subtask". You shall interpret the issuetype of the motion merchandise from the transcript, whether it is unclear set it to "Job".
- venture: The venture to which the motion merchandise belongs. You shall interpret the venture of the motion merchandise from the transcript, nevertheless if it's not clear, you shall set it to None.
- guardian: If the motion merchandise is a subtask, you shall set the guardian of the motion merchandise to the title of the guardian motion merchandise. If the guardian motion merchandise shouldn't be clear or the motion merchandise shouldn't be a subtask, you shall set it to None.
- youngsters: If the motion merchandise is a guardian job, you shall set the youngsters of the motion merchandise to the titles of the kid motion objects. If the youngsters motion objects will not be clear or the motion merchandise shouldn't be a guardian job, you shall set it to None.
"""
def create_action_items_agent() -> Agent:
return Agent(
identify="Motion Objects Extractor",
directions=AGENT_PROMPT,
output_type=ActionItemsList,
mannequin=Config.get().brokers.mannequin,
)
As you’ll be able to see, within the AGENT_PROMPT
we inform the agent very detailed that its job is to extract motion objects and supply an in depth description of how we wish the motion objects to be extracted.
Tickets Creator
This agent takes the checklist of motion objects and creates Jira points. We’ll create it in taskpilot/local_agents/tickets_creator.py
.
# taskpilot/local_agents/tickets_creator.py
from brokers import Agent
from utils.config_parser import Config
from utils.agents_tools import create_jira_issue
from utils.fashions import CreateIssuesResponse
AGENT_PROMPT = """
You might be an assistant that creates Jira points given motion objects.
You can be given an inventory of motion objects and for every motion merchandise you shall create a Jira difficulty utilizing the `create_jira_issue` software.
You shall accumulate the responses of the `create_jira_issue` software and return them because the supplied sort `CreateIssuesResponse` which incorporates:
- action_items: checklist containing the action_items that have been supplied to you
- error_messages: checklist containing the error messages returned by the `create_jira_issue` software every time there was an error attempting to create the problem.
- success_messages: checklist containing the response messages returned by the `create_jira_issue` software every time the problem creation was profitable.
- textual content: A textual content that summarizes the results of the tickets creation. It shall be a string created as following:
f"From the {len(action_items)} motion objects supplied {len(success_messages)} have been efficiently created within the Jira venture.n {len(error_messages)} didn't be created within the Jira venture.nnError messages:n{error_messages}"
"""
def create_tickets_creator_agent() -> Agent:
return Agent(
identify="Tickets Creator",
directions=AGENT_PROMPT,
instruments=[create_jira_issue],
mannequin=Config.get().brokers.mannequin,
output_type=CreateIssuesResponse
)
Right here we set the instruments
parameter and provides the agent the create_jira_issue
software, which we’ll create within the subsequent step.
Step 5: Offering Instruments
One of the vital highly effective options of brokers is their skill to make use of instruments to work together with the skin world. One may argue that the usage of instruments is what turns the interplay with an LLM into an agent. The OpenAI Brokers SDK permits the brokers to make use of three forms of instruments:
- Hosted instruments: Offered straight from OpenAI corresponding to looking the net or recordsdata, laptop use, working code, amongst others.
- Operate calling: Utilizing any Python operate as a software.
- Brokers as instruments: Permitting brokers to name different brokers with out handing off.
For our usecase, we will probably be utilizing operate calling and implement a operate to create the Jira points utilizing Jira’s REST API. By private alternative, I made a decision to separate it in two recordsdata:
- In
taskpilot/utils/jira_interface_functions.py
we are going to write the features to work together by way of HTTP Requests with the Jira REST API. - In
taskpilot/utils/agents_tools.py
we are going to write wrappers of the features to be supplied to the brokers. These wrapper-functions have extra response parsing to supply the agent a processed textual content response as an alternative of a JSON. However, the agent must also be capable of deal with and perceive JSON as response.
First we implement the create_issue()
operate in taskpilot/utils/jira_interface_functions.py
:
# taskpilot/utils/jira_interface_functions.py
import os
from typing import Non-compulsory
import json
from urllib.parse import urljoin
import requests
from requests.auth import HTTPBasicAuth
from utils.config_parser import Config
JIRA_AUTH = HTTPBasicAuth(Config.get().jira.person, str(os.getenv("ATLASSIAN_API_KEY")))
def create_issue(
project_key: str,
title: str,
description: str,
issuetype: str,
duedate: Non-compulsory[str] = None,
assignee_id: Non-compulsory[str] = None,
labels: Non-compulsory[list[str]] = None,
priority_id: Non-compulsory[str] = None,
reporter_id: Non-compulsory[str] = None,
) -> requests.Response:
payload = {
"fields": {
"venture": {"key": project_key},
"abstract": title,
"issuetype": {"identify": issuetype},
"description": {
"content material": [
{
"content": [
{
"text": description,
"type": "text",
}
],
"sort": "paragraph",
}
],
"sort": "doc",
"model": 1,
},
}
}
if duedate:
payload["fields"].replace({"duedate": duedate})
if assignee_id:
payload["fields"].replace({"assignee": {"id": assignee_id}})
if labels:
payload["fields"].replace({"labels": labels})
if priority_id:
payload["fields"].replace({"precedence": {"id": priority_id}})
if reporter_id:
payload["fields"].replace({"reporter": {"id": reporter_id}})
endpoint_url = urljoin(Config.get().jira.url_rest_api, "difficulty")
headers = {"Settle for": "utility/json", "Content material-Sort": "utility/json"}
response = requests.put up(
endpoint_url,
knowledge=json.dumps(payload),
headers=headers,
auth=JIRA_AUTH,
timeout=Config.get().jira.request_timeout,
)
return response
As you’ll be able to see, we have to authenticate to our Jira account utilizing our Jira person and a corresponding API_KEY
that we are able to get hold of on Atlassian Account Management.
In taskpilot/utils/agents_tools.py
we implement the create_jira_issue()
operate, that we’ll then present to the TicketsCreator
agent:
# taskpilot/utils/agents_tools.py
from brokers import function_tool
from utils.fashions import ActionItem
from utils.jira_interface_functions import create_issue
@function_tool
def create_jira_issue(action_item: ActionItem) -> str:
response = create_issue(
project_key=action_item.venture,
title=action_item.title,
description=action_item.description,
issuetype=action_item.issuetype,
duedate=action_item.due_date,
assignee_id=None,
labels=None,
priority_id=None,
reporter_id=None,
)
if response.okay:
return f"Efficiently created the problem. Response message: {response.textual content}"
else:
return f"There was an error attempting to create the problem. Error message: {response.textual content}"
Essential: The @function_tool
decorator is what makes this operate usable for our agent. The agent can now name this operate and cross it an ActionItem
object. The operate then makes use of the create_issue
operate which accesses the Jira API to create a brand new difficulty.
Step 6: Configuring the Utility
To make our utility parametrizable, we’ll use a config.yml
file for the configuration settings, in addition to a .env
file for the API keys.
The configuration of the applying is separated in:
brokers
: To configure the brokers and the entry to the OpenAI API. Right here we’ve two parameters:mannequin
, which is the LLM that shall be utilized by the brokers, andOPENAI_API_KEY
, within the.env
file, to authenticate the usage of the OpenAI API. You’ll be able to get hold of an OpenAI API Key in your OpenAI Dev Platform.jira
: To configure the entry to the Jira API. Right here we’d like 4 parameters:url_rest_api
, which is the URL to the REST API of our Jira occasion;person
, which is the person we use to entry Jira;request_timeout
, which is the timeout in seconds to attend for the server to ship knowledge earlier than giving up, and at lastATLASSIAN_API_KEY
, within the.env
file, to authenticate to your Jira occasion.
Right here is our .env
file, that within the subsequent step will probably be loaded to our utility within the essential.py
utilizing the python-dotenv
library:
OPENAI_API_KEY=some-api-key
ATLASSIAN_API_KEY=some-api-key
And right here is our config.yml
file:
# config.yml
brokers:
mannequin: "o4-mini"
jira:
url_rest_api: "https://your-domain.atlassian.web/relaxation/api/3/"
person: "[email protected]"
request_timeout: 5
We’ll additionally create a config parser at taskpilot/utils/config_parser.py
to load this configuration. For this we implement the Config
class as a singleton (that means there can solely be one occasion of this class all through the applying lifespan).
# taskpilot/utils/config_parser.py
from pathlib import Path
import yaml
from pydantic import BaseModel
class AgentsConfig(BaseModel):
mannequin: str
class JiraConfig(BaseModel):
url_rest_api: str
person: str
request_timeout: int
class ConfigModel(BaseModel):
brokers: AgentsConfig
jira: JiraConfig
class Config:
_instance: ConfigModel | None = None
@classmethod
def load(cls, path: str = "config.yml") -> None:
if cls._instance is None:
with open(Path(path), "r", encoding="utf-8") as config_file:
raw_config = yaml.safe_load(config_file)
cls._instance = ConfigModel(**raw_config)
@classmethod
def get(cls, path: str = "config.yml") -> ConfigModel:
if cls._instance is None:
cls.load(path)
return cls._instance
Step 7: Bringing It All Collectively in essential.py
Lastly, in taskpilot/essential.py
, we’ll deliver the whole lot collectively. This script will load the assembly transcript, create an occasion of the TaskPilotRunner
, after which name the run()
technique.
# taskpilot/essential.py
import os
import asyncio
from dotenv import load_dotenv
from taskpilot_runner import TaskPilotRunner
# Load the variables within the .env file
load_dotenv()
def load_meeting_transcript_txt(file_path: str) -> str:
# ...
return meeting_transcript
async def essential():
print("TaskPilot utility beginning...")
meeting_transcript = load_meeting_transcript_txt("meeting_transcript.txt")
await TaskPilotRunner().run(meeting_transcript)
if __name__ == "__main__":
asyncio.run(essential())
Step 8: Monitoring Our Runs within the OpenAI Dev Platform
As talked about, one of many benefits of the OpenAI Brokers SDK is that, because of its tracing function, it’s potential to visualise your complete execution circulate of our brokers. This makes it simple to debug and perceive what’s occurring underneath the hood within the OpenAI Dev Platform.
Within the Traces Dashboard one can:
- Monitor every run of the brokers workflow.

- Perceive precisely what the brokers did inside the agent workflow and monitor efficiency.

- Debug each name to the OpenAI API in addition to monitor what number of tokens have been utilized in every enter and output.

So make the most of this function to judge, debug and monitor your agent runs.
Conclusion
And that’s it! On this eight easy steps we’ve carried out an utility that may robotically create Jira points from a gathering transcript. Due to the straightforward interface of the OpenAI Brokers SDK you’ll be able to simply create brokers programmatically that can assist you automatize your duties!
Be at liberty to clone the repository (the venture as described on this put up is in department function_calling
), strive it out for your self, and begin constructing your individual AI-powered functions!
💡 Coming Up Subsequent:
In an upcoming put up, we’ll dive into find out how to implement your individual MCP Server to additional lengthen our brokers’ capabilities and permit them to work together with exterior programs past your native instruments. Keep tuned!
🙋♂️ Let’s Join
You probably have questions, suggestions, or simply need to comply with together with future tasks:
Reference
This text is impressed by the “OpenAI: Agents SDK” course from LinkedinLearning.