Agent Skillsと歩むAndroidのUI実装

こんにちは あるいは こんばんは。これは Kyash Advent Calendar 202520日目の記事です。
KyashでAndroidエンジニアをしている 牧山(@_rmakiyama)です。

最近はもっぱらClaude CodeやCodexといったAIエージェントと戯れる日々を過ごしています。いい時代ですね。Kyashの開発チーム全体としても、こうしたAIツールを開発フローにどう組み込んでいくか、日々模索が続いています。

しかし、モバイルアプリのUI実装の領域においては、なかなか思ったようなアウトプットが出ず、活用が思うようにできていない状態でした。
個人的にはUI実装はとても楽しい時間ではあるものの、チームのアウトプットを最大化するうえでとても伸びしろのある部分だと感じています。

そこで、どうするとうまく付き合えるのか試行錯誤している途中経過を紹介します。

前提として

Android開発におけるUI実装での試行錯誤になります。デザインにはFigmaを使っており、これをClaude CodeからFigma Dev Mode MCPを利用してデザインデータを取得しています。

ただFigma Dev Mode MCPを利用しても、なかなかどうして活用が進みませんでした。

思うようなアウトプットではないとは?

試行錯誤をするにあたり、AIが生成するUI実装のコレジャナイ感について考えてみると、大きく2つの課題がありました。

  • 構造化の欠如
    • 1つの巨大なComposable関数に全てベタ書きされる
    • デザインの意図が構造に現れていない
  • UI実装におけるコンテキストの欠如

これらは場合によって周辺のコードを読んで、よしなに適用してくれることもありますが、LLMの非決定性に左右されるためうまく制御できているとは言えません。

最初に試したこと

思ったようなアウトプットを出すには、LLMの決定性を高めることが重要だと感じました。そこでまずは2つのアプローチを試してみました。

  1. UI の構造を先に人間が設計する
  2. FigmaトークンとJetpack ComposeのTheme実装のマッピングをドキュメント化する

1. UI の構造を先に人間が設計する

思ったようなアウトプットに感じないときの原因のひとつの、「構造化の欠如」の検証として、自分で構造だけを先に定義してから残りを実装してもらう実験をしてみました。

未リリースの機能開発をしながらトライをしているので、ここではAIに内容を塗り替えてもらって紹介します。
Kyashでは、FragmentにComposeを乗せる実装になっていて、Scaffoldを持つScreen関数の中にSections関数があるイメージです。このSectionsの構造を下記のように準備しました。

@Composable
fun RecipeDetailSections(...) {
    Column(...) {
        RecipeHeader(....)
        SectionDivider()
        CookingInfoSummary(...)
        SectionDivider()
        IngredientsSection(...)
        SectionDivider()
        CookingStepsSection(...)
    }
}

@Composable
private fun RecipeHeader(
    recipeName: String,
    modifier: Modifier = Modifier,
) {
    TODO()
}

...

あとは各セクションの中身を実装していくだけなので、かなりお膳立てしている状態ではあります。

プロンプトとしては、なにができないかも見る意味で、詳細な指示は一切せずに投げています。マスクしていますが実際に下記のように依頼しています。

Figmaで選択している画面の実装をしたいです。
今回のスコープは、XXXと部分と、YYYからZZZまでのUIです。実装してください。

結果のスクリーンショットは割愛しますが、いい精度のアウトプットが出てきました。粗はありますが、繰り返しになるスタイリングを避けるために、コンポーネントを作ってくれていたのには感心しましたね。これは周辺のコードをうまく参考にしたようです。
今回はあえてFigmaトークンと実装のマッピングをしていませんでしたが、8割くらいはうまく合わせてくれていました。

想定通りではあるものの、FigmaトークンとマッピングしづらいTheme実装や、定義済みのデザインコンポーネントの利用がされていないなどは見受けられたので、これをやるだけでも人間の修正はだいぶ少なく済みそうです。

2. FigmaトークンとTheme実装のマッピングをドキュメント化する

次に、もうひとつの課題であった「UI実装におけるコンテキストの欠如」の検証として、Figmaトークンと実装のマッピングを行いました。

KyashではColorやTypographyなどをKIG (Kyash Interface Guidelines) としてFigmaトークンで用意しており、Androidではこれを実装に落とし込んでいます。詳細は割愛しますが、興味があればこちらもご参照ください。
これらのマッピングをマークダウンファイルとして用意し、プロンプトでファイル参照することで解決を試みました。

実際のドキュメントの一部を抜粋して紹介します。

# KIGテーマシステムリファレンス

