<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sumesh Ramasamy</title>
    <description>The latest articles on DEV Community by Sumesh Ramasamy (@sumesh-ramasamy).</description>
    <link>https://dev.to/sumesh-ramasamy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3984213%2Fd8c980a9-fcab-4397-be43-8ab2bc29ea2b.jpg</url>
      <title>DEV Community: Sumesh Ramasamy</title>
      <link>https://dev.to/sumesh-ramasamy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sumesh-ramasamy"/>
    <language>en</language>
    <item>
      <title>Building a Multi-Agent PR Reviewer in Microsoft Agent Framework 1.10</title>
      <dc:creator>Sumesh Ramasamy</dc:creator>
      <pubDate>Wed, 01 Jul 2026 04:40:01 +0000</pubDate>
      <link>https://dev.to/sumesh-ramasamy/building-a-multi-agent-pr-reviewer-in-microsoft-agent-framework-110-2l5j</link>
      <guid>https://dev.to/sumesh-ramasamy/building-a-multi-agent-pr-reviewer-in-microsoft-agent-framework-110-2l5j</guid>
      <description>&lt;p&gt;Microsoft Agent Framework (MAF) reached 1.0 GA on April 3, 2026. I wanted to try it on something small, useful, and honest to my own workflow rather than a hello-world demo, so I built &lt;code&gt;pr-triage-agent&lt;/code&gt;: a CLI that takes a GitHub PR URL and runs three specialist agents concurrently (Security, Performance, Style), then hands their findings to a Consolidator agent that produces a structured markdown report.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/sumesh-ramasamy/pr-triage-agent" rel="noopener noreferrer"&gt;https://github.com/sumesh-ramasamy/pr-triage-agent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post walks through what it does, why MAF was a natural fit, the architecture, and the four places the installed 1.10 API diverges from the docs (which cost me an hour before I figured out what was going on).&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pr-triage-agent&lt;/code&gt; runs like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python triage.py https://github.com/pallets/flask/pull/6013
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It fetches the PR diff, fans it out to three specialist agents concurrently via MAF's &lt;code&gt;ConcurrentBuilder&lt;/code&gt;, then consolidates their JSON findings into a single markdown report printed with &lt;code&gt;rich&lt;/code&gt;. Full run in ~10 seconds, ~$0.004 on &lt;code&gt;gpt-4o-mini&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Example output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkf7fck2fee83b1exw9ik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkf7fck2fee83b1exw9ik.png" alt="Example Run" width="800" height="785"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Microsoft Agent Framework
&lt;/h2&gt;

&lt;p&gt;I ship small PRs constantly, both to my own repos and to codebases at work. Triaging them for the obvious stuff, hardcoded secrets, N+1 queries, functions that should have been split, is exactly the kind of pattern-matching work I've been curious to see LLMs handle in a structured, multi-agent way.&lt;/p&gt;

&lt;p&gt;Two things drew me to MAF specifically. First, the concurrent workflow primitive is exactly the shape of the PR triage problem: fan out to N specialists, collect their outputs, hand them to a consolidator. In frameworks without this primitive, you're gluing together async patterns yourself. Second, MAF is Microsoft's first-party framework for agents, and I wanted to build fluency in it while it's still fresh, before the ecosystem crowds up.&lt;/p&gt;

&lt;p&gt;I also just wanted to build something in it rather than only read the docs. Reading and building are very different levels of understanding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub PR URL
     │
     ▼
[github_utils.fetch_pr_diff]  ← filters binaries, caps diff at ~20K tokens
     │
     ▼
[MAF ConcurrentBuilder]
     ├──► SecurityAgent      (hardcoded secrets, injection, auth)
     ├──► PerformanceAgent   (N+1, unbounded loops, sync I/O)
     └──► StyleAgent         (naming, docstrings, magic numbers)
     │
     ▼
[Aggregator collects specialist JSON]
     │
     ▼
[ConsolidatorAgent]           ← merges into structured markdown
     │
     ▼
[rich renderer to stdout]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each specialist returns a JSON block with severity, findings, and a summary. The Consolidator merges the three JSONs into a single markdown report. The whole pipeline coordinator is under 100 lines because &lt;code&gt;ConcurrentBuilder&lt;/code&gt; does the heavy lifting.&lt;/p&gt;

&lt;h2&gt;
  
  
  What surprised me about MAF 1.10
&lt;/h2&gt;

&lt;p&gt;This is the section I want to spend the most time on, because I lost about an hour to it and the fix wasn't obvious from the docs.&lt;/p&gt;

&lt;p&gt;I started by following the official Microsoft Learn quickstart. Import statements from the docs failed. Class names in the docs didn't exist in the installed package. When I introspected &lt;code&gt;agent-framework==1.10.0&lt;/code&gt; directly, I found the actual API surface, and it turned out to be cleaner than the docs suggest, but different enough that copying and pasting sample code doesn't work.&lt;/p&gt;

&lt;p&gt;Four specific divergences I hit, and the fixes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Agent class name.&lt;/strong&gt; Docs show &lt;code&gt;ChatAgent&lt;/code&gt;. The installed 1.10.0 package exposes &lt;code&gt;Agent&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Docs
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agent_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatAgent&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_agent&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;

