フォトシンス エンジニアブログ

株式会社Photosynth のテックブログです

組み込みの知見をAIレビュワーに継承させてみた

組み込みの知見をAIレビュワーに継承させてみた

〜ベテランエンジニアの暗黙知ADR化してAIレビューに活かす〜

この記事は Akerun Advent Calendar 2024 - Qiita の17日目の記事です。

こんにちは。 いとう です。ファームウェアやってます。今年は実際に取り掛かった仕事が機密伴う仕事ばっかりだったので記事化は諦め、今回はアドベントカレンダーのために一念発起して組み込み開発全体に役に立つことがないか考えて記事を書きました。

はじめに

組み込みファームウェア開発では、ハードウェアの制約や開発環境の特性など、さまざまな暗黙知が存在します。これらの知見は多くの場合、ベテランエンジニアの経験として属人化しており、チーム全体での共有が課題となっています。今回は、これらの暗黙知ADR(Architecture Decision Record)として文書化し、AIによるコードレビューに活用した取り組みについて考えてみました。

暗黙知の例と影響

メモリ制約による問題

組み込み開発では、一見問題ない修正が思わぬ事態を引き起こすことがあります。例えば、ある開発者が計算精度向上のために整数演算を浮動小数点演算に変更したところ、大きな問題が発生しました。

// Before: 整数演算による重力加速度の計算
// センサー出力を物理値(m/s^2)に変換
// 9.80665...を10倍した98で近似
int32_t acceleration = (raw_value * 98) >> 10;  // 98/1024で近似

// After: 浮動小数点演算に変更
float acceleration = raw_value * 9.80665f / 1024.0f;

このシンプルな変更により、ARM7TDMI(FPU非搭載)プロセッサ上でソフトウェア浮動小数点ライブラリが自動的にリンクされ、わずか数行の修正でROMサイズが4KB増加。リリース直前のタイミングでFlashROMの容量制限に引っかかるという冷や汗モノの事態となりました。

組込み開発の現場では、FPUが搭載されていないプロセッサ(Cortex-M0/M0+など)が今でも現役。浮動小数点演算をコードに入れただけで、バイナリサイズが数KB増える事態に。原因は「ソフトウェア浮動小数点ライブラリ」が自動的にリンクされるため。

さらに驚くべきことに、Cortex-M0には除算器すら搭載されていません。つまり、単純な割り算もソフトウェアで実装することになり、想像以上に重い処理になってしまうのです。そのため、上記のコードは以下のように書き換えるのが定石です:

// 最適化バージョン:シフト演算を活用
// 9.80665を1024倍した10042で近似
int32_t acceleration = (raw_value * 10042) >> 10;

「きれいなコード」を目指して浮動小数点演算を使用した若手エンジニアが、リリース直前に容量制限にひっかかり青ざめる—— そんな光景は、組み込み開発ではよくある話です。

これを防ぐには、コードレビューで「これってFPU載ってましたっけ?」の一言を投げかけることが重要かもしれません。では、このような知見をどのようにチームで共有し、AIによるコードレビューに活かしていけばよいのでしょうか?

割り込みコンテキスト

ある日、新人エンジニアが相談に来ました。「デバッグプリントを増やしたら、たまにシステムが落ちるんです...」

void interrupt_handler(void)
{
    process_sensor_data();
    // 問題のコード
    LOG_INF("Interrupt: processed sensor data. value=%d", sensor_value);
}

たまにしか発生しない問題のため長い時間がかかった調査の末、判明した恐ろしい真相。RTOSの標準ログ機能のバックエンドとして使用していたSegger RTTが、バッファが一定量溜まると内部でmutexを取得しようとしていたのです。

つまり...

  1. 割り込みハンドラからのログ出力
  2. バッファが溜まると内部でmutex取得
  3. 割り込みコンテキストからmutexは取得できない!
  4. assert発生 → リブート