## 概要

KIG (Kyash Interface Guidelines) デザイントークンの実装リファレンス。

**アクセス方法:**
- Colors: `KyashTheme.colors.primary`
- Typography: `KyashTheme.typography.body`
- Spacing: `KyashTheme.spacing.m`
- Shapes: `KyashTheme.shapes.medium`

## Colors

### Material 系

| Figmaトークン | 実装プロパティ | 値 |
|--------------|---------------|-----|
| Color/Fill/Primary-Blue | `KyashTheme.colors.primary` | #209FFF |
| Color/Fill/Primary-Blue (Light) | `KyashTheme.colors.primaryLight` | #DBF0FF |
| Color/Fill/Success-Green | `KyashTheme.colors.secondary` | #00BF9D |
...

...

## Spacing

| Figmaトークン | 実装プロパティ | 値 |
|--------------|---------------|-----|
| SpacingXXXS | `KyashTheme.spacing.xxxs` | 2.dp |
| SpacingXXS | `KyashTheme.spacing.xxs` | 4.dp |
| SpacingXS | `KyashTheme.spacing.xs` | 8.dp |
...

## Typography

| 実装プロパティ | サイズ | 行高 |
|---------------|--------|------|
| `KyashTheme.typography.largeTitle` | 40sp | 1.5em |
| `KyashTheme.typography.title1` | 36sp | 1.5em |
| `KyashTheme.typography.title2` | 24sp | 1.5em |
...

今回も同様に、プロンプトは、詳細な指示をせずに投げています。実際のプロンプトは下記のように依頼しています。

@feature/ooo/src/main/java/co/kyash/xxx/yyy/XxxScreen.ktの実装をしてください。
対象の画面デザインは下記です。
https://www.figma.com/design/1234567-xxx

@docs/kig_design_token_reference.md を見てThemeを参照するようにしてください。

UIの詳細はSectionsファイルを作り、Sections関数を定義して詳細はそちらに実装してください。

こちらもスクリーンショットを割愛しますが、値をベタ書きすることはなくなり、すべてKIGのTheme実装を使ってくれるという結果になりました。目論見が達成できて嬉しい。今回はTheme実装とのマッピングのみをしましたが、コンポーネント実装の情報も与えると精度向上が期待できそうです。

学び

ここまでの試行錯誤を通して、知識やノウハウ、コンテキストを適切に与えると、わりと精度の高いアウトプットが得られることがわかってきました。

しかし、UIの構造を考える部分を委譲できていなかったり、コンテキストが断片的になっていてUI実装自体が体系化されていなかったりと、あまりスケールしない状態でした。

次に、チームでうまくワークさせるには、という観点からトライをしてみました。

Agent Skills というアプローチ

最初はClaude CodeのSubagentsを活用することを考えていました。しかし、UIの構造を考えるうえでのノウハウや参考の実装、FigmaトークンとTheme実装のマッピング表などのコンテキストをすべて含めていくと、わりと大きめのSubagentになりそうという感覚がありました。

ちょうどこのころAnthropicがSkillsを発表し、下記の観点からUI実装にも使えそうという判断をしました。

  • 段階的な情報開示による効率的なコンテキスト消費
  • UI実装というワークフローを専門知識としてパッケージングできる
  • 汎用性をもたせ複数のSubagentsからも利用可能

Agent Skillsの説明については割愛します。これらの記事がとても参考になりました!

UI実装フローの整理

Skillsを作っていくにあたり、まずは自分がいつもどのようにUI実装をしているかを整理しました。

ざっくりこんな感じのフローです。

ここで、自身が開発するときに1番気を使っているのが「セクションの構造を実装」するフェーズです。「思うようなアウトプットではない」と感じるときも、ここのギャップが1番大きいと感じていました。

フローを整理していくと、UI実装には各フェーズにおいてさまざまな知識やノウハウが詰まっていることに気づきます。どうりでただ「Figmaをもとに実装して。〇〇を参考にして。」くらいのラフなプロンプトでうまくいくほうが珍しい。Skillsを作っていく前にここを自分の中でも洗い出していきました。

知識の言語化の一例

AndroidのUI実装では、Jetpack Composeが使われます。Composeではこう書くよな、よく間違えるよな、学びたてのときはよく悩んだな、というポイントを補足していきます。

たとえば、クリック可能な要素にはModifier#clickableをよく使いますが、これはModifierのチェーンの順序で見た目の結果が変わります。一例ですが下記のような違いがあります。

