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枚だけだった🌱 ~「AI に答えさせてはいけない6件」の話~

0
Posted at

前セツ

どうも、今年あんまりスズメバチみてないな~ な、え~すけさんですよ。
※毎年通勤時に執拗に追いかけられることがあったが、今年まだないので命がヒリヒリしない。。。

前回、自作フレームワーク hatake + AI で業務システムを1本まるごと作って、
21.0人日 → 3.75人日になった話を書きました。

で、たぶん一番知りたいのはここだと思うんですよ。

で、人は具体的に何をしたの?

定義 345行もアプリ 1,052行も AI が書いたんですが、AI に依頼を出したのは人です。
その人が何を用意して、何を決めて、何を投げたか。
これが残ってないと誰も真似できないので、全部リポジトリに残しました。

→ https://github.com/ASIL-E-Hatake/hatake-example

この記事はその解説です。
※ hatake の話というより「AI に業務システムを作らせるときの分担」の話なので、
別のツールを使ってる人にもだいたい当てはまるはずです。たぶん。

結論:人の仕事は3か所だけ

[人] 案件の説明を書く(普通の日本語でいい)
  ↓
[AI] 枠組みの外を仕分ける(認証・締め処理・採番…)
  ↓
[人] 前書き(hatake.project.yaml)を育てる ← 下書きは AI が起こす
  ↓
[AI] 定義を書く → 自分で読み返して直す
  ↓
[人] 返ってきた問いに答える ← ★ここが本番
  ↓
[AI] 設計資料を起こす(画面一覧・遷移図・設計書・API 一覧)
  ↓
[人] 何を確かめるかを決める → [AI] 回して記録を作る

人がやるのは ① 案件の説明を書く ② 前書きを育てる ③ 問いに答える の3つ。
あとは道具が答えます。

そして人にしか書けない紙が2枚あります。

案件の説明 と 案件の前書き(hatake.project.yaml)。
ここを AI に埋めさせると、それっぽいけど誰も決めていないことが設計書として残ります。

ここが今日いちばん言いたいところです。

① 案件の説明を書く(人・57行)

普通の日本語で、知ってることを書き出すだけ。書式は要りません。
実際に書いたのはこんな感じです。

# どんなシステムか

人事部と購買部が使う、社内マスタの管理画面。いまは Excel で管理していて、
更新のたびに情シスに依頼が来ている。これを自分たちで直せるようにしたい。

利用者は社内だけ。同時に使うのは10人くらい。

# 管理したいマスタ

## 社員マスタ
社員番号・氏名・カナ・所属部署・役職・入社日・退職日・メール・内線・在籍区分。
- 社員番号は6桁の数字。あとから変えられない
- 退職者も残す(消さない)。一覧では既定で**在籍者だけ**を出す
- 入社日 ≤ 退職日

# 決まっていないこと

- 同時に同じレコードを直したときにどうするか
- 社員番号の採番を誰がやるか(人事が手で決める? 自動?)
- 退職者の扱い(消す? 残す?)── たぶん残す
- 与信限度額の端数

「できないこと・やらないこと」が一番大事です。
書かないと AI は書ける方に倒すので。

ログインは社内の Active Directory を使いたいが、今回は間に合わないので
**ID とパスワードをこのシステムで持つ**。将来 AD に差し替える。

あと「決まっていないこと」を決まっていないと書いていいのもポイントです。
「たぶん残す」でいい。後で AI に聞かれるので。

② 枠組みの外を仕分ける(AI に投げる)

投げた依頼文、そのまま:

docs/1-要件定義/案件の説明.md を hatake where --from で仕分けて、
**枠組みの外のもの**を教えて。

外と出たものは定義に書かずに、前書きの premises か logic に残したい。

この案件では 7件が「枠組みの外」 と出ました。いちばん大きいのが認証:

