I built an AI agent that handles my entire GitLab workflow — so I don't have to

Part 7 of my Beginners Guide to Building AI Agents — one agent, 15 tools, from branch creation to merge with AI code review baked in. The architecture, the design decisions, and how to adapt it.

▶ Watch the video walkthrough

Part 6 — Day-to-day GitLab/GitHub interaction in plain text
Part 7 — A UI so non-technical users can drive it in plain text

A few weeks ago I caught myself doing the same thing for the fourth time that day: creating a branch, committing files, opening a merge request, copy-pasting the diff into ChatGPT for a review, posting the summary back to GitLab, then repeating the whole dance after my reviewer left comments.

It wasn’t hard. It was just relentlessly repetitive. Every step required a context switch, a tab switch, or a copy-paste. The code was the interesting part — everything around it was friction.

So I built an agent to own that friction.

This is Part 7 of my Beginners Guide to Building AI Agents series. By the end of this article, you’ll understand exactly how the agent works, why the architecture was designed the way it was, and how to adapt it for your own team.


What Problem Does This Actually Solve?

Before diving into code, let me be precise about the problem. Most developer workflow pain isn’t about any single step — it’s about the transitions between steps:

Each transition is low-effort in isolation. Together, they add up to a workflow that’s 30% code and 70% ceremony. The agent eliminates the ceremony.


The Architecture: One File, 15 Tools, Three LLM Backends

The agent is built on LangChain with a clean tool-calling loop. Here’s the high-level design:

You (natural language) → Agent Loop → GitLab REST API v4
                              ↕
                    LLM (Gemini / OpenAI / Ollama)
                              ↕
                    Your local file system

The key design decision: the agent reads your files from disk, but never writes production code. You write code in VS Code. The agent reads those files and pushes them to GitLab. GitLab is always the source of truth — the agent holds no local git state.

This keeps the agent firmly in its lane: orchestration and automation, not code generation.

The 15 Tools at a Glance

The agent exposes exactly 15 tools, organized in three logical layers:

File & Environment - get_working_directory — shows where the agent looks for your local files - set_working_directory — redirects to a different repo root mid-session - get_current_time — utility for timestamping operations

GitLab Read Operations - get_gitlab_projects — lists your projects with IDs - get_merge_requests — lists open/merged/closed MRs - get_mr_diff — fetches the full code diff for any MR - get_mr_comments — retrieves all review threads with note IDs

GitLab Write Operations - create_branch — creates a new branch from any ref - add_commit_push — atomic add + commit + push (replaces those three git commands) - commit_from_local_files — reads your actual local files and pushes them - open_merge_request — opens an MR with Jira integration - review_merge_request — runs an AI code review and saves the report - post_mr_comment — publishes the review to the GitLab MR discussion - reply_to_mr_comment / resolve_mr_thread — handles reviewer feedback threads

Compound Operations - address_review_comments — commits fixes + replies to threads + resolves them in one step - full_workflow — the complete branch → commit → MR → review pipeline in one instruction - mark_mr_ready + merge_mr — finalize and merge - create_ci_pipeline — adds .gitlab-ci.yml and triggers CI on merge


The Real Developer Workflow

Here’s the 11-step workflow the agent orchestrates:

Step 1  — git clone (you do this)
Step 2  — create_branch
Step 3  — Write code in VS Code (you do this)
Step 4  — commit_from_local_files
Step 5  — open_merge_request
Step 6  — review_merge_request + post_mr_comment
Step 7  — get_mr_comments
Step 8  — Fix in VS Code, then address_review_comments
Step 9  — mark_mr_ready
Step 10 — merge_mr
Step 11 — create_ci_pipeline

You own steps 1 and 3 — writing and cloning. Everything else is a natural language prompt.


The commit_from_local_files Tool: The Heart of the Workflow

The most important tool in the set is commit_from_local_files. It’s what makes the workflow feel real rather than toy-like.

Instead of giving the agent file contents as strings (fragile, annoying, impractical), you just tell it what changed:

You: "I updated tools/slack_tool.py, created tests/test_slack.py,
      and deleted old_slack_helper.py — commit to feature/SCRUM-10"

The agent resolves those paths relative to your working directory, reads the actual bytes from disk, base64-encodes them, and pushes them to GitLab via the Commits API — all in one atomic operation. It supports create, update, and delete in a single commit.

@tool
def commit_from_local_files(
    branch_name:    str,
    commit_message: str,
    created_files:  str = "",
    updated_files:  str = "",
    deleted_files:  str = "",
    project_id:     str = "",
) -> str:

The created_files, updated_files, and deleted_files parameters accept comma-separated paths. The tool reads each file, builds the GitLab Commits API payload, and posts it. One call, one commit, no git required.


The AI Code Review Pipeline

The review workflow is a three-step pipeline:

  1. review_merge_request — fetches MR metadata and the full diff, then returns a structured prompt for the LLM to write the review.
  2. save_mr_review — saves the completed review to mr_reviews/ with a timestamp.
  3. post_mr_comment — publishes the review to the GitLab MR discussion thread.

The review covers seven dimensions: change summary, code quality, potential bugs, security concerns, missing tests, specific improvement suggestions, and an overall verdict (APPROVE / REQUEST CHANGES / NEEDS DISCUSSION).

