C# 50行でOpenAI Codex CLIを任意のLLMに繋ぐコンセプト画像

OpenAI Codex CLIをClaude・Gemini・Llamaの上で動かす — C# 50行で

OpenAIのCodex CLIは優れたエディタエージェントUXを提供します(shellツール、apply_patch、plan tracking がすべて揃っています)。問題は、2026年2月時点でOpenAI Responses APIだけをサポートしていることです。Chat Completionサポートは削除されており(codex-rs/model-provider-info/src/lib.rsのWireApi enumにはResponsesのみが残っています)、Chat Completionのみをサポートするエンドポイント(Ollama、LM Studio、お気に入りのLlama runner)はそのまま閉ざされてしまいます。本記事は.NET 10のfile-basedプログラムとMicrosoft.Extensions.AIのIChatClient抽象化を活用し、50行のC#一ファイルでResponses互換サーバーを立て、OpenRouterを介してCodex CLIを任意のモデル上で動作させた過程をまとめます。 はじめに Codex CLIはResponsesを話すどんなサーバーとも気持ちよく対話します。model_provider configブロックがまさにこのために存在します。つまり、好きなモデルでバックされたResponses互換HTTPエンドポイントを立てさえすれば、Codexは汎用フロントエンドになり、頭脳はユーザーが選べます。 最近私が気に入っているトリックは次のとおりです。Microsoft.Extensions.AIのベンダー中立なIChatClient抽象化の上で、OpenAI Chat CompletionサーバーとResponses APIサーバーを同時に動かす50行のC#スクリプトを起動します。バックエンドはOpenRouterに向けます(APIキー1つで Claude、Gemini、Llama、GPTなど数百のモデルを使えます)。そしてCodexに対して、OpenAIではなくこのローカルスクリプトと対話するよう設定します。 最終的な結果は、OpenAI Codex CLIがAnthropicのClaude 3.5 Sonnetの上で動作する状態です(あるいはその日に使いたい別のモデルの上でも)。 構成要素 私が自分で公開しているCadenza.AgentというMSBuild SDKを使います。単一の.csファイルを実行可能なエージェントサーバーに変換するSDKで、.NET 10のfile-basedプログラム向け単一ファイルスクリプティングSDKファミリーの一部です(dotnet run script.csと同じ発想ですが、より豊富なTier-1 API(Tool、UseOllama、UseOpenAi、Runなど)を提供します)。Agentバリアントは次を公開します。 POST /v1/chat/completions — Aider / Continue / Cursor / Copilot BYOK / sgpt 向け POST /v1/responses — Codex CLI 向け どちらもユーザーが構成した同じIChatClientでバックされます。バックエンドを変えても wireフォーマットはそのままです。 LLM側はOpenRouterを使います。OpenAIのChat Completion wireフォーマットを別のbase URLでそのまま提供するので、Microsoft.Extensions.AI.OpenAIのChatClientをそのまま挿して使えます。環境変数1つ、任意のモデル。 Codexの設定はCODEX_HOME環境変数のトリックを活用します。~/.codex/config.tomlを編集する代わりに、Codexが指すディレクトリをサンプル専用に別途作っておけば、そこから新しいconfig.tomlを読みます。これでユーザーのグローバル設定に一切触れない、自己完結型のサンプルが作れます。 スクリプト バックエンドのすべて、ファイル1つです。 #!/usr/bin/env dotnet run #:sdk Cadenza.Agent@1.0.14 using System.ClientModel; using OpenAI; var apiKey = Env.Get("OPENROUTER_API_KEY") ?? throw new InvalidOperationException("OPENROUTER_API_KEY env var missing"); var model = Env.Get("OPENROUTER_MODEL") ?? "anthropic/claude-3.5-sonnet"; ServedModelName = "cadenza-codex-openrouter"; // サンプル専用のCodex homeディレクトリを作成。 var codexHome = Path.Combine(Env.Cwd, ".cadenza-codex-openrouter"); MakeDir(codexHome); var catalogPath = Path.Combine(codexHome, "cadenza-catalog.json").Replace('\\', '/'); var configToml = $""" model = "cadenza-codex-openrouter" model_provider = "cadenza" model_catalog_json = "{catalogPath}" [model_providers.cadenza] name = "Cadenza.Agent (OpenRouter-backed)" base_url = "http://localhost:8080/v1" wire_api = "responses" env_key = "CADENZA_API_KEY" stream_idle_timeout_ms = 300000 """; WriteText(Path.Combine(codexHome, "config.toml"), configToml); // Catalog JSON: Codex に提供するモデル id を宣言して "Defaulting to // fallback metadata" 警告を防ぎます。フィールドは codex-rs/protocol/src/ // openai_models.rs の ModelInfo スキーマ基準 — すべてのキーが必須です。 var catalogJson = """ { "models": [{ "slug": "cadenza-codex-openrouter", "display_name": "Cadenza (OpenRouter)", "description": "OpenRouter-backed agent served by Cadenza.Agent", "supported_reasoning_levels": [], "shell_type": "default", "visibility": "list", "supported_in_api": true, "priority": 50, "availability_nux": null, "upgrade": null, "base_instructions": "", "supports_reasoning_summaries": false, "support_verbosity": false, "default_verbosity": null, "apply_patch_tool_type": "freeform", "truncation_policy": { "mode": "tokens", "limit": 8192 }, "supports_parallel_tool_calls": true, "context_window": 200000, "max_context_window": 200000, "auto_compact_token_limit": 180000, "effective_context_window_percent": 95, "experimental_supported_tools": [] }] } """; WriteText(Path.Combine(codexHome, "cadenza-catalog.json"), catalogJson); WriteLine($"Codex config generated at: {codexHome}"); WriteLine("In another terminal, run:"); WriteLine($" $env:CODEX_HOME = \"{codexHome}\""); WriteLine($" $env:CADENZA_API_KEY = \"any-non-empty-string\""); WriteLine($" codex"); // OpenRouter を LLM バックエンドとして接続。 var openAiOptions = new OpenAIClientOptions { Endpoint = new Uri("https://openrouter.ai/api/v1") }; var chatClient = new OpenAI.Chat.ChatClient(model, new ApiKeyCredential(apiKey), openAiOptions) .AsIChatClient(); UseChatClient(chatClient); await Run(); これがすべてです。プロジェクトファイルも、.csprojも、Program.csもありません。一番上の#:sdkディレクティブが.NET 10のfile-basedプログラムシステムに「このスクリプトはCadenza.Agent SDKを使う」と知らせ、SDKがHTTPサーバー、Responses wireフォーマット、すべてのパッケージ参照を引き込みつつ、Tool、UseOllama、UseChatClient、Runを名前空間なしですぐ呼べる名前として公開します。 ...