しかも、デバッガを繋いでいると発生しない(ログ出力が間に合う)という厄介な代物でした。

割り込みハンドラは神聖な領域。そこでは以下が絶対に禁止です:

  • メモリの動的確保
  • 同期処理(mutex, semaphoreなど)
  • 時間のかかる処理全般

これ以来、私たちのチームには「割り込みハンドラでのログ出力を見たら質問する」という暗黙の文化が根付きました。でも、こんな暗黙知、どうやって継承していけばいいのでしょうか?

ADRによる知見の文書化

組み込み開発では、ベテランエンジニアの「あの時の不具合」という武勇伝のような経験が、実は重要な技術的知見だったりします。今回はそんな暗黙知ADR(Architecture Decision Record)として文書化してみることにしました。

ADRのフォーマット

フォーマットは https://github.com/moomoo-ya/LADR-template を使わせていただきました。

以下に主なADRを示します。ベテランエンジニアの方々なら、タイトルを見ただけで「あぁ、あるある」と思い当たる節があるのではないでしょうか。各ADRは折りたたんでありますので、気になる項目をクリックして詳細をご確認ください。

なお、これらのADRはこの記事のために作成した例示用のものです。実際の現場ではより詳細な内容や、プロジェクト固有の制約が加わることでしょう。

浮動小数点の使用禁止

adr-01-浮動小数点の使用禁止.md

# Architecture Decision Records: 浮動小数点の使用禁止

## Date - 日付

2024-12-14

## Context - 文脈

プロジェクトで使用している Cortex M0 プロセッサは浮動小数点演算ユニット(FPU)を搭載していない。このため、float や double などの浮動小数点型を使用すると、ソフトウェアエミュレーションによって処理が行われ、以下の問題が発生する:

- ROM サイズの増大
- 実行速度の低下

## Status - 採用状況

Accepted

## Decision - 決定事項

すべてのコードにおいて以下の型の使用を禁止する:

- float
- double
- long double

代わりに以下の手法を使用する:

1. 整数型による演算 (int, long など)
2. ビットシフトを用いたスケーリング
3. 参照テーブルによる近似計算
4. 整数による比例計算

## Consequences - 結果

### メリット

- ROM サイズの削減
- 処理速度の向上
- 予測可能な実行時間

### デメリット

- 固定小数点演算の実装が必要
- 計算精度の制限

除算演算のガイドライン

adr-02-除算演算のガイドライン.md

# Architecture Decision Records: 除算演算のガイドライン

## Date - 日付

2024-12-14

## Context - 文脈

Cortex-M0 プロセッサは除算命令を持たず、コンパイラにより複数の命令に展開される。これは以下の課題を引き起こす:

- 実行時間が長い
- 命令数が多くコードサイズが増加
- 実行時間が一定でない

一方で、除算が必要な計算は一定数存在する。

## Status - 採用状況

Accepted

## Decision - 決定事項

除算を使用する前に、以下の代替手段を検討する:

1. シフト演算での代替
   - 2のべき乗による除算はシフト演算で実装

2. 定数による乗算への変換
   - コンパイル時に計算可能な除数は逆数を乗算に変換

3. テーブル参照
   - 頻繁に使用される値は Look Up Table で代替

上記で対応できない場合のみ除算を使用可能とする。
除算を使用する場合は、使用理由をコメントとして記載する。

コメント例:
    ```c
    // 任意の値での除算が必要なため、
    // シフトや定数乗算での代替が不可能
    result = value / divisor;
    ```

## Consequences - 結果

### メリット

- 処理性能の向上
- コードサイズの削減
- 実行時間の予測性向上

### デメリット

- 実装の複雑化
- コードレビューでの確認項目増加

固定幅整数型の使用義務化

adr-03-固定幅整数型の使用義務化.md

# Architecture Decision Records: 固定幅整数型の使用義務化

## Date - 日付

2024-12-14