// paddingを含めた範囲全体がクリック可能
Modifier
    .fillMaxWidth()
    .clickable { }
    .padding(16.dp)

// paddingの内側だけがクリック可能
Modifier
    .fillMaxWidth()
    .padding(16.dp)
    .clickable { }

そのほかにも、よく知られた知識はいくつもあります。自分はよくAPI Guidelines for @Composable components in Jetpack Composeを参照するのでこれもうまく取り込むことを考えました。

ノウハウの言語化の一例

UI実装を行うとき、どういうことを考えて実装しているかを考えてみました。フローにも示していますが、Figmaを見て、UIの構造を把握して、構造を実装しています。ここの暗黙知を与えてあげると、気になっていた「構造化の欠如」が解決するのではと考えました。

自分が考えていることを整理してみると、大きく5つのポイントを意識していることがわかりました。

  • デザインの4大原則を適用する
  • 視覚的なまとまりを捉える
  • 責任の分離を意識する(Single Responsibility)
  • 複雑度のバランスを整える
  • 抽象度を揃える(SLAP: Single Level of Abstraction Principle)

ただFigmaの見た目だけを表現できるコードを書くのではなく、開発者がコードからUIの構造を把握しやすく、デザインの意図を汲んだ構造にすることで、保守性や変更容易性を高めることを意識しています。

画面のトップレベルの例としては、下記のようなイメージです。

// good
Column(...) {
  ProfileAvatarSection()
  PersonalInfoCard()
  SectionDivider()
  LinkedAccountsSection()
  SectionDivider()
  DangerZone()
}

// good
Column(...) {
  UserIdentitySection()
  SectionDivider()
  AccountSettingsSection()
}

// not good: 上からすべて読まないと理解できない
Column(...) {
  Box(...) {
      Image(...)
      Icon(...)
  }
  Card(...) {
      // ...
  }
  // ...
}

// not good: 細かすぎる分割
Column {
  ProfileImage()
  EditBadge()
  UserNameText()
  NameField()
  EmailField()
  PhoneField()
  GoogleAccountRow()
  AppleAccountRow()
  AddAccountButton()
  LogoutButton()
  DeleteAccountLink()
}

Agent Skills 全体の構成

これらをベースに詳細を言語化し、Claude Codeが同じようなフローができるようにステップを組み、それぞれのタイミングで適切な知識を追加できるようなSkillsを目指しました。

スキルのファイル構成はこのようになっています。

figma-to-compose
├── examples
│   ├── screen-structure.md
│   └── section-grouping.md
├── references
│   ├── android-resources.md
│   ├── compose-best-practices.md
│   ├── kig-components.md
│   └── kig-theme-system.md
└── SKILL.md

自身のUI実装フローを整理してできたステップごとに、Kyashならではのコンテキストや自身の知識やノウハウ、参考実装などを references や examples として配置しています。これらはオンデマンドでロードされる点がSkillsの良いところですね。
たとえば、前述したFigmaトークンとTheme実装のマッピングkig-theme-system.mdとして references に配置しています。セクションわけのノウハウはsection-grouping.mdとしてサンプルコードとともに examples に配置しています。

SKILL.mdは、Claudeの作業に合わせて下記のようなステップに分けています。

ステップごとに、適宜必要な references や examples への参照を示し、適切なタイミングで知識・ノウハウをロードしてもらう構成となります。

試行錯誤ポイントとしては、Claude Codeに依存してしまいますがAskUserQuestionを使うように指示をして、HITL(Human-in-the-Loop)を挟むようにしています。
たとえばStep3では、UIをセクション分割して構造化してもらいますが、分割の仕方には人間でも悩むことがあります。そのため、いくつか選択肢を考えてもらい、その特徴や考え方の背景を示してもらって、人間が選択できるようにしています。
また、Step5では、類似の実装があれば開発者に示すように指示しています。最近同じような実装書いたな、昔たしか似たところで工夫して実装したな、などは経験からくるものがあるため、無作為にコードを検索してもらうより精度が高くなるのではないかと考えています。

Skillsのアウトプット

実際に既存の画面の実装を手元で削除して、Skillsを使って作り直してもらいました。
まずはそのアウトプットについて、FigmaJetpack ComposeのPreviewで比較してみます。

Figma アウトプット Preview

そこまで複雑ではない画面ですが、かなり精度が高いと言って良さそうです。
デザインの再現度もそうですが、UIのコードとしても構造化がされていて、ここについても大きく前進した感覚があります。

この実装中に、セクションのグルーピング提案のフェーズで実際に Claude Code が提案した内容をそのまま紹介します。

