<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>CI-CD on Luke Little</title><link>https://lukelittle.com/tags/ci-cd/</link><description>Recent content in CI-CD on Luke Little</description><image><title>Luke Little</title><url>https://lukelittle.com/images/og-default.png</url><link>https://lukelittle.com/images/og-default.png</link></image><generator>Hugo -- 0.152.2</generator><language>en</language><lastBuildDate>Thu, 12 Feb 2026 09:00:00 -0500</lastBuildDate><atom:link href="https://lukelittle.com/tags/ci-cd/index.xml" rel="self" type="application/rss+xml"/><item><title>Building a GitHub PR Reviewer with Bedrock Agents and Action Groups</title><link>https://lukelittle.com/posts/2026/02/building-a-github-pr-reviewer-with-bedrock-agents-and-action-groups/</link><pubDate>Thu, 12 Feb 2026 09:00:00 -0500</pubDate><guid>https://lukelittle.com/posts/2026/02/building-a-github-pr-reviewer-with-bedrock-agents-and-action-groups/</guid><description>How to build an intelligent PR review agent that automatically analyzes pull requests and provides feedback using AWS Bedrock Agents</description><content:encoded><![CDATA[<p>Code reviews are essential for maintaining code quality, but they can be time-consuming and often repetitive. Developers find themselves commenting on the same issues across multiple pull requests: missing tests, inconsistent naming, inadequate error handling, and numerous other routine concerns. This creates a bottleneck in the development process, as team members wait for their code to be reviewed while reviewers struggle to balance thorough reviews with their own development work.</p>
<p>An AI assistant can address this challenge by analyzing pull requests before human reviewers, catching common issues and allowing the team to focus on more complex aspects of the code review. This approach doesn&rsquo;t replace human judgment but enhances it, ensuring that routine issues are caught consistently while freeing up developer time for deeper analysis.</p>
<p>This post demonstrates how to build an automated PR reviewer using AWS Bedrock Agents that analyzes code changes and provides feedback directly in GitHub.</p>
<h2 id="what-were-building">What we&rsquo;re building</h2>
<p>A PR review agent that:</p>
<ol>
<li>Gets triggered automatically when a new PR is opened or updated</li>
<li>Fetches the PR diff from GitHub</li>
<li>Analyzes the changes using AWS Bedrock</li>
<li>Posts a detailed review comment with suggestions</li>
<li>Tracks review history in DynamoDB</li>
</ol>
<p>The goal isn&rsquo;t to replace human reviewers, but to complement them by identifying common issues, style violations, and potential bugs before human review begins.</p>
<h2 id="real-world-applications">Real-World Applications</h2>
<p>This solution addresses common development challenges across different contexts:</p>
<p><strong>Enterprise Development Teams</strong>: In large organizations with strict coding standards, the agent ensures consistency across hundreds of developers, reducing the burden on senior engineers who often shoulder the bulk of review responsibilities.</p>
<p><strong>Open Source Projects</strong>: Maintainers can use the PR reviewer to handle the initial assessment of community contributions, ensuring they meet project guidelines before dedicating their limited time to review.</p>
<p><strong>Educational Settings</strong>: Computer science programs can deploy the agent to provide students with immediate feedback on their code submissions, helping them learn best practices without requiring instructor intervention for every issue.</p>
<p><strong>Continuous Integration Pipelines</strong>: The PR reviewer can become part of a broader CI/CD strategy, providing code quality assessments alongside traditional test runs and builds.</p>
<p><strong>Onboarding New Team Members</strong>: New developers on a project receive immediate feedback on their work that helps them understand team coding standards more quickly, accelerating their integration into the team.</p>
<h2 id="architecture-overview">Architecture overview</h2>
<p><img src="/posts/2026/02/building-a-github-pr-reviewer-with-bedrock-agents-and-action-groups/github-bedrock-pr-reviewer-architecture_hu_92007e8c5143642d.webp" srcset="/posts/2026/02/building-a-github-pr-reviewer-with-bedrock-agents-and-action-groups/github-bedrock-pr-reviewer-architecture_hu_2a9db8872adee776.webp 750w, /posts/2026/02/building-a-github-pr-reviewer-with-bedrock-agents-and-action-groups/github-bedrock-pr-reviewer-architecture_hu_92007e8c5143642d.webp 1500w" sizes="(max-width: 800px) 100vw, 750px"
       width="1500" height="712" alt="Architecture diagram showing a GitHub PR reviewer built with AWS Bedrock Agents" loading="lazy" decoding="async"></p>