## Context - 文脈

プロジェクトでは PC 上でのシミュレータと組み込み機器の両方で動作するコードを扱う。プラットフォーム間で以下の問題が存在する:

- int や long のサイズがプラットフォームにより異なる
- plain charの符号の有無が処理系定義である
- シミュレータと実機で異なる動作をする可能性がある
- デバッグが困難になる

## Status - 採用状況

Accepted

## Decision - 決定事項

### 1. 数値型として使用する場合

以下の型の使用を禁止し、代わりに固定幅整数型を使用する:

禁止する型:
- int
- long
- short
- unsigned int
- unsigned long
- unsigned short
- plain char (数値として使用する場合)

使用する型:
- int8_t, uint8_t
- int16_t, uint16_t
- int32_t, uint32_t
- int64_t, uint64_t

### 2. 文字列処理の場合

文字列処理においては以下のルールに従う:

    ```c
    // OK: 文字列リテラルと文字列処理
    const char* str = "hello";
    char buffer[16];

    // NG: 数値処理としてのplain char
    char value = 65;  // 符号の解釈が環境依存

    // OK: 数値処理には固定幅整数型を使用
    uint8_t value = 65;
    ```

## Consequences - 結果

### メリット
- プラットフォーム間での一貫した動作
- コードの意図が明確
- デバッグの容易さ
- 文字列処理の標準ライブラリとの互換性維持

### デメリット
- 既存コードの修正が必要
- タイピング量の増加
- 用途による型の使い分けが必要

割り込みコンテキストでのメモリアロケーション禁止

adr-04-割り込みコンテキストでのメモリアロケーション禁止.md

# Architecture Decision Records: 割り込みコンテキストでのメモリアロケーション禁止

## Date - 日付

2024-12-14

## Context - 文脈

割り込みハンドラは予測不可能なタイミングで実行され、リアルタイム性と信頼性が要求される。以下の問題が懸念される:

- 動的メモリ確保はヒープロックによりデッドロックの可能性がある
- メモリ断片化によりアロケーション失敗の可能性がある
- スタック使用量の予測が困難になる
- システム全体の応答性が低下する

## Status - 採用状況

Accepted

## Decision - 決定事項

割り込みコンテキストにおいて以下の操作を禁止する:

1. 禁止される関数:
   - malloc()
   - free()
   - new
   - delete
   - その他動的メモリ確保を行う関数

2. 代替手段:
   - 静的メモリ確保
   - プール管理
   - メッセージキュー

## Consequences - 結果

### メリット
- デッドロックの防止
- メモリ使用量の予測性向上
- システムの信頼性向上
- 割り込み応答時間の安定化

### デメリット
- 実装の制約
- 静的メモリ管理の必要性

0除算の実行時チェック義務化

adr-05-0除算の実行時チェック義務化.md

# Architecture Decision Records: 0除算の実行時チェック義務化

## Date - 日付

2024-12-14

## Context - 文脈

組み込みシステムにおいて、0除算は以下の重大な問題を引き起こす可能性がある:

- 未定義動作によるシステムの予期せぬ振る舞い
- ハードウェア例外の発生
- システムのクラッシュやリセット
- デバッグの困難さ

特に製品の出荷後は、このような問題の修正が極めて困難となる。

## Status - 採用状況

Accepted

## Decision - 決定事項

すべての除算処理において、以下のルールを適用する:

1. 除算前の0チェックを必須とする:
    ```c
    // 必須の実装パターン
    if (denominator != 0) {
        result = numerator / denominator;
    } else {
        // エラー処理(状況によって異なる)
    }
    ```

2. 禁止されるパターン:
    ```c
    // 禁止:チェックなしの除算
    result = numerator / denominator;
    ```

3. コンパイル時に確定する定数による除算は除く
    ```c
    // OK: コンパイル時に確定する除算
    const int32_t FACTOR = 100;
    result = value / FACTOR;
    ```