[枠組みの外(hatake は持たない)] 認証(ログイン・トークンの発行)
  hatake は持たない。ログイン画面は定義で作れるが、資格を確かめるのはサーバか認証基盤。

hatake は業務ロジックも承認フローも認証も持たないと決めてるので、
ここで「外」と出たものを定義に書かせようとすると、
AI は画面の中に業務ロジックを書き始めます。

これ、動いちゃうから気づきにくくて、あとで剥がすのがいちばん高いんですよ。
先に仕分けるのはそのためです。

※ 道具が当たりすぎることもあります。 「与信限度額は金額(カンマ区切りで見せたい)」まで
「業務ロジック(与信)」に当たった。これは見せ方の話なので定義で書ける。
仕分けは下書きで、読むのは人です。全部鵜呑みにしない。

③ 前書きを育てる(人・149行)

hatake.project.yaml = 定義を書く前に AI に読ませる1枚です。

節 何を書くか 機械が見るか
system.what / users 何のシステムか・誰が使うか 見ない(人と AI が読むだけ)
system.premises できないこと 見ない
glossary 業務の言葉 → 定義に書く名前 見る(定義と突き合わせる)
logic 業務ロジックの置き場(中身は書かない) 見る
naming 名前の形(camelCase など) 見る

実物の一部:

system:
  premises:
    # **できないこと**を書く場所。書かないと、AI は書ける方に倒す。
    - ログインは hatake の外。画面は定義で作るが、資格を確かめるのは API 側。
    - 社員番号は採番しない。人事が決めた番号を手で入れる(既存の Excel と揃えるため)。
    - 退職者は消さない。在籍区分を「退職」にするだけ。
    - 監査(誰がいつ何を直したか)は API 側で残す。画面は残さない。

glossary:
  # 「業務の言葉 → 定義に書く名前」。同じものが画面ごとに違う字になるのを止める。
  - term: 在籍区分
    field: employmentStatus
    avoid: [ステータス]
    note: 在籍 / 休職 / 退職。「退職フラグ」とは呼ばない(3値あるので)

logic:
  # 業務ロジックの**置き場**だけを書く(規則の中身は書かない)。
  - what: ログインして資格を確かめる
    where: outside
    why: hatake は認証を持たない。画面は作れるが、確かめるのは API と認証基盤

下書きは AI に起こさせていいです。ただし言い方が大事:

hatake project --draft --from docs/1-要件定義/案件の説明.md で下書きを起こして。
ただし**私が言っていないことは書かないで**。

返ってくる下書きは薄いです。 資料の見出しから拾えるところしか拾わないので、
ほとんど空で返ってくる。それでいいんですよ。
想像で埋まって返ってくるほうが困るので。育てるのは人。

ここで踏んだ地雷2つ

決めごとは「守れるものだけ」書く。

naming.suffix.number: Amount と書いたら、creditLimit を creditLimitAmount に
しろと言われ続けました。業務の言葉から離れるので、こっちが折れてやめた。

守れない決めごとを書くと助言が鳴りっぱなしになって、そのうち誰も読まなくなります。
これが一番マズい。

辞書の見出しは広く取りすぎない。

glossary の term と avoid は部分一致で効くので、「区分」を見出しにすると
「在籍区分」まで巻き込みます。実際やらかして、
「employmentStatus は supplier で始めろ」と言われ出しました。🤦

  - term: 取引先区分
    field: supplierType
    avoid: [種別, タイプ]
    note: >-
      法人 / 個人。**見出しを「区分」にしない**(在籍区分まで巻き込んで、助言が
      「employmentStatus は supplier で始めろ」と言い出す。実際に一度そうなった)

このコメント、実際に前書きに残ってます。踏んだ地雷はその場に書いておくのがいい。

④ 定義を書かせる(AI の仕事)

投げた依頼文:

definitions/hatake.project.yaml を読んでから、社員・部署・取引先の3マスタを
1枚の app 定義にして definitions/app.yaml に書いて。