LoanTopScreenのセクショングルーピングとして、以下のどちらが適切ですか?

**オプション1: 細かめのセクション分割**
\```kotlin
@Composable
fun LoanTopSections(
    state: LoanTopReactor.State,
    onApplyClick: () -> Unit,
    onUsageHistoryClick: () -> Unit,
    onNextRepaymentDetailClick: () -> Unit,
    onEarlyRepaymentInfoClick: () -> Unit,
    onEarlyRepaymentClick: () -> Unit,
    modifier: Modifier = Modifier,
) {
    Column(modifier = modifier) {
        // 上部: 入金可能額 + ボタン
        BorrowableAmountSection(
            borrowableAmount = state.displayBorrowableAmount,
            applyEnabled = state.applyEnabled,
            onApplyClick = onApplyClick,
        )
        // カード: 利用額 + 次回支払い
        UsageAndRepaymentCard(
            borrowingAmount = state.displayBorrowingAmount,
            nextRepaymentInfo = state.nextRepaymentInfo,
            onUsageHistoryClick = onUsageHistoryClick,
            onNextRepaymentDetailClick = onNextRepaymentDetailClick,
        )
        // カード: 繰り上げ返済
        if (state.shouldShowEarlyRepayment) {
            EarlyRepaymentCard(
                onInfoClick = onEarlyRepaymentInfoClick,
                onClick = onEarlyRepaymentClick,
            )
        }
    }
}
\```

**オプション2: よりシンプルな分割**
\```kotlin
@Composable
fun LoanTopSections(
    state: LoanTopReactor.State,
    onApplyClick: () -> Unit,
    onUsageHistoryClick: () -> Unit,
    onNextRepaymentDetailClick: () -> Unit,
    onEarlyRepaymentInfoClick: () -> Unit,
    onEarlyRepaymentClick: () -> Unit,
    modifier: Modifier = Modifier,
) {
    Column(modifier = modifier) {
        // 上部パート
        UpperSection(
            borrowableAmount = state.displayBorrowableAmount,
            applyEnabled = state.applyEnabled,
            onApplyClick = onApplyClick,
        )
        // 下部パート(カード群)
        LowerSection(
            borrowingAmount = state.displayBorrowingAmount,
            nextRepaymentInfo = state.nextRepaymentInfo,
            shouldShowEarlyRepayment = state.shouldShowEarlyRepayment,
            onUsageHistoryClick = onUsageHistoryClick,
            onNextRepaymentDetailClick = onNextRepaymentDetailClick,
            onEarlyRepaymentInfoClick = onEarlyRepaymentInfoClick,
            onEarlyRepaymentClick = onEarlyRepaymentClick,
        )
    }
}
\```

このSkillsを使ってみると、HITLがいい味を出していて、コードの構造を先にチェックできるのがとても良い。UpperSectionなど命名としてもうちょっと考慮必要だな、というような改善は見えますが、ここはのびしろですね。
構造に納得がいっていると、ちょっとUIの修正をするときに、コンポーネント単位で修正ができる体験がとても良かったです。

これから

現時点では個人の試行錯誤に目処をつけ、チームに共有をして使っていこう!というフェーズです。
今のワークフローは、チームというよりは私個人のノウハウを詰め込んだという状態です。Kyashのモバイルチームとしてフィットさせスケールさせていくために、チームでスキルを使用し、フィードバックサイクルを回したいと考えています。

悪くないアウトプットは出ていますが、まだまだのびしろはあると考えています。

  • Skillsのブラッシュアップ
    • 知識・ノウハウの補填
    • 適切な自由度があるか
    • 適切なガードレールがあるか
    • デザイナーとの連携
    • Claude Codeに依存しすぎない設計
  • Skillsの粒度の検討
    • 今回は一気通貫型でトライ
    • スキルとして切り出して汎用性を高めることも検討
  • LLMに委譲するラインを見極める
    • 完璧なUIを実装させることを目指さない
    • (今はまだ)人間がやったほうがうまくいく作業もある

すべてをAIに任せるんだ!ではなく、どううまく業務に溶け込ませられるかを探っていきたいです。
モバイルのUI実装での知見をたくさん聞きたいので、目を輝かせてお待ちしております!


仲間を募集しています!

Kyashでは一緒に最高のプロダクト、最高の組織にしていくぞ!という仲間を募集しています 🙌🏻
ちょっとでも興味がある、そんな人は、面識あるなしにかかわらずせび気軽にお話しましょう!

求人一覧はこちらから 🙋🏻‍♂️