&lt;span class="c1"&gt;# Installed 1.10.0
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agent_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;...,&lt;/span&gt; &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;...,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. OpenAI chat client model kwarg.&lt;/strong&gt; Docs say &lt;code&gt;model_id&lt;/code&gt;. Installed package raises &lt;code&gt;TypeError&lt;/code&gt; on that. Actual kwarg is &lt;code&gt;model&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Docs
&lt;/span&gt;&lt;span class="nc"&gt;OpenAIChatClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;...,&lt;/span&gt; &lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Installed 1.10.0
&lt;/span&gt;&lt;span class="nc"&gt;OpenAIChatClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;...,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. ConcurrentBuilder location.&lt;/strong&gt; Not at the package root. Lives in &lt;code&gt;agent_framework.orchestrations&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agent_framework.orchestrations&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ConcurrentBuilder&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Aggregator response shape.&lt;/strong&gt; Docs show &lt;code&gt;r.agent_run_response.messages[-1].text&lt;/code&gt;. Installed version delivers &lt;code&gt;list[AgentExecutorResponse]&lt;/code&gt; where each has &lt;code&gt;.agent_response.text&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The way I found these was by introspecting the installed package with &lt;code&gt;dir()&lt;/code&gt; and reading a few open GitHub issues on the &lt;code&gt;microsoft/agent-framework&lt;/code&gt; repo. Once I stopped trusting the docs and started trusting the installed package, the friction dropped to zero.&lt;/p&gt;

&lt;p&gt;This is what working with a 1.0 framework three months out from GA looks like: the shape is right, the API is stable enough to build on, but the surface is still moving faster than the documentation site. That's a normal tradeoff of being early, not a criticism. I'd rather build fluency now than wait for every corner of the docs to catch up.&lt;/p&gt;

&lt;p&gt;I documented all four in the repo's &lt;a href="https://github.com/sumesh-ramasamy/pr-triage-agent#maf-1100-api-notes" rel="noopener noreferrer"&gt;MAF 1.10.0 API notes&lt;/a&gt; section, in case anyone else hits the same friction.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;p&gt;Two things I'd change with more time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured output.&lt;/strong&gt; MAF supports &lt;code&gt;response_format&lt;/code&gt; on the run call for native Pydantic-typed structured outputs, and I'd prefer to use it over asking the model to "return only JSON" in the instructions. I skipped it because of open issue &lt;a href="https://github.com/microsoft/agent-framework/issues/3325" rel="noopener noreferrer"&gt;#3325&lt;/a&gt;, where &lt;code&gt;response.value&lt;/code&gt; returns &lt;code&gt;None&lt;/code&gt; in versions after &lt;code&gt;1.0.0b260107&lt;/code&gt;. My workaround was a small tolerant JSON parser that handles both markdown-fenced JSON and JSON-with-preamble. It works reliably, but it's a workaround. Once the upstream fix lands, I'll swap to native structured outputs and write about the migration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Specialist prompts.&lt;/strong&gt; The three specialist prompts (Security, Performance, Style) are strong opinion but not tuned. On a proper next iteration, I'd build a small evaluation harness with a set of golden PRs where I already know what the specialists should flag, then adjust the prompts against that. Right now the prompts are shaped by intuition, not measurement. That's fine for a first version. It won't scale.&lt;/p&gt;

&lt;p&gt;I also cut scope hard. This was a one-evening build, not a production tool. GitHub Action integration, webhook mode, and multi-repo support are all on the roadmap but not in v0.1.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Public roadmap on the repo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Models support (one PAT for both PR fetching and LLM inference)&lt;/li&gt;
&lt;li&gt;Azure OpenAI + Foundry variant&lt;/li&gt;
&lt;li&gt;GitHub Action mode (post triage as a PR comment automatically)&lt;/li&gt;
&lt;li&gt;Native structured output via &lt;code&gt;response_format&lt;/code&gt; (pending upstream fix for &lt;a href="https://github.com/microsoft/agent-framework/issues/3325" rel="noopener noreferrer"&gt;issue #3325&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Custom specialist agents via plugin config&lt;/li&gt;
&lt;li&gt;Evaluation harness with golden PRs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these is a small commit and a small follow-up post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/sumesh-ramasamy/pr-triage-agent" rel="noopener noreferrer"&gt;github.com/sumesh-ramasamy/pr-triage-agent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quickstart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/sumesh-ramasamy/pr-triage-agent
&lt;span class="nb"&gt;cd &lt;/span&gt;pr-triage-agent
python3.13 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env  &lt;span class="c"&gt;# add OPENAI_API_KEY and GITHUB_TOKEN&lt;/span&gt;
python triage.py https://github.com/pallets/flask/pull/6013
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Preview cost without making any LLM calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python triage.py &amp;lt;URL&amp;gt; &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're building on MAF and hitting similar friction between docs and the installed API, I'd like to hear about it. Issues and PRs welcome.&lt;/p&gt;

</description>
      <category>microsoft</category>
      <category>ai</category>
      <category>microsoftagentframework</category>
      <category>python</category>
    </item>
  </channel>
</rss>