2026年5月27日 · 3 分 ·  rkttu
C#とPythonが出会う機械学習インターロップの概念図

C#からHugging Faceモデルを呼ぶ:DotNetPy 0.6.0でWhisper・sentence-transformers・Stable Diffusionを動かす

週末に小さなC#ライブラリ DotNetPy の0.6.0をリリースしました。CPythonのC APIを直接呼び出して、.NETアプリの中でPythonを動かすインターロップライブラリです。本記事は0.6.0に含まれる3つの機械学習サンプル(sentence-transformersによる意味検索、Whisperによる音声認識、Stable Diffusion Turboによる画像生成)をどうまとめたか、そしてその過程でPEP 703 free-threaded CPythonをどのように検証したかを記録したものです。 出発点:手元にあるのはC#だけ、モデルはHugging Faceにある 数か月ごとに同じパターンが繰り返されます。字幕用にWhisperが必要、検索用にsentence-transformerが必要、ときにはStable Diffusionのようなモデルを使いたい。しかし手元にあるのはC#一つだけ。こんなとき、よく取られる回避策にはそれぞれ決定的な欠点があります。 ONNXに変換する。 画像系やエンコーダ系のモデルにはよく合いますが、新しいアーキテクチャやdiffusionパイプラインでは、変換そのものが別プロジェクトになります。 Pythonマイクロサービスとして立ち上げる。 プロセスが2つ、デプロイのシナリオが2つ、そしてホットパスにネットワークホップが1つ追加されます。 外部APIを呼び出す。 コストがかかり、インターネット接続が必要で、データが箱の外に出ます。 pythonnet や CSnakes を使う。 堅実な選択肢ですが、pythonnetはまだNative AOTをサポートしておらず、CSnakesはSource Generatorベースのワークフローを強制します。さらに、両ライブラリともfree-threaded CPythonビルドに対する公開された検証結果をまだ出していません。 私はもう少し薄い選択肢が欲しかったのです。C#コードの中にPythonスニペットを文字列としてインラインで書き、配列をそのまま渡し、JSON形式で結果を受け取り、全体がAOTコンパイルされて単一バイナリになる 形です。それがDotNetPyの目標であり、以下の3つのサンプルはすべてGPUのない普通のWindows 11ノートPCで最初から最後まで動作します。 サンプル1 — sentence-transformers による意味検索 最初のサンプルは小さなコーパスを埋め込み、クエリをエンコードして、最も類似する上位K件の文を返します。戻り値はDotNetPyValueで、内部的にはJSONドキュメントをラップしたもので、GetString()・GetInt32()・GetDouble()、そしてパスベースのプロパティアクセスを通じて.NETの世界に戻ってきます。 using DotNetPy; using DotNetPy.Uv; using var project = PythonProject.CreateBuilder() .WithProjectName("dotnetpy-ml-embeddings") .WithPythonVersion("==3.12.*") .AddDependencies( "sentence-transformers==2.7.0", "transformers==4.40.2", "torch>=2.2,<2.5") .Build(); await project.InitializeAsync(); var executor = project.GetExecutor(); executor.Execute(@" import numpy as np from sentence_transformers import SentenceTransformer model = SentenceTransformer('all-MiniLM-L6-v2') "); var corpus = new[] { "Python is a popular programming language for data science.", "C# and .NET are great for building enterprise applications.", "Rust offers memory safety without garbage collection.", "Pizza is delicious with various toppings.", // … }; var query = "Tell me about programming languages"; using var hits = executor.ExecuteAndCapture(@" corpus_emb = model.encode(corpus, normalize_embeddings=True) query_emb = model.encode([query], normalize_embeddings=True)[0] sims = corpus_emb @ query_emb top_idx = np.argsort(-sims)[:3] result = [ {'rank': int(rank + 1), 'score': float(sims[i]), 'text': corpus[int(i)]} for rank, i in enumerate(top_idx) ] ", new Dictionary<string, object?> { { "corpus", corpus }, { "query", query } }); foreach (var hit in hits!.RootElement.EnumerateArray()) { Console.WriteLine($" {hit.GetProperty("rank").GetInt32()}. " + $"[{hit.GetProperty("score").GetDouble():F3}] " + $"{hit.GetProperty("text").GetString()}"); } 実際の出力はこうなります。 ...

2026年5月11日 · 4 分 ·  rkttu
クラウドプラットフォームアーキテクチャのコンセプトイメージ

RoleEntryPointからFoundryCBAgentまで — .NET開発者が見たMicrosoft Foundry Hosted Agentのアーキテクチャ