## Consequences - 結果

### メリット
- 実行時の予期せぬ動作の防止
- システムの信頼性向上
- デバッグの容易さ
- 問題の早期発見

### デメリット
- コードサイズの増加
- 実行パフォーマンスへの影響
- コーディング量の増加

ISR命名規則と制約

adr-06-ISR命名規則と制約.md

# Architecture Decision Records: ISR命名規則と制約

## Date - 日付

2024-12-14

## Context - 文脈

割り込みハンドラ(ISR)は以下の特性を持つ:
- 非同期で実行される
- 高い優先度を持つ
- 他の処理を遮断する
- リアルタイム性が要求される

特にログ出力に関して、以下の重大な問題が判明している:
- Zephyr RTTログバックエンドにおいて、特定条件下でミューテックスによるブロッキング待ちが発生
- これによりISRコンテキストで例外が発生するリスクがある
- システム全体の信頼性に関わる問題となる

## Status - 採用状況

Accepted

## Decision - 決定事項

1. 命名規則:
    ```c
    // 必須:_isr サフィックス
    void timer0_handler_isr(void)
    void uart_rx_isr(void)
    ```

2. ISR内での禁止事項:

- ログ出力の禁止(RTTを含むすべてのログバックエンド)

    ```c
    // 禁止:ISR内でのログ出力
    void uart_rx_isr(void) {
        LOG_ERR("Error detected");  // NG: ミューテックスによるブロッキングのリスク
    }
    ```

- デバッグプリントの禁止
- 長時間の処理
- 動的メモリ確保
- ブロッキング処理

3. 推奨される実装パターン:
    ```c
    void timer0_handler_isr(void) {
        // 最小限の処理のみ
        flag_set(EVENT_TIMER);
        // タスクへの通知
        notify_task(TASK_ID_TIMER);
        // ログが必要な場合はタスクコンテキストで実行
    }
    ```

## Consequences - 結果

### メリット
- ISRの明確な識別
- リアルタイム性の確保
- コードレビューの容易さ
- 実行時間の予測性向上
- ミューテックスによる例外の防止
- システムの信頼性向上

### デメリット
- タスクとの連携実装が必要
- コーディングの制約増加
- デバッグ情報の取得に工夫が必要

再帰呼び出しの制限

adr-07-再帰呼び出しの制限.md

# Architecture Decision Records: 再帰呼び出しの制限

## Date - 日付

2024-12-14

## Context - 文脈

組み込みシステムにおいて、スタックには以下の制約がある:
- 物理メモリが限られている
- スタックサイズが少なめに設定されている
- スタックオーバーフローは致命的な問題を引き起こす
- 実行時のスタック使用量の予測が困難

再帰呼び出しは特に以下の問題を引き起こす可能性がある:
- 呼び出し深度によってスタック使用量が変動
- 最悪ケースの予測が困難
- スタックオーバーフローのリスク

## Status - 採用状況

Accepted

## Decision - 決定事項

1. 基本ルール:
- 再帰呼び出しの使用を原則として禁止
- 反復(ループ)による実装を推奨

2. 例外的な許可条件:
- スタック使用量の解析が完了している
- 最大呼び出し深度が明確
- 解析内容を詳細にコメントで記録

3. 許可される実装例:
    ```c
    /*
    * スタック使用量解析結果
    * アーキテクチャ: Cortex-M0+
    * コンパイラ: GCC 10.2.1
    * コンパイラオプション: -Os -g
    * 解析ツール: ARM Compiler 6.16
    * 最大呼び出し深度: 4
    * 1回あたりのスタック使用量: 16バイト
    * 最大スタック使用量: 64バイト
    * 解析日: 2024-12-14
    */
    int32_t factorial(int32_t n) {
        if (n <= 1) return 1;
        return n * factorial(n - 1);
    }
    ```

