👥

Gemini CLIでKiroみたいなCLI型コーディングエージェント作ったのでPersonaという機能を紹介します

に公開

はじめに

https://aws.amazon.com/jp/blogs/news/introducing-kiro/

Kiroがエディタ型コーディングエージェントで使いづらかったので、ShaftというCLI型コーディングエージェントを作りました。

https://gitlab.com/tkithrta/shaft

開発には主にGemini CLIを使いました。いわゆるバイブコーディングというやつです。

https://cloud.google.com/blog/ja/topics/developers-practitioners/introducing-gemini-cli

どこかで見たことある流れですね。

https://zenn.dev/tkithrta/articles/d3fa29b400c2e9

そうです。以前Goで作ったものをPythonで書き直しました。

ツール内部でGitを使わないところが最も大きな違いですが、Kiroの影響を受け、何となくGitruckに追加していたPersonaという機能を強力なものにしたのが今回の大きな特徴です。
また、PythonとLiteLLMを使うことで、Gitruckで問題になっていたWindowsで使えない点や、Gemini以外のモデルを使用できなかった点も解決しています。

その他の要件定義や設計はGitruckの頃からほとんど変わっていませんので、詳しくは前回の記事を参照してください。
今回はShaftでKiroのような仕様駆動開発を実現するペルソナ機能について紹介します。

ペルソナ

Shaftは、LLM(大規模言語モデル)と対話するための強力なCLIツールです。
その中でも特にユニークなのが「ペルソナ」機能です。
この記事では、ペルソナ機能を使ってLLMの応答をタスクに合わせて最適化する方法を解説します。

ペルソナ機能とは?

ペルソナ機能は、LLMに特定の役割や性格(ペルソナ)を与えることで、その応答スタイルや内容を制御する仕組みです。
Shaftでは、コマンド実行時にペルソナを指定するだけで、内部的に使われるプロンプトテンプレートが切り替わります。

例えば、「コードをレビューするプログラマー」として振る舞わせたり、「物語を紡ぐ小説家」として応答させたりすることが可能です。
これにより、単に質問応答を繰り返すだけでなく、より専門的で文脈に沿ったアウトプットをLLMから引き出すことができます。

ペルソナの使い方

ペルソナの指定は、-pまたは--personaオプションを使います。

shaft <command> "<prompt>" -p <persona_name> [options]

例えば、あるPythonコードについて、「プログラマー」と「システムアナリスト」の視点でそれぞれ説明を求めてみましょう。

# プログラマーとして質問
shaft ask "この関数の役割は?" -f app.py -p programmer

# システムアナリストとして質問
shaft ask "この機能がビジネス全体に与える影響は?" -f app.py -p systems_analyst

programmerペルソナはコードの技術的な側面に焦点を当てた回答を生成し、systems_analystペルソナはより大局的、戦略的な視点からの回答を返す傾向があります。

組み込みペルソナ

shaftには、いくつかの便利なペルソナが標準で組み込まれています。

  • programmer: コードの解説、リファクタリング、デバッグなど、一般的なプログラミングタスク向け。(デフォルト)
  • novelist: ドキュメント作成、ブログ記事執筆、物語の創作など、創造的なテキスト生成向け。
  • systems_analyst: システム全体の設計やアーキテクチャに関する戦略的な分析、計画向け。
  • systems_architect: 技術設計書や要件定義書の作成など、具体的な設計ドキュメント生成向け。

利用可能なペルソナの一覧は、output personaコマンドで確認できます。

shaft output persona

ペルソナのマッチング方法

ペルソナの指定には、2つのマッチング方法があります。

1. 完全一致

ペルソナ名を正確に指定すると、対応するテンプレートが1つだけ使用されます。

# programmer.json テンプレートを使用
shaft ask "..." -p programmer

2. プレフィックス一致

ペルソナ名の前方部分だけを指定すると、そのプレフィックスで始まるすべてのテンプレートが使用されます。
これは、特定の役割に対して複数のバリエーションを用意したい場合に便利です。

# `systems_architect-`で始まる全てのテンプレートを使用
# (例: systems_architect-1.json, systems_architect-2.json)
shaft ask "..." -p systems_analyst

カスタムペルソナの作成

shaftの真価は、独自のカスタムペルソナを作成できる点にあります。
プロジェクト固有のコーディング規約を教え込んだり、特定のフォーマットでドキュメントを生成させたりといった、高度なカスタマイズが可能です。