この記事は2026年3月時点で、パブリックプレビュー状態にあるMicrosoft Foundry Hosted Agentの設計を、.NETアーキテクチャの系譜の中で分析します。現時点でプロダクション利用を推奨する記事ではなく、エンタープライズAIアーキテクトが注目すべき設計の方向性に焦点を当てています。 エージェントをプロダクションに載せるということ エンタープライズでAIエージェントをプロダクションにデプロイしようとする際、現在の選択肢は2つです。 宣言的エージェント。 Microsoft Foundryポータルでプロンプトとツールを組み合わせて作るno-code方式です。素早く作成できますが、複雑な分岐ロジックやマルチステップワークフロー、外部システムとの精密な連携をコードで制御できません。一定レベルを超えた瞬間に限界に突き当たります。 セルフホストコンテナ。 エージェントロジックをコードで直接記述し、コンテナに入れてデプロイする方式です。自由度は完全ですが、HTTPサーバー構成、認証、会話状態管理、スケーリング、モニタリング、バージョン管理をすべて自前で実装する必要があります。そして決定的に、コンテナ内でエージェントが何をしているか プラットフォームには一切わかりません。 LLMをどう呼び出したか、ツールをどの順序で使ったか、なぜそのレスポンスを生成したかがブラックボックスです。 Hosted Agentはこの間の空白を埋めます。コードレベルの自由度を維持しつつ、エージェントの動作がプラットフォームに構造的に透明に公開される第3の道です。 Windows Azureを覚えていますか 現在のMicrosoft Azureは、2010年2月に Windows Azure という名前で商用リリースされました。2014年3月にMicrosoft Azureにリブランディングされるまで、このプラットフォームのアイデンティティは現在とはかなり異なっていました。 Windows Azureは本質的に PaaSプラットフォーム でした。VMを直接操作するIaaSは当初存在せず、2012年になってようやくVirtual Machinesが追加されました。出発点はCloud Services(Web Role、Worker Role)、SQL Azure(現在のAzure SQL Database)、Storage Servicesのようなマネージドサービスでした。開発者がインフラを意識せずコードと設定ファイルだけでデプロイすることが核心の約束であり、これは当時AWSがEC2中心のIaaSで市場を切り開いていたのとは根本的に異なるアプローチでした。 2014年のリブランディング後、AzureはAWSを強く意識しながらIaaS機能を急速に拡充しました。VM、VNET、Load Balancer、そして後のAKSまで。市場で「クラウド=仮想マシンを借りて使うこと」という認識が支配的だった時期にIaaSを強化したのは商業的に正しい判断でしたが、その過程でWindows Azure時代のPaaS中心の哲学は多少希薄化しました。 しかしMicrosoftの本来の強みは常にPaaSとSaaSにありました。.NET Framework、Visual Studio、SQL Server、Office 365、SharePoint — この会社は開発者とインフォメーションワーカーに マネージドプラットフォーム 上で働かせることに30年以上の歴史を持っています。Windows Azure時代のCloud Servicesはその哲学のクラウド拡張であり、その後のApp Service、Azure Functions、Container Appsも同じ文脈にあります。 この背景を理解すると、Hosted Agentがなぜこの形で設計されたかが明確になります。MicrosoftはAIエージェントという新しいワークロードに対して、IaaS的アプローチ(セルフホストコンテナを直接管理)ではなくPaaS的アプローチ(プラットフォームがホスティングし、開発者はロジックに集中)を選択したのです。これはAWSがBedrock Agentsで取る方向とも、GoogleがVertex AI Agent Builderで取る方向とも異なります。MicrosoftのPaaS DNAがAI時代に再び発現したと見ることができます。 どこかで見たパターン .NET開発者であれば、そしてAzureの歴史を共に歩んできた開発者であれば、Hosted Agentのアーキテクチャに強いデジャヴを感じるでしょう。 Windows Azure Cloud Servicesの時代を思い出してみましょう。あの時も「開発者はビジネスロジックだけを書き、プラットフォームが提供するホストプロセスがそのロジックを包んでインフラに載せる」というのが核心の約束でした。RoleEntryPointを継承し、OnStart()、Run()、OnStop()を実装すれば、WaWorkerHost.exeがそのコードをロードして実行し、Fabric Controllerがインスタンスのプロビジョニングとヘルスモニタリングを担当していました。 Hosted Agentの構造は、このパターンをAIエージェントドメインに再解釈したものです。 ...

2026年3月17日 · 3 分 ·  rkttu
単一のC#ファイルで実行されるコードのコンセプトイメージ

.NETの新ジャンル:NuGet-Free Single File C#コーディングの時代

C#がスクリプト言語のように軽くなるのではなく、スクリプト言語が羨むほど速くなるということです。 はじめに .NET 10で導入されたdotnet run file.cs——いわゆる** file-based app**——は、.csprojファイルなしで単一の.csファイルだけでC#コードを実行できる機能です。しかし、現在この機能の実行速度は初回実行基準でWindowsで約1.5秒、WSL2で約0.8秒レベルです。Pythonのpython script.pyが50ms前後であることと比較すると、まだ「スクリプティング」と呼ぶには物足りない水準です。 しかし、現在.NETエコシステムで同時に進行中の2つの大きな変化が、この状況を根本的に変える可能性があります: dotnet run file.csのビルド最適化 — MSBuildをバイパスしてRoslynを直接呼び出す戦略 Runtime Async (async2) — async/awaitをランタイムレベルで処理し、ステートマシンのオーバーヘッドを除去 この2つが組み合わさると、** NuGetパッケージなしでBCLだけで書くシングルファイルC#プログラム** が独立したコーディングジャンルとして確立される可能性があります。この記事では、その技術的基盤と将来像を描きます。 File-Based Appの現状:MSBuildというボトルネック 本質:.csproj + .cs = 1つの.cs File-based appはC#スクリプト(.csx)とは根本的に異なります。.csxは別のスクリプトホストがランタイムで解釈しますが、file-based appは** コンパイル時に仮想.csprojに変換** されて正規のビルドパイプラインを通ります。出力は通常のプロジェクトと同一のmanaged DLLです。 // これらのディレクティブが.csprojの内容になる #:package System.CommandLine@2.0.0 // ここからが実際のコード Console.WriteLine("Hello, file-based app!"); 問題:MSBuildの重さ dotnet run hello.csを実行すると内部で起こること: ステップ 所要時間(概算) 説明 CLIロード ~200ms .NETランタイムJIT、CLIコマンドディスパッチ MSBuildエンジンロード ~200ms ビルドエンジン初期化 SDK targets評価 ~300ms 数百の.props/.targetsファイルの順次評価 NuGet restore ~100ms+ パッケージ依存性解決(キャッシュ済みの場合) Roslynコンパイル ~200ms 実際のC# → IL変換 実行 ~50ms 結果のDLL実行 合計約1.5秒 のうち実際の「コンパイル+実行」は約250msに過ぎません。残りはすべてMSBuild関連のオーバーヘッドです。 ...

