個人でも作るものが多いので、Issueを立てたら自動で進んでくれる仕組みが欲しくなりました。仕事でもこういう仕組みを作っていますが、個人で一から作り直すと別の学びもありそうだったので、agentcellsという名前で作り始めています。
工場の機械を1つずつ定義して、そこを成果物が流れていくように組み立てていくので、作っていてかなり楽しいです。まだ完成していませんが、作りながら分かったことをtipsとしてまとめます。
Issueを立てると、cellが順に進めて、迷ったらコメントで聞いてくる
triage(Issueを読んで着手するか決める)、implement(実装してPRを作る)、review(PRをレビューする)のように、1つの仕事を回す単位をcellと呼んでいます。人間がやるのは、Issueを立てることと、コメントで返事をすることだけです。
試しているのは、個人で作っているゲームのリポジトリです。戦闘のカメラ演出を頼んだIssueでは、こんなやり取りになりました。
- triageが、Issueの要望と既存の構想メモを読んで、完了の条件を書いてimplementに回す
- implementが2Dで傾ける演出を作ってPRを出し、reviewの指摘を直す
- 私が「背景がないからカメラが動いている感がない」とコメントすると、背景を描き足す
- 「2Dの回転ではなく、3Dで敵を中心に回り込むイメージ」とコメントすると、3Dの投影で作り直す
人に頼むときと同じように、コメントで返すだけで少しずつ意図に寄っていきました。
cellは機械で、ラベルの上を成果物が流れる
状態はGitHubのラベルだけで持っています。データベースは持っていません。
| ラベル | 意味 |
|---|---|
ac:triage などcellの名前 |
そのcellが拾う |
ac:working |
処理中 |
ac:needs-human |
人間の返事を待っている |
ac:in-review |
人間の検品を待っている |
cellは設定ファイルで1つずつ定義します。
[[cell]]
name = "review"
on = "pull_request"
label = "ac:review"
next = ["implement", "done"] # 進めてよい先
max_rounds = 3 # 往復の上限
model = "sonnet"
agentcells tick を10分ごとに動かして、ラベルの付いたIssueやPRを1件ずつcellに通しています。今は設計のcellがなく、記録もGitHubだけの簡単な作りです。
tips: AIには「次はどこか」とコメントの中身しか返させない
いちばん効いたのは、AIにGitHubへ書き込ませないことでした。
- ラベルの付け替え、push、PRの作成、コメントの投稿は、すべてCLIがやります。AIは結果を返すだけです
- 次の行き先は、
claude -pの--json-schemaでnextを選べる先のenumにしています。許されていない先はそもそも出せません - 往復は3回までにしています。人間が返事をすると、また3回まで動けます。AIどうしの差し戻しが止まらなくなるのを防ぐためです
- Bashの許可は
agentcells cell:*の1つだけです。コミット、ファイルの削除、ビルドやテストは、CLIに入れたラッパーを通します。settings.jsonの許可が1行で済みます
決まりで止まると、AIはその理由を書いて人間に回してきます。実際に、次のようなコメントが来ました。
- 「テストを実行できず動作を確かめていないので、レビューには回さず止めています」
- 「新しいコミットがないとreviewに回せない決まりなので、needs-humanにしています」
プロンプトに「迷ったら人間に聞いて」と書くだけだと、守るかどうかはAI次第になります。仕組みで縛っておくと、止まったときの理由がはっきりします。
tips: GitHubのラベル検索は、付け替えた直後の状態を返さない
gh issue list --label で仕事を探していたら、同じcellが2回動きました。ラベルでの一覧はGitHubの検索を使っていて、ラベルを変えてからしばらくは、前の状態のまま見つかるためです。
GraphQLでリポジトリのIssueとPRを読み、開いているものとラベルで絞る形に変えました。
tips: claude -p を無人で動かすときに確かめたこと
-
--setting-sources ""を付けて、使う人の設定も、作業するリポジトリの.claude/settings.jsonも読ませないようにしています。リポジトリの設定にあるhooksはsandboxの外で動き、しかもAIがブランチに書けるためです - このときリポジトリのCLAUDE.mdも読み込まれないので、プロンプトで「CLAUDE.mdやAGENTS.mdがあれば読んで」と頼んでいます
- 時間切れなどでclaudeを止めても、AIがBashで起動したビルドやテスト(裏で動かしたテスト用のサーバーなど)は止まらずに残ります。Claude CodeがBashのコマンドを、claudeとは別のグループのプロセスとして動かしているためです。残ったプロセスは、そのあとCLIがpushのために用意するGitHubのトークンを読めてしまいます。そこで、AIが起動したプロセスを探してすべて止めてから書き込むようにしています。止めきれなかったときは、GitHubには何も書きません
tips: 公開リポジトリで回すなら、動かすIssueを自分で選ぶ
agentcellsのリポジトリは公開しています。書き込みの権限を持つ人が「早くやってほしい」とラベルを付けたら勝手に動く、ということは避けたいです。
- 動かすのは、Assigneesに自分がいて、cellのラベルを自分で付けたIssueだけにしています
- 指示として扱うのは自分のコメントだけで、ほかの人のコメントは参考としてプロンプトに入れています
- sandboxで読める場所は、読ませる場所を並べる形にしています。読ませない場所を並べる形だと、並べ忘れた場所の秘密が読めてしまうからです
- pushの前に、秘密らしいものが入っていないか、
.claude/や.github/workflows/を変えていないか、シンボリックリンクを足していないかをCLIが確かめます
※まだ課題が残っているところもあり、対応中です。
次は自動マージと、詰まっている場所の監視
今はPRのレビューが終わると、必ず人間の検品に回ります。次は、内容によって自動でマージするか人間に回すかを決めるcellを作ろうと思います。cellがどこで止まりやすいかを記録から数えて、Issueにするcellも作る予定です。
機械を1つ足すたびに流れ方が変わるので、引き続き楽しみながら作っていこうと思います。
この記事はZennにも投稿しています。