4. 禁止される実装例:
    ```c
    // NG: スタック解析なしの再帰
    void process_tree(node_t* node) {
        if (node == NULL) return;
        process_tree(node->left);
        process_tree(node->right);
    }
    ```

## Consequences - 結果

### メリット
- スタック使用量の予測性向上
- システムの信頼性向上
- 実行時の安全性確保
- メモリ使用量の明確化

### デメリット
- 一部のアルゴリズム実装が複雑化
- スタック解析の工数が必要
- コード可読性が低下する可能性

### 運用上の注意点
- スタック解析は環境が変わるたびに再実施
- コンパイラのバージョンアップ時は要再解析
- 最適化オプションの変更時は要再解析

ADRについて

ここまでいくつかのADRを例示しましたが、読者の皆さんの現場でも「あるある」な事例が思い浮かんだのではないでしょうか? マイコンの機種固有の問題や、開発環境の制約、プロジェクト特有のノウハウなど、組み込み開発には多くの暗黙知が存在します。

このようなADRをチームで作成・運用していく過程自体が技術継承となります。また、後からチームに参加したメンバーにとっても、「なぜそうしているのか」を理解する貴重な情報源となるでしょう。

さて、せっかく形式知化したこれらのナレッジ。人力でコードレビューに活用するのも良いのですが、これをAIに学習させることで、より効率的なコードレビューができないでしょうか?そう考えた私は、あるツールを試してみることにしました...

AIレビュワーの活用

今回は Anthropic が発表し、話題になっている MCP を使用してリポジトリ内に格納したADR文書をもとにAIにレビューしてもらえないか検討してみました。

プロジェクトの構成

project/
├── adr/                      # ADRを格納するディレクトリ
│   ├── adr-01-浮動小数点の使用禁止.md
│   ├── adr-02-除算演算のガイドライン.md
│   └── adr-03-固定幅整数型の使用義務化.md
│
├── main.c
└── README.md                # プロジェクトの説明

MCPを使ったレビュー

Model Context Protocol (MCP) は、AIモデルに対してコンテキストを提供するための標準化されたプロトコルです。今回はこのMCPを利用して、ADRの内容をAIに理解させ、コードレビューを自動化してみました。

MCPの実装は https://github.com/modelcontextprotocol/servers から入手可能です。今回は以下の2種類のサーバーを使用します:

  • Local File Server: ローカルファイルシステム上のADRファイルを読み込むために使用
  • Git Server: リポジトリの任意の時点での差分を取得するために使用(git_diffがリリース版には提供されていないため、最新ソースをクローンして使用しています)

設定ファイル

claude_desktop_config.json の設定例:

{
    "mcpServers": {
        "git": {
            "command": "/Users/yuito/.local/bin/uv",
            "args": [
                "--directory",
                "/Users/yuito/src/github.com/modelcontextprotocol/servers/src/git",
                "run",
                "mcp-server-git"
            ]
        },
        "filesystem": {
            "command": "/Users/yuito/.volta/bin/npx",
            "args": [
                "-y",
                "@modelcontextprotocol/server-filesystem",
                "/Users/yuito/"
            ]
        },
    }
}

(コマンドが絶対パスで指定しているのは volta が悪さをしているのか PATH が通っているのに環境でコマンドが見つからないことがあったためです。原因の追及はしていませんが、ひとまず絶対パスを指定することで動作するようになりました。)

プロンプト例

コードレビューをしてもらいます
/path/to/git/dir/adr ディレクトリに複数のADR (Architecture Decision Record) があります。それぞれのADRについて、内容を確認してください。
/path/to/git/dir/ ディレクトリで git_diff を使って targetは origin/main..origin/source_branch で差分を取得してください。
この変更点に対して ADR に違反していることがないかチェックしてください。明確にADRに記載されている禁止事項があったときだけ指摘してください。

レビュー例