2026年3月16日 · 4 分 ·  rkttu
虫眼鏡でコードを検査するコンセプトイメージ

AIが見たことのない.NET UIフレームワークでIDEを作るとき、エージェントはどうやってAPIを把握するのか

2026-02-08 — LibraStudio開発記 #1 背景 なぜまた別の.NET IDEなのか .NETエコシステムにおけるIDEの選択肢は、実はそれほど広くありません。Visual Studioは強力ですがWindows専用であり、Community Editionでさえ商用利用に制約があります。VS Code + C# Dev Kitの組み合わせも優れていますが、C# Dev Kitがプロプライエタリライセンスである点は変わりません。結局、重要な部分でベンダーロックインが発生し、その上に構築したツールチェーンとワークフロー全体が特定ベンダーの意思決定に依存してしまいます。 かつてはSharpDevelopがWindowsで、MonoDevelop(Xamarin Studio)がクロスプラットフォームでその役割を担っていました。しかしSharpDevelopは2017年に開発が停止し、MonoDevelopはXamarinに統合された後、事実上スタンドアロンIDEとしての役目を終えました。それ以降、最新の.NET(Core以降)開発環境を適切にサポートする** オープンソースでリベラルライセンスのクロスプラットフォームIDE** は登場していません。 この点がずっと惜しいと感じていました。そこでLibraStudioを始めました。ElectronやVS Codeベースではなく、純粋な.NETネイティブIDEを目指します。一人でVisual Studioレベルのものを作ろうというわけではありません。しかし2026年にはAIベースのコードエディタの力を借りることができます。AIエージェントにフレームワークAPIの探索、ボイラープレートの生成、繰り返しの実装を委任すれば、一人でカバーできる範囲は過去とはまったく異なります。 UIフレームワークにはAprillz.MewUIを使用しています。NativeAOTに対応し、XAMLなしでC#コードだけでUIを構築する軽量フレームワークです。このフレームワークは、私と同じく韓国の代表的な.NET開発者コミュニティであるDotNetDev(.NET Dev、https://forum.dotnetdev.kr/)で活動しているソン・ヨンジェ氏のオープンソースプロジェクトです。 AIが知らない新生フレームワークという難関 MewUIはまったく新しいコンセプトの新生UIフレームワークです。XAMLベースのWPFやAvaloniaUIとは異なり、純粋なC# fluent APIでUIを構築する独自の設計を持っています。当然、公式ドキュメントが豊富ではなく、Stack Overflowに関連する質問が蓄積されてもいません。 これに.NETというプラットフォーム自体の問題も重なります。大規模言語モデルの学習データにおいて、.NET/C#はJavaScriptやPythonに比べて相対的に学習頻度が低いです。メジャーフレームワークであるWPFやWinFormsでさえ不正確なコードを生成することがあるのに、学習データにほとんど含まれていないMewUIのAPIを正確に当てる可能性は極めて低いです。 このような状況でAIコーディングエージェントに「最小限のタブベースのテキストエディタを作ってください」と依頼すると、何が起こるでしょうか。 一般的なアプローチ:推測と反復 通常のAIコーディングエージェントのワークフローは次のとおりです: 学習データから類似のAPIを思い出してコードを書きます ビルドします エラーが出たらエラーメッセージを見て修正します 2〜3を繰り返します この方式は、学習データに豊富に含まれているメジャーフレームワーク(WPF、React、SwiftUIなど)ではうまく機能します。しかし、MewUIのようにまったく新しいコンセプトの新生フレームワーク――しかも相対的に学習頻度が低い.NETベース――では、** 推測の精度が極めて低くなります**。メソッド名、パラメータの順序、オーバーロードの有無、イベントシグネチャ――すべてが外れる可能性があります。 別のアプローチ:アセンブリを直接覗く 今回の作業では、私自身が開発したHandMirror MCPを使用しました。HandMirrorは、NuGetパッケージの** コンパイル済みアセンブリを直接検査する** MCP(Model Context Protocol)サーバーです。 Webドキュメントを検索する代わりに、実際の.dllを分析し、次の情報を返します。注目すべきは、HandMirrorが一般的な.NETリフレクション(System.Reflection)ではなくMono.Cecilを使用している点です。Cecilは.NETアセンブリのメタデータをランタイムロードなしに直接読み取るため、検査対象アセンブリの.NETランタイムバージョンに影響されません。.NET Framework 4.x用ライブラリでも.NET 10対象でも同様に分析できます: すべての名前空間と型の一覧 各型のコンストラクタ、プロパティ、メソッド、イベントのシグネチャ Extension methodがどの名前空間にあるか 継承階層構造 実際に得られた情報 Aprillz.MewUI v0.9.1を検査した結果: 178個のパブリック型、14個の名前空間 MultiLineTextBoxがTextBaseを継承し、Text、Placeholder、AcceptTab、Wrap、IsReadOnlyなどのプロパティを持つこと TabControl.SelectionChangedがAction<int>であること(ドキュメントがなければAction<TabItem>と推測していたでしょう――実際そうでした) FileDialog.OpenFile()がOpenFileDialogOptionsを受け取り、Title、Filter、Ownerなどのプロパティを持つこと Menu.Item()とContextMenu.Item()のオーバーロードが異なること――Menu.Itemにはショートカット文字列パラメータがない ObservableValue<T>がSubscribe()、NotifyChanged()、Set()メソッドを持つこと この情報だけで、エディタのコアコードをほぼ正確に記述できました。 結果:初回ビルドでエラー3個 コード全体を書き上げた後、初回ビルドで発生したエラーはわずか3個でした: ...

2026年2月8日 · 2 分 ·  rkttu
オープンソースプロジェクト間の協力とコードポーティングを象徴するイメージ

