バイナリークロスエントロピーを偏微分する
バイナリークロスエントロピーを $w_i$ で偏微分し, 以下の式を算出する.
$$ \boxed{\frac{\partial L}{\partial w_i} = \frac{1}{N}\sum_{s=1}^{N}(y_s - t_s)x_{si}} $$
バイナリークロスエントロピー(BCE)を $w_i$ について偏微分する
バイナリークロスエントロピーの式
$$
\boxed{L = \frac{1}{N} \sum_{s=1}^{N} \left( - t_s \log y_s - (1 - t_s) \log (1 - y_s) \right)}
$$
式中の $y_s$ は以下ロジスティック関数の出力である. $$ \boxed{y=\frac{1}{1+e^{-u}}} $$
また, ロジスティック関数の式中の $u$ は, 以下ニューロンの活性の出力である. $$ \boxed{\begin{aligned} u &= w_1x_1 + w_2x_2 + \dots + w_nx_n + b \\ &= \sum_{i=1}^{n} w_i x_i + b \\ &= \boldsymbol{w} \cdot \boldsymbol{x} + b \end{aligned}} $$
これらを合成関数で表すと以下のようになり,
$$ L(y(u(w))) $$
チェーンルールで表すと以下のようになる.
$$ \frac{\partial L}{\partial w_{i}} = \sum_{s=1}^{N} \frac{\partial L}{\partial y_s} \frac{\partial y_s}{\partial u_s} \frac{\partial u_s}{\partial w_i} $$
微分は以下のような加法性の性質がある. つまり, 微分を行った後に 総和の演算を行うことは問題がない. $$ \frac{d}{dx}[g(x) + h(x)] = g^\prime(x) + h^\prime(x) $$
ので, $\frac{\partial L}{\partial y_s}$, $\frac{\partial y_s}{\partial u_s}$, $\frac{\partial u_s}{\partial w_{i}}$ の3つについて偏微分の計算を行う.
$\frac{\partial L}{\partial y_s}$ (BCE を $y_s$ で偏微分する)
バイナリークロスエントロピー(BCE)
$$ \boxed{L = \frac{1}{N} \sum_{s=1}^{N} \left( - t_s \log y_s - (1 - t_s) \log (1 - y_s) \right)} $$
偏微分する
微分は以下のような加法性の性質がある.
加法性
$$
\frac{d}{dx}[g(x) + h(x)] = g^\prime(x) + h^\prime(x)
$$
つまり項毎に微分をした後に加算しても同じなので,
総和記号(Σ)の後ろに続く $\left( - t_s \log y_s - (1 - t_s) \log (1 - y_s) \right)$ の微分について考える.
ちなみに, $\frac{1}{N}$ の部分は定数倍なので, 微分をしてもそのまま残る.
まず, $-t_s \log y_s$ の部分は, 自然対数の微分公式を適用し以下のように微分できる.
$$ -t_s \log y_s {⇒} -\frac{t_s}{y_s} $$
続いて, $-(1 - t_s) \log (1 - y_s)$ を計算する.
まず, $-(1-t_s)$ は $y_s$ に依存しない定数とみなし, そのまま残す(定数倍規則).
$\log (1 - y_s)$ は さらに関数に分解し, チェーンルールを用いて計算する.
- $f(x) = \log(x)$
- $h(y) = 1-y$
$$ \begin{aligned} f^\prime(x) &= \frac{1}{x} \\ h^\prime(y) &= -1 \\ f^\prime(h(y)) \times h^\prime(y) &= \frac{1}{1-y}(-1) \\ &= -\frac{1}{1-y} \\ \end{aligned} $$
以上の式を組み合わせると以下のように計算できる.
$$
\begin{aligned}
\frac{\partial}{\partial y_s} &= \frac{1}{N}\left[-\frac{t_s}{y_s} - {(1-t_s)}(-\frac{1}{(1-y_s)}) \right] \\
&= \frac{1}{N}\left[-\frac{t_s}{y_s} + \frac{(1-t_s)}{(1-y_s)}\right] \\
&= \frac{1}{N}\left[-\frac{t_s \times (1 - y_s)}{y_s \times (1 - y_s)} + \frac{(1-t_s) \times y_s}{(1-y_s) \times y_s}\right] \\
&= \frac{1}{N}\left[-\frac{(t_s - t_sy_s)}{(y_s - y_s^{2})} + \frac{(y_s-t_sy_s) }{(y_s-y_s^{2})}\right] \\
&= \frac{1}{N}\left[\frac{-(t_s - t_sy_s) + (y_s-t_sy_s)}{(y_s - y_s^{2})}\right] \\
&= \frac{1}{N}\frac{y_s - t_s}{y_s(1 - y_s)} \
\end{aligned}
$$
特定の添え字( $s$ )を持った $y_s$ で偏微分するので,
Σ で加算されていた他の項は 0 になる. つまり, Σ は消える点に注意.
(冒頭に記載した $w_i$ での微分は, 総和で演算する各項に $w_i$ を含む都合上, 総和記号が消えていなかった)
$\frac{\partial y_s}{\partial u_s}$ (ロジスティック関数を偏微分する)
ロジスティック関数を微分する - yshinya09のブログ を参照. 以下式が求められる. $$ y^\prime = y(1-y) $$
$\frac{\partial u_s}{\partial w_{i}}$ (ニューロンの活性式を $w_i$ で偏微分する)
ニューロンの活性式を $w_{i}$ で偏微分する.
$$
u_s = w_1x_{s1} + w_2x_{s2} \dots + w_ix_{si} + \dots + w_nx_{sn} + b
$$
偏微分なので, $w_i$ が含まれる項以外の変数は全て定数として扱うため, 消える.
$w_ix_{si}$ は $w_i$ に関する一次の項なので, 線形関数の微分公式を適用すると $x_{si}$ になる.
結果, $\frac{\partial u_s}{\partial w_{i}}$ は以下のようになる.
$$ \begin{aligned} \frac{\partial}{\partial w_{i}} & = (w_1x_{s1} + w_2x_{s2} \dots + w_ix_{si} + \dots + w_nx_{sn} + b) \\ &= 0 + 0 \dots + x_{si} + \dots + 0 + 0 \\ &= x_{si} \end{aligned} $$
$\frac{\partial L}{\partial y_s}$, $\frac{\partial y_s}{\partial u_s}$, $\frac{\partial u_s}{\partial w_{i}}$ を組み合わせる
$y_s$ について偏微分したクロスエントロピーの導関数. $$ \begin{aligned} \frac{\partial L}{\partial y_s} &= \frac{1}{N}\frac{y_s - t_s}{y_s(1 - y_s)} \ \end{aligned} $$ $u_s$ について微分したロジスティック関数の導関数. $$\frac{\partial y_s}{\partial u_s} = y_s(1-y_s)$$ $w_i$ について偏微分したニューロンの活性式の導関数. $$ \begin{aligned} \frac{\partial u_s}{\partial w_{i}} = x_{si} \end{aligned} $$
これらを合成関数の微分の基本公式を使用して計算する.
合成関数の微分の基本公式 $$ \lbrace f(g(x))\rbrace^{\prime} = f^{\prime}(g(x)) ×g^{\prime}(x)
$$
- BCE の偏微分に $y_s$(ロジスティック関数) を代入するのが正しい気もするが, 計算が煩雑になるので, $y_s$ のまま使用する.
- ロジスティック関数の導関数には, $u_s$ が使用されないので, 活性式の導関数と乗算のみ行う.
チェーンルールにより:
$$ \frac{\partial L}{\partial w_{i}} = \sum_{s=1}^{N} \frac{1}{N}\frac{y_s - t_s}{y_s(1 - y_s)} \times y_s(1-y_s) \times x_{si} $$
ここで $y_s(1-y_s)$ が約分されて: $$ \boxed{\frac{\partial L}{\partial w_{i}} = \frac{1}{N}\sum_{s=1}^{N}(y_s - t_s) x_{si}} $$ この関数を使用することで, $w_i$ に関する, 損失関数上の傾きを知ることができる.
以上です.
ロジスティック関数を微分する
活性化関数として使用されるシグモイド関数のロジスティック関数を微分します。
ロジスティック関数
$$ \boxed{y=\frac{1}{1+e^{-u}}} $$
関数を分解する
微分するにあたり, まずはロジスティック関数を2つの関数に分解する.
- $f(x) = \frac{1}{x}$
- $g(u) = 1+e^{-u}$
そうすると, ロジスティック関数を $f(g(u))$ のような合成関数として表せる.
合成関数の微分の基本公式を適用する.
合成関数の微分の基本公式
$$ \lbrace f(g(x))\rbrace^{\prime} = f^{\prime}(g(x)) ×g^{\prime}(x)
$$
$f^\prime(g(x))$ と $g^\prime (x)$ をそれぞれ算出した後に, 乗算をおこなう.
$f(x) = \frac{1}{x}$ を微分する
べき乗の微分の公式 を使用して, $f^\prime(x)$ を算出する.
$$
f^\prime(x) = -x^{-2} = - \frac{1}{x^{2}}
$$
合成関数の微分の基本公式に従い, $x$ に $g(u)$ を代入すると以下になる.
$$ f^\prime(g(u)) = -\frac{1}{(1+e^{-u})^{2}} $$
$g(u) = 1+e^{-u}$ を微分する
$g(u)$ を微分するにあたり, こちらも以下2つの関数に分解する.
- $h(z) = 1+e^{z}$
- $i(u)=-u$
先ほどと同じように, 合成関数の微分の基本公式を適用する.
$h(z) = 1+e^{z}$ を微分する
定数は微分すると 0 になり, 指数関数部分は微分しても同じ式の為, $h^\prime(z)$ は以下のようになる.
$$
h^\prime(z) = e^{z}
$$
合成関数の微分の基本公式に従い, $z$ に $i(u)$ を代入する.
$$
h^\prime(i(u)) = e^{-u}
$$
$i(u)=-u$ を微分する
$i(u)$ は一次関数の微分の公式を適用することで以下が導ける.
$$ i^\prime(u) = -1 $$
$g^\prime(u)$ を求める
$f^\prime(i(u))$ と $i^\prime(u)$ がそれぞれ求められたので乗算し, $g^\prime(u)$ を求める. $$ g^\prime(u)=e^{-u}×-1 = -e^{-u} $$
ロジスティック関数の微分を求める
ロジスティック関数の微分に必要な $f^{\prime}(g(u))$ と $g^{\prime}(u)$ をそれぞれ算出することができたので, 乗算を行い導関数を求める.
$$
\begin{aligned}
y^\prime &= -\frac{1}{(1+e^{-u})^{2}}×-e^{-u} \\
y^\prime &= \frac{e^{-u}}{(1+e^{-u})^{2}}
\end{aligned}
$$
$y^\prime$ について
思い返すとロジスティック関数の式は以下のような形.
先ほど求めた ロジスティック関数の導関数 の中によく似た形が現れており,
$y$ を使った式に置き換えることができる.
$$
y=\frac{1}{1+e^{-u}}
$$
後述の式内で使用する為,
先にロジスティック関数から $e^{-u}$ を算出する式を作る.
$$
\begin{aligned}
e^{-u}=\frac{1-y}{y} \
\end{aligned}
$$
ロジスティック関数の導関数の一部をロジスティック関数で置き換える.
$$
\begin{aligned}
y^\prime &= \frac{e^{-u}}{(1+e^{-u})^{2}} \\
y^\prime &= \frac{e^{-u}}{(1+e^{-u})(1+e^{-u})} \\
y^\prime &= \frac{1}{(1+e^{-u})} \times \frac{1}{(1+e^{-u})} × e^{-u} \\
y^\prime &= y \times y \times \frac{1-y}{y} \\
y^\prime &= y(1-y)
\end{aligned}
$$
ロジスティック関数の微分は $y^\prime = y(1-y)$ でも表現できることがわかった.
以上です.
LoslynAnalyzerを作成してUnityで使ってみる
はじめに
LoslynAnalyzer を使用することで,
ソースコードをチェックし問題があれば
error や warning として表示する機能を独自に実装することができます。
Unity でも使用することができるので,
今回は簡単な Loslyn Analyzer プロジェクトを作成し,
Unity に適用するまでを試してみます。
環境
Windows 11 Home 24H2
Unity バージョン 6000.1.6f1
Visual Studio : 2022
Analyzer のソリューションを作成する
Visual Studio にて, 「新しいプロジェクトの作成」を行います。
C# の「Analyzer with Code Fix (.NET Standard)」テンプレートを選択し, 任意のディレクトリに作成します。
自分は Unity の Assets ディレクトリと同階層に SampleAnalyzer を作成し指定しました。
.NETFramework は 4.8 で作成しました。
他の記事だと .NETStandard 2.0 を選択していましたが,
作成した Analyzer の csproj に以下の記述があったのでよしとしました。
<TargetFramework>netstandard2.0</TargetFramework>
ソリューションを作成すると
いくつかのプロジェクトで構成されていることがわかるかと思います。
各プロジェクトの説明は以下です(GPT談)。
- Analyzer.csproj : コードアナライザーの本体を実装するプロジェクトで違反を検出するロジックを記述する
- Analyzer.CodeFixes.csproj : コードの問題を自動修正するためロジックを実装する
- Analyzer.Paclage.csproj : Analyzer と CodeFixes を NuGet パッケージとしてビルドするのに使用する
- Analyzer.Test.csproj : アナライザーとコードフィックスの単体テストを記述する
- Analyzer.Vsix.csproj : Visual Studio の拡張機能(VSIXファイル)としてビルドするのに使用する
今回はエラーだけ出せれば良いので,
Analyzer と Test 以外は削除してしまいました。
今回は確認用に以下のようなコードを用意しました。
Analyzer.csproj 以下の cs ファイルの内容を書き換えます。
このコードは Debug.Log を使用するとエラーにするものです。
using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace SampleAnalyzer { [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class SampleAnalyzerAnalyzer : DiagnosticAnalyzer { private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( id: "SA0001", title: "SampleAnalyzer Error", messageFormat: "messageFormat", category: "Usage", defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true, description: "description"); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterSyntaxNodeAction(AnalyzeInvocation, SyntaxKind.InvocationExpression); } private static void AnalyzeInvocation(SyntaxNodeAnalysisContext ctx) { var invocation = (InvocationExpressionSyntax)ctx.Node; var symbolInfo = ctx.SemanticModel.GetSymbolInfo(invocation); if (!(symbolInfo.Symbol is IMethodSymbol methodSymbol)) { return; } var containingType = methodSymbol.ContainingType; if (containingType == null || containingType.ToDisplayString() != "UnityEngine.Debug") return; var methodName = methodSymbol.Name; if (methodName == "Log" || methodName == "LogWarning" || methodName == "LogError" || methodName == "LogException") { // Error. ctx.ReportDiagnostic(Diagnostic.Create(Rule, invocation.GetLocation())); } } } }
ビルド
Analyzer の準備ができたので dll を作成します。
Visual Studio のビルドメニューからバッチビルドを選択し, ビルドを実行します。
以下に dll が生成されるかと思います。
SampleAnalyzer\SampleAnalyzer\SampleAnalyzer\bin\Release\netstandard2.0\SampleAnalyzer.dll
Unity に配置する
以下の公式ドキュメントを参考に Unity のディレクトリに dll を複製して配置します。
(ちなみに explorer から D&D で Project View に移動させれば勝手に複製されます)
Unity - Manual: Create and use a source generator
添付画像のように設定して完了です。

添付画像のようにコンソールに Debug.Log を使用している箇所のエラーが出ていれば成功です。

Visual Studio Code でも, Debug.Log を使用するとエラーになることが確認できました。

おわりに
今後 AI にコードを書かせる機会が増えてくるので,
コンパイルエラーで気づけるようにしておくのは今後必要になるかもなと思いました。
Analyzer は既成のものを使用する手法もあるので, そちらも試していきたいです。
以上.
ClaudeCodeのベストプラクティスを読む1
はじめに
Claude Code のベストプラクティス記事が開発元の Anthropic から出ていました。
Claude Code を扱う上で知っておいたほうが良いことが記載されているので,
読みながら自分の参照用にまとめます。
今回まとめた箇所は元記事の一部で,
検証はあまりできていないので注意してください。
正確な内容については以下を参照してください。
https://www.anthropic.com/engineering/claude-code-best-practices
環境の最適化
Claude Code はプロンプトに自動的にコンテキストを取り込みながら
実装をしてくれますが, 特に意識せずに ClaudeCode を使用していると,
コンテキストの収集に時間とトークンを消費してしまいます。
工夫することでこのコンテキストの収集を最適化できます。
CLAUDE.md ファイルの作成
CLAUDE.md は会話を始めたタイミングで,
Claude が自動的にコンテキストとして読み込むファイルです。
ファイル内容の記載形式に決まりは無く,
簡潔に人が読みやすい 形でまとめることが推奨されています。
また, 以下のような情報を記述するとよいとのことです。
- よく使う Bash コマンド
- 中核となるファイルやユーティリティ関数
- コーディング規約
- テスト実行手順
- リポジトリの運用ルール(例: ブランチ命名規則、マージかリベースか など)
- 開発環境のセットアップ方法(例: pyenv の利用、使用可能なコンパイラ)
- プロジェクト特有の予期しない挙動や警告
- Claude に記憶してほしいその他の情報
便利機能として, claude に対して, /init を実行すると
Claude が自動で CLAUDE.md のひな形を生成してくれます。
CLAUDE.md を配置できる場所
リポジトリのルート
最も一般的な置き場所。
共有したくない場合は CLAUDE.local.md と名付けて
.gitignore に追加することも可能。
実行ディレクトリより上位の任意の親ディレクトリ
モノレポで便利。
例: root/hoge で claude を実行し,
root/CLAUDE.md と root/hoge/CLAUDE.md の両方が存在する場合,
どちらも自動的にコンテキストに取り込まれる。
実行ディレクトリより下位の任意の子ディレクトリ
上記の逆パターン。
子ディレクトリのファイルを編集・参照するときに,
その都度 Claude が必要な CLAUDE.md を読み込む。
ホームディレクトリ (~/.claude/CLAUDE.md)
すべての Claude セッションに適用される。
CLAUDE.mdのチューニング
CLAUDE.md は常に参照される為,
ファイルの内容は洗練させる必要があります。
CLAUDE.md を更新したら, どの記述がモデルへどう影響するのか
時間をかけて検証することを推奨します。
CLAUDE.md への追記
CLAUDE.md への追記は, 手動でファイルを編集しても良いですが,
# キーを押して Claude に内容を伝え, 関連個所に反映させる方法も有用です。
Anthropic 社内では, 時々 CLAUDE.md を
Prompt Improver(プロンプト改善ツール)にかけたり,
"IMPORTANT" や "YOU MUST" などの強調語を加えて
指示の遵守率を向上させているとのことです。
Claude の許可ツール一覧を管理する
デフォルトの挙動では,
Claude Code はファイル書き込みやコマンド実行など,
システムを変更する可能性があるすべての操作に対して許可を求めます。
安全なツールや巻き戻し可能なツールは許可を求めないように設定することが可能です。
許可ツールを管理する 4 つの方法:
1. セッション中のプロンプトで "Always allow" を選択する
2. /permissions コマンドで許可リストを追加・削除する
- 例:
- EditBash を追加して常にファイル編集を許可
- (git commit:*) を追加して git commit を許可
- mcp__puppeteer__puppeteer_navigate を追加して Puppeteer MCP でのナビゲーションを許可
3. .claude/settings.json または ~/.claude.json を手動編集(前者はチーム共有のためにソース管理に入れることを推奨)
4. セッション単位で --allowedTools CLI フラグを使う
GitHub を使う場合は GitHub CLI をインストールする
GitHub CLI は GitHub の操作が可能になるコマンドラインツールのことです。
GitHub CLI をインストールすることで,
Claude が CLI を通して issue や PR の作成, コメント閲覧が可能になります。
GitHub CLI を使わずに GitHub API や GitHub の MCP サーバ経由で操作することもできますが,
GitHub CLI の方がスムーズに機能します。
おわりに
CLAUDE.md を適切にメンテナンスすることが, 時間とコストの浪費削減に大きく寄与しそうですね。
Max プランを契約していない方は, Token の上限にすぐ引っかかってしまうので,
この辺りのチューニングに時間をかけてみても良いかもと思いました。
また続きもまとめる予定です。以上.
Claude Code を使って自分の行動ログを集計する GAS を書いてもらう
はじめに
先日 Claude Code が Claude Pro プランでも利用することができるようになりました。
使用量に限りがあるものの定額でエージェント型のコーディングツールに使えるのはありがたいですね。
これを機に Claude Code を使ってみようかと思いました。
今回, Claude Code に作成してもらうのは睡眠や食事などの活動のログの集計を行う GAS です。
活動ログの送信は Google Form から行い, そのタイムスタンプと活動内容から
合計時間を算出する簡単なプログラムを書いてもらうことにしました。
以前から自分は何か活動するたびに「活動ログ」を記録していたのですが,
その集計処理を作成するのが面倒で先延ばしにしていたので簡単にできたら嬉しいですね。
Claude Code について
Claude Code はコマンドラインで操作可能なエージェント型のコーディングツールです。
以下コマンドでインストールできます。詳しくはドキュメントを読んでください。
npm install -g @anthropic-ai/claude-code # Claude Code をインストール claude # 起動
https://docs.anthropic.com/ja/docs/agents-and-tools/claude-code/overview
Windows では利用できないとのことなので, WSL2 上にインストールして利用します。
準備
まずは Claude Code を使用するための clasp のプロジェクトを用意します。
cd で移動したら, 以下の手順でこのプロジェクトについての CLAUDE.md を作成してもらいます。
claude # Claude を起動 summarize this project # 簡単なコマンドを流す /init # CLAUDE.md を作ってもらう
CLAUDE.md はプロジェクトに関する概要みたいなもので,
Claude がこのファイルを読み取って作業を行います。詳しくは以下のドキュメントを参照してください。
https://docs.anthropic.com/ja/docs/agents-and-tools/claude-code/overview
以下のように /init を実行して CLAUDE.md を作成してもらいます。

お願いしてみる
Claude Code に作ってもらうのは, 活動ログを参照し, 日ごとに各活動の合計時間を算出する GAS です。
docs/spec.md に以下のような簡単な仕様をまとめて Claude Code に参照して開発してもらうことにしました。
# 活動ログ集計処理について
- Google Form から活動のログを送信します
- 送信した活動ログは "フォームの回答 1" シートに記入されます
- "フォームの回答 1" シートの構造
- A列2行目移行にタイムスタンプ
- B列2行目以降に活動の内容
- "フォームの回答 1"シートを参照し, "日別集計" シートに活動時間を集計する処理を書いてください
- "日別集計" について
- A列に日付
- B列以降に活動の内容
- 各活動の時間を 1 つ前の活動ログから算出し, 合計時間を算出してください
- 1 つ前の活動が日を跨いだ場合, 当日と前日に分けて集計を行ってください
- この処理はフォームから新しい登録があった際に実行されるようにしてください
以下日本語の指示で実装の依頼をします。
@docs/spec.md を参照して活動ログの合計時間を算出する処理を作成してください
ちょくちょく「こんなことしようとしてるけどやってええか」と聞かれるので,
ええでと返していると, 1分くらいで完成したと返ってきました。

適当にデータを用意して実行してみます。

エラーになることなく処理が通り, スプレッドシートに出力されたのですが,
あっているか以前にみづらすぎるので修正をお願いすることにしました。

ちなみにターミナル上で一度 Claude Code とのやりとりを閉じてしまった場合は
以下のコマンドで前回のやりとりの続きから再開することができます。
claude --continue
以下のようにコードの修正を依頼しました。
処理を実行したところ出力結果が見づらかったので修正してください。列のサイズはヘッダー文字列の長さに合わせていださい。合計時間は 1:00 や 13:00 のように表示してください。
また軽いカンバセーションをしていると 1分くらいで完了したと返ってきました。

コードを実行してみたところ, 合計時間がだいぶ読みやすくなりました。
仮データと見比べてみると合計時間も正しそうです。

列のサイズが見切れてしまっていたので, また以下のようにお願いしました。
ヘッダーの文字列には絵文字が含まれます。絵文字を考慮した長さにしてもらえますか?

こちらの修正もすぐ終わりヘッダーの文字長に合わせて
列のサイズを調整するようになりました。ばっちりです。
一度もコードを書かずに完成させることができました。

Claude に commit と伝えると以下みたいにコミットしてもらえます。
commit e55f48a4946b20976af76b962467df2ce00d9457 (HEAD -> main, origin/main, origin/HEAD)
Author: <>
Date: Mon Jun 16 19:42:24 2025 +0900
Implement comprehensive activity log aggregation with time formatting and emoji-aware column sizing
- Add activity time calculation based on intervals between consecutive activities
- Implement cross-day activity splitting for accurate daily aggregation
- Add time formatting to display hours:minutes format (e.g., 1:00, 13:15)
- Implement emoji-aware column width calculation for better display
- Add automatic form submission trigger setup
- Refactor code structure with improved type definitions and error handling
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
感想
適当に指示するだけでプログラムができますが, 無駄なやりとりでも token を消費します。
テストの実行まで任せたり, 適切な仕様書を用意するなど
エージェントを使用する環境や指示の仕方を工夫することで
手戻りを減らして効率化できそうだと思いました。
Pro プランであれば, 使用できる token の量も限られているので,
そういった効率化が token の節約にも繋がってきそうですね。
UnitySearchで困ったところ
はじめに
Unity Search にプロジェクトで使用するデータを検索する機能を追加しようとした際に,
いくつか実装に手間取ったところがあったのでまとめました。
docs.unity3d.com
環境
Unity : 2022.3.10f1
困ったところ
filterId を検索フィールドに入力した時にのみ動作してほしい
愚直に SearchProvider を実装すると SearchWindow を開いた際,
検索フィールドに何も入力していないのに該当する要素が全て表示されてしまいます。

filterId を検索フィールドに入力した時にのみ
その filterId が有効になるようにするには
SearchProvider.isExplicitProvider に true を設定します。
[SearchItemProvider] private static SearchProvider CreateProvider() { return new SearchProvider("sample", "Sample Search Provider") { isExplicitProvider = true, filterId = "s:", fetchItems = FetchItems, }; } private static IEnumerable<SearchItem> FetchItems(SearchContext context, List<SearchItem> items, SearchProvider provider) { return AssetDatabase.GetAllAssetPaths().Select(path => provider.CreateItem(path)); }
以下のように filterId の s: を入力した時のみ要素が表示されるようになります。

Table View のカラムを追加したい
Table View とは Search Window の右下の格子状のアイコンを押した時の状態のことを指します。
この状態では Tree View のように要素ごとにカラムが表示され, より多くの情報を確認できます。

Table View では SearchWindow 右上の + ボタンから
カラムを追加することができます。
Table View で利用可能なカラムを追加するには
SearchProvider.fetchColumn に
IEnumerable
コンテキストメニューに要素を追加することができます。
[SearchItemProvider] private static SearchProvider CreateProvider() { return new SearchProvider("sample", "Sample Search Provider") { filterId = "s:", fetchItems = FetchItems, }; } private static IEnumerable<SearchItem> FetchItems(SearchContext context, List<SearchItem> items, SearchProvider provider) { return AssetDatabase.GetAllAssetPaths().Select(path => provider.CreateItem(path)); } private static IEnumerable<SearchColumn> FetchColumns(SearchContext context, IEnumerable<SearchItem> items) { yield return new SearchColumn("path/sample column", "sample selector", "search column provider") { width = 200 }; }

カラムに表示するデータの抽出や描画の処理は後述の SearchColumnProviderAttribute を使用して定義します。
カラムの表示を変更したい
SearchColumnProviderAttribute を使用することで,
カラムの表示方法を定義するフォーマットを追加することができます。
docs.unity3d.com
このフォーマットは特定の SearchProvider に依存せず,
どの SearchProvider でも使用できるようになるようです。
なので, 表示内容を保有するデータは SearchProvider 毎に専用のデータ型を定義するより,
Dictionary<string, object> のようなものを使用するのが良いかもしれません。
Attribute の引数に provider を設定しますが, これは SearchProvider の id ではなく,
SearchColumnProvider の id となる名前を設定します。
[SearchItemProvider] private static SearchProvider CreateProvider() { return new SearchProvider("sample", "Sample Search Provider") { filterId = "s:", fetchItems = FetchItems, fetchColumns = FetchColumns, }; } private static IEnumerable<SearchItem> FetchItems(SearchContext context, List<SearchItem> items, SearchProvider provider) { foreach (var path in AssetDatabase.GetAllAssetPaths()) { var data = new Dictionary<string, object>() { { "sample selector", path } }; yield return provider.CreateItem(path, null, null, null, data: data); } } private static IEnumerable<SearchColumn> FetchColumns(SearchContext context, IEnumerable<SearchItem> items) { yield return new SearchColumn("path/sample column", "sample selector", "search column provider") { width = 200 }; } // [SearchColumnProvider("search column provider")] private static void SampleSearchColumnProvider(SearchColumn searchColumn) { searchColumn.getter = args => (args.item.data as Dictionary<string, object>)[args.column.selector]; searchColumn.drawer = args => { GUI.Button(args.rect, args.value.ToString()); return args.value; }; }

要素を右クリックした時のコンテキストメニューを追加したい
SearchActionsProviderAttribute で特定の SearchProvider にコンテキストメニューにアクションを追加できます。
[SearchItemProvider] private static SearchProvider CreateProvider() { return new SearchProvider("sample", "Sample Search Provider") { filterId = "s:", fetchItems = FetchItems, fetchColumns = FetchColumns, }; } // 略 [SearchActionsProvider] private static IEnumerable<SearchAction> LogAction() { // Provider 毎に使用可能な action を設定する. yield return new SearchAction("sample", "Log") { handler = item => Debug.Log((item.data as Dictionary<string, object>)["sample selector"]), }; }

通信などで遅延して要素を表示したい
一覧表示するデータを通信して取得したい時など,
遅延して要素を SearchWindow に表示したいことがあります。
正しいやり方か不明ですが, FetchItems の中で非同期処理をトリガーし,
取得が完了したタイミングで SearchService.RefreshWindows() を実行してあげると,
再度 FetchItems が走り, 表示を更新することができます。
[SearchItemProvider] private static SearchProvider CreateProvider() { return new SearchProvider("sample", "Sample Search Provider") { filterId = "s:", fetchItems = FetchItems, fetchColumns = FetchColumns, }; } private static List<SearchItem> m_CachedItems; private static IEnumerable<SearchItem> FetchItems(SearchContext context, List<SearchItem> items, SearchProvider provider) { if (m_CachedItems != null) { foreach (var item in m_CachedItems) { yield return item; } yield break; } m_CachedItems = new(); GetAllAssetPathsAsync().ContinueWith(result => { foreach (var path in AssetDatabase.GetAllAssetPaths()) { var data = new Dictionary<string, object>() { { "sample selector", path } }; m_CachedItems.Add(provider.CreateItem(path, null, null, null, data: data)); } SearchService.RefreshWindows(); }).Forget(); yield break; } private static async UniTask<List<string>> GetAllAssetPathsAsync() { await UniTask.WaitForSeconds(5); return AssetDatabase.GetAllAssetPaths().ToList(); }
検索処理を実装したい
QueryEngine を使用することで, クエリ演算子を用いた検索機能を実装することが可能です。
また, スペース区切りで or 検索になるなど, 基本的なフィルタ機能を備えているので, 大体これで事足りそうです。
docs.unity3d.com
[SearchItemProvider] private static SearchProvider CreateProvider() { return new SearchProvider("sample", "Sample Search Provider") { filterId = "s:", fetchItems = FetchItems, fetchColumns = FetchColumns, }; } private static IEnumerable<SearchItem> FetchItems(SearchContext context, List<SearchItem> items, SearchProvider provider) { var searchItems = AssetDatabase.GetAllAssetPaths() .Select(path => { var data = new Dictionary<string, object>() { { "sample selector", path } }; return provider.CreateItem(path, null, null, null, data: data); }).ToList(); var queryEngine = new QueryEngine<SearchItem>(); // クエリのトークンと使用するデータを追加. sample:○○ でフィルタできるようになる. queryEngine.AddFilter("sample", x => (string)(x.data as Dictionary<string, object>)["sample selector"]); // 検索に使用するテキストを指定. 今回は CreateItem で path を id に使用しているので, id を指定. queryEngine.SetSearchDataCallback(x => new[] { x.id }); var query = queryEngine.ParseQuery(context.searchQuery); foreach (var item in query.Apply(searchItems)) { yield return item; } }

【Unity】ヘルプボックスを表示する PropertyDrawer
はじめに
インスペクター上のフィールドにヘルプボックスを表示するアトリビュートを作成しました。
使用イメージ
[HelpBox(@"Help Box Help Box Help Box")] [SerializeField] private float m_Value;

ソースコード
HelpBoxAttribute
using System; using UnityEngine; [AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)] public class HelpBoxAttribute : PropertyAttribute { public readonly string Text; public HelpBoxAttribute(string text) { Text = text; } }
HelpBoxDrawer
using System; using UnityEditor; using UnityEngine; [CustomPropertyDrawer(typeof(HelpBoxAttribute))] public class HelpBoxDrawer : PropertyDrawer { private float m_Height; private HelpBoxAttribute m_HelpBoxAttribute => attribute as HelpBoxAttribute; public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { var helpBoxAttribute = attribute as HelpBoxAttribute; EditorGUI.PropertyField(position, property, label, true); position = new Rect(0.0f, position.y + EditorGUI.GetPropertyHeight(property, label, true) + 5.0f, position.width, m_Height); EditorGUI.HelpBox(position, helpBoxAttribute.Text, MessageType.Info); } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { var content = new GUIContent(m_HelpBoxAttribute.Text); m_Height = GUI.skin.box.CalcHeight(content, EditorGUIUtility.currentViewWidth); }