今回はZephyrの基本的なLチカサンプルに対しての変更をレビューしてもらうこととしました。

main.c(クリックで展開)

/*
 * Copyright (c) 2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>

/* 1000 msec = 1 sec */
#define SLEEP_TIME_MS   1000

/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0)

/*
 * A build error on this line means your board is unsupported.
 * See the sample documentation for information on how to fix this.
 */
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);

int main(void)
{
    int ret;
    bool led_state = true;

    if (!gpio_is_ready_dt(&led)) {
        return 0;
    }

    ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    if (ret < 0) {
        return 0;
    }

    while (1) {
        ret = gpio_pin_toggle_dt(&led);
        if (ret < 0) {
            return 0;
        }

        led_state = !led_state;
        printf("LED state: %s\n", led_state ? "ON" : "OFF");
        k_msleep(SLEEP_TIME_MS);
    }
    return 0;
}

試しに次のような変更点を加えます。Lチカ周期に使用している整数リテラル浮動小数点にしてみました。ちゃんと指摘できるでしょうか?やってみます。

diff --git a/main.c b/main.c
index 4cab496..f4c5e4b 100644
--- a/main.c
+++ b/main.c
@@ -9,7 +9,7 @@
 #include <zephyr/drivers/gpio.h>
 
 /* 1000 msec = 1 sec */
-#define SLEEP_TIME_MS   1000
+#define SLEEP_TIME_MS   1000.0f
 
 /* The devicetree node identifier for the "led0" alias. */
 #define LED0_NODE DT_ALIAS(led0)

結果

実際にやってみると何度かtoolの使用確認が出てきて・・・

最終的にこのような結果が得られました。

はい、承知しました。以下の手順で進めていきます:

1. まずADRの内容を確認します
2. 次にgit diffで差分を確認します
3. 最後に差分がADRに違反していないかチェックします

まず、ADRの内容を確認しましょう:

それぞれのADRの内容を確認していきます:

次に、git diffで差分を確認します:

ADRの内容と差分を照らし合わせて、違反がないかチェックします。

この変更内容を確認したところ、以下のADRに対する明確な違反が見つかりました:

1. ADR-01「浮動小数点の使用禁止」への違反
   - 変更点で `SLEEP_TIME_MS` の値を `1000.0f` と定義していますが、これは明確に浮動小数点数(float型)の使用であり、ADR-01で禁止されている float 型の使用に該当します。
   - ADR-01では、float, double, long double の使用が明確に禁止されています。
   - 代わりに整数値の `1000` を使用するべきです。

つまり、この変更は ADR-01 に違反しており、以下のように修正する必要があります:

    ```c
    #define SLEEP_TIME_MS   1000    // 整数値を使用
    ```

その他のADRについては、この変更による違反は見られません。

ちゃんと指摘できていますね!

GitLab対応MCP Serverを使ったレビューの自動化

上記のClaudeを使ったレビュー結果を手動でGitLabにコピペするのも一つの手ですが、せっかくならレビューコメントの投稿まで自動化したいところです。

GitLabのサーバー自体はMCP純正で提供されていますが、実際のレビューに必要なToolがありませんでした。そこで、GitLab APIを利用したMCP Serverを自作することにしました。

必要なAPIは以下の3種類です: - マージリクエストの詳細取得 - マージリクエストの最新バージョン取得 - ディスカッション(レビューコメント)の投稿

実装したサーバーのコードは以下のリポジトリで公開しています:

github.com

プロンプト例

