We got model fusion at home
In this chunk I’m going to document a little OpenCode1 setup that uses multiple (well, two in this case but your plan is your limit) LLMs to review a GitHub pull request.
It’s a simple exercise in fusing multiple different models to perform a task and can be customized for any given LLM that you have access to and used for any task that benefits from this kind of multi-llm analysis.
I’m going to cut to the chase in a second but just for the sake of completeness, let me set the stage a bit.
A little background
The review bottleneck
Regardless of what you think about it, the reality of software development is that these days barely anyone writes code by hand professionally. Old news by now, I know, and I feel like we’ve all become these mythical 10x developers at least if LoC/PR is your main metric. It’s also not just about speed. I feel that, with AI, it’s much easier to jump on a problem that’s usually outside of your comfort zone in the codebase. So, most of my work these days has been looking at diffs, either monitoring my own coding agent at work or reviewing other people’s changes - both something that requires a lot of effort to stay focused or radical context shifts.
AI-assisted code reviews
So, it’s not a coincidence that the whole market of AI code review tools23 has started to emerge and your company probably already uses one. These are usually integrated in your GitHub flow and will kick in once your PR is live, which is very nice, but personally, I like to do my reviewing on-demand and in the comfort of my own coding agent so I can:
- Review my own stuff before wrapping up a PR - this saves me and a reviewer a lot of back-and-forward over some low-hanging stuff
- Review other people’s work - using AI to review large diffs or changes on an unknown repository corner is a nice way to break the ice before you dig deeper
The LLM dilemma
Personally, OpenCode has been my coding agent of choice for some time now and I usually switch different models periodically as I notice some of them becomes better over the others. Now, I don’t have any scientific method to compare these, it’s mainly a gut feeling and vibe-checking the internet. This means I am the last person you should listen to about LLM comparisons but I feel like that the balance shifts as new versions of these are released so it’s good jump around from time to time.
Or, just use them in parallel. If you are using any kind of non-proprietary coding agent, it should be possible to set up a team of AI agents that will work on a task so, in my case, I set a couple of review agents in OpenCode, each using a model of my choice to achieve a task - review a pull request.
OpenCode agents
Briefly: OpenCode enables you to set up AI agents4 that can be invoked with a tab command by name. Each agent, at minimum, has a name, system prompt and a model. You can also create subagents that primary agents can use on-demand, kind of like tools. I guess similar concepts exists in other open coding agents so look it up.
Now, let’s see how we can set these up for parallel, multi-model code reviews.
Setting up OpenCode review agents
For the sake of simplicity, let’s say we want to do a simultaneous review using the latest Claude and GPT models. For this we will set up a main agent and two subagents:
- Primary agent - let’s call it
review-orchestrator, it’s job will be to go trough each sub-agent’s findings and create a deduplicated list with source attributed. Since this task is fairly simple, we don’t need to use a flagship model for this. - Subagents - we’ll create one for each model we want to use:
captain-claude- reviews the pull request using Anthropic model of our choicecaptain-gpt- same but with an OpenAI model
So the process will be something like this:
┌───────────────────────┐ Findings:
│ │ - title: ...
>> Hey, ─────► │ review-orchestrator │ ────────► - description: ...
please review │ │ - source: Claude/GPT
this PR: #234 └─────▲──────────▲──────┘ - suggested fix: ....
findings │ │ findings
open questions │ │ open questions Open Questions: ....
┌───────────────────┐ ┌───────────────────┐
│ │ │ │
│ captain-claude │ │ captain-gpt │
│ │ │ │
└───────────────────┘ └───────────────────┘
The main agent
All OpenCode agents are defined in your opencode.json file in the agent section. For our main agent, we want to set the mode: primary, give it some instructions and let’s use a simpler model for it’s task:
{
"agent": {
"review-orchestrator": {
"description": "Runs Claude and GPT review subagents, returns one source-attributed review list.",
"mode": "primary",
"model": "anthropic/claude-sonnet-5",
"temperature": 0,
"prompt": "You are a code review orchestrator. First invoke both subagents `captain-claude` and `captain-gpt` with the same task. Run them in parallel when possible.\n\nScope handling:\nIf no argument is supplied, review changes introduced on the current branch against `master` or `main`.\nIf a GitHub PR URL is supplied, review that PR using read-only GitHub CLI commands. Prefer `gh pr view <url>` for metadata and `gh pr diff <url>` for the patch. Do not check out the PR, switch branches, push, edit files, or modify the worktree.\n\nAsk each subagent to return code review findings with file and line references. After both subagents return, merge their outputs into one deduplicated review list. Attribute each merged finding with `Source: Claude`, `Source: GPT`, or `Source: Claude+GPT`. If a model reports something that is not tied to changed code, put it under Open Questions. Do not omit anything from agent finding, let user judge what makes sense. Final response format: `## Findings`, then numbered findings as `[Severity] path:line - title` with `Source: ...`, `Why it matters: ...`, and `Suggested fix: ...`; then `## Open Questions` if needed; then `## No Findings` if there are no findings."
}
}
}
Subagents
These are pretty much the same as the primary agent, with two differences:
- Subagents use
mode: subagent - We’ll set
hidden: trueso these cannot be invoked on their own
Here’s how captaint-claude will look like:
{
"agent": {
"captain-claude": {
"description": "Independent read-only branch code reviewer using Claude models",
"mode": "subagent",
"hidden": true,
"model": "anthropic/claude-fable-5",
"temperature": 0,
"steps": 50,
"prompt": "You are an independent code reviewer. Do not modify files.\n\nScope handling:\nIf reviewing the current branch, review only changes introduced on the current branch against the base branch supplied by the orchestrator. Use git to identify the merge base and diff, typically `git merge-base <base> HEAD` and `git diff <base>...HEAD`, then read relevant files to verify behavior.\nIf a GitHub PR URL is supplied, review that PR using read-only GitHub CLI commands. Prefer `gh pr view <url>` for metadata and `gh pr diff <url>` for the patch. Do not check out the PR, switch branches, push, edit files, or modify the worktree. Return findings only, ordered by severity. For each finding include severity, file:line, title, why it matters, evidence from the diff/code, and suggested fix."
}
}
}
And the other one is basically the same.
Now I appreciate that prompting here is not perfect but works very well and it sets a nice baseline for future improvements.
A couple of additional things
Adding a bit of flexibility to what’s reviewed
With this set, I am able to invoke the review orchestrator as tab command and it’ll do it’s job but I would like to be able to review changes on my current branch or from a specified GitHub URL. Since agent prompts cannot be that easily parameterized, we will create an additional command5, called full-review with a PR URL as a parameter. We’ll use that command to invoke our primary agent. This parameter is optional so we’ll instruct it to use the current branch if the URL is not provided.
Commands are also specified in your opencode.json file, under the command section and here’s how it should look like:
{
"command": {
"full-review": {
"description": "Review current branch or a GitHub PR with Claude and GPT",
"agent": "review-orchestrator",
"template": "Run a full code review using the configured two-model review process. Argument: $ARGUMENTS\n\nIf the argument is empty, review changes introduced on the current branch against `master`/`main`.\n\nIf the argument is non-empty, treat it as a GitHub PR URL and review that PR. Use GitHub CLI read-only commands such as `gh pr view` and `gh pr diff` to inspect the PR. Return one deduplicated source-attributed review list."
}
}
}
Setting the guardrails
Since these are review agents, it makes sense to make them read-only so they don’t go crazy implementing fixes that we don’t want them to. Now, this is not so straight-forward and will depend on the task that each agent is doing, but for our case all three agents can be restricted to read-only git actions with this permission settings (should be added to all three agents):
{
"agent": {
"your-agent": {
"permission": {
"read": "allow",
"glob": "allow",
"grep": "allow",
"list": "allow",
"edit": "deny",
"bash": {
"*": "deny",
"git status": "allow",
"git status *": "allow",
"git branch": "allow",
"git branch *": "allow",
"git rev-parse *": "allow",
"git merge-base *": "allow",
"git diff": "allow",
"git diff *": "allow",
"git show *": "allow",
"git log *": "allow",
"git ls-files": "allow",
"git ls-files *": "allow",
"git blame *": "allow",
"gh pr view *": "allow",
"gh pr diff *": "allow",
"gh api repos/*/pulls/*": "allow"
},
"task": {
"*": "deny",
"captain-claude": "allow",
"captain-gpt": "allow"
},
"external_directory": "deny",
"todowrite": "deny",
"webfetch": "deny",
"websearch": "deny"
}
}
}
}
Wrapping up
With all of this in place, I am able to kick off my review in OpenCode like this:
/full-review
To review changes on my current branch, or:
/full-review https://github.com/MiloradFilipovic/bytesizedchunks/pulls/1234
To review a specific PR.
Just to summarize, this is how your final opencode.json file structure should look like:
{
"$schema": "https://opencode.ai/config.json",
"agent": {
"review-orchestrator": {
"description": "...",
"mode": "primary",
"model": "openai/gpt-5.5",
"temperature": 0,
"prompt": "...",
"permission": {}
},
"captain-claude": {
"description": "...",
"mode": "subagent",
"hidden": true,
"model": "anthropic/claude-fable-5",
"temperature": 0,
"steps": 50,
"prompt": "...",
"permission": {}
},
"captain-gpt": {
"description": "...",
"mode": "subagent",
"hidden": true,
"model": "openai/gpt-5.5",
"temperature": 0,
"steps": 50,
"prompt": "...",
"permission": {}
}
},
"command": {
"full-review": {
"description": "...",
"agent": "review-orchestrator",
"template": "..."
}
}
}
Pushing this forward
I tried keeping the example simple enough so you should probably be able to use this approach in your coding agent (as long as it’s configurable enough). With a few prompting updates, the output format is also pretty adjustable so feel free to play around with it.
Here are a couple of ideas that can push this approach beyond simple 2-model review setup:
- Use multiple models - instead of limiting to 2, obviously this can be extended to any number of subagents
- Use this for other tasks - similar setup can be created for any other task type (planning a feature, implementing it, researching a topic, …) where you need multi-llm expertise.
So hopefully this gets you enough info to get started with your own little home-brewed model fusion6. Until next time.
OpenCode - The open source AI coding agent ↩︎
CodeRabbit - AI code reviews ↩︎
Agents - OpenCode docs ↩︎
Commands - OpenCode docs ↩︎
Fusion - Multi-model Analysis on OpenRouter ↩︎