AI時代のオープンソース貢献:HwpLibSharpポーティングプロジェクトから学んだこと

AI時代のオープンソース貢献:HwpLibSharpポーティングプロジェクトから学んだこと Microsoft MVPとして活動して17年になります。その間、.NETコミュニティで最も多く受けた質問の一つが「C#でHWPファイルをどう扱えばいいですか?」でした。韓国のハンコム社の公式ライブラリはWindowsとCOMベースであり、クロスプラットフォーム.NET環境では事実上、解決策がありませんでした。 そんな中、@neolord0さんのhwplibを見つけました。Javaで書かれた、純粋にHWPファイルフォーマットをパースするオープンソースライブラリです。「これを.NETに移植すればコミュニティに貢献できる」とすぐに思いましたが、簡単なことではありませんでした。コードベースが膨大な上に、今も継続的にアップデートされていたからです。 2026年、AIコーディングアシスタントと共にこの作業を始めました。 一回限りのポーティングではなく「同期化」 一見、HWPは頻繁に変わるフォーマットではないため、一度ポーティングすれば手を加える必要はないと思いがちです。しかし、あらゆる技術は変化し続けます。元のプロジェクトが今も活発にメンテナンスされているのはもちろん、.NET技術自体も進化しているため、一回限りの作業では不十分です。 従来のフォーク(fork)は、時間が経つほど原本との距離が開きます。最終的に「自分たちのバージョン」と「オリジナル」がそれぞれ別の道を歩むことになります。当然の流れですが、それでも別のアプローチを選びました。単なるフォークではなく、** 原本と共に生き続ける「移植された実装(Ported Implementation)」** というアイデンティティを明確にしたのです。 元プロジェクト: hwplib (Java) ↓ 定期的な同期 ポーティングプロジェクト: HwpLibSharp (C#) ↓ .NET特化の改善 エコシステム拡張 そのため、READMEにもこう記載しました。 「本プロジェクトの意思決定および判断の優先権は、オリジナルプロジェクトの作者である @neolord0 氏の意思を優先します。」 これは礼儀のために書いた文章ではありません。2つのプロジェクトが長期的に共存するためには、誰が方向性を決めるのかを最初から明確にする必要があると判断したからです。 AIと共に行ったポーティング作業 正直に言えば、AIコーディングアシスタントなしでこのプロジェクトを進めていたら、初期ポーティングだけで6ヶ月以上かかっていたでしょう。アップストリーム同期は到底手が出せず、結局放置された「もう一つのレガシー」になっていたはずです。 しかし、AIと一緒に作業することで、まったく異なるアプローチが可能になりました。 AIがうまくできたこと 構文変換はほぼ完璧でした。Javaのgetter/setterをC#プロパティに変換し、命名規則をC#コンベンションに合わせ、nullチェックをNullable参照型に変換する作業は、ほとんど自動化できました。 ライブラリのマッピングでもAIの助けは大きかったです。Apache POIをOpenMcdfに置き換える際、「Javaでこのライブラリが果たす役割を.NETでは何で代替できるか」を素早く見つけてくれました。元プロジェクトがアップデートされるたびに変更点を追跡してC#バージョンに反映する反復作業でも、ヒューマンエラーを大幅に削減してくれました。 Upstream変更の自動追跡 この過程で特に効果的だったのは、AIエージェントを活用した** upstream変更の自動追跡** 方式でした。元のhwplibプロジェクトに新しいコミットが入るたびに、AIエージェントが変更されたJavaソースファイルと対応するC#ソースファイルを比較分析し、実装上の差異を検出します。 この同期を体系的に管理するために、ポーティングされたすべてのC#ソースファイルの先頭に、元のJavaファイルとの対応関係を明示するヒントヘッダーを残しました。 // ===================================================================== // Java Original: kr/dogfoot/hwplib/util/compressors/Compressor.java // Repository: https://github.com/neolord0/hwplib // ===================================================================== このヘッダーがあれば、AIエージェントは「このC#ファイルの元はJava側のどのファイルか」を即座に把握できます。upstreamでCompressor.javaが変更されると、AIが対応するC#ファイルを見つけてdiffを分析し、不足している変更点や実装上の差異をレポートしてくれます。人間が数百のファイルを一つずつ照合する必要なく、AIが「この部分が原本と異なっているので確認が必要です」と教えてくれる方式です。 実際にこの方式を導入した後、upstream同期にかかる時間が従来比80%以上短縮されました。以前は変更ログを読んで関連ファイルを一つずつ探しながら手動で反映していたのが、今ではAIが変更リストと影響範囲を自動的に整理してくれるようになりました。 AIができなかったこと 一方、HWPファイルのSection-Paragraph-Control構造、各コントロールの意味、韓国語ワープロ文書ならではの特性といったドメイン知識は、完全に自分の役割でした。AIがもっともらしく提案したコードが実際に正しいかどうかの検証は、やはり人間がやるべき仕事です。 戦略的な意思決定も同様でした。Native AOTをサポートするか、Blazor WebAssemblyでどう動作させるか、どの.NETバージョンまでサポートするか。こうした判断には.NETエコシステム全般への理解と、実際のユーザー環境に対する感覚が必要です。ライセンス文言、原作者との関係設定、韓国の開発者コミュニティの文脈を反映する作業も、AIが下書きを作り、自分が仕上げるという方法で進めました。 .NETエコシステムに合わせた再設計 単にコードを移すだけで終わりではありませんでした。.NET開発者が自然に使えるよう、原本プロジェクトの哲学と意図を損なわない範囲でAPIを整える作業も必要でした。 // Blazor WebAssemblyサポート(ファイルシステムなしでストリームから) var hwpFile = HWPReader.FromStream(memoryStream); // Native AOT互換、URLからの非同期ロード var hwpFile = await HWPReader.FromUrlAsync(url); これらの機能はオリジナルのJavaバージョンには存在しません。しかし.NET開発者なら当然期待するものです。おかげで、Azure FunctionsでHWPを処理したり、Blazorでブラウザ上にHWPをレンダリングすることも可能になりました。 ...