Because the review is structured as a LangChain tool, the agent can do this without any extra plumbing:

You: "review MR !3 and post the AI review to GitLab"

The agent calls review_merge_request, writes the review prose, calls save_mr_review, then calls post_mr_comment. Three tool calls, zero manual steps.


Addressing Review Comments: The Closed Loop

Most agents stop at “generate a review.” This one closes the loop.

After your reviewer leaves comments, the address_review_comments tool lets you fix things in your IDE and then say:

You: "I fixed slack_tool.py — reply to thread abc123 saying it's fixed and resolve it"

The agent: 1. Reads your updated slack_tool.py from disk 2. Commits the fix to the branch 3. Posts a reply to the reviewer’s thread 4. Marks the thread as resolved

This is the sequence that most developers do manually, tab by tab. Here it’s a single natural language sentence.


Multi-LLM with Automatic Fallback

One of the more practical engineering decisions in the agent is the LLM fallback chain. At startup, the agent builds an ordered list of providers:

Preferred → remaining cloud providers → Ollama (local, last resort)

The active provider is resolved from: CLI flag (--gemini, --openai, --ollama) → .env → default. If the active provider fails with a recoverable error (auth failure, rate limit, network timeout, quota exhaustion), the agent silently switches to the next provider in the chain and announces the switch.

def _is_fallback_error(exc: Exception) -> bool:
    fallback_signals = [
        "api key", "quota", "rate_limit", "resource_exhausted",
        "too many requests", "429", "overloaded", "503", "timeout", ...
    ]
    return any(signal in str(exc).lower() for signal in fallback_signals)

This means the agent keeps running even if Gemini hits a quota limit mid-session. For a developer who runs this during a sprint, that resilience matters.


Jira Integration: Closing the Story-to-MR Link

When opening an MR, you can pass a jira_key:

You: "open MR for feature/SCRUM-10-slack-retry linked to SCRUM-10"

The agent appends a formatted Jira link to the MR description:

---
**Jira:** [SCRUM-10](https://yourorg.atlassian.net/browse/SCRUM-10)

Small thing. But when your PM is reviewing sprint progress and every MR traces back to a story, you look like you have your act together.


The full_workflow Shortcut

For green-field features where you want to go from zero to reviewed MR in one command:

You: "full workflow: branch feature/SCRUM-11-mr-review,
     I created tools/mr_tool.py, updated README.md,
     deleted old_helper.py,
     MR title: feat: GitLab MR review tool, link SCRUM-11"

The agent runs create_branch → commit_from_local_files → open_merge_request → review_merge_request → post_mr_comment sequentially, reporting progress at each step. If any step fails, it stops and explains what went wrong.


Setup in Five Minutes

Prerequisites: Python 3.10+, a GitLab Personal Access Token with api scope, and at least one LLM API key (Gemini, OpenAI, or a local Ollama installation).

pip install langchain-core langchain-google-genai langchain-openai langchain-ollama python-dotenv

Create a .env file:

GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx
GITLAB_USERNAME=yourusername
GITLAB_PROJECT_IDS=12345678
GITLAB_BASE_URL=https://gitlab.com
GITLAB_DEFAULT_BRANCH=main

LLM_PROVIDER=gemini
GEMINI_API_KEY=AIzaSy...

# Optional Jira integration
JIRA_BASE_URL=https://yourorg.atlassian.net
JIRA_EMAIL=you@yourorg.com
JIRA_API_TOKEN=your-jira-token

Run from your repo root (important — the agent reads files relative to wherever you start it):

cd /path/to/your/repo
python gitlab_mr_review_agent.py
# or with a specific LLM:
python gitlab_mr_review_agent.py --gemini

What I Learned Building This

1. Tool granularity is a design choice, not a given. I could have built three mega-tools (read, write, review) or 40 micro-tools (one per API endpoint). 15 tools hit a sweet spot: each tool does one coherent thing, the LLM can reason about which to call, and compound tools like full_workflow compose the granular ones.

2. The agent should never be the source of truth. GitLab owns the code. Your IDE owns the file. The agent orchestrates between them. When I tried building an agent that also stored file state, it became a liability. Stateless orchestration is simpler and more reliable.

3. Natural language input needs forgiving parameter parsing. LLMs will sometimes pass commit_message when you defined the parameter as title. The code handles this gracefully with aliases rather than failing hard. Build for how LLMs actually behave, not how you wish they would.

4. A fallback chain is table stakes. For anything you run during real work, single-provider reliability isn’t good enough. The fallback chain added maybe two hours of work and has saved the session multiple times.


What’s Next

This is Part 7 of the series. Coming up:


The Full Code

The complete agent — all 15 tools, the fallback chain, and the CLI — is a single Python file (~2,000 lines). You can find it in the GitLab repo linked in my profile.

If you’re following the series, this is the file that replaces both gitlab_mr_review_agent.py and gitlab_workflow_tools.py from the original Part 7. No cross-file imports, everything self-contained.


If this was useful, follow along — Part 8 is coming next week. And if you’ve built something similar for GitHub or Bitbucket, I’d love to compare notes in the comments.

AI AgentsGitLabLangChain
← All posts