THINKING MEGANE

Jev的なDecision Modelの実現方式を分類してみる

JevはTypeSafe AI社からリリースされた意思決定のためのモデルです。その推論速度と汎用性の高さから注目されていますが、具体的なアーキテクチャは公開されておらず、モデルの再現を試みる取り組みも活発に行われています。

自分自身もJevそのものを使って遊ぶというより、その方式の解明や、それを通したローカル環境での実現に興味があり、色々調べていました。 せっかくなので、見つけた実現方式を分類しながらまとめておこうと思います。

Jevとは

Jevは意思決定モデルとして、背景情報であるState、そのStateにおける質問群Questions、これらの質問に対する回答の選択肢Candidatesを自然言語で与えると、モデルが選択した回答を確信度(Confidence)付きで返すモデルです。 この回答は、従来のLLMのように1トークンずつ推論するdecode過程を経ずに行われるとされています。 また、複数の質問に対してもその回答時間はあまり増加しないともされています。

これらの特徴により、複雑な背景情報や高度な推論が必要とされず、かつ、即時性が求められる意思決定の場面への適用が考えられます。 加えて、LLMのように自然言語で幅広いタスクを扱え、タスクごとの追加学習なしで導入できるという汎用性の高さも注目すべきところです。

以下では、このような要件を再現するためのいくつかの取り組みを分類していきます。

分類について

このエントリでは、実現方式を大きくは「既存のLM headをそのまま利用する方式」と「潜在表現から専用のHeadを学習する方式」の2系統に分類しています。 それぞれ、Direct Logit型、Decision Head型と呼ぶことにします。 特に、Decision Head型の方はいくつかの派生があり、候補の処理方法や複数質問の並列化方法がそれぞれ異なるため小分類を示します。

Direct Logit型

Jev再現実装として最初に想起されるのは、学習済みのLLMに対してState/Question/Candidatesを渡し、prefill過程を経て得られた次トークン用の語彙全体に対するlogits(以下、語彙logits)を用いる方式です。 通常の推論ではこの語彙logitsを素として次の1トークンの選択を繰り返すdecode過程が発生しますが、プロンプトを工夫して、回答候補を1トークンで識別(例えば、A: chicken、B: beefの、A/B)できるようにするだけで、decode過程を省略して候補を選択できます。 これだけであれば構造化出力のdecode部分を省いたものと考えられますが、この方式では、候補に対応する種類数のトークンのlogitを用いてsoftmax操作を実行することで、候補のスコアも求められます。

この方式での再現実装としては、以下が挙げられます。

この方式の利点はなんと言っても、学習済みのオープンウェイトモデルをそのまま利用できる点だと思います。実際に、Jev対応forkでは、様々なモデルをそのままJevっぽく振る舞わせることができるようになっています。 大きなモデルの知識量や指示追従性能の恩恵に預かることができるので汎用性も高いと言えそうです。

一方で、複数質問への対応は、バッチ推論での解決です(State部分のKVキャッシュの共有などによる冗長計算の削減の仕組みなどは提供されていました)。 この課題へのモデルのアーキテクチャ水準での解決策として、拡散推論モデルであるDiffusionGemmaなどを使った並列回答が提案されています。 推論サーバーであるvLLM上でこの機能の実装提案では、質問や回答欄を初期canvasとして用いて、canvas中の全トークンに対して並列推論した結果のうち、回答欄に対応するトークンのlogitから選択結果を得ます。 ただし、手元で試した中では、canvas中の全てのトークンが推論対象であったり、初期canvasのランダム性に依存して回答が変動し、単純にノイズを取り除くためのラウンド数を増やしても安定するとは限らなかったです。

なお、この方式での選択結果のスコアは、どのtokenを次に出すかの相対比率であり、その回答が正しい確率としてキャリブレーションされた値ではないことにも注意する必要があります。

Decision Head型

次に、潜在表現から専用のHeadを学習する方式について整理します。 この系統では、潜在表現を作る方式にBERTなどのEncoder型のモデルを使うか、GPT系のDecoder型のモデルを使うかで分類してみました。

Encoder系

潜在表現の獲得のため自然に採用されるのは、事前学習済みのBERTやRoBERTa、ModernBERTといったEncoder系のモデルであろうかと思います。 Jevの再現実装は、これらのモデルから得られる潜在表現ベクトルを入力とする分類用のヘッドをつけ、Jevの想定するState、Questionと、回答の組でそのヘッドをファインチューニングする方式が考えられます。 元々、多肢選択問題にも利用されており、各選択肢に対応するスコアも出力することができるので、入出力の形式の観点からは、Jevらしさは高いと思えます。

この方式による再現実装としては、modernbert-ja-310m-jevが挙げられます。 この方式での再現実装は、蓄積されてきたBERTとファインチューニングのやり方が踏襲できること、モデルのパラメータ数が(近年のLLMモデルと比べると)少ないことによる、推論速度をはじめとする取り回しの良さが利点です。

しかしながら、パラメータ数の少なさに加え、チャットやエージェントを想定したモデルのような指示追従能力を前提にできないこともあり、それを補うためのタスクごとのファインチューニングが必要となり、汎用性が低下します。 また、候補ごとにState/Question/Candidateの組で推論するため、質問や候補数に応じて計算量は増加します。 もちろんバッチ推論による並列化は可能ですが、候補ごと(または工夫により質問ごと)の計算という構造自体は残ります。

この計算量の課題に対して、layaは、全ての候補を一つの系列で入力し、各候補の潜在表現を得ることで解決を図ります。 layaでは、[CLS] <type> instructions [SEP] [MASK] opt0 [MASK] opt1 ... [SEP] state [SEP]のように、候補ごとに[MASK]トークンを配置し、それらのトークンに対応する潜在表現の一覧を分類器に渡します。 なお、この配置であっても、双方向のアテンションであるので、各候補のMASKトークンには質問や候補の内容も反映されています。 分類器は、比較的複雑で、2層のTransformerが組み込まれています。 また、ベースとなるモデルはModernBERT-largeを、このためにファインチューンしているようです。 さらに、回答確率がキャリブレーションされることを目標としたRLCDによる学習も採用しています。

