0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【AI駆動開発#2】組込みエンジニアがPythonでUDS診断ツールを作った話 〜AIにコードを頼む前に設計が必要だった〜

0
Posted at

2026年5月頃、Claude.ai(チャット)を使って趣味で作ったツールの開発記録です(全4回の第2回)。

はじめに

コードを書く前に、最初にAIへ渡すプロンプトを作りました。今回はその中身と、レイヤー設計について書きます。


過去の失敗

以前AIでコードを書いたときは、次のような失敗がありました。

失敗 原因
実行するとエラーになる 要件が曖昧なままコードを生成させた
全機能を一度に作ってエラーだらけ 段階的に進める指示がなかった
使い方がわからない 説明を求めなかった
出力の上限で途中で止まる 1回の依頼が大きすぎた

最初のプロンプト

上の失敗を踏まえて、最初のプロンプトに次の内容を入れました。

■ 役割・目的
  シニアエンジニアとしてUDS診断ツールをTDD+ATDDで開発する

■ アーキテクチャ要件
  Application / UDS / Protocol / Transport の4層構成
  DoIPとUDS on CANをStrategyパターンで切り替え可能にする

■ 開発ステップ(段階的に)
  Step1: 0x22のみ
  Step2: 0x10, 0x3E
  Step3: GUI追加
  ...

■ 開発手順(1Stepごとに)
  テスト作成 → 最小実装 → リファクタリング
  → README更新 → Gitコミット用ファイル出力

組込み開発でいう、要件定義書と開発計画書を合わせたものです。1回の依頼が小さくなり、Stepごとに動作確認しながら進められました。


レイヤー設計

┌─────────────────────────────────────────┐
│ Application Layer  DiagnosticService    │
├─────────────────────────────────────────┤
│ UDS Layer          ReadDataByIdentifier │
│                    ECUReset, ClearDTC...│
├─────────────────────────────────────────┤
│ Protocol Layer     ProtocolBase(I/F)    │
│                    DoIPProtocol         │
├─────────────────────────────────────────┤
│ Transport Layer    TCP / CAN            │
└─────────────────────────────────────────┘

組込み開発のレイヤーに当てはめると次のとおりです。

ツールのレイヤー 組込み開発で近いもの
Application Layer アプリケーション層
UDS Layer ミドルウェア
Protocol Layer HAL
Transport Layer ドライバ

レイヤーを分けたことで、AIへの依頼を「このレイヤーのこのクラスを実装して」という単位にできました。依頼が小さいほど、出力の確認も楽になります。


Strategyパターンで通信方式を切り替える

# Protocol層の抽象インターフェース
class ProtocolBase(ABC):
    @abstractmethod
    def connect(self) -> None: ...
    @abstractmethod
    def send(self, uds_payload: bytes) -> None: ...
    @abstractmethod
    def receive(self) -> bytes: ...

# DoIP実装
class DoIPProtocol(ProtocolBase):
    def connect(self) -> None:
        self._client = DoIPClient(
            ecu_ip_address=self._host,
            ecu_logical_address=self._target,
            tcp_port=self._port,
        )
    def send(self, uds_payload: bytes) -> None:
        self._client.send_diagnostic(uds_payload)
    def receive(self) -> bytes:
        return self._client.receive_diagnostic()

# UDS on CANを作る場合(同じI/Fを実装する。今回は未実装)
class UDSonCANProtocol(ProtocolBase):
    def connect(self) -> None: ...
    def send(self, uds_payload: bytes) -> None: ...
    def receive(self) -> bytes: ...

上位のDiagnosticServiceは、どのプロトコルを使っているかを知りません。

# 渡すプロトコルを変えるだけで通信方式が変わる
service = DiagnosticService(protocol=DoIPProtocol(...))
service = DiagnosticService(protocol=UDSonCANProtocol(...))  # 実装した場合
result  = service.read_data_by_identifier(did=0xF190)

上位が下位の詳細を知らずに済む、という点はHAL設計と同じです。


ライブラリを使うか、自前で作るか

最初は学習のために自前で実装するつもりでしたが、途中でライブラリを使う方針に変えました。

観点 自前実装 ライブラリ
保守性 SIDを追加するたびに修正が必要 ライブラリ側が対応
信頼性 バグを作り込むリスクがある 利用実績がある
開発速度 フレーム処理の実装に時間がかかる ツール本体の処理に集中できる

役割分担は次のようにしました。

udsoncan    → UDSフレームの生成・解析
doipclient  → DoIPフレームの生成・TCP通信
(どちらもProtocol層・UDS層のクラスでラップする)

AIが間違えた例

AIが書いた requirements.txt のパッケージ名が間違っていました。

# AIが書いたもの(間違い)
python-udsoncan>=2.0

# 正しいパッケージ名
udsoncan>=1.0

pip install でエラーになって気づきました。GitHubのリポジトリ名が python-udsoncan なので、それと混同したと思われます。

AIの出力は、実行してみるまで正しいかわかりません。実行して確かめる手段として、テストを用意しました(#3で説明します)。


まとめ

  • 最初のプロンプトに、設計方針と開発ステップを書いた
  • レイヤーを分けたことで、AIへの依頼を小さくできた
  • AIは間違えるので、実行して確認する前提で進めた

次回

#3では、実ECUなしでテストするために作ったECUシミュレータについて書きます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?