GitLabプロジェクト **project_id** の マージリクエスト **merge_request_iid** 番のコードレビューをしてもらいます。 
まず **project_dir**/adr ディレクトリに複数のADR (Architecture Decision Record) があります。それぞれのADRについて、内容を確認してください。
マージリクエストの内容を取得してください
マージリクエストのプロジェクトのgitディレクトリは **project_dir** と同期しています。一旦git fetchしてください
マージリクエストの情報から git_diff を使って targetは origin/target_branch..origin/source_branch で差分を取得してください。
この変更点に対して ADR に違反していることがないかチェックしてください。明確にADRに記載されている禁止事項があったときだけ指摘してください。指摘にあたる処理が見当たらなければここで終了です
指摘ごとに以下の手順を繰り返してください
gitの差分から指摘の行番号(複数ある場合は最後の行)を割り出してください。
そして、gitlab discussion を作成してください。
  - base_sha/head_sha/start_sha は get_merge_request_latest_version より取得できます
  - new_path, old_path は指摘したいファイルの名称です
  - new_lineは先ほど割り出した行番号です
  - old_lineは常にnullでお願いします
ADRに違反する項目の指摘事項をまとめbodyに入れてください。

結果

承知しました。順を追って作業を進めていきます。

1. まずADRの内容を確認します:

各ADRの内容を確認します:

2. マージリクエストの内容を取得します:

3. gitのfetchを実行します:

4. 差分を確認します:

5. バージョン情報を取得します:

ADRの内容とマージリクエストの差分を確認したところ、以下のADR違反が見つかりました:

1. ADR-01「浮動小数点の使用禁止」に違反しています。

コードでは `1000.0f` という浮動小数点数を使用していますが、ADR-01では明確に float の使用を禁止しています。

この違反について、GitLab discussionを作成します:

これにて、すべてのADR違反の確認と指摘が完了しました。

ちゃんと投稿できました。

完全自動レビュー対応

ここからは少し遊び心のある実装をご紹介します。実用的かどうかは読者の皆さんの判断にお任せしますが...。

現状のMCPを使ったレビューでは、以下のような手間が残っています:

  • プロジェクトIDとマージリクエストIDの指定
  • 連動するGitリポジトリディレクトリパスの指定
  • 新規チャット時の "Allow for This Chat" ボタンの連打

これらを自動化できないか考えました。そこで、MacAppleScriptを活用して完全自動化に挑戦してみました:

  1. GitLab CIのRunner環境としてMacを設定
  2. AppleScriptで:
    • Claudeの起動
    • プロンプトの入力自動化
    • "Allow for This Chat" ボタンの自動クリック

GitLab CIの設定例です。

workflow:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' # MRイベントの場合のみ実行

auto_review:
  stage: build
  script:
    - osascript ./script/auto-reviewer.scpt ${CI_PROJECT_ID} ${CI_MERGE_REQUEST_IID} ${CI_PROJECT_DIR} & PID=$!;
    - osascript ./script/monitor-process-and-directory.scpt $PID ${CI_PROJECT_DIR}
  tags:
    - advent-calendar
  timeout: 300s

さらに、無機質になりがちなレビューコメントに遊び心を加えてみました。プロンプトに含む構文ファイルさえ用意すれば色々な構文でレビューしてくれます。

実装上の注意点

実装したスクリプトを使用する際には、以下の点に注意が必要です:

  1. Electronアプリへのアクセシビリティのため:

  2. 初期設定時の注意:

  3. パフォーマンスの課題:

    • プロンプト実行完了時にUI要素の監視が重くなる現象が発生
    • 対策として別スクリプトを並列実行
    • プロンプト完了は .done ファイルの出力で検知

実装したAppleScriptは以下のリポジトリで公開しています:

github.com

これによって誰かがMRを作成すると私のmacの画面がぴょこぴょこ動いてしまい全く実用的ではありませんが余っているMacがあれば、個性的なAIレビュワーとして活躍させてみてはいかがでしょうか?

記事の終わりに変更差分とAIレビュアーが行った指摘を並べて終わりとさせていただきます。

0除算

差分

ユーザー入力の数値で割り算を実行する

diff --git a/main.c b/main.c
index 4cab496..f92c2a2 100644
--- a/main.c
+++ b/main.c
@@ -34,6 +34,12 @@ int main(void)
                return 0;
        }
 
