How I Set Up an AI Agent That Reads My GitHub Issues
Learn to build a GitHub issue triage agent with Python and OpenAI, using a minimal agent loop and tool schema.
Before you start
You have a GitHub repository and issues pile up faster than you can triage them. You want an AI agent that reads new issues, classifies them, suggests labels, and drafts a response. This tutorial walks through building a minimal but functional agent that does exactly that.
The agent uses the OpenAI API with function calling to fetch issue details, classify them, and post a comment. You will need a GitHub personal access token and an OpenAI API key. The code is Python and runs locally or in a GitHub Action.
- A GitHub repository with issues enabled
- A GitHub personal access token with repo scope
- An OpenAI API key with access to gpt-4o-mini
- Python 3.10 or newer installed on your machine
Step 1: Set up the project structure
Create a project directory and a virtual environment. Then install the required packages: requests for GitHub API, openai for the agent, and python-dotenv for environment variables.
The project will have three files: agent.py for the main logic, tools.py for GitHub actions, and .env for secrets.
mkdir github-issue-agent
cd github-issue-agent
python -m venv venv
source venv/bin/activate
pip install requests openai python-dotenvStep 2: Define the GitHub tools
The agent needs tools to fetch issue details and to comment on issues. Each tool is a Python function that calls the GitHub REST API. The function signatures and docstrings define the schema that the model will see.
We use the requests library for simplicity. The BASE_URL is the GitHub API endpoint. The headers include the token for authentication.
# tools.py
import os
import requests
BASE_URL = "https://api.github.com"
HEADERS = {
"Authorization": f"token {os.getenv('GITHUB_TOKEN')}",
"Accept": "application/vnd.github.v3+json"
}
def get_issue(owner: str, repo: str, issue_number: int) -> dict:
"""Fetch details of a specific issue."""
url = f"{BASE_URL}/repos/{owner}/{repo}/issues/{issue_number}"
response = requests.get(url, headers=HEADERS)
response.raise_for_status()
return response.json()
def add_comment(owner: str, repo: str, issue_number: int, body: str) -> dict:
"""Add a comment to an issue."""
url = f"{BASE_URL}/repos/{owner}/{repo}/issues/{issue_number}/comments"
response = requests.post(url, headers=HEADERS, json={"body": body})
response.raise_for_status()
return response.json()- Get the token from environment variables, never hardcode it.
- Use the same headers for all requests.
- The function docstrings are used by the model to understand the tool.
Step 3: Build the agent loop
The agent loop is a while loop that sends the conversation to the model, checks if the model wants to call a tool, executes the tool, and feeds the result back. This continues until the model produces a final answer.
We define the tools schema in a list of dictionaries. Each tool has a type, function name, description, and parameters. The parameters are a JSON schema that the model uses to generate arguments.
# agent.py
import os
import json
from openai import OpenAI
from tools import get_issue, add_comment
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
TOOLS = [
{
"type": "function",
"function": {
"name": "get_issue",
"description": "Fetch details of a GitHub issue",
"parameters": {
"type": "object",
"properties": {
"owner": {"type": "string"},
"repo": {"type": "string"},
"issue_number": {"type": "integer"}
},
"required": ["owner", "repo", "issue_number"]
}
}
},
{
"type": "function",
"function": {
"name": "add_comment",
"description": "Add a comment to a GitHub issue",
"parameters": {
"type": "object",
"properties": {
"owner": {"type": "string"},
"repo": {"type": "string"},
"issue_number": {"type": "integer"},
"body": {"type": "string"}
},
"required": ["owner", "repo", "issue_number", "body"]
}
}
}
]
def run_agent(user_input: str) -> str:
messages = [{"role": "user", "content": user_input}]
while True:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=TOOLS,
tool_choice="auto"
)
message = response.choices[0].message
messages.append(message)
if message.tool_calls:
for tool_call in message.tool_calls:
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
if name == "get_issue":
result = get_issue(**args)
elif name == "add_comment":
result = add_comment(**args)
else:
result = {"error": "Unknown tool"}
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result)
})
else:
return message.content- The model decides which tool to call based on the user request.
- Tool results are appended as a tool role message.
- The loop exits when the model returns a plain text answer.
Step 4: Run the agent on a sample issue
Now test the agent with a sample issue. Set the environment variables, then run a script that calls the agent with a prompt to triage a specific issue.
The agent will fetch the issue, classify it, and post a comment. The comment includes a label suggestion and a draft response.
export GITHUB_TOKEN="your_github_token"
export OPENAI_API_KEY="your_openai_key"
python -c "from agent import run_agent; print(run_agent('Triage issue #1 in owner/repo. Suggest a label and draft a response.'))"- Replace owner/repo with your actual repository.
- The issue number must exist.
- Check the comment appears on the issue.
Verify it worked
After running, check the GitHub issue page. You should see a comment from your account with a suggested label and a draft response. The agent may also suggest a label like bug or enhancement.
If you do not see the comment, check the terminal output for errors. Common issues include missing permissions on the token or incorrect repository name.
# Check the last comment on the issue
curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/repos/owner/repo/issues/1/comments | jq '.[-1].body'Step 5: Automate with a GitHub Action
To run the agent automatically on new issues, create a GitHub Action workflow. The workflow triggers on issues and runs a Python script that processes the issue.
The workflow uses a Docker container with Python and installs dependencies. It passes the issue number and repository as inputs to the script.
# .github/workflows/triage.yml
name: Triage Issue
on:
issues:
types: [opened]
jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- run: pip install -r requirements.txt
- name: Run agent
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
python -c "from agent import run_agent; run_agent(f'Triage issue #{github.event.issue.number} in {github.repository}. Suggest a label and draft a response.')"- Add the workflow file to your repository.
- Set OPENAI_API_KEY as a repository secret.
- The default GITHUB_TOKEN has permissions to comment on issues.
- Test with a new issue.
Troubleshooting
If the agent does not comment, check the workflow logs. The most common problems are invalid API keys, insufficient token permissions, or the model refusing to call tools.
Ensure the GitHub token has the issues:write permission. If you use a fine-grained token, select Issues: Write in the permissions.
If the model does not call tools, try adding a more explicit instruction like 'Use the get_issue tool to fetch the issue first.'
# Test the GitHub token permissions
curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/repos/owner/repo/issues/1 | jq '.title'What I would do
For a production setup, I would add a moderation step before allowing the agent to comment. The agent could draft a response and then a human approves it via a pull request or a Slack bot.
I would also add more tools: list labels, apply labels, and search similar issues. This makes the agent more useful for triage.
Here is a starter configuration for the tools list with label management.
# Additional tools
TOOLS.append({
"type": "function",
"function": {
"name": "add_labels",
"description": "Add labels to an issue",
"parameters": {
"type": "object",
"properties": {
"owner": {"type": "string"},
"repo": {"type": "string"},
"issue_number": {"type": "integer"},
"labels": {"type": "array", "items": {"type": "string"}}
},
"required": ["owner", "repo", "issue_number", "labels"]
}
}
})
def add_labels(owner: str, repo: str, issue_number: int, labels: list) -> dict:
"""Add labels to an issue."""
url = f"{BASE_URL}/repos/{owner}/{repo}/issues/{issue_number}/labels"
response = requests.post(url, headers=HEADERS, json={"labels": labels})
response.raise_for_status()
return response.json()FAQ
Answers to the questions that come up most often on this topic.
- Q: Do I need a paid OpenAI plan? A: You need API access, which requires a paid account. The gpt-4o-mini model is inexpensive, but you still need to add a payment method.
- Q: Can I use a local model? A: Yes, you can replace the OpenAI client with a local model like Llama 3 via Ollama, but you need to adapt the tool calling format.
- Q: How do I prevent the agent from posting unwanted comments? A: Add a human approval step, or run the agent in a dry-run mode that only prints the draft.
- Q: What if the agent fails to parse the tool arguments? A: Ensure the JSON schema matches the function signature exactly. Test with a simple call first.
Next steps
Now that you have a working agent, try extending it to handle pull requests or to summarize weekly issues. You can also add a database to track classifications over time.
Run the agent on a few real issues and adjust the prompt to improve accuracy. The key is to iterate on the tool descriptions and the system prompt.
# Run the agent on a real issue in your repo
python -c "from agent import run_agent; print(run_agent('Triage issue #42 in yourname/yourrepo. Suggest a label and draft a response.'))"Key takeaways
- Apply one concrete change from this post before collecting more reading.
- Prefer browser-side tools when the work involves secrets, tokens, or PII.
- Document the why next to the how so the next reviewer inherits context.
FAQ
- Who is this guide on ai-agents for?
- Working developers who need a practical take on how i set up an ai agent that reads my github issues — not a marketing overview. Skim the sections, apply one tip, then come back when you hit an edge case.
- Do I need an account to use the related tools?
- No. code.live tools run in your browser with no signup. Nothing you paste is uploaded to a server for the client-side utilities linked from this post.
- How often is this article updated?
- This post was published August 26, 2026. Fundamentals stay stable; check linked tool pages and official docs when version-specific behavior matters.