2026年2月7日 · 1 分 ·  rkttu
AIコーディングツールと開発者のバランスの取れた関係

AIコーディングツール、「遅れをとる」という不安に振り回されない方法

最近のAIコーディングツール業界のニュースを見ていると、新しいツールが登場するたびに「これが未来だ」「使わなければ遅れをとる」というメッセージが過度に強調されているのがわかります。バックグラウンドエージェント、並列AIセッション、自律コーディング—毎週新しい概念が登場し、それを導入しなければ時代に遅れているかのように感じさせられます。 しかし、このようなメッセージをそのまま受け入れることが本当に健全なアプローチでしょうか?私はそうではないと思います。 Hypeはどのように作られるのか AIコーディングツール業界のhypeには構造的な理由があります。AI技術で収益を上げなければならない企業の経営者は、投資家や株主を満足させなければならない立場にあります。そのため、彼らのメッセージには二重の聴衆問題が存在します。開発者には「生産性向上」を約束しながら、同時に投資家には「市場支配力」と「不可欠なツール」という物語を提供しなければなりません。 この過程で「半分だけ正しい言葉」が溢れ出します。「AIがコーディングを革新する」は間違っていませんが、「今すぐこのツールを使わなければ淘汰される」という飛躍が一緒にパッケージングされます。完全な嘘は簡単に反論されますが、誇張された真実は検証が困難だからです。 このようなメッセージはAI企業から始まり、先を行こうとする一部の企業の経営者や技術リーダー、そしてインフルエンサーを経て、実務開発者に届きます。問題は、中間の伝達者たちが自分の利害関係のためにメッセージをフィルタリングするよりも、より強く増幅させる傾向があるということです。「最新技術を導入している」というイメージ自体が彼らの市場でのポジショニングであり営業ツールになるからです。 バックグラウンドエージェントの約束と現実 最近強調されているトレンドの一つが「バックグラウンドエージェント」です。VS CodeのAgent HQ、Google AntigravityのManager Viewなどの機能が代表的です。これらのツールの核心的な約束は、タスクを委任すればAIが別の環境で自律的に実行し、開発者は他の作業ができるというものです。複数のエージェントを並列で実行すれば生産性が何倍にも向上するという主張も付いてきます。 しかし、この約束には根本的な問題があります。バックグラウンドエージェントは本質的にHuman-In-The-Loop(HITL)を意図的に排除する方向で設計されています。「非同期作業」「自律実行」「委任後の確認」という概念自体が、中間介入ポイントをなくすことを前提としています。 実際に事故はすでに発生しています。GoogleのAntigravityエージェントがハードドライブのパーティション全体を削除した事件が報告されています。従来のチャットウィンドウで作業する場合、各ステップで結果を確認して途中で介入できますが、バックグラウンドエージェントは間違った方向に進んでも完了するまで認識しにくいのです。 免責条項とマーケティングの矛盾 興味深い点は、すべてのAIサービス事業者が「AIは間違える可能性があるので必ず確認してください」という免責条項を明示していることです。しかし同時に、マーケティングでは「自律的に働くエージェント」を強調しています。この二つのメッセージを同時に真剣に受け止めると、並列エージェントの効用はかなり限定的にならざるを得ません。 「複数のエージェントを並列で実行してこそ本当の生産性」という主張は、「AI出力は必ず確認せよ」という警告と正面から衝突します。一つのセッションの出力でも検証が必要なのに、それを五つ同時に実行すれば検証の負担が五倍になるのであって、生産性が五倍になるのではありません。ボトルネックが生成から検証に移動するだけで、全体のスループットが線形に増加するわけではありません。 逆説的に、五人を並列で投入しても、管理と確認なしには品質は保証されません。調整コストが増加し、一貫性が崩れ、成果物の品質のばらつきが大きくなります。これはソフトウェアエンジニアリングで数十年間繰り返し検証されてきた事実です。AIエージェントがこの原則から自由である理由はありません。 技術リーダーが持つべき姿勢 CTOや技術意思決定者の立場から見ると、この状況で最も重要な徳目は「冷静な距離感と判断」です。問題はこれを実践するのが構造的に難しいということです。冷静な判断を下すと「革新に消極的だ」「トレンドを知らない」という評価を受けるリスクがあり、その判断が正しかったことは数年後にようやく検証されます。 マクロ的に見ると、技術に対する深い理解(アンカー)がない状態で意思決定をしなければならない経営者は、外部のシグナルに頼らざるを得ません。しかし、そのシグナルの大部分が利害関係の絡んだソースから来るため、簡単に不安になり、簡単に焦ってしまう脆弱な状態に置かれることになります。 一つの実用的な代替案はLab戦略です。実験の範囲とコストを制御しながらも、組織が新しい技術を学習し評価できる空間を確保することです。失敗コストが低い領域(内部ツール、プロトタイプ)では積極的なツール選択を許可し、コアビジネス領域には検証済みのツールを維持します。こうすれば「私たちもAIツールを積極的に活用している」という物語を維持しながら、実際のビジネスリスクは管理できます。 ツール(Harness)の重要性:馬と馬具の比喩 最近、AnthropicのリサーチャーBoris Chernyが興味深い比喩を提示しました。「Claudeは馬(horse)で、Claude Codeはharness(馬具)だ。馬に乗るには鞍が必要で、その鞍が馬に乗る時に大きな違いを生む」というものです。彼の説明によると、AIコーディングが長い間うまく機能しなかった理由は、モデルが十分に良くなかったこともありますが、モデルの上のscaffolding(ツール)が十分に良くなかったことも大きな理由でした。 この比喩で重要な点は、人間のプログラマーがまだループの中にいるということです。馬具を使って馬を望む方向に導くように、開発者はツールを通じてAIを制御します。そして、そのツールの品質が成果物の品質を大きく左右します。 伝統的なIDEインフラの再発見:Visual Studio 2026の事例 この文脈で、Visual Studio 2026のデバッガエージェントは注目に値します。この機能は、単体テストが失敗した時に次のプロセスを自律的に実行します。ワークスペースからコンテキスト(テストコード、関連ソース、最近の修正)を収集し、失敗原因についての仮説を形成し、分析に基づいてコードを修正し、デバッガでテストを実行して検証し、問題が続けばデバッガのインサイトを活用して仮説を精緻化し、テストが通過するまで繰り返します。 ここで重要なのは、これらすべてが数十年間蓄積されたIDEインフラの上で動作するという点です。ブレークポイント操作、変数状態のリアルタイム追跡、コールスタック分析、シンボル解決、プロファイリング—これらはターミナルベースの軽量エージェントでは簡単に実装できない領域です。AIが「仮説を立てて検証する」ことは、このようなインフラが支えてこそ意味のある自動化になります。 .NET開発者の観点から見ると、トレンドに従ってターミナルベースのツールに移行することが必ずしも生産性向上を意味するわけではありません。むしろ、完成度の高い伝統的なIDEがAIと組み合わさった時に、より強力なシナジーが発生する可能性があります。新しいものが常により良いわけではありません。 同期セッションの高度化という現実的な代替案 バックグラウンドエージェントやHITLなしで実行されるモードに対する確信や信頼がなければ、無理して使用する理由はありません。むしろ、現在のようにHITLが維持される同期セッションベースのワークフローをより深く見つめながら高度化することが、安全性と効率性を同時に確保する戦略です。 プロンプトパターンの体系化、コンテキスト提供方式の最適化、IDE統合機能の深い活用、作業単位の適切な分割などの領域で熟練度が高まれば、新しいツールが登場しても、その価値を冷静に評価できる基準が生まれます。 結論:ツールの新規性よりツール使用の深さ 「確認してください」という警告が形式的な免責ではなく実質的な運用指針であるならば、現在のAIツールの適切な使用方法は、HITLが維持される単一または少数のセッションの深い活用です。並列自律エージェントは、AIの信頼性が今よりはるかに高くなった後にこそ意味のある選択肢になるでしょう。 ツールの新規性よりツール使用の深さが実務の生産性に大きな影響を与えます。そしてこれがhypeに振り回されない実質的な競争力になります。新しいツールが登場するたびに「これが解決する実際の問題は何か」を最初に問う習慣を身につけてください。 ツールは手段であって、アイデンティティではありません。

