Published on 00/00/0000
Last updated on 00/00/0000
Published on 00/00/0000
Last updated on 00/00/0000
Share
Share
INSIGHTS
11 min read
Share
With the power of AI (artificial intelligence) and agent-based systems, software developers can fully automate everyday DevOps tasks, such as interacting with CI/CD systems, creating infra resources, or executing more complex workflows.
This blog will demonstrate how to create a simple ReAct agent application that performs tasks such as retrieving GitHub repo descriptions or topics through standard REST APIs. The app uses LangGraph to define the agentic workflow. We'll also showcase how the ReAct (Reasoning and Acting) framework allows the agent to decide when and how to use tools effectively.
By following the ReAct framework shown below, we ensure that the AI agent reasons step-by-step through tasks, providing deterministic execution and transparency in agent actions.
The workflow loops as the agent reasons whether to call the tools and executes them if needed. The results are fed back to the agent for further decision-making.
The process works like this:
Let's walk through the components depicted in the diagram.
In summary, the flow begins with a user application making an HTTP request to the FastAPI server, which triggers the agent to evaluate it. The workflow then enters a decision phase within LangGraph. Depending on the task, the agent may use tools like calling the GitHub API for information. The process continues until the agent concludes the goal is met, after which it returns the output to the client.
Here is the code implementation for building a simple ReAct AI agent to retrieve information from GitHub repositories using the GitHub API. The application uses FastAPI to interact with external clients, LangGraph to manage workflows, and an LLM to reason and make decisions.
Note: Complete source code, Juypter notebook, and instructions on how to run the following code are available at this GitHub repo
Code Disclaimer: The code provided in this blog is for educational and informational purposes only. While every effort is made to ensure the code is functional and accurate, it is provided "as-is" without any guarantees. Always review and test code in a safe environment before using it in production.
python -m venv venv
source venv/bin/activate
pip install fastapi[standard] langgraph langchain_openai langchain_core langserve[server] requests
In this section, we create the tools the agents will invoke based on their reasoning.
Define a function call to get the GitHub repo description based on the repo name and GitHub org name.
from langchain_core.tools import tool
import requests
# Define tools
@tool
def get_github_repo_description(repo_name: str, org_name: str) -> str:
"""
Fetches the description of a GitHub repository.
Args:
repo_name (str): The name of the repository.
org_name (str): The name of the organization or user that owns the repository.
Returns:
str: The description of the repository if available, otherwise a message indicating
that no description is available or an error message if the request fails.
Raises:
requests.exceptions.RequestException: If there is an issue with the HTTP request.
"""
url = f"https://api.github.com/repos/{org_name}/{repo_name}"
headers = {
"Authorization": f"token {os.getenv('GITHUB_TOKEN')}"
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
repo_info = response.json()
return repo_info['description'] if repo_info['description'] else f"No description available for {repo_name} in {org_name}"
else:
return f"Failed to fetch description for {repo_name} in {org_name}. Status code: {response.status_code}"
Define a function call to get GitHub repo topics based on the repo name and GitHub org name.
@tool
def get_github_repo_topics(repo_name: str, org_name: str) -> str:
"""
Fetches the topics of a GitHub repository.
Args:
repo_name (str): The name of the repository.
org_name (str): The name of the organization or user that owns the repository.
Returns:
str: A comma-separated string of topics if available, otherwise a message indicating
that no topics are available or an error message if the request fails.
Raises:
requests.exceptions.RequestException: If there is an issue with the HTTP request.
"""
url = f"https://api.github.com/repos/{org_name}/{repo_name}/topics"
headers = {
"Authorization": f"token {os.getenv('GITHUB_TOKEN')}",
"Accept": "application/vnd.github.mercy-preview+json"
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
repo_info = response.json()
topics = repo_info.get('names', [])
return ", ".join(topics) if topics else f"No topics available for {repo_name} in {org_name}"
else:
return f"Failed to fetch topics for {repo_name} in {org_name}. Status code: {response.status_code}"
This section defines the SimpleAIAgent class, which powers our agent's reasoning capabilities. The user can choose to use OpenAI or Azure OpenAI based on the environment variable (default is openai)
from langchain_openai import ChatOpenAI, AzureChatOpenAI
from langgraph.graph import MessagesState
from langchain_core.messages import HumanMessage, SystemMessage
from langgraph.graph import START, StateGraph
from langgraph.prebuilt import tools_condition, ToolNode
from langgraph.checkpoint.memory import MemorySaver
import datetime
import json
class SimpleAIAgent():
'''
Simple AI Agent is a class that initializes and configures an AI assistant using LLMs.
'''
def __init__(self, thread_id: str):
# Specify a thread
self.config = {"configurable": {"thread_id": thread_id}}
# Get the desired model name
model_name = os.getenv("LLM_MODEL_NAME", "openai")
if model_name == "azure_openai":
deployment = os.getenv("AZURE_OPENAI_DEPLOYMENT")
api_version = os.getenv("AZURE_OPENAI_API_VERSION")
llm = AzureChatOpenAI(
azure_deployment=deployment,
api_version=api_version,
temperature=0,
max_tokens=None,
timeout=None,
max_retries=2
)
elif model_name == "openai":
llm = ChatOpenAI(
model=os.getenv("OPENAI_MODEL"),
temperature=0,
max_tokens=None,
timeout=None,
max_retries=2
)
tools = [
get_github_repo_description,
get_github_repo_topics
]
llm_with_tools = llm.bind_tools(tools)
# System message
sys_msg = SystemMessage(content=(
"You are a helpful Assistant tasked with performing tasks.\n"
"You can assist with github repo operations \n"
), pretty_repr=True)
# Node
def assistant(state: MessagesState):
return {"messages": [llm_with_tools.invoke([sys_msg] + state["messages"])]}
# Graph
self.builder = StateGraph(MessagesState)
# Define nodes: They do the actual work
self.builder.add_node("simple_ai_agent", assistant)
self.builder.add_node("tools", ToolNode(tools))
# Define edges: these determine how the control flow moves
self.builder.add_edge(START, "simple_ai_agent")
# Add conditional edges
self.builder.add_conditional_edges(
"simple_ai_agent",
tools_condition,
"tools"
)
# Add an edge to the tool node
self.builder.add_edge("tools", "simple_ai_agent")
checkpointer = MemorySaver()
self.react_graph_memory = self.builder.compile(checkpointer=checkpointer)
self.react_graph_memory = self.builder.compile(checkpointer=checkpointer)
def interact(self, human_message:str):
try:
# Specify an input
messages = [HumanMessage(content=human_message)]
# return messages
messages = self.react_graph_memory.invoke({"messages": messages}, self.config)
# print messages
for message in messages['messages']:
print("-" * 80)
print(f"Type: {type(message).__name__}, Content: {message.content}")
if message.additional_kwargs and 'tool_calls' in message.additional_kwargs:
print(f"Tool Call ID: {message.additional_kwargs['tool_calls'][0]['id']}, Name: {message.additional_kwargs['tool_calls'][0]['function']['name']}, Arguments: {message.additional_kwargs['tool_calls'][0]['function']['arguments']}")
print("-" * 80)
return messages['messages'][-1].content
except Exception as e:
print(e)
Create the ChatBotQuestion BaseModel and define get_uuid utility function.
from pydantic import BaseModel
import json
import uuid
class ChatBotQuestion(BaseModel):
question: str
def get_uuid() -> str:
unique_id = str(uuid.uuid4())
print(f"Generated UUID: {unique_id}")
return unique_id
Define FastAPI app
from fastapi import FastAPI
# Initialize the SimpleAIAgent
agent = SimpleAIAgent(get_uuid())
app = FastAPI()
@app.post("/question")
def ask_question(question: ChatBotQuestion):
"""
Interacts with a chatbot agent to ask a question and retrieve the response.
Args:
question (ChatBotQuestion): An object containing the question text and chat session ID.
Returns:
str: The content of the second message in the response from the chatbot agent.
Note:
The function returns the content of the message at index 1 because the response
is expected to be a list of messages, where the first message (index 0) is a HumanMessages,
and the second message is an AIMessage.
"""
response = agent.interact(question.question)
return str(response)
Start Fast API App
async def main():
config = uvicorn.Config(app, host="localhost", port=8000)
server = uvicorn.Server(config)
await server.serve()
if __name__ == '__main__':
asyncio.run(main())
Client request code in Python:
import requests
import json
url = "http://localhost:8000/question"
payload = json.dumps({
"question": "get repo description for simple-ai-agent repo in sriaradhyula org"
})
headers = {
'Content-Type': 'application/json'
}
response = requests.post(url, headers=headers, data=payload)
print(response.text)
Sample Output:
The description of the repository "simple-ai-agent" in the "sriaradhyula" organization is:
"This repo demonstrates building a simple ReAct AI agent to perform tasks such as retrieving GitHub repo details via REST APIs, using LangGraph to define workflows."
Chain of thought explanation: Annotated with HumanMessage, AIMessage, ToolMessage
--------------------------------------------------------------------------------
Type: HumanMessage, Content: get repo description for simple-ai-agent repo in sriaradhyula org
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Type: AIMessage, Content:
Tool Call ID: call_HBjY0hWZNR5r0ZFcvlmgN4D7, Name: get_github_repo_description, Arguments: {"repo_name":"simple-ai-agent","org_name":"sriaradhyula"}
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Type: ToolMessage, Content: This repo demonstrates building a simple ReAct AI agent to perform tasks such as retrieving GitHub repo details via REST APIs, using LangGraph to define workflows.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Type: AIMessage, Content: The description for the "simple-ai-agent" repository in the "sriaradhyula" organization is:
"This repo demonstrates building a simple ReAct AI agent to perform tasks such as retrieving GitHub repo details via REST APIs, using LangGraph to define workflows."
--------------------------------------------------------------------------------
Client request code in Python:
import requests
import json
url = "http://localhost:8000/question"
payload = json.dumps({
"question": "check if the repo description has any matching topics in the repo topics in repo simple-ai-agent in sriaradhyula org"
})
headers = {
'Content-Type': 'application/json'
}
response = requests.post(url, headers=headers, data=payload)
print(response.text)
Sample Output:
The description of the repository is: “This repo demonstrates building a simple ReAct AI agent to perform tasks such as retrieving GitHub repo details via REST APIs, using LangGraph to define workflows.”
The topics of the repository are: “agent, ai, langgraph, python”
Matching topics in the description and topics list are: "agent", "ai", and "langgraph".
Chain of thought explanation: Annotated with HumanMessage, AIMessage, ToolMessage
--------------------------------------------------------------------------------
Type: HumanMessage, Content: check if the repo description has any matching topics in the repo topics in repo simple-ai-agent in sriaradhyula org
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Type: AIMessage, Content:
Tool Call ID: call_K4aY0WTQxRVJjtQBsnUsjN0A, Name: get_github_repo_description, Arguments: {"repo_name": "simple-ai-agent", "org_name": "sriaradhyula"}
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Type: ToolMessage, Content: This repo demonstrates building a simple ReAct AI agent to perform tasks such as retrieving GitHub repo details via REST APIs, using LangGraph to define workflows.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Type: ToolMessage, Content: agent, ai, langgraph, python
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Type: AIMessage, Content: The description of the repository "simple-ai-agent" is:
"This repo demonstrates building a simple ReAct AI agent to perform tasks such as retrieving GitHub repo details via REST APIs, using LangGraph to define workflows."
The topics of the repository are:
"agent, ai, langgraph, python"
Matching topics in the description and topics list are:
- "ai"
- "agent"
- "langgraph"
--------------------------------------------------------------------------------
We demonstrated how to build a simple ReAct agent using LangGraph and FastAPI. The agent can perform tasks like fetching GitHub repository descriptions or topics through REST APIs. Using the ReAct framework, the agent evaluates each step to determine when to invoke tools, ensuring its actions are both predictable and transparent. This modular and explainable design can be easily extended to automate various DevOps tasks, giving you a powerful tool for simplifying everyday workflows.
At Outshift by Cisco, we’re building an open, secure, and interoperable ecosystem for autonomous AI. Stay up to date on the latest developments.
Get emerging insights on innovative technology straight to your inbox.
Discover how AI assistants can revolutionize your business, from automating routine tasks and improving employee productivity to delivering personalized customer experiences and bridging the AI skills gap.
The Shift is Outshift’s exclusive newsletter.
The latest news and updates on generative AI, quantum computing, and other groundbreaking innovations shaping the future of technology.