要件は docs/1-要件定義/案件の説明.md のとおり。特に:
- 社員番号は6桁の数字。**採番しない**(前書きの決めごと)
- 退職者は消さない。在籍区分を変えるだけ
- 内線は admin と hr だけに見せる
- 取引先の検索は、範囲(与信限度額)と複数選択(締め日)を使いたい
- インボイス登録番号は法人のときだけ必須

書けたら hatake check --project にかけて、**警告ゼロ**になるまで直して。
**助言(好み)は当てる前に何を当てるか教えて**。案件の判断が要るものは私が決める。

必ず入れる3つ:

なぜ
前書きを先に読ませる 読ませないと用語が揺れる(「在籍区分」が「ステータス」になる)
警告ゼロまで 警告は事実。直すのに業務の判断が要らないので AI に任せていい
助言は勝手に当てさせない 助言は好み。案件によって答えが違う

この「事実」と「好み」を分けてるのが地味に効きます。
hatake check は1回で4つの欄を、混ぜずに返すので:

欄 何を言うか 誰が相手か
事実(検証) 書いたのに効かない AI が直していい
読み返し 意図どおりか 人が読む
好み(助言) 書いていないと不便かも 人が選ぶ
人が決めること 定義に書けない 人が答える(次の章)

実際に道具に止められた話

「まとめて退職にする」の上限は 100 件ですが、この表は1ページ 50 件です。
選べるのは**画面に出ている行**だけなので、100 件は選べません=**この上限は一度も効きません**。

一括処理の上限を100件にしたら止められました。書けるし、画面も普通に出るし、
押してみても気づけないやつ。上限を 50件(hr は 20件)に直しました。

助言も1件出ました:

「まとめて退職にする」の失敗の言い方は件数だけです。**どの行が落ちたか**を言えないので、
押した人は全部やり直すか、当たりを付けて探すことになります。

これは業務として妥当だったので当てました(文言に {failedKeys} を足した)。
当てるかどうかは人が決める、が設計として効いた場面です。

⑤ 問いに答える(★ここが人の本番)

で、ここが一番言いたかったところです。

雑な依頼から画面を起こすと、書けることは全部書けてしまうんですよ。
だから出来上がった定義に空欄が無い。空欄が無いと、決まってないようには見えない。

そして動かした後にこう言われる。

  • 「2人で同時に直したらどうなるの」
  • 「消したやつって残ってるの」
  • 「この端数、切り捨てでいいんだっけ」

全部、定義に書けないことなんです。だから検証も助言も言えない(あの2つは
定義に書けることしか見ない)。ここだけがスッポリ抜ける。

なので hatake ask という道具を作りました。投げた依頼文:

hatake ask definitions/app.yaml --project definitions/hatake.project.yaml
を回して、決めていないことを出して。

**答えは私が決めるので、勝手に決めて書かないで。**

返ってきた6件

問い(id) 何を聞かれたか 人が決めたこと
concurrency 同時に直したらどちらが残るか 更新日時が変わっていたら弾いて読み直させる
partial-failure 一括が途中で失敗したら 1件ずつ確定して、失敗した行だけ残す
validation-server 画面の検証をサーバでも見るか 見る(同じ定義で回す)
role-source 役割はどこから来るか ログイン時にこのシステムが返す(将来 AD)
audit 監査を残すか 削除と一括は必ず残す
notify 一括のあと知らせるか 知らせない

どれも AI には答えられません。 業務の判断なので。
AI が答えると「それっぽいけど誰も決めていない設計」ができあがります。
そして半年後に必ず揉めるのがこの6件です。

聞かれ方はこんな感じ:

1. [concurrency] 同じ1件を2人が同時に直したら、どちらの入力を残しますか
   (後から押した人を通す/弾いて読み直させる)。
   聞く理由: 定義に書けるのは「入力できる」までで、**誰の入力が残るか**は保存する側の話。
   決めないと: 先に保存した人の入力が黙って消える。しかも画面には両方とも「保存しました」と出る。
   誰の担当か: サーバの担当