+       int32_t denominator;
+       printf("Enter the interval denominator: ");
+       scanf("%d", &denominator);
+
+       interval = SLEEP_TIME_MS / denominator;
+
        while (1) {
                ret = gpio_pin_toggle_dt(&led);
                if (ret < 0) {
@@ -42,7 +48,7 @@ int main(void)
 
                led_state = !led_state;
                printf("LED state: %s\n", led_state ? "ON" : "OFF");
-               k_msleep(SLEEP_TIME_MS);
+               k_msleep(interval);
        }
        return 0;
 }

指摘

ちいかわ構文

割り込みコンテキスト

差分

タイマーによるLチカをしちゃった例

diff --git a/main.c b/main.c
index 4cab496..db7cd67 100644
--- a/main.c
+++ b/main.c
@@ -19,11 +19,31 @@
  * See the sample documentation for information on how to fix this.
  */
 static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
+struct k_timer my_timer;
+
+void timer_expiry_function(struct k_timer *dummy)
+{
+       static bool *led_state = NULL;
+
+       if (led_state == NULL) {
+               led_state = (bool *)malloc(sizeof(bool));
+               *led_state = false;
+       }
+
+       int ret = gpio_pin_toggle_dt(&led);
+       if (ret < 0) {
+               return;
+       }
+
+       led_state = !led_state;
+       printf("LED state: %s\n", led_state ? "ON" : "OFF");
+}
+
+K_TIMER_DEFINE(my_timer, timer_expiry_function, NULL);
 
 int main(void)
 {
        int ret;
-       bool led_state = true;
 
        if (!gpio_is_ready_dt(&led)) {
                return 0;
@@ -34,15 +54,10 @@ int main(void)
                return 0;
        }
 
+       k_timer_start(&my_timer, K_MSEC(SLEEP_TIME_MS), K_MSEC(SLEEP_TIME_MS));
+
        while (1) {
-               ret = gpio_pin_toggle_dt(&led);
-               if (ret < 0) {
-                       return 0;
-               }
-
-               led_state = !led_state;
-               printf("LED state: %s\n", led_state ? "ON" : "OFF");
-               k_msleep(SLEEP_TIME_MS);
+               k_sleep(K_FOREVER);
        }
        return 0;
 }

指摘

printfも使っているので複数の指摘

ちいかわ構文

mallocの指摘

おじさん構文

再帰呼び出し

差分

Lチカを再帰呼び出しで実現した意欲作

index 4cab496..46e1637 100644
--- a/main.c
+++ b/main.c
@@ -20,10 +20,26 @@
  */
 static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
 
+void on_led(void);
+void off_led(void);
+
+void on_led(void){
+               gpio_pin_set_dt(&led, 1);
+               printf("LED state: ON");
+               k_msleep(SLEEP_TIME_MS);
+               off_led();
+}
+
+void off_led(void){
+               gpio_pin_set_dt(&led, 0);
+               printf("LED state: OFF");
+               k_msleep(SLEEP_TIME_MS);
+               on_led();
+}
+
 int main(void)
 {
        int ret;
-       bool led_state = true;
 
        if (!gpio_is_ready_dt(&led)) {
                return 0;
@@ -34,15 +50,8 @@ int main(void)
                return 0;
        }
 
-       while (1) {
-               ret = gpio_pin_toggle_dt(&led);
-               if (ret < 0) {
-                       return 0;
-               }
 
-               led_state = !led_state;
-               printf("LED state: %s\n", led_state ? "ON" : "OFF");
-               k_msleep(SLEEP_TIME_MS);
-       }
+       on_led();
+
        return 0;
 }

指摘

ツンデレ構文


株式会社フォトシンスでは、一緒にプロダクトを成長させる様々なレイヤのエンジニアを募集しています。 photosynth.co.jp Akerunにご興味のある方はこちらから akerun.com