Decoder系

最後に潜在表現をQwenなどのDecoder系のモデルから得る方式です。 kevでは、<state> state <q> question <opt> opt0 </opt> <opt> opt1 </opt> ... <decide>のように、State、Question、各Candidateを特殊トークンで区切って1系列に配置します。 そして、各候補末尾の</opt>に対応する潜在表現と、全候補を読み終えた<decide>に対応する潜在表現を取り出し、分類器で比較します。 なお、この特殊トークンは追加導入ではなく、Qwen系モデルなどであまり使われていないとされる<|fim_prefix|>などを、別用途で利用しているようです。

Direct Logit型で重要な要素であった次トークンの語彙logitsを使わず、その手前の潜在表現から独自に分類器を構築するのはやや不思議ではありますが、以下の意図があるのではないかなあと考えています。

  1. 複数質問・候補との親和性: 各質問や候補に対応する特殊トークンの潜在表現を直接利用できるため、複数質問の並列処理や、A/B/Cのような回答ラベルに依存しない可変数の候補を扱いやすい。
  2. 分類器との親和性: 語彙logitをそのまま利用するのではなく、候補ごとの</opt>と全候補を読んだ<decide>の潜在表現から、意思決定に適した比較方法自体を学習できる。

分類器自体は比較的単純で、<decide>と各</opt>の潜在表現をそれぞれ線形変換した後、その内積によって候補のスコアを計算します。

まとめ

本エントリでは、Jev的なDecision Modelを実現する方式について、Direct Logit型とDecision Head型として整理しました。

調査から、単純に「文章を生成せず、候補の中から選択する」だけであれば、既存のLLMの次トークンlogitを利用することで比較的簡単に実現できることが改めて分かりました。 並列回答についてもモデル側というよりは推論サーバー側の機構に移譲すれば良いと決めてしまえば、構成としてはかなり近づけられそうです。 Jevの魅力の一つは、自然言語で幅広いタスクを記述でき、ある程度の精度でそのまま利用できる汎用性にあると思います。その観点では、ある程度大きなオープンウェイトの汎用モデルを推論サーバー上で効率よく動かす方向が実用的に思えました。

一方で、Jevらしい複数質問への並列回答の計算効率や、Confidenceまで含めて考えると途端に方式が複雑になることも確認できました。 もちろん、利用量が十分に多くコストに見合うのであれば、Layaやkevのように用途に合わせた独自モデルを学習するのが最も良さそうです。一方、すべてのタスクごとにモデルやAPIを増やしていくのも運用上は大変かなと思います。その場合、モデル自体は共通化しつつ、利用側でjev-alignのように、実データや人間のフィードバックを使ってプロンプトを継続的に改善していく仕組みと組み合わせるのも面白そうです。

個人的には、Confidenceはもっと積極的に使われてもよい機能だと感じました。単に最も良い候補を選ぶだけでなく、「十分な確信がないので今回は採用しない」「人間に確認する」といった判断まで含められると、Decision Modelの使い道は広がるでしょう。このConfidenceを自前のモデルやDirect Logit型の仕組みでどう実現していくかは、今後も追跡していきたいと思います。

おまけ

今回出てきたTransformerの内部表現やAttention、潜在表現についてもう少し理解したい場合は、以前作った以下の資料も参考になるかもしれません。よければご参照ください。


本エントリは2026/09/20までに調べた内容を元に執筆しました。 読まれた時点では、各方式もさらにアップデートされたり他の方式も登場しているかもしれないことにご留意ください。

tmuxのpopupを使ってターミナル画面でClaude Codeと棲み分ける

Claude CodeをはじめとするCLI型のコーディングエージェントはターミナル画面中の一定領域を占有します。

元々自分は、ターミナルマルチプレクサであるtmuxを利用していて、リポジトリをプロジェクトとみなし、一つのウィンドウを対応させ、その中でペインを分ける構成をとっていました。 その配置と各ペインのサイズのなかで、エージェントの入る余地はなく、どうしてもペイン内で共有させるか、異なるウィンドウを割り当てる必要がありました。 しかしながら、役割を共有させたペインの裏側や異なるウィンドウに散らばったエージェントのセッションを探し回るのは非常に面倒に感じていました。

そんな中、以下のtmux popupの活用記事がしっくりきたので、参考にさせてもらいました。

利用の様子

Popup

画面の中央の前面領域がtmux popupでClaude Codeを動かしている箇所です。 このpopupの表示・非表示を指定したキーバインドで瞬時にトグルできる体験が非常に良いです。 トグルの様子は、上記の記事のデモが分かりやすいのでぜひご覧いただきたいです。

特に、tmuxのセッションのdetachとattachを行うため、popupを隠しても処理が継続され、ターミナル領域をお気に入りの配置のままコードの確認などが快適に行えるのが非常に有用です。 また、以下の設定で見ていただける通り、tmux側のpopupやキーバインドを使うため、操作中のClaude Codeに干渉しない点もポイントが高いです。

(余談ですがIS_DEMO環境変数をつけてClaude Codeを起動すると組織名やメールアドレスが非表示になってこういうスクリーンショットの時に便利です)

実際の設定

上記の記事はfishだったのでzshで使うために以下のようにしました。

functions/_tmuxpopupでカレントディレクトリ(リポジトリ名)をベースにセッション名を組み立て、セッションがなければ作成、あればattachとdetachをトグルできるようにしています。 セッション名の組み立てのところを自分の環境に合わせると使いまわせると思います。