2026年1月27日 · 1 分 ·  rkttu
JavaからNETへのコード移植を表現した抽象的なイメージ

Java hwplibを.NETに移植する:AIと共に歩んだオープンソース移植の旅

始まりは単純な好奇心から 「HWPファイルを.NETで直接扱えたらいいのに…」 こんな考えをした.NET開発者は私だけではないでしょう。HWPファイルは韓国で公共機関を中心に今でも広く使われている文書形式ですが、.NETエコシステムではこれを適切に扱えるオープンソースライブラリがありませんでした。 これまで.NETでHWPファイルを扱うには、Windows OS限定でアレアハングルをインストールすると付属するHWP ActiveXコントロールのCOMタイプライブラリを呼び出して制御する程度しかありませんでしたが、残念ながらこれさえもサポートが終了し、今は道が閉ざされた状態です。 そんな中、Javaで書かれたhwplibを発見しました。neolord0さんが継続的にメンテナンスしてきたこのライブラリは、HWPファイルの読み書きを完成度高くサポートしていました。 以前なら、このようなライブラリを移植することは、強い使命感と目的意識がなければなかなか手を出せる作業ではありませんでした。しかし、高性能なAIモデルが多く登場し、今なら新しい挑戦ができるのではないかという好奇心がありました。 こうして始まった3週間の旅を共有します。 数字で見るプロジェクト 本格的な話の前に、Javaバージョンのプロジェクトの規模を数字でまとめてみましょう。 項目 内容 元プロジェクト neolord0/hwplib (Java) 移植プロジェクト rkttu/libhwpsharp (C#) ターゲットフレームワーク .NET Standard 2.0, .NET Framework 4.7.2, .NET 8 総コミット数 54個 開発期間 2025-12-16 ~ 2026-01-08(約3週間) 初期移植コード 641ファイル、50,010行 641ファイル、5万行。正直、最初にこの数字を見たときは「これは可能なのか?」という疑問が湧きました。 初日:AIと共に行った大規模コード変換 5万行を1日で? 2025年12月16日、プロジェクトを開始しました。通常の状況であれば、5万行のJavaコードをC#に変換するには数ヶ月かかったでしょう。しかし、AIコーディングアシスタントがゲームチェンジャーでした。 AIにJavaファイルを渡し、C#に変換してもらいました。もちろん、機械的な変換だけでは不十分でした。JavaとC#は似ているように見えますが、微妙な違いが多いからです。 // Java public byte getValue() { return value; } public void setValue(byte value) { this.value = value; } // C# public byte Value { get; set; } このようなパターン変換はAIがうまく処理してくれました。しかし、本当の問題は別にありました。 最大の難関:Apache POIからOpenMcdfへ OLE2複合ドキュメントの沼 HWPファイルはMicrosoftのOLE2複合ドキュメント形式(Compound File Binary Format)に基づいています。簡単に言えば、1つのファイルの中に複数の「ストリーム」がフォルダ構造のように格納されている形式です。 JavaではApache POIライブラリがこの形式を扱いますが、.NETには直接対応するものがありません。代わりにOpenMcdfというライブラリを使う必要がありましたが、残念ながらApache POIとはAPIが完全に異なります。 ...

2026年1月8日 · 2 分 ·  rkttu
TableClothカタログUIアップデート

テーブルからカードへ:TableClothカタログUIの現代化

はじめに 韓国でインターネットバンキングを利用したことがあれば、各種セキュリティプログラムのインストール要求に頷くことでしょう。ActiveXは姿を消しましたが、その代わりとなった数多くのセキュリティプラグイン—AhnLab Safe Transaction、TouchEn nxKey、Veraportなど—は依然として私たちのPCへのインストールを要求しています。 TableCloth(食卓布)プロジェクトは、これらのセキュリティプログラムをWindows Sandboxという隔離された環境で実行できるようにするオープンソースツールです。TableClothカタログは、どの金融サイトでどのセキュリティプログラムが必要かを整理したデータベースの役割を果たしています。 今回のアップデート概要 TableClothカタログプロジェクトに5件のコミットが適用されました。今回のアップデートは** フロントエンド**、** バックエンド**、** DevOps** の3つの領域にわたる総合的な改善です。 領域 変更内容 主要技術 フロントエンド カタログWebページの全面再設計 カードUI、カテゴリフィルター、レスポンシブレイアウト バックエンド ビルドツールアーキテクチャのリファクタリング Generic Host、依存性注入、構造化ロギング DevOps 品質管理自動化ツールの追加 画像検証、未使用リソース整理、ファビコン収集改善 各領域で何が変わったのか、なぜそのような決定を下したのか、詳しく見ていきます。 フロントエンド:テーブルからカードへ なぜUIを変更する必要があったのか? 既存のカタログページは典型的なHTMLテーブル形式でした。カテゴリ、サービス名、必要なパッケージリストが行と列で整列された構造です。機能的には問題ありませんでしたが、いくつかの限界がありました。 まず、100を超えるサービスをスクロールしながら探す必要がありました。モバイル環境では横スクロールが発生してユーザビリティが低下し、視覚的な階層構造がないため情報の把握が困難でした。何より、カテゴリ別のフィルタリングができず、目的のサービスを素早く見つけることが難しい状況でした。 これらの問題を解決するため、カードベースのUIへの全面改編を決定しました。 XSLトランスフォームで実装したレスポンシブカードUI TableClothカタログの興味深い点は、データがXML形式で保存され、Webページが** XSLT(XSL Transformations)** を通じてレンダリングされることです。サーバーサイドのロジックなしに、ブラウザが直接XMLをHTMLに変換します。 デザイントークンシステムの導入 モダンなCSS設計の核心は** デザイントークン** です。色、間隔、影などをCSS変数として定義することで、一貫性のあるデザインを維持しながらメンテナンスが容易になります。 :root { /* カラーパレット */ --primary-color: #2563eb; --primary-hover: #1d4ed8; --bg-color: #f8fafc; --card-bg: #ffffff; --text-primary: #1e293b; --text-secondary: #64748b; --border-color: #e2e8f0; /* シャドウ */ --shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06); --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05); /* レイアウト */ --radius: 12px; } このカラースキームはTailwind CSSのデフォルトパレットからインスピレーションを得ています。スレート系のテキストカラーとブルー系のアクセントカラーを組み合わせることで、クリーンかつプロフェッショナルな印象を与えることができます。 ...

2025年12月8日 · 2 分 ·  rkttu
TableClothプロジェクト カタログビルダーアップデート

TableClothカタログビルダー、.NET 10とFBAで現代化

TableClothプロジェクトとは TableClothは、韓国のインターネットバンキング環境で必要なセキュリティプラグインをWindows Sandbox環境で安全に使用できるようにするオープンソースプロジェクトです。様々な金融機関のウェブサイトで要求されるセキュリティプログラムを隔離された環境で実行し、ホストシステムの安全を確保します。 カタログリポジトリとは TableClothCatalogリポジトリは、TableClothプロジェクトで参照する金融機関サイト別のセキュリティプログラムリストを保管する保存場所です。銀行、証券会社、保険会社などのウェブサイトで要求されるセキュリティプラグイン情報が体系的に整理されており、カタログビルダーツールはこの情報を基にTableClothアプリで使用可能な形式に加工します。 最近のカタログビルダーアップデート 最近、このリポジトリに重要なアップデートが適用されました。今回のコミットでは、カタログビルダーツールが.NET 10にアップグレードされ、プロジェクト構造が大幅に簡素化されました。 主な変更点 .NET 10アップグレード # 変更前 dotnet-version: 8.0.x # 変更後 dotnet-version: 10.0.x ビルドパイプラインが.NET 8から.NET 10にアップグレードされました。これにより、最新ランタイムのパフォーマンス向上と新しい言語機能を活用できるようになりました。 File-Based App(FBA)方式への移行 最も目立つ変化はプロジェクト構造の簡素化です。従来は別途の.csprojプロジェクトファイルとソリューションファイル(.sln)を使用していましたが、今回のアップデートでこれらのファイルが削除され、単一のC#スクリプトファイル(catalogutil.cs)に統合されました。 #!/usr/bin/env dotnet #:package IronSoftware.System.Drawing@2025.9.3 #:property PublishAot=false using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Png; using System.Collections.Concurrent; // ... これが.NETの** File-Based App(FBA)** 方式です。FBAは以下のような利点を提供します: プロジェクトファイル不要:.csprojなしで単一の.csファイルでアプリケーション実行可能 インラインパッケージ参照:#:packageディレクティブでNuGetパッケージを直接参照 インラインビルドプロパティ設定:#:propertyディレクティブでビルドプロパティを設定 シバン(Shebang)サポート:#!/usr/bin/env dotnetでUnixスタイルの実行をサポート Graceful Shutdownサポート 新しいバージョンでは、プロセス終了時により安定した処理が可能になりました: // Graceful shutdown タイムアウト var gracefulShutdownTimeout = TimeSpan.FromSeconds(5); Console.CancelKeyPress += (sender, e) => { if (cts.IsCancellationRequested) { // 2回目のCtrl+C:強制終了 Console.Out.WriteLine("Info: Force exit requested. Terminating immediately..."); e.Cancel = false; return; } Console.Out.WriteLine($"Info: Cancellation requested. Shutting down gracefully (timeout: {gracefulShutdownTimeout.TotalSeconds}s)..."); Console.Out.WriteLine("Info: Press Ctrl+C again to force exit."); e.Cancel = true; // 即時終了を防止 cts.CancelAfter(gracefulShutdownTimeout); cts.Cancel(); }; 主な特徴: ...

2025年12月6日 · 1 分 ·  rkttu