作成手順

  1. テンプレートディレクトリの準備
    カスタムペルソナの定義ファイル(JSON形式)を、以下のいずれかのディレクトリに配置します。

    • プロジェクトルート: ./.shaft/templates/
    • ホームディレクトリ: ~/.shaft/templates/
  2. JSONテンプレートの作成
    コマンド(askまたはtask)に対応するサブディレクトリ内に、JSONファイルを作成します。例えば、askコマンド用のmy_reviewerペルソナを作るなら、./.shaft/templates/ask/my_reviewer.jsonとなります。

以下は、カスタムペルソナのテンプレート例です。

.shaft/templates/ask/my_reviewer.json

{
  "system_content": "あなたは経験豊富なコードレビュアーです。提供されたコードを分析し、簡潔かつ建設的なフィードバックを日本語で提供してください。特に、可読性と保守性の観点から指摘をお願いします。",
  "user_content": [
    "レビュー対象のファイルは以下の通りです。",
    "{% for file_path, content in target_contents.items() %}",
    "--- File: {{ file_path | relative_path }} ---",
    "```",
    "{{ content }}",
    "```",
    "",
    "{% endfor %}",
    "---",
    "レビュー依頼: {{ task_description }}"
  ],
  "json_schema": {
    "type": "array",
    "items": {
      "type": "object",
      "properties": {
        "file_path": {
          "type": "string",
          "description": "レビュー対象のファイルパス"
        },
        "explanation": {
          "type": "string",
          "description": "コードレビューのコメント"
        }
      },
      "required": [
        "file_path",
        "explanation"
      ]
    }
  }
}
  • system_content: LLMの役割や前提条件を定義します。
  • user_content: ユーザーが入力するプロンプトのテンプレートです。Jinja2形式で変数を埋め込めます。
  • json_schema: LLMに期待する出力形式をJSON Schemaで定義します。これにより、安定した構造化データを取得できます。

カスタムペルソナの利用

作成したペルソナは、組み込みペルソナと同様に-pオプションで呼び出せます。

shaft ask "この関数のリファクタリング案をください" -f src/main.py -p my_reviewer

デフォルトファイルパスの指定

asktaskコマンド用のペルソナでは、テンプレートにdefault_file_pathキーを追加することで、-f--fileオプションが指定されなかった場合のデフォルトの対象ファイルを定義できます。

例えば、systems_architectペルソナで設計書をレビューする場合、テンプレートに以下のように記述します。

.shaft/templates/ask/systems_architect-2_000.json

{
  "system_content": "あなたは優秀なシステムアーキテクトです。...",
  "user_content": [...],
  "json_schema": {...},
  "default_file_path": [
    "./.shaft/shaft/design.md"
  ]
}

この設定により、-fを省略してコマンドを実行すると、自動的に./.shaft/shaft/design.mdが対象ファイルとして扱われます。これにより、特定のペルソナが特定のドキュメントを継続的に参照したり編集したりするワークフローを簡略化できます。

実践例:小説家ペルソナで物語を執筆

ペルソナ機能のユニークな使用例として、novelistペルソナを使って物語を連続執筆させてみましょう。

taskコマンドは、-fで指定したファイルが存在しない場合は新規作成し、存在する場合は追記・修正を行います。
これを利用して、ループ処理で章を重ねていくことができます。

# Bashの例
for i in {1..9}; do shaft task "34歳ニートが異世界転生する物語の第$i章を書いてください" -f mushokutensei.txt -p novelist; done

# BusyBox Ashの例
for i in $(seq 1 9); do shaft task "34歳ニートが異世界転生する物語の第$i章を書いてください" -f mushokutensei.txt -p novelist; done

# Windowsコマンドプロンプトの例
FOR /L %i IN (1,1,9) DO shaft task "34歳ニートが異世界転生する物語の第%i章を書いてください" -f mushokutensei.txt -p novelist

このコマンドは、mushokutensei.txtというファイルに、指定したテーマで物語の各章を次々と書き加えていきます。
novelistペルソナが創造的な文章生成を担うことで、一貫性のある物語が自動で紡がれていきます。

おわりに

Shaftでは、requirements.mddesign.mdを使用し要件定義や設計を行うKiroのようなsystems_architectペルソナだけではなく、strategy.mdconcept.mdを使用したsystems_analystペルソナで企業活動や経営戦略を考えることもできます。

他にもファイルとディレクトリを整理するメイドペルソナなど、今後追加していきたいペルソナのアイデアがいくつかありますので、不足している機能を追加しながら引き続きShaftに関する記事を紹介していきたいと思います。

Shaftはコーディングエージェントの様々な問題を根本から解決し、非常に使いやすいものに仕上がって非常に満足しています。

皆さんもぜひ使ってみてください!

https://gitlab.com/tkithrta/shaft/-/blob/main/docs/ja/getting_started.md
https://gitlab.com/tkithrta/shaft/-/blob/main/docs/ja/user_guide.md

おまけ

Shaftで執筆していたらgemini/gemini-2.5-flashのtypo見つけてきてびびるやつ。

type tmp\new.txt | shaft task -r README.md -r "docs\ja\*.md" -f tmp\article.md
Processing files:
- C:\Users\shaft\Desktop\shaft-main\tmp\article.md
Using context from:
- C:\Users\shaft\Desktop\shaft-main\README.md
- C:\Users\shaft\Desktop\shaft-main\docs\ja\getting_started.md
- C:\Users\shaft\Desktop\shaft-main\docs\ja\user_guide.md
Proposed change for C:\Users\shaft\Desktop\shaft-main\tmp\article.md:
Explanation:
...
Proposed change for C:\Users\shaft\Desktop\shaft-main\docs\ja\getting_started.md:
Explanation:
### 問題

ドキュメント内で、デフォルトのLLMモデルとして `gemini/gemini-2.5-pro` が指定されています。しかし、`gemini-2.5-pro` というモデルは現時点では存在しません。これは `gemini-1.5-pro` の誤記である可能性が高いです。

### 解決策

他のモデル名の修正との一貫性を保つため、デフォルトモデル名を `gemini/gemini-1.5-pro` に修正します。これにより、ユーザーが混乱することなく、実在するモデル名を参照できるようになります。
Warning: LLM suggested a change for a non-target file C:\Users\shaft\Desktop\shaft-main\docs\ja\getting_started.md. Skipping.
Proposed change for C:\Users\shaft\Desktop\shaft-main\docs\ja\user_guide.md:
Explanation:
### 問題

ドキュメント内の `ask` コマンドのオプション説明で、LLMモデル名の例として `gemini/gemini-2.5-frash` が記載されています。これは以下の2つの点で誤りです。

1.  `frash``flash` のタイポです。
2.  `gemini-2.5` というモデルは存在せず、`gemini-1.5` の誤記と思われます。

### 解決策

モデル名を `gemini/gemini-1.5-flash` に修正し、正確な情報を提供します。
Warning: LLM suggested a change for a non-target file C:\Users\shaft\Desktop\shaft-main\docs\ja\user_guide.md. Skipping.
Proposed change for C:\Users\shaft\Desktop\shaft-main\docs\ja\user_guide.md:
Explanation:
### 問題

ドキュメント内の `task` コマンドのオプション説明で、LLMモデル名の例として `gemini/gemini-2.5-frash` が記載されています。これは以下の2つの点で誤りです。

1.  `frash``flash` のタイポです。
2.  `gemini-2.5` というモデルは存在せず、`gemini-1.5` の誤記と思われます。

### 解決策

モデル名を `gemini/gemini-1.5-flash` に修正し、正確な情報を提供します。
Warning: LLM suggested a change for a non-target file C:\Users\shaft\Desktop\shaft-main\docs\ja\user_guide.md. Skipping.
Proposed change for C:\Users\shaft\Desktop\shaft-main\docs\ja\user_guide.md:
Explanation:
### 問題

ドキュメント内で、デフォルトのLLMモデルとして `gemini/gemini-2.5-pro` が指定されています。しかし、`gemini-2.5-pro` というモデルは現時点では存在しません。これは `gemini-1.5-pro` の誤記である可能性が高いです。

### 解決策

他のモデル名の修正との一貫性を保つため、デフォルトモデル名を `gemini/gemini-1.5-pro` に修正します。これにより、ユーザーが混乱することなく、実在するモデル名を参照できるようになります。
Warning: LLM suggested a change for a non-target file C:\Users\shaft\Desktop\shaft-main\docs\ja\user_guide.md. Skipping.
Successfully applied patch to C:\Users\shaft\Desktop\shaft-main\tmp\article.md

Discussion