「決めないとどうなるか」が書いてあるのが大事で、
これが無いと人は判断できないんですよね。

答えたら前書きに戻す

答えは前書きの logic に1行足して answers: [<印>] を付けます。
次から同じ問いは出ません。

で、ここでもやらかしました。

questions.decided[0]: "soft-delete" という問いはありません
(npx hatake ask --kinds で引けます)

soft-delete とか auth-owner とか、それっぽいけど存在しない id を書いて落とされた。
正しくは erase(消したものを残すか)でした。

勝手な id が通らない=「答えたつもりで答えていない」が作れない、という設計です。
落とされるのが正しい。

区分を間違えても落とされました:

logic[2].answers: "numbering" は「サーバの担当」ですが、where に outside と
書いてあります(担当が違うなら、答えになっていないか区分が間違っています)。

Claude Code の設定(毎回同じ説明をしない)

ここは細かい運用の話。

hatake-example/
├─ CLAUDE.md                     リポジトリ全体の決めごと
└─ apps/<案件>-prj/
    ├─ CLAUDE.md                 その案件の決めごと(前書きから生成した節つき)
    └─ .claude/
        ├─ settings.json         許す操作・許さない操作
        └─ commands/             定型の依頼(スラッシュコマンド)

CLAUDE.md の一部を前書きから生成してます。

案件の説明・用語・決めごとって、前書き(hatake.project.yaml)にもう書いてあるんですよ。
同じことを CLAUDE.md にも手で書くと、片方だけ直したときに必ず食い違う。

なので前書きを正にして、貼る側を生成します:

<!-- hatake:project:begin -->
<!-- hatake:project:end -->
npx hatake project definitions/hatake.project.yaml --agents --merge CLAUDE.md

印の中だけが入れ替わるので、外に書いたもの(ブランチ名・レビューの回し方)は消えません。
そして古くなってないかを CI で見る:

npx hatake project definitions/hatake.project.yaml --agents --check --merge CLAUDE.md

違ってたら終了コード 1。AI が古い決めごとを読むのを機械で止める、という話です。

※ ブランチ名やコミット規約は前書きに書きません。定義に現れないので機械が突き合わせ
られず、必ず腐るので。それは CLAUDE.md の印の外に手で書く。

よく使う依頼はスラッシュコマンドに

毎回同じ文を打たなくて済むように .claude/commands/ に入れてます。

打つもの 何をするか
/仕分け 資料を hatake where --from にかけて、枠組みの外を出す
/前書き 前書きの下書きを起こす(言っていないことは書かせない)
/画面 前書きを読ませてから定義を書かせ、check が通るまで直させる
/検収 check と ask を回して、残っているものを出す

やってはいけないこと(まとめ)

なぜ
前書きを AI に想像で埋めさせる 案件のことは人しか知らない。埋まった紙は嘘の設計書
問いに AI が答える 業務の判断。答えは案件ごとに違う
「外」と出たものを定義に書かせる 画面の中に業務ロジックが入る。フレームワークが壊れる
助言を全部当てさせる 助言は好み。当てるほど良い定義になるわけではない
定義を手で書く 書けるけど、手で書くと前書きとの突き合わせが効かない

最後のやつ、逆説的なんですが、人が定義を手で書くと機械のチェックが効かなくなるんですよ。
AI に書かせて道具にかけるほうが速いし正確、という話。

次

きれいな話ばかり書いたので、次は転んだ話です。

13件踏みました。 本体のバグ3件を含めて、見栄えのために減らさず全部出します。
あと「直ってないもの・向いてない案件・試してないこと」も。

導入を検討する人が見るべきなのは、たぶん成功談よりこっちなので。

→ 記事4: 転んだ話13件と、まだ出来ていないこと

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?