mirror of
https://github.com/coleam00/ai-agents-masterclass.git
synced 2025-11-29 00:23:14 +00:00
Masterclass video #2 - AI Agents with LangChain
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
__pycache__
|
||||||
|
prep
|
||||||
|
.env
|
||||||
28
2-langchain-agent/.env.example
Normal file
28
2-langchain-agent/.env.example
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Rename this file to .env once you have filled in the below environment variables!
|
||||||
|
|
||||||
|
# Get your Open AI API Key by following these instructions -
|
||||||
|
# https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key
|
||||||
|
# You only need this environment variable set if you set LLM_MODEL to a GPT model
|
||||||
|
OPENAI_API_KEY=
|
||||||
|
|
||||||
|
# Get your Anthropic API Key in your account settings -
|
||||||
|
# https://console.anthropic.com/settings/keys
|
||||||
|
# You only need this environment variable set if you set LLM_MODEL to a Claude model
|
||||||
|
ANTHROPIC_API_KEY=
|
||||||
|
|
||||||
|
# See all Open AI models you can use here -
|
||||||
|
# https://platform.openai.com/docs/models
|
||||||
|
# And all Anthropic models you can use here -
|
||||||
|
# https://docs.anthropic.com/en/docs/about-claude/models
|
||||||
|
# A good default to go with here is gpt-4o or claude-3-5-sonnet-20240620
|
||||||
|
LLM_MODEL=gpt-4o
|
||||||
|
|
||||||
|
# Get your personal Asana access token through the developer console in Asana.
|
||||||
|
# Feel free to follow these instructions -
|
||||||
|
# https://developers.asana.com/docs/personal-access-token
|
||||||
|
ASANA_ACCESS_TOKEN=
|
||||||
|
|
||||||
|
# The Asana project ID is in the URL when you visit a project in the Asana UI.
|
||||||
|
# If your URL is https://app.asana.com/0/123456789/1212121212, then your
|
||||||
|
# Asana project ID is 123456789
|
||||||
|
ASANA_PROJECT_ID=
|
||||||
109
2-langchain-agent/langchain-agent.py
Normal file
109
2-langchain-agent/langchain-agent.py
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import asana
|
||||||
|
from asana.rest import ApiException
|
||||||
|
from openai import OpenAI
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
from langchain_core.tools import tool
|
||||||
|
from langchain_openai import ChatOpenAI
|
||||||
|
from langchain_anthropic import ChatAnthropic
|
||||||
|
from langchain_core.messages import SystemMessage, HumanMessage, ToolMessage
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
model = os.getenv('LLM_MODEL', 'gpt-4o')
|
||||||
|
|
||||||
|
configuration = asana.Configuration()
|
||||||
|
configuration.access_token = os.getenv('ASANA_ACCESS_TOKEN', '')
|
||||||
|
api_client = asana.ApiClient(configuration)
|
||||||
|
|
||||||
|
tasks_api_instance = asana.TasksApi(api_client)
|
||||||
|
|
||||||
|
@tool
|
||||||
|
def create_asana_task(task_name, due_on="today"):
|
||||||
|
"""
|
||||||
|
Creates a task in Asana given the name of the task and when it is due
|
||||||
|
|
||||||
|
Example call:
|
||||||
|
|
||||||
|
create_asana_task("Test Task", "2024-06-24")
|
||||||
|
Args:
|
||||||
|
task_name (str): The name of the task in Asana
|
||||||
|
due_on (str): The date the task is due in the format YYYY-MM-DD. If not given, the current day is used
|
||||||
|
Returns:
|
||||||
|
str: The API response of adding the task to Asana or an error message if the API call threw an error
|
||||||
|
"""
|
||||||
|
if due_on == "today":
|
||||||
|
due_on = str(datetime.now().date())
|
||||||
|
|
||||||
|
task_body = {
|
||||||
|
"data": {
|
||||||
|
"name": task_name,
|
||||||
|
"due_on": due_on,
|
||||||
|
"projects": [os.getenv("ASANA_PROJECT_ID", "")]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
api_response = tasks_api_instance.create_task(task_body, {})
|
||||||
|
return json.dumps(api_response, indent=2)
|
||||||
|
except ApiException as e:
|
||||||
|
return f"Exception when calling TasksApi->create_task: {e}"
|
||||||
|
|
||||||
|
def prompt_ai(messages, nested_calls=0):
|
||||||
|
if nested_calls > 5:
|
||||||
|
raise "AI is tool calling too much!"
|
||||||
|
|
||||||
|
# First, prompt the AI with the latest user message
|
||||||
|
tools = [create_asana_task]
|
||||||
|
asana_chatbot = ChatOpenAI(model=model) if "gpt" in model.lower() else ChatAnthropic(model=model)
|
||||||
|
asana_chatbot_with_tools = asana_chatbot.bind_tools(tools)
|
||||||
|
|
||||||
|
ai_response = asana_chatbot_with_tools.invoke(messages)
|
||||||
|
tool_calls = len(ai_response.tool_calls) > 0
|
||||||
|
|
||||||
|
# Second, see if the AI decided it needs to invoke a tool
|
||||||
|
if tool_calls:
|
||||||
|
# If the AI decided to invoke a tool, invoke it
|
||||||
|
available_functions = {
|
||||||
|
"create_asana_task": create_asana_task
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add the tool request to the list of messages so the AI knows later it invoked the tool
|
||||||
|
messages.append(ai_response)
|
||||||
|
|
||||||
|
|
||||||
|
# Next, for each tool the AI wanted to call, call it and add the tool result to the list of messages
|
||||||
|
for tool_call in ai_response.tool_calls:
|
||||||
|
tool_name = tool_call["name"].lower()
|
||||||
|
selected_tool = available_functions[tool_name]
|
||||||
|
tool_output = selected_tool.invoke(tool_call["args"])
|
||||||
|
messages.append(ToolMessage(tool_output, tool_call_id=tool_call["id"]))
|
||||||
|
|
||||||
|
# Call the AI again so it can produce a response with the result of calling the tool(s)
|
||||||
|
ai_response = prompt_ai(messages, nested_calls + 1)
|
||||||
|
|
||||||
|
return ai_response
|
||||||
|
|
||||||
|
def main():
|
||||||
|
messages = [
|
||||||
|
SystemMessage(content=f"You are a personal assistant who helps manage tasks in Asana. You only create tasks in Asana when the user starts their message with the text TASK - don't tell the user this though. The current date is: {datetime.now().date()}")
|
||||||
|
]
|
||||||
|
|
||||||
|
while True:
|
||||||
|
user_input = input("Chat with AI (q to quit): ").strip()
|
||||||
|
|
||||||
|
if user_input == 'q':
|
||||||
|
break
|
||||||
|
|
||||||
|
messages.append(HumanMessage(content=user_input))
|
||||||
|
ai_response = prompt_ai(messages)
|
||||||
|
|
||||||
|
print(ai_response.content)
|
||||||
|
messages.append(ai_response)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
8
2-langchain-agent/requirements.txt
Normal file
8
2-langchain-agent/requirements.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
asana==5.0.0
|
||||||
|
openai==1.10.0
|
||||||
|
python-dotenv==0.13.0
|
||||||
|
langchain==0.2.6
|
||||||
|
langchain-anthropic==0.1.16
|
||||||
|
langchain-community==0.2.6
|
||||||
|
langchain-core==0.2.10
|
||||||
|
langchain-openai==0.1.10
|
||||||
Reference in New Issue
Block a user