<p>Here&rsquo;s how the system works:</p>
<pre tabindex="0"><code>GitHub Webhook → EventBridge → Lambda → Bedrock Agent with Action Groups → GitHub API → PR Comments
</code></pre><p>The key components:</p>
<ul>
<li><strong>GitHub Webhook</strong>: Triggers on PR events</li>
<li><strong>EventBridge</strong>: Routes events to Lambda</li>
<li><strong>Lambda</strong>: Processes GitHub events and invokes Bedrock Agent</li>
<li><strong>Bedrock Agent</strong>: Coordinates the review process</li>
<li><strong>Action Groups</strong>: Custom Lambda functions that the agent can call</li>
<li><strong>DynamoDB</strong>: Tracks review history and status</li>
</ul>
<p>This event-driven architecture ensures the review process begins automatically whenever a pull request is opened or updated, without requiring any manual intervention.</p>
<h2 id="what-youll-need">What you&rsquo;ll need</h2>
<ul>
<li>AWS Account with Bedrock access</li>
<li>GitHub repository where you want to enable reviews</li>
<li>GitHub Personal Access Token with repo permissions</li>
<li>Basic familiarity with AWS and GitHub</li>
</ul>
<h2 id="step-1-create-the-action-group-lambda-functions">Step 1: Create the Action Group Lambda functions</h2>
<p>First, let&rsquo;s create the Lambda functions our Bedrock Agent will use as action groups. We need three functions:</p>
<h3 id="1-get-pr-diff-lambda">1. Get PR Diff Lambda</h3>
<p>This function fetches the pull request details and diff from GitHub:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> json
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> os
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> boto3
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> requests
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> github <span style="color:#f92672">import</span> Github
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">lambda_handler</span>(event, context):
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">try</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Extract parameters from the event</span>
</span></span><span style="display:flex;"><span>        payload <span style="color:#f92672">=</span> json<span style="color:#f92672">.</span>loads(event[<span style="color:#e6db74">&#39;body&#39;</span>])
</span></span><span style="display:flex;"><span>        repo_name <span style="color:#f92672">=</span> payload[<span style="color:#e6db74">&#39;repo_name&#39;</span>]
</span></span><span style="display:flex;"><span>        pr_number <span style="color:#f92672">=</span> payload[<span style="color:#e6db74">&#39;pr_number&#39;</span>]
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Initialize GitHub</span>
</span></span><span style="display:flex;"><span>        g <span style="color:#f92672">=</span> Github(os<span style="color:#f92672">.</span>environ[<span style="color:#e6db74">&#39;GITHUB_TOKEN&#39;</span>])
</span></span><span style="display:flex;"><span>        repo <span style="color:#f92672">=</span> g<span style="color:#f92672">.</span>get_repo(repo_name)
</span></span><span style="display:flex;"><span>        pr <span style="color:#f92672">=</span> repo<span style="color:#f92672">.</span>get_pull(int(pr_number))
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Get PR details and diff</span>
</span></span><span style="display:flex;"><span>        pr_title <span style="color:#f92672">=</span> pr<span style="color:#f92672">.</span>title
</span></span><span style="display:flex;"><span>        pr_description <span style="color:#f92672">=</span> pr<span style="color:#f92672">.</span>body <span style="color:#f92672">or</span> <span style="color:#e6db74">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>        pr_author <span style="color:#f92672">=</span> pr<span style="color:#f92672">.</span>user<span style="color:#f92672">.</span>login
</span></span><span style="display:flex;"><span>        pr_files <span style="color:#f92672">=</span> [f<span style="color:#f92672">.</span>filename <span style="color:#66d9ef">for</span> f <span style="color:#f92672">in</span> pr<span style="color:#f92672">.</span>get_files()]
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        diff_url <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;https://github.com/</span><span style="color:#e6db74">{</span>repo_name<span style="color:#e6db74">}</span><span style="color:#e6db74">/pull/</span><span style="color:#e6db74">{</span>pr_number<span style="color:#e6db74">}</span><span style="color:#e6db74">.diff&#34;</span>
</span></span><span style="display:flex;"><span>        headers <span style="color:#f92672">=</span> {<span style="color:#e6db74">&#39;Authorization&#39;</span>: <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;token </span><span style="color:#e6db74">{</span>os<span style="color:#f92672">.</span>environ[<span style="color:#e6db74">&#39;GITHUB_TOKEN&#39;</span>]<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>}
</span></span><span style="display:flex;"><span>        diff_response <span style="color:#f92672">=</span> requests<span style="color:#f92672">.</span>get(diff_url, headers<span style="color:#f92672">=</span>headers)
</span></span><span style="display:flex;"><span>        diff <span style="color:#f92672">=</span> diff_response<span style="color:#f92672">.</span>text
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Return everything the agent needs</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#39;statusCode&#39;</span>: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#39;body&#39;</span>: json<span style="color:#f92672">.</span>dumps({
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#39;pr_title&#39;</span>: pr_title,
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#39;pr_description&#39;</span>: pr_description,
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#39;pr_author&#39;</span>: pr_author,
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#39;pr_files&#39;</span>: pr_files,
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#39;pr_diff&#39;</span>: diff
</span></span><span style="display:flex;"><span>            })
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">except</span> <span style="color:#a6e22e">Exception</span> <span style="color:#66d9ef">as</span> e:
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> {<span style="color:#e6db74">&#39;statusCode&#39;</span>: <span style="color:#ae81ff">500</span>, <span style="color:#e6db74">&#39;body&#39;</span>: json<span style="color:#f92672">.</span>dumps({<span style="color:#e6db74">&#39;error&#39;</span>: str(e)})}
</span></span></code></pre></div><h3 id="2-analyze-code-lambda">2. Analyze Code Lambda</h3>
<p>This function sends the code diff to Bedrock for analysis:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> json
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> os
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> boto3
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">lambda_handler</span>(event, context):
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">try</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Extract parameters from the event</span>
</span></span><span style="display:flex;"><span>        payload <span style="color:#f92672">=</span> json<span style="color:#f92672">.</span>loads(event[<span style="color:#e6db74">&#39;body&#39;</span>])
</span></span><span style="display:flex;"><span>        diff <span style="color:#f92672">=</span> payload[<span style="color:#e6db74">&#39;diff&#39;</span>]
</span></span><span style="display:flex;"><span>        languages <span style="color:#f92672">=</span> payload<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#39;languages&#39;</span>, [])
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Create prompt for the LLM</span>
</span></span><span style="display:flex;"><span>        prompt <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        Please review the following code diff and provide actionable feedback:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        </span><span style="color:#e6db74">{</span>diff<span style="color:#e6db74">}</span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        In your analysis, please look for:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        1. Potential bugs or logic errors
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        2. Security vulnerabilities
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        3. Performance issues
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        4. Code style and best practices
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        5. Missing tests or documentation
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        Format your response as:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        ## Summary
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        (Brief overview of the changes and their purpose)
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        ## Critical Issues
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        (List any serious problems that must be fixed)
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        ## Suggestions
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        (List minor issues and improvements)
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        ## Positive Notes
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        (Highlight good practices in the code)
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        &#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Call Bedrock for analysis</span>
</span></span><span style="display:flex;"><span>        bedrock <span style="color:#f92672">=</span> boto3<span style="color:#f92672">.</span>client(<span style="color:#e6db74">&#39;bedrock-runtime&#39;</span>)
</span></span><span style="display:flex;"><span>        response <span style="color:#f92672">=</span> bedrock<span style="color:#f92672">.</span>invoke_model(
</span></span><span style="display:flex;"><span>            modelId<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;anthropic.claude-3-sonnet-20240229-v1:0&#39;</span>,
</span></span><span style="display:flex;"><span>            contentType<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;application/json&#39;</span>,
</span></span><span style="display:flex;"><span>            accept<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;application/json&#39;</span>,
</span></span><span style="display:flex;"><span>            body<span style="color:#f92672">=</span>json<span style="color:#f92672">.</span>dumps({
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;anthropic_version&#34;</span>: <span style="color:#e6db74">&#34;bedrock-2023-05-31&#34;</span>,
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;max_tokens&#34;</span>: <span style="color:#ae81ff">2000</span>,
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;messages&#34;</span>: [{<span style="color:#e6db74">&#34;role&#34;</span>: <span style="color:#e6db74">&#34;user&#34;</span>, <span style="color:#e6db74">&#34;content&#34;</span>: prompt}]
</span></span><span style="display:flex;"><span>            })
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Parse and return response</span>
</span></span><span style="display:flex;"><span>        response_body <span style="color:#f92672">=</span> json<span style="color:#f92672">.</span>loads(response[<span style="color:#e6db74">&#39;body&#39;</span>]<span style="color:#f92672">.</span>read())
</span></span><span style="display:flex;"><span>        analysis <span style="color:#f92672">=</span> response_body[<span style="color:#e6db74">&#39;content&#39;</span>][<span style="color:#ae81ff">0</span>][<span style="color:#e6db74">&#39;text&#39;</span>]
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#39;statusCode&#39;</span>: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#39;body&#39;</span>: json<span style="color:#f92672">.</span>dumps({<span style="color:#e6db74">&#39;analysis&#39;</span>: analysis})
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">except</span> <span style="color:#a6e22e">Exception</span> <span style="color:#66d9ef">as</span> e:
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> {<span style="color:#e6db74">&#39;statusCode&#39;</span>: <span style="color:#ae81ff">500</span>, <span style="color:#e6db74">&#39;body&#39;</span>: json<span style="color:#f92672">.</span>dumps({<span style="color:#e6db74">&#39;error&#39;</span>: str(e)})}
</span></span></code></pre></div><h3 id="3-post-comment-lambda">3. Post Comment Lambda</h3>
<p>This function posts the review feedback to the GitHub PR and records the review in DynamoDB:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> json
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> os
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> boto3
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> time
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> github <span style="color:#f92672">import</span> Github
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">lambda_handler</span>(event, context):
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">try</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Extract parameters from the event</span>
</span></span><span style="display:flex;"><span>        payload <span style="color:#f92672">=</span> json<span style="color:#f92672">.</span>loads(event[<span style="color:#e6db74">&#39;body&#39;</span>])
</span></span><span style="display:flex;"><span>        repo_name <span style="color:#f92672">=</span> payload[<span style="color:#e6db74">&#39;repo_name&#39;</span>]
</span></span><span style="display:flex;"><span>        pr_number <span style="color:#f92672">=</span> payload[<span style="color:#e6db74">&#39;pr_number&#39;</span>]
</span></span><span style="display:flex;"><span>        comment <span style="color:#f92672">=</span> payload[<span style="color:#e6db74">&#39;comment&#39;</span>]
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Record review in DynamoDB</span>
</span></span><span style="display:flex;"><span>        dynamodb <span style="color:#f92672">=</span> boto3<span style="color:#f92672">.</span>resource(<span style="color:#e6db74">&#39;dynamodb&#39;</span>)
</span></span><span style="display:flex;"><span>        table <span style="color:#f92672">=</span> dynamodb<span style="color:#f92672">.</span>Table(os<span style="color:#f92672">.</span>environ[<span style="color:#e6db74">&#39;REVIEW_TABLE&#39;</span>])
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        review_id <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">{</span>repo_name<span style="color:#e6db74">}</span><span style="color:#e6db74">-</span><span style="color:#e6db74">{</span>pr_number<span style="color:#e6db74">}</span><span style="color:#e6db74">-</span><span style="color:#e6db74">{</span>int(time<span style="color:#f92672">.</span>time())<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>        table<span style="color:#f92672">.</span>put_item(
</span></span><span style="display:flex;"><span>            Item<span style="color:#f92672">=</span>{
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#39;review_id&#39;</span>: review_id,
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#39;repo_name&#39;</span>: repo_name,
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#39;pr_number&#39;</span>: pr_number,
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#39;timestamp&#39;</span>: int(time<span style="color:#f92672">.</span>time()),
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#39;comment&#39;</span>: comment
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Post comment to GitHub</span>
</span></span><span style="display:flex;"><span>        g <span style="color:#f92672">=</span> Github(os<span style="color:#f92672">.</span>environ[<span style="color:#e6db74">&#39;GITHUB_TOKEN&#39;</span>])
</span></span><span style="display:flex;"><span>        repo <span style="color:#f92672">=</span> g<span style="color:#f92672">.</span>get_repo(repo_name)
</span></span><span style="display:flex;"><span>        pr <span style="color:#f92672">=</span> repo<span style="color:#f92672">.</span>get_pull(int(pr_number))
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Format the comment for GitHub</span>
</span></span><span style="display:flex;"><span>        formatted_comment <span style="color:#f92672">=</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">## AI Code Review 🤖
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"></span><span style="color:#e6db74">{</span>comment<span style="color:#e6db74">}</span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">---
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">*This review was automatically generated by the PR Review Agent. [Learn more](https://example.com/pr-agent)*
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        &#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        pr<span style="color:#f92672">.</span>create_issue_comment(formatted_comment)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#39;statusCode&#39;</span>: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#39;body&#39;</span>: json<span style="color:#f92672">.</span>dumps({
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#39;review_id&#39;</span>: review_id,
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#39;status&#39;</span>: <span style="color:#e6db74">&#39;Comment posted successfully&#39;</span>
</span></span><span style="display:flex;"><span>            })
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">except</span> <span style="color:#a6e22e">Exception</span> <span style="color:#66d9ef">as</span> e:
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> {<span style="color:#e6db74">&#39;statusCode&#39;</span>: <span style="color:#ae81ff">500</span>, <span style="color:#e6db74">&#39;body&#39;</span>: json<span style="color:#f92672">.</span>dumps({<span style="color:#e6db74">&#39;error&#39;</span>: str(e)})}
</span></span></code></pre></div><h2 id="step-2-set-up-dynamodb-for-tracking-reviews">Step 2: Set up DynamoDB for tracking reviews</h2>
<p>You&rsquo;ll need a DynamoDB table to track review history. In production, you&rsquo;d define this in your infrastructure-as-code using Terraform or CloudFormation. The table needs:</p>
<ul>
<li>Partition key: <code>review_id</code> (String)</li>
<li>PAY_PER_REQUEST billing mode for cost efficiency</li>
</ul>
<p>This table will store metadata about each review, including repository, PR number, timestamp, and the content of the review comment.</p>
<h2 id="step-3-create-the-bedrock-agent">Step 3: Create the Bedrock Agent</h2>
<p>Now let&rsquo;s create the agent that will orchestrate the entire review process:</p>
<ol>
<li>
<p>In the Bedrock console, go to &ldquo;Agents&rdquo; → &ldquo;Create agent&rdquo;</p>
</li>
<li>
<p>Name it &ldquo;PRReviewAgent&rdquo;</p>
</li>
<li>
<p>Select Claude 3.5 Sonnet for the foundation model</p>
</li>
<li>
<p>Create three action groups:</p>
<p>a. <strong>GetPRDiff</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;actionGroupName&#34;</span>: <span style="color:#e6db74">&#34;GetPRDiff&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;description&#34;</span>: <span style="color:#e6db74">&#34;Retrieves the diff for a GitHub pull request&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;apiSchema&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;openapi&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;payload&#34;</span>: <span style="color:#e6db74">&#34;openapi: 3.0.0\ninfo:\n  title: GitHub PR Diff API\n  version: 1.0.0\npaths:\n  /getPRDiff:\n    post:\n      summary: Get the diff for a GitHub pull request\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              required:\n                - repo_name\n                - pr_number\n              properties:\n                repo_name:\n                  type: string\n                  description: The repository in format &#39;owner/repo&#39;\n                pr_number:\n                  type: integer\n                  description: The pull request number\n      responses:\n        200:\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  pr_title:\n                    type: string\n                  pr_description:\n                    type: string\n                  pr_author:\n                    type: string\n                  pr_files:\n                    type: array\n                    items:\n                      type: string\n                  pr_diff:\n                    type: string&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;actionGroupExecutor&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;lambda&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;lambdaArn&#34;</span>: <span style="color:#e6db74">&#34;[YOUR-GET-PR-DIFF-LAMBDA-ARN]&#34;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>b. <strong>AnalyzeCode</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;actionGroupName&#34;</span>: <span style="color:#e6db74">&#34;AnalyzeCode&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;description&#34;</span>: <span style="color:#e6db74">&#34;Analyzes code for potential issues and improvements&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;apiSchema&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;openapi&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;payload&#34;</span>: <span style="color:#e6db74">&#34;openapi: 3.0.0\ninfo:\n  title: Code Analysis API\n  version: 1.0.0\npaths:\n  /analyzeCode:\n    post:\n      summary: Analyze code diff for issues and suggestions\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              required:\n                - diff\n              properties:\n                diff:\n                  type: string\n                  description: The code diff to analyze\n                languages:\n                  type: array\n                  items:\n                    type: string\n                  description: Programming languages in the diff (optional)\n      responses:\n        200:\n          description: Successful analysis\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  analysis:\n                    type: string&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;actionGroupExecutor&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;lambda&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;lambdaArn&#34;</span>: <span style="color:#e6db74">&#34;[YOUR-ANALYZE-CODE-LAMBDA-ARN]&#34;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>c. <strong>PostComment</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;actionGroupName&#34;</span>: <span style="color:#e6db74">&#34;PostComment&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;description&#34;</span>: <span style="color:#e6db74">&#34;Posts a review comment to a GitHub pull request&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;apiSchema&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;openapi&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;payload&#34;</span>: <span style="color:#e6db74">&#34;openapi: 3.0.0\ninfo:\n  title: GitHub Comment API\n  version: 1.0.0\npaths:\n  /postComment:\n    post:\n      summary: Post a comment to a GitHub pull request\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              required:\n                - repo_name\n                - pr_number\n                - comment\n              properties:\n                repo_name:\n                  type: string\n                  description: The repository in format &#39;owner/repo&#39;\n                pr_number:\n                  type: integer\n                  description: The pull request number\n                comment:\n                  type: string\n                  description: The comment text to post\n      responses:\n        200:\n          description: Successful response\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  review_id:\n                    type: string\n                  status:\n                    type: string&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;actionGroupExecutor&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;lambda&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;lambdaArn&#34;</span>: <span style="color:#e6db74">&#34;[YOUR-POST-COMMENT-LAMBDA-ARN]&#34;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
<li>
<p>Configure the agent&rsquo;s instructions:</p>
<pre tabindex="0"><code>You are a helpful GitHub Pull Request reviewer designed to analyze code changes and provide constructive feedback. Your goal is to help developers improve their code by identifying issues and suggesting improvements.

When a PR is submitted:
1. Get the PR diff using the GetPRDiff action
2. Analyze the code for issues using the AnalyzeCode action
3. Post a helpful review comment using the PostComment action

Your reviews should be constructive and educational. Focus on:
- Potential bugs or logic errors
- Security vulnerabilities
- Performance issues
- Code style and best practices
- Missing tests or documentation

Use a professional tone and be concise but thorough in your feedback.
</code></pre></li>
</ol>
<p>These instructions are crucial as they define how the agent will behave when reviewing code. The careful wording encourages constructive feedback while maintaining a professional tone.</p>
<h2 id="step-4-create-the-main-lambda-function">Step 4: Create the main Lambda function</h2>
<p>Now create the main Lambda function that will be triggered by GitHub events and coordinate the review process:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> json
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> os
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> boto3
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> logging
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Initialize Bedrock Runtime client</span>
</span></span><span style="display:flex;"><span>bedrock_agent_runtime <span style="color:#f92672">=</span> boto3<span style="color:#f92672">.</span>client(<span style="color:#e6db74">&#39;bedrock-agent-runtime&#39;</span>)
</span></span><span style="display:flex;"><span>logger <span style="color:#f92672">=</span> logging<span style="color:#f92672">.</span>getLogger()
</span></span><span style="display:flex;"><span>logger<span style="color:#f92672">.</span>setLevel(logging<span style="color:#f92672">.</span>INFO)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">lambda_handler</span>(event, context):
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">try</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Parse the GitHub webhook event</span>
</span></span><span style="display:flex;"><span>        github_event <span style="color:#f92672">=</span> json<span style="color:#f92672">.</span>loads(event[<span style="color:#e6db74">&#39;body&#39;</span>])
</span></span><span style="display:flex;"><span>        headers <span style="color:#f92672">=</span> event<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#39;headers&#39;</span>, {})
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># Check if it&#39;s a pull request event we care about</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> headers<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#39;X-GitHub-Event&#39;</span>) <span style="color:#f92672">==</span> <span style="color:#e6db74">&#39;pull_request&#39;</span>:
</span></span><span style="display:flex;"><span>            action <span style="color:#f92672">=</span> github_event<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#39;action&#39;</span>)
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            <span style="color:#75715e"># Process only opened or synchronized (updated) PRs</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> action <span style="color:#f92672">in</span> (<span style="color:#e6db74">&#39;opened&#39;</span>, <span style="color:#e6db74">&#39;synchronize&#39;</span>):
</span></span><span style="display:flex;"><span>                pr <span style="color:#f92672">=</span> github_event[<span style="color:#e6db74">&#39;pull_request&#39;</span>]
</span></span><span style="display:flex;"><span>                repo_name <span style="color:#f92672">=</span> github_event[<span style="color:#e6db74">&#39;repository&#39;</span>][<span style="color:#e6db74">&#39;full_name&#39;</span>]
</span></span><span style="display:flex;"><span>                pr_number <span style="color:#f92672">=</span> pr[<span style="color:#e6db74">&#39;number&#39;</span>]
</span></span><span style="display:flex;"><span>                
</span></span><span style="display:flex;"><span>                <span style="color:#75715e"># Invoke the Bedrock agent to perform the review</span>
</span></span><span style="display:flex;"><span>                response <span style="color:#f92672">=</span> bedrock_agent_runtime<span style="color:#f92672">.</span>invoke_agent(
</span></span><span style="display:flex;"><span>                    agentId<span style="color:#f92672">=</span>os<span style="color:#f92672">.</span>environ[<span style="color:#e6db74">&#39;BEDROCK_AGENT_ID&#39;</span>],
</span></span><span style="display:flex;"><span>                    agentAliasId<span style="color:#f92672">=</span>os<span style="color:#f92672">.</span>environ[<span style="color:#e6db74">&#39;BEDROCK_AGENT_ALIAS_ID&#39;</span>],
</span></span><span style="display:flex;"><span>                    sessionId<span style="color:#f92672">=</span><span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">{</span>repo_name<span style="color:#e6db74">}</span><span style="color:#e6db74">-</span><span style="color:#e6db74">{</span>pr_number<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>,
</span></span><span style="display:flex;"><span>                    inputText<span style="color:#f92672">=</span><span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Review pull request #</span><span style="color:#e6db74">{</span>pr_number<span style="color:#e6db74">}</span><span style="color:#e6db74"> in repository </span><span style="color:#e6db74">{</span>repo_name<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>                )
</span></span><span style="display:flex;"><span>                
</span></span><span style="display:flex;"><span>                <span style="color:#75715e"># Process agent response</span>
</span></span><span style="display:flex;"><span>                completion <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">for</span> event <span style="color:#f92672">in</span> response<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#39;completion&#39;</span>, []):
</span></span><span style="display:flex;"><span>                    chunk <span style="color:#f92672">=</span> json<span style="color:#f92672">.</span>loads(event[<span style="color:#e6db74">&#39;chunk&#39;</span>][<span style="color:#e6db74">&#39;bytes&#39;</span>]<span style="color:#f92672">.</span>decode())
</span></span><span style="display:flex;"><span>                    <span style="color:#66d9ef">if</span> chunk[<span style="color:#e6db74">&#39;type&#39;</span>] <span style="color:#f92672">==</span> <span style="color:#e6db74">&#39;message&#39;</span>:
</span></span><span style="display:flex;"><span>                        completion <span style="color:#f92672">+=</span> chunk[<span style="color:#e6db74">&#39;message&#39;</span>][<span style="color:#e6db74">&#39;content&#39;</span>][<span style="color:#ae81ff">0</span>][<span style="color:#e6db74">&#39;text&#39;</span>]
</span></span><span style="display:flex;"><span>                
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">return</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#e6db74">&#39;statusCode&#39;</span>: <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>                    <span style="color:#e6db74">&#39;body&#39;</span>: json<span style="color:#f92672">.</span>dumps({<span style="color:#e6db74">&#39;status&#39;</span>: <span style="color:#e6db74">&#39;Agent invoked successfully&#39;</span>})
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> {<span style="color:#e6db74">&#39;statusCode&#39;</span>: <span style="color:#ae81ff">200</span>, <span style="color:#e6db74">&#39;body&#39;</span>: json<span style="color:#f92672">.</span>dumps({<span style="color:#e6db74">&#39;status&#39;</span>: <span style="color:#e6db74">&#39;Ignored PR action&#39;</span>})}
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> {<span style="color:#e6db74">&#39;statusCode&#39;</span>: <span style="color:#ae81ff">200</span>, <span style="color:#e6db74">&#39;body&#39;</span>: json<span style="color:#f92672">.</span>dumps({<span style="color:#e6db74">&#39;status&#39;</span>: <span style="color:#e6db74">&#39;Ignored webhook event&#39;</span>})}
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">except</span> <span style="color:#a6e22e">Exception</span> <span style="color:#66d9ef">as</span> e:
</span></span><span style="display:flex;"><span>        logger<span style="color:#f92672">.</span>error(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;Error: </span><span style="color:#e6db74">{</span>str(e)<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> {<span style="color:#e6db74">&#39;statusCode&#39;</span>: <span style="color:#ae81ff">500</span>, <span style="color:#e6db74">&#39;body&#39;</span>: json<span style="color:#f92672">.</span>dumps({<span style="color:#e6db74">&#39;error&#39;</span>: str(e)})}
</span></span></code></pre></div><h2 id="step-5-create-the-api-gateway">Step 5: Create the API Gateway</h2>
<ol>
<li>Create a new REST API in API Gateway</li>
<li>Add a POST method for the root resource</li>
<li>Set the integration type to Lambda Function and select your main function</li>
<li>Deploy the API to a stage and note the URL</li>
</ol>
<p>The API Gateway serves as the entry point for GitHub webhooks, receiving events when pull requests are opened or updated.</p>
<h2 id="step-6-set-up-the-github-webhook">Step 6: Set up the GitHub webhook</h2>
<ol>
<li>Go to your GitHub repository</li>
<li>Navigate to Settings → Webhooks → Add webhook</li>
<li>Set the Payload URL to your API Gateway URL</li>
<li>Set Content type to application/json</li>
<li>Select &ldquo;Let me select individual events&rdquo; and check &ldquo;Pull requests&rdquo;</li>
<li>Click &ldquo;Add webhook&rdquo;</li>
</ol>
<p>With the webhook in place, GitHub will now notify your system whenever pull requests are created or updated, triggering the automated review process.</p>
<h2 id="testing-the-system">Testing the system</h2>
<p>To test your PR review agent:</p>
<ol>
<li>Create a simple PR with some code changes</li>
<li>Check that the webhook is triggered (visible in GitHub webhook settings)</li>
<li>Verify your Lambda is invoked (check CloudWatch logs)</li>
<li>Check that a comment appears on the PR with the review</li>
</ol>
<p>You should see a comprehensive review comment that identifies potential issues, makes suggestions for improvement, and highlights positive aspects of the code changes.</p>
<h2 id="enhancing-the-agent">Enhancing the agent</h2>
<p>Here are some ways to improve your PR reviewer:</p>
<p><strong>Language-specific rules</strong>: Extend the AnalyzeCode lambda to apply language-specific linters or static analysis tools for more precise feedback based on the programming language.</p>
<p><strong>Contextual awareness</strong>: Include repository history, architecture documentation, or team standards in the analysis to provide more relevant and contextual suggestions.</p>
<p><strong>Review customization</strong>: Allow teams to set review focus areas through configuration. For example, some teams might prioritize security checks while others emphasize performance.</p>
<p><strong>Learning from feedback</strong>: Track which suggestions developers implement versus ignore to improve future recommendations and reduce false positives.</p>
<p><strong>PR metadata analysis</strong>: Consider PR size, files changed, and complexity when determining the review strategy. Large PRs might receive different handling than small, focused changes.</p>
<p><strong>Inline comments</strong>: Enhance the agent to post specific comments on individual lines in the diff rather than just a summary comment.</p>
<p><strong>Pre-commit integration</strong>: Offer developers the ability to run the same analysis locally before submitting their PR, using pre-commit hooks.</p>
<h2 id="cost-considerations">Cost considerations</h2>
<p>This architecture is cost-efficient for most development teams:</p>
<ul>
<li>Lambda costs: Typically covered by the free tier for small to medium teams</li>
<li>API Gateway: Approximately $1 per million requests</li>
<li>Bedrock API: Around $0.015 per 1,000 tokens with Claude Sonnet</li>
<li>DynamoDB: Pay-per-request pricing with minimal storage needs</li>
</ul>
<p>The total cost will vary based on your team&rsquo;s PR volume and the size of code changes being reviewed, but for most teams, it remains quite affordable compared to the developer time saved.</p>
<h2 id="security-considerations">Security considerations</h2>
<p>When implementing this:</p>
<ul>
<li>Store API tokens in AWS Secrets Manager</li>
<li>Set IAM permissions using least privilege</li>
<li>Consider the sensitivity of code being reviewed</li>
<li>Remember that agents aren&rsquo;t perfect at security analysis</li>
</ul>
<p>While the PR reviewer can identify many common security issues, it should not be your only security control. Critical security reviews should still be performed by security experts, especially for sensitive components.</p>
<h2 id="conclusion">Conclusion</h2>
<p>By combining AWS Bedrock Agents with Action Groups, this architecture creates an intelligent PR review system that helps maintain code quality while reducing the time developers spend on repetitive aspects of code reviews.</p>
<p>The solution provides several key benefits:</p>
<ol>
<li><strong>Consistency</strong>: Every PR receives the same baseline level of review</li>
<li><strong>Early detection</strong>: Catches common issues before human reviewers see the code</li>
<li><strong>Educational feedback</strong>: Provides context and explanations for suggested improvements</li>
<li><strong>Focus</strong>: Allows human reviewers to concentrate on architecture and business logic</li>
<li><strong>Historical tracking</strong>: Stores reviews for future reference and improvement</li>
</ol>
<p>While this implementation uses specific AWS services, the architectural patterns can be adapted to other cloud providers or integrated with different version control systems beyond GitHub.</p>
<p>This approach to automated code review represents a practical application of generative AI that delivers immediate value to development teams: better code quality, faster reviews, and more time for developers to focus on creative and complex challenges rather than routine feedback.</p>
]]></content:encoded></item></channel></rss>