function _tmuxpopup() {
  local initial_cmd="${1:-}"
  local title="${2:-}"

  local width='80%'
  local height='80%'

  local session
  session="$(tmux display-message -p -F '#{session_name}' 2>/dev/null)" || return 1

  local pane_path
  pane_path="$(tmux display-message -p -F '#{pane_current_path}')" || return 1

  local home_src="${HOME}/src"
  local key=""

  if [[ "$pane_path" == ${home_src}/*/*/*(|/*) ]]; then
    local rest="${pane_path#${home_src}/}"
    local parts=(${(s:/:)rest})
    local org="${parts[2]}"
    local repo="${parts[3]}"
    repo="${repo%-wt}"
    key="${org}/${repo}"
  else
    key="${pane_path:t}"
  fi

  local safe_key="${key//\//_}"
  safe_key="${safe_key//[^A-Za-z0-9_.-]/_}"

  local popup_session="popup_${title}_${safe_key}"

  if [[ "$session" == popup_* ]]; then
    tmux detach-client
    return 0
  fi

  local exec_cmd
  if [[ -n "$initial_cmd" ]]; then
    exec_cmd="tmux attach -t ${popup_session} || tmux new -s ${popup_session} '${initial_cmd}'"
  else
    exec_cmd="tmux attach -t ${popup_session} || tmux new -s ${popup_session}"
  fi

  tmux display-popup \
    -d "#{pane_current_path}" \
    -xC -yC \
    -w "$width" -h "$height" \
    -T "$title" \
    -E "$exec_cmd"
}

上記の関数はpopup時に起動したいコマンドとpopupのタイトルを受け付けられる共通関数なので、Claude Codeを立ち上げるため具体的な実装をfunctions/_tmuxpopup-claudeに記述しました。

function _tmuxpopup-claude() {
  _tmuxpopup "claude" "claude"
}

上記の関数たちを.zshrcでautoloadしておきます。これでこの関数がいつでも呼び出せるようになります。

autoload -Uz _tmuxpopup
autoload -Uz _tmuxpopup-claude

実際の呼び出しはコマンドではなくtmux.confにキーバインドとして登録しておきます。こうすることで、popup内のClaude Code起動中でもpopupをいつでも隠すことができます。

bind , run-shell 'zsh -ic "_tmuxpopup-claude"'

これでprefix + ‘,‘でpopupをトグルできるようになりました。非常に快適です。

並列実行

これだけだと再紹介にしかならないので、並列実行のために試しているものも紹介しておきます。

ccmanagerはClaude Codeをはじめとするコーディングエージェントの並列実行を管理するためのツールです。 エージェント単位でgit worktreeを割り当てることができるので、並列実行時のコンフリクトなども回避することができます。

上で紹介した_tmuxpopupは起動したいコマンドを差し替えるだけで使えるのでccmanagerを呼ぶようにするだけで良いです。

function _tmuxpopup-ccmanager() {
  _tmuxpopup "ccmanager" "ccmanager"
}

これでプロジェクトごとの並列実行はかなり便利になりますが、いわゆる承認疲れの現象が発生するため、ある程度の権限を持たせた上で安全に実行したくなります。 そこで、Dev Containerと組み合わせます。 幸い、ccmanagerはDev Containerの利用も視野に入れているようで、探すと専用のオプションがあります。 それを踏まえて、先の関数を以下のように変更できます。

function _tmuxpopup-ccmanager() {
  _tmuxpopup \
    'ccmanager --devc-up-command "devcontainer up --docker-path podman --workspace-folder ." --devc-exec-command "devcontainer exec --docker-path podman --workspace-folder ."' \
    'ccmanager'
}

podmanを使う場合のオプションが含まれているので環境に応じて適宜読み替えていただければと思います。 また、予めDev Container CLIもインストールしておく必要があります。

これで、プロジェクトのDev Containerの設定(.devcontainerディレクトリ)を使ってccmanagerがよしなにやってくれます。 なお、コンテナ内にClaude Codeが含まれる必要があるため、Dev Container Featuresなどで追加しておくと良いです。

以下のように設定し、Claude Codeのpopupと使い分けることができます。

bind . run-shell 'zsh -ic "_tmuxpopup-ccmanager"'

おわりに

tmux popupを使うことでClaude Codeとターミナル領域をうまく棲み分けることができて非常に快適になりました。 しかしながら、並列実行の部分は実はまだ試行錯誤中です。 特に、Dev Container + git worktree + Claude Codeの相性はあまり良くないと思っていて、マウント周りを工夫する必要があり、なかなか全てのリポジトリで気軽に使えてないのが現状です。 また、Dev Containerであれローカルであれ、単純に自分が起点にエージェントとやりとりする構造から逆転させないと目指す並列は近づけないなあとも思っているので、引き続き試行錯誤していきたいと思います。

プロジェクトのスケルトンをテンプレートから展開するstampというCLIツールを作った

CLIツールを多く作っていると、最初のコミットまでに機械的に準備する自分なりの一式(利用するコマンドラインパーサーによる初期実装、GitHub Actionの定義、Makefile、ライセンスファイル等々)ができてくるかと思います。 このような一式を予め用意しておくことが、普段通りの開発作法に従ってスムーズに開発を始めるための土台となります。 また、最近では、コーディングエージェントへ依頼する際に、このような一式を揃えて実装時の補助線としてもらう方が、認識が揃うことも多いように感じています。

自分の場合、この一式の準備には直近のリポジトリからコピーしてくることが多いですが、細々とした置換などが面倒でした。 秘蔵スクリプトを持っている方も多いのではないでしょうか。

このようなニーズのため、プロジェクトのスケルトンを管理するツールが開発されています(copierなどがあるようです)。 一方で、これらのツールはテンプレートの適用後の継続的な同期まで面倒を見るために、プロジェクト開始時に一度展開できれば良いという自分のユースケースに対しては機能的に持て余してしまう印象がありました。

そこで、自分用にstampと言うCLIツールを作りました。

機能は単純で、予め用意しておいた、テンプレート一式(ツールがスタンプなのでシートと呼んでいます)をディレクトリ構造のままコピーしてくるだけです。 各ファイルはGo言語のテンプレートとして処理されるため、{{.name}}のような記述に対して、動的な値を設定できます。

# my-appシートにhello.txtを登録(拡張子.stampでテンプレートとして解釈)
$ echo "Hello {{.name}}!" > "$(stamp config-dir)/sheets/my-app/hello.txt.stamp"
# my-appシートを適用
$ cd /path/to/workspace
$ stamp -s my-app name=alice
# 結果を確認
$ ls
hello.txt
$ cat hello.txt
Hello alice!

chezmoiをご存知の方は、これを都度任意のディレクトリへapplyできるものと考えてもらうと想像しやすいかもしれません。

使用例

自身の具体的な利用ケースとして、Go言語のCLIのリポジトリを作成するシートを用意しています(というかこのために作った)。 拡張子.stampのファイルに含まれるモジュール名などを作成したいCLIツールの名称等に合わせてコピー時に設定することができます。

$ tree $(stamp config-dir)
~/.config/stamp/
├── sheets
│   └── go-cli            # go-cliというシート名で配下のディレクトリ構造を管理
│       ├── bootstrap.sh  # go mod tidyやgitのinitial commitなどを行う(後述)
│       ├── cmd           # 以下はCLIツールのスケルトンやMakefile、LICENSEなど
│       │   ├── cli_test.go
│       │   ├── cli.go.stamp
│       │   └── version.go
│       ├── go.mod.stamp
│       ├── internal
│       │   └── greet
│       │       ├── greet_test.go
│       │       └── greet.go
│       ├── LICENSE.stamp
│       ├── main.go.stamp
│       ├── Makefile.stamp
│       └── README.md.stamp
└── stamp.yaml            # ユーザー名などのデフォルト値を管理(後述)

以下のように実行すると、テストが通る状態でプロジェクトを開始できます。また、GitHubにpushすればtagprを用いたリリースの設定まで完了した状態になります。

APPNAME=sample; \
mkdir "$APPNAME" \
  && cd "$APPNAME" \
  && stamp -s go-cli appname="$APPNAME" \
  && sh bootstrap.sh

興味がある方はこちらを参照ください。シートをさらにchezmoiで管理していますが内容はそのまま確認できます。

便利な機能

stampの基本機能はこれだけですが、利用にあたり少し便利な機能ももう少し作っているので軽く紹介します。

シート登録の支援

既存のディレクトリやファイルをシートとして登録するためのcollectサブコマンドを用意しています。

# カレントディレクトリを再帰的にテンプレートとして(.stamp拡張子をつけて)
# sampleシートとして登録
$ stamp collect -s sample -t .

デフォルト値の指定

$(stamp config-dir)/stamp.yamlにシートのテンプレート展開時にデフォルトで指定する値をキーごとに設定しておくことができます。 コマンド実行時に指定があればそちらが優先されます。

year: 2026
username: monochromegane

パラメータ不足チェック機能

事前にテンプレートに必要なパラメータの値を覚えておいたり覗いて回って思い出すのは時間がかかります。 stampでは、必要なパラメータをうろ覚えのまま実行しても、不足があれば適用前に教えてくれます。どのファイルに何のパラメータが必要かが分かるので、便利です。

例えば、先ほどのgo-cliでは、stamp.yamlで定義しているデフォルトの値の他に、必ずappnameと言うキーに対して値を与えなければなりませんが、これをせずに実行するとこういうエラーがでます。

# stamp -s go-cli appname=monochromeganeとして呼ぶべき
$ stamp -s go-cli
Error: stamp failed: missing required template variables:

  - appname
    used in:
      - .gitignore.stamp
      - LICENSE.stamp
      - Makefile.stamp
      - README.md.stamp
      - cmd/cli.go.stamp
      - go.mod.stamp
      - main.go.stamp

Provide missing variables using:
  - Command line: stamp -s <sheet> -d <dest> appname=<value>
  - Config file: Create stamp.yaml in sheet or config directory

その他

将来シートを増やした際の共通シートが欲しくなることを想定して、複数シートを同時に指定できる機能なども設けています。

なお、機能をシンプルに保ちたいため、適用後に実施したいスクリプトなどを実行する機能は持たせないことにしました。 ひとまずは、シート中に以下のようなbootstrap.shなどを用意し、stampコマンド実行後に続けて実行する運用にしています。

#!/usr/bin/env sh
set -e

go mod tidy
git init
git add . ":!./$0"
git commit -m "Add project skeleton"
make test

rm -- "$0"

おわりに

今回のツール実装はClaude Codeに任せ、コマンドラインとしての使い心地の観点からコメントしながら進めました。 ただし、出発点は上述のシート相当のプロジェクト構成を与えた上で実装を開始してもらったため、実装の方向性については想定した範囲に収まりやすかったかなと感じています。

CLIツール開発の着手時の迅速化のみならず、コーディングエージェントへの実装補助線を与えるという点でも役立つツールかなと思いますので、よければ使ってみてください。

dotfilesを5年ぶりに整備した

なんとdotfilesも5年更新していなかった。 なにかと気忙しいのではあるが、木こりのジレンマに陥らないよう再起動していきたい。

さて、昨年末にMacbook Proを新調してまっさらな開発環境が手に入ったのに合わせてdotfilesを見直した。

大きくは以下。

  • dotfiles管理をchezmoiに変更
  • 開発ツール管理をmiseに変更
  • ターミナルエミュレーターをGhosttyに変更

以下、それぞれについて簡単にメモをしておく。

chezmoi

各ツールの設定などは大幅に変えていないのだが、dotfiles管理にはchezmoiを導入した。 これまで自作インストールスクリプトは、メンテナンスできていなかったし、設定ファイルの変更後のリポジトリへの反映ルールのようなものがなく反映漏れなど生じていたので、これを機にchezmoiの流儀に任せることにした。

設定ファイル

既存dotfilesがある場合、ファイルをchezumoi addしていくと、chezmoiの命名規則に沿ったファイル名でリポジトリに加えていくことができる。 命名規則はホームディレクトリ以下のディレクトリもしくはファイル名のそれぞれについて.dot_に置き換えたものになる。 例えば、~/.config/zsh/.zshrcであればdot_config/zsh/dot_zshrcとして管理する。

自分の場合は、このタイミングでXDG Base Directoryに合わせるようにもしたかったのと新しいPCで~が綺麗だったのもあり、chezmoi側(chezmoi cdで移動できるリポジトリのclone先。~/.local/share/chezmoi)で上記の命名に合わせて変更した上で、chezmoi applyをすることで反映する方法を取った。

$ chezmoi init git@github.com:monochromegane/dotfiles.git
$ chezmoi cd
$ mv zshrc dot_config/zsh/dot_zshrc
(snip)
$ chezmoi apply

今もリポジトリへの反映を忘れないよう基本はchezmoi側で修正してapplyをする運用にしている。

なお、dotfiles内の実行権限が必要なファイルについてはファイルに属性付与ではなく、bin/executable_git_diff_wrapperのように命名規則で制御する必要がある。

インストールスクリプト

chezmoiではインストールスクリプトも管理できる。 dotfilesリポジトリの直下におくこともできるが.chezmoiscriptsという名前のディレクトリを作ると複数スクリプトがあっても整理されるのでこの構成を採用した。

管理用ツールを導入するからには動作確認の必要な自作スクリプトの役割は最低限にしたかったので、パッケージ管理ツールのインストールのみに抑え、各パッケージのインストールなどはパッケージマネージャーに任せるようにしている。

具体的には、ツールや開発環境のパッケージ管理のためのHomebrew、miseと、Vim用のミニマルなパッケージ管理であるminpacだけをインストールしている。

$ chezmoi cd
$ ls .chezmoiscripts
run_once_after_01_install-brew.sh   run_once_after_03_install-mise.sh
run_once_after_02_install-minpac.sh

上記のような命名にすることで、apply後に一度だけスクリプトが順番に実行される。

mise

開発環境やツール類はHomebrewで雑多に入れていたのと、言語ランタイムの*env系が増えてしまっていたのを整理すべく、miseに寄せることにした。

“The front-end to your dev env"というコンセプト通り、開発環境構築に関連する多くのことができるのだが、ひとまずは、グローバルに各種言語ランタイムといくつかのツールを入れるようにしている。 ディレクトリごとにmise.tomlを作って利用するツールを使い分けられるのはとても良さそう。

例えば、このブログで使っているhugoコマンドなどはブログ用のリポジトリでだけ使えれば良いのでmise use go:github.com/gohugoio/hugo@latestとすれば、そのディレクトリ配下では~/.local/share/mise/installs/go-github-com-gohugoio-hugo/0.154.2/bin/hugoがPATH環境変数に追加されてコマンドが認識される状態になる。

なお、caskが便利なのもあり、しばらくはBrewfileによるHomebrewと併用していくことになると思う。

Ghostty

長年iTerm2を使っていて特に不満はなかったが、chezmoi導入にあたって設定をファイルで管理したくなり、Ghosttyを使ってみることにした。

“Zero Configuration Philosophy"を謳っているだけあり、以下の設定で十分、動作も軽快で特に問題なく利用できている。念の為iTerm2もまだ入れているがこのまま乗り換えで良さそう。

command = /opt/homebrew/bin/zsh -l
mouse-hide-while-typing = true

# Theme
theme = Dracula+
background = #0f1015

# Title bar
macos-titlebar-proxy-icon = hidden

# Font
font-family = Moralerspace Neon
font-thicken = true
font-thicken-strength = 1
font-feature = -dlig
font-size = 15

# Cursor
shell-integration-features = no-cursor
cursor-style = block
cursor-style-blink = false

# TERM
term = "xterm-256color"

フォントとテーマ

せっかくなので、フォントやテーマも変更してみた。

Ghosttyの標準だと日本語表示が微妙だったので、フォントとしてMoralerspaceを導入し、Neonを設定し、上記設定にあるように太め大きめにしている。

また、テーマには、ダーク系のテーマでVim側ともマッチするものとして、Dracula+テーマを採用した。 Dracula Theme for Ghosttyとしてもあるが、標準で選択できるようになっている。 Vim側もDracula Theme for Vimとして公開されているので、minpackで以下のように設定を追加した。

call minpac#add('dracula/vim', {'name': 'dracula'})

テーマそのままだと背景がグレー寄りで個人的にはややぼやけたように見えてしまうので、Ghostty側でbackgroundを設定し、以下のようにctermbgをNoneにすることでGhostty側の背景色と同じにして統一感を出している。

"colorschemeを設定する
function! s:customize_dracula_bg() abort
  highlight Normal       ctermbg=None
  highlight CursorLine   ctermbg=None cterm=underline
endfunction

augroup DraculaCustomization
  autocmd!
  autocmd ColorScheme dracula call s:customize_dracula_bg()
augroup END

colorscheme dracula

GhosttyでDraculaで、背景色#0f1015の青みがかった夜空みたいな色でコンセプト的にも統一できてなかなか気に入っている。

その他

いわゆるコーディングエージェント系の設定も見直していってるが長くなるのでまた次回とする。

後記的なもの

昨年、ブログを一本も書いていないことに気づいたのが今日。 年始に『「書くこと」の哲学 ことばの再履修』を読みながら、今回の記事のような、やったことや事実ベースの「書きえる」ことすら書かずして、何を書けようという気持ちからブログ再開してみたのであるが、ただ書き出すことの爽快さを思い出せてとても良かった。 気負わずに再開していきたい。

2024

今年は、九州大学大学院システム情報科学府博士後期課程を修了し、博士(情報科学)の学位を授与されたことが、一番のハイライトだった。 指導くださった先生方、ペパボ研究所の皆さんをはじめとする同僚、切磋琢磨した社会人学生の皆さん、そして家族へ、改めて心から感謝したい。

達成感でいっぱいであるものの、年度中の博士論文執筆と事業への貢献の両立は、想像以上に大変だった。 研究においては元々、事業への貢献も求められるため、自身の専門領域から様々な分析や提案、実装を並行して進めていたが、博士論文の執筆中は、その複雑さと膨大さゆえに脳内のリソースを極限まで使い続ける日々であり、これまで以上に研究と実務の両立の難易度が高かったように思う。 それでも、最後までやり切ることができたことに、今はただ安堵している。 一方で、これらの分析や提案については、伝え方や巻き込む人々の選定をもっと考えることで、より事業成果につなげられたのではないかという反省もある。

来年は博士として、今の専門性に加え、さらに高い期待が寄せられる立場となるはずである。 不確実で混沌とした課題領域において、比較・体系化・言語化を通じて、誰もがその課題を共有し、迅速かつ効果的な対策を講じられる基盤を整えることが求められる。 このスキルを活かし、事業に携わる人々が互いの専門性を最大限に発揮し、成果につながる環境を作り上げることを目指していきたい。

振り返れば、多忙の連続であった一年で、正直なところ鮮明な記憶が残っていない。 それでも、この積み重ねや振り返りが今後の糧になると信じ、今後も一歩一歩進んでいきたい。 来年もどうぞよろしくお願いいたします。

実績

以下、実績を列挙する。

論文

博論と国際会議論文1本。本数としては少なくなってしまったが博論と並行して国際会議に投稿・発表し、博論の質を向上させることができたのは自身を褒めてあげたい。 来年はより水準の高い会議へも挑戦していきたい。

国内発表

福岡で開催した技術イベントで1回(博論の公聴会を除く)。 来年は全国規模の技術イベントへの復帰を果たしたいところ。

OSS

AIを開発環境に取り込んでいくためのツール群を整えた。来年はもう一歩先の統合を目指したい。

コミュニティ活動

Fukuoka.goを再開できたのがよかった。いつの間にか10周年だったので来年3月ごろにお祝い的な会も開催を考えている。学会系では、研究会の運営委員を継続している。

ブログ

3本。ここ数年に比べると増えた。OSS紹介、思い出、考えのまとめなど雑多な内容だが、博士課程が終わってとにかく思いついたら書くことができているので来年も継続的に書いていきたい。

その他

研究所で開催した社内向けの勉強会の記事。来年はAI前提を踏まえこれらを事業にどんどん適用していく。

国際会議採択のニュースリリース。 来年はプレスリリース目指して頑張りたい。

みんなと仲良くすること

ここ数ヶ月、思うように成果が出なかったことに対するボスからのフィードバックを中心に自分なりに来年の仕事の指針としてまとめておく。

多くの事業では、環境の変化へ追従するためにプロジェクトの優先順位は目まぐるしく整理・最適化されていく。 また、近年では、データ駆動な意思決定、機械学習やAIのような新しい自動化の導入によって、事業に求められる水準も高度化している。 このような質・量ともに認知負荷の高い状況に対処するため、専門性を高めた分業やチーム体制が出来上がるのは必然である。 しかしながら、例え、各々が目的やビジョンを揃えていたとしても、皆が足並みを揃えて進むことは存外に難しい。 この要因として、分業した専門の観点ごとにゴールまでの過程における課題の内容や具体度が異なることが考えられる。 描く経路が異なれば、当然、足並みが揃うことはない。

よって、ある程度以上の規模や難易度の仕事を成功させるなら、関わる人々の間での関心度を高めないといけない。 しかし、分業による過程のカプセル化と無関心は紙一重である。 せっかく認知負荷を下げることができたのだから、わざわざ相手の事情を汲むような工程を挟まずに、阿吽の呼吸で足並みを揃えて進んでほしいと思いたくもなる。 それでもやはり、関わる範囲が広範囲だったり内容が高度になるに伴い、表面上の理解や想像による補完では立ち行かなくなってしまう。 だからこそ、互いの専門領域の広さと難しさを前提とし、労力を払って関心を維持することが仕事を成功させるために必要になってくる。

もちろん、全体としてのコミュニケーションコストを低減させるため、各々の専門領域に照らした道程について、聞き手に応じた表現で過不足なく効率的に伝えることが求められる。 そのために、伝えたいことを抽象的な概念上の枠組みに変換し、効率的に伝えるために図解をはじめとした表現方法を駆使するスキルが求められる。 発信側の労力も抑えるため、この実現にはAIも積極的に活用し、聞き手に応じたバリエーション生成まで即時に行える仕組みも構築したい。 また、関心は関係する人々がそれぞれ双方向に接続されている状態が望ましいが、返報性の原理を期待して、まずは制御可能な自身から関心を維持することを始めるのが良いだろう。

ペパボで大切にしている三つのことのうち一つに「みんなと仲良くすること」というものがある。 これを表面上の摩擦がない状態と捉えてしまうと無関心に通じてしまうので、仕事の達成を念頭において、伝えるべきことをしっかり伝えるというのが、自身のこれまでの理解であった。 ただ、これも一方向のコミュニケーションに過ぎず、ある種の無関心とも言えるのではないかと考え直したのが今回である。 今後は、仕事を達成するため、もう一段階踏み込んだ「みんなと仲良くすること」を常に意識的に実施していきたい。 そうすることで、無関心による安易な想像に起因する問題を回避したり、問題が生じたとしても、なぜ齟齬が発生しているのかを構造ベースで捉えて建設的に解決に進めることができると思う。


実際はフィードバックは以下のように端的にまとめてくれていたのだが、自分なりの解釈に落とし込むために書いてみました。これぐらいズバッとまとめられるようになりたい。

feedback

ペパボに入って12年が経った。気付けば博士になっていた

先月、博士(情報科学)の学位を取得した。

12年前、福岡 おもしろい it 会社と検索をして、株式会社paperboy&co.(現GMOペパボ株式会社)に運用開発エンジニアとして転職した時1からすると、予想だにしなかった状態である。 転機は2017年1月だった。 ペパボで切磋琢磨できる仲間に恵まれ、OSS活動や登壇を通したアウトプット、社内での成果を自負する一方で、社内でもレベルの高いエンジニアとの仕事の機会を通して、自分の限界も感じ始めていた。 そんな折、研究所の所長から研究職への打診があった。 確か、当時進めていたログ活用基盤の構築とその内容の研究所のコンセプトへの親和性からだったように記憶している。 感じていた限界を「コンピュータサイエンス」ってやつや「研究的アプローチ」ってやつで突破できるかもしれない、研究って何もわからないけれども何とかなるだろう、渡りに船だとばかりにこの誘いに乗ることにした。

結果、丸々二年間、滑稽なぐらい迷走した2。 「研究」をするぞと意気込みは空回りし、研究所の同僚からのアドバイスも虚しく、納得する成果を出せずに時間だけが過ぎ、短時間で効果が出るものを求め悪循環が始まった。 元来の己の精神的な弱さ、四十歳前後という年齢特有の焦り、人生最高潮に達したあらゆるものに対する固執が目を曇らせ、足を引っ張った。 長く暗い時期だったが、ほんとうにほんとうにありがたいことに、さまざまな人に支えられ、何度も内省を進めることでかろうじて持ち直していった。 研究所の同僚、エンジニアと研究者が集うWSA研3、研究者の先輩方、そして家族には感謝の念が尽きることはない。 それでも距離をおくことを選択した縁もあり、たくさん迷惑をかけてしまった。

「研究」は難しい。 これは高度という意味での難しさではなく、ソフトウェアによる課題解決とは目的が違うことに起因する難しさについてである。 博士課程を終えて、研究とは「人類が新たに学べる領域を作り出すこと」であると考えるようになった。 すなわち、ソフトウェアによる課題解決は、目的に連なる目標のうちの一つであり、その解決も含めて、新たに学べる状態に仕上げねばならない。 そして、後進が学べる状態であるには、どこがこれまでの方法と異なるのか、どれぐらいの効果があるのか、それらが主張とその裏付けという形で明確に述べられたかといった要件に落とし込まれる。 これは、論文における「新規性」「有用性」「了解性」の観点に対応し、論文を記述する際には、これらの観点を満たすための様々なサーベイやライティング手法を駆使して臨むことになる。 そのような過程を経ることで初めてそのアウトプットは、未来の誰かの手による学べる領域の更なる拡大を支えていくことができる。 落とし穴は、ソフトウェアによる課題解決を目標ではなく目的に据えてしまうことである。 ここに陥ると、課題の解決こそが研究であり、課題解決のみで研究であるためには、大層な水準の課題を解決せねばならないとなりかねない。 自分が最初に躓いたのはここではないかと思う。

この頃の自身の振り返りを読むと、感情との付き合い方4の他に、研究とは何かを手探りで模索している様子が見て取れる5 6 7。 解決したことを伝えていかないといけないのだな、そのためにはたくさんの根拠を示さないといけないのだな、だから一朝一夕じゃ無理で時間をかけて向き合うことが必要なんだな、程度の理解度ではあるものの、この辺りから前に進み出したのだろうと思う。 2020年6月に初めてジャーナルの採録通知を受け取り8、10月には社会人学生として九州大学大学院システム情報科学府博士後期課程へ社会人学生として進学した9。 進学の後押しも研究所の所長だったと思う。 これまでの経緯もあり、及び腰だった自分に「研究職続けるなら博士号はあった方がいいし、いずれは取るのだから最短で効果が出せることをやったらいいんじゃないですか」と言われ、それもそうだなと挑戦を決めた。 暗中模索の中でも進めていた十本弱の研究報告やジャーナルの実績をもって修士を飛ばしての出願と入学が認められたこともあり、積み重ねは無駄じゃなかったなあ、ようしやるぞとここからは順風満帆。 といかないのがいかにも自分らしくて思い出しても苦笑してしまうのだが。

博士課程の修了には一定数の実績が必要となる。 自分の場合は、在学中に国内ジャーナル論文をもう一本、一定水準以上の国際会議での採択が一回が最低基準であった。 国内ジャーナルこそなんとか通せたものの、国際会議については、2021年5月の初投稿から丸二年、七回目の挑戦にしてようやくの採択となり長い我慢の時期を過ごした10。 そんな中、指導教官は一貫して、研究という最短ルートはない道程において、同じ道を回ることなく、常に何かしらの前進という成果を得るためのアドバイスを与えてくれたと思う。 それは例えば「難しいものと認識した上でそれに挑戦する喜び」であったり「ダメだった場合は改善してただ次に行くだけ」であったり、「自分で決めて悔いのないように進む」こと、そして「結果に対して本質的な面白さを見出して言語化し、これまでの取り組みと有機的に関連づけていくこと」などである。 博士課程という自分の研究の型を体得していく中で、指導教官の存在は本当にありがたいものであった。 指導教官は、分野の専門性が重なっていることが望ましいが、それ以上に相性があると思うので、できれば共著を先に一度執筆するなどコミュニケーションを取れると良いと思う。 幸い、自分の場合は、進学前の共同研究の際に知り合うことができたのでこの点でも恵まれていたと思う。

とはいえ、博士課程は、自身で主体的に研究を推進しなければならない。 これは、博士課程が「人類が新たに学べる領域を作り出すこと」という研究を達成する、「その方法論を、特定の研究テーマに基づく実践を通して身につける課程」だからだと思われる。 新たに学べる領域を作り上げているのであるから、指導教官にとってもわからない部分を切り開いていくのであり、そしてその開拓の方法は、自身でやり方を模索していかなければならない。 主体性については、能動的に動くことが前提の社会人であれば特段問題にならないかと思う。 博士課程での大変さは、この研究を推し進めるやり方の模索に対するフィードバックが、アウトプットに対してのみ得られるという点だったと思う。 それは主に査読結果や、国際会議での発表に対する議論の形を取るが、いずれにせよ論文の形にまとめる必要がある。 これは研究を学ぶには研究をしなければならないという構造であり、研究テーマ自体も発展させなければならないことも相まって、随分鍛えられたと感じている。 幸い、指導教官のアドバイスはこれに即したものであり、博士論文に着手する前の三年間で研究報告や不採択のものも含めると十六本、おおよそ二、三ヶ月に一本は何かしら研究成果を出して論文執筆していたことになる。 成長が遅いのは仕方ないとはいえ、研究開発員として、事業への貢献も求められる中でも諦めずに課程を継続したことは自分を褒めてあげたい。

2024年4月には、追加でもう一つ国際会議に採択され、最終的に、入学前のジャーナル論文と合わせて計四本の実績をもとに、学位申請のための博士論文を執筆した11。 博論の執筆では、これまでの実績を「人類が新たに学べる領域」として再構成する。 四本の論文の整合性を取りながら、領域の世界観の確立も必要で、非常に難易度の高い執筆であり、実績達成以上にまだ大変な工程が残っているのかと驚いた。 しかしながら、この工程を通して初めて、ボトムアップな進め方であった自身の研究テーマに対し、トップダウンからの位置付けを与えることができたように思える。 実際に、自身の研究観である「人類が新たに学べる領域を作り出すこと」というのもこの段階を経て感じるようになった。 これまで各論文においても「新規性」「有用性」「了解性」の観点を満たせるよう突き詰めてきたが、複数論文をまとめる中で、それぞれの点が関連し、補い合う形で(まだ非常に狭いながら)初めて領域として形をなしていったように思う。 なお、世界観の確立については、ペパボ研究所のコンセプトである「なめらかなシステム」についての、所長との数年間に渡るディスカッションが非常に役立った。 このようなハイコンセプトな思考だけでなく実践的にも抽象的にも自在に観点を行き来しながら多面的に研究を見直す機会があるのがペパボ研究所の長所だと思う。 博士課程を考えている方はぜひ検討してほしい。 その後、公聴会を経て、2024年9月25日付けで博士(情報科学)の学位を授与された。 在学は四年間。 標準年度の三年には間に合わなかったが、なんとか修了に漕ぎ着けることができて安堵している。

さて、ここまでして博士課程を終了する必要はあったのか。そしてこれからも研究をする必要はあるのか。 サンクコストを差し引いたとしても、自分自身としては「はい」と答えたい。 「課題解決」と「研究(新たに学べる領域を作り出すこと)」は相補的な関係であり、それぞれの存在がお互いを必要としていると思う。 また、特にソフトウェアやエンジニアリングの分野においては、それぞれは明確に分離している訳でもないと思う。 研究的なアプローチによって、同じ課題に対しても体系化・一般化・言語化・比較整理などを通して、多面的に捉え直し、新しい観点からちょっと面白い発想が出るかもしれない。 研究による成果物によって、ある課題に対してある状況において有用な方式を広く適用できるかもしれない。 その方式がうまくいかない場合も、理論的な裏付けもしくは再現可能な部分評価を元に、そこから学んで最短で適用できる改善を思いつけるかもしれない。 学べる状態にするのは手間がかかる。 それでもどこかの誰かの巨人の肩になるのだと信じて、研究というのを仕事にする人がいても良いのではないかと思う。

もちろん、企業研究所に所属する研究員としては、より実践的に、博士課程で得たスキルを早速活用していきたい。 改めて、博士号を取得したということは、不確実で混沌とした課題領域においても、比較・体系化・言語化を通して、誰もがその課題を共有し、時間をかけることなく効果のある対策を講じられる基盤を整えられるスキルを持つ、すなわち、学べる領域を作り出せる人材であることが期待されるようになったのだと思う。 今後は、自身の研究テーマの推進はもちろんのこと、さまざまな施策において、所属する研究所のミッションである「研究開発により『事業を差別化できる技術』を生み出す」ことができるよう、さらに精進していきたい。 そして、これまで辛抱強く支えてくれたペパボに恩返ししていきたい。

ペパボに入社して十二年。 タイトルの「気付けば」も大袈裟ではなくて、特に研究職になってからは時間が飛ぶようにすぎていった。 それでも、いろんなことがあったけれども、誰かと比較してではなく、過去最高の自分に仕上がっているというのは四十三歳にしては悪くないのではないか。 これからも「虚仮の一念、岩をも通す」の精神で頑張っていきたい。

お知らせ

僕のキャリアを支援してくれたことからも分かるように、GMOペパボ株式会社はどうすればインターネットを面白くできるのか、これを真面目に考えてさまざまななやり方で取り組んでいます。 研究開発的なアプローチに対してでも良いですし、事業に対してでも大丈夫です。 こんなペパボに興味持たれた方、もっと雰囲気知りたい方、お気軽にお声がけください。

ペパボ研究所

参考

Archives