kkamegawa's weblog

Visual Studio,TFS,ALM,VSTS,DevOps関係のことについていろいろと書いていきます。Google Analyticsで解析を行っています

AI駆動開発(大阪)でGitHub Coding Agent のお話してきました

aid.connpass.com

ちょっと機会をいただいてAI駆動開発大阪支部でGitHub Copilot Coding Agentの話をさせていただきました。

speakerdeck.com

デモ失敗した…なかなかうまくいかんですね。

Azure DevOps 2025/5/19の更新

Azure DevOps Sprint 256(切りいいですね)リリースノートの翻訳を行いました。オリジナルはこちらからご覧ください。

learn.microsoft.com

今回GitHub Copilotをレビュワーとして追加してみたのですが、Copilot Agentが翻訳したちょこちょこ間違えたマークアップや、私が消すの忘れていたオリジナル英文(確認のため残しています)消すの忘れていたとかちゃんと指摘してくれました。これは便利。

今回もやや小幅ですが、重要な通知がいくつかありますね。Windows Server 2019イメージ(と同時にVisual Studio 2019)がHosted Runnerからなくなります。今のところ6/30に使えなくなるそうです。GitHub Actionsも同じですね。C# や C++のコンパイラそのものはWindows Server 2022にも含まれているので、ソリューションファイルの移行だけでいいはずです。ただ、古いC++コンパイラは消えると思います。

GitHub Advanced SecurityからNuGetの依存関係で低品質アラートが表示されなくなるのはありがたいですね。

Jupyter Notebookの差分が分かるのはありがたいですね。とはいえ、GitHubのようにGitHubの中で実行できるわけもないのですが…。

Test Plans改良計画はまだまだ続くようで、フィードバックは少ないけど、YAMLパイプラインの許可と自動テストがやっとサポートされました。いやー長かったですね…これやってほしかったけど、やってくれないのではないかとずっと思っていました。どちらも2年とか3年前のチケットです😅。

GitHubの華々しさとは違いますが、ぼちぼち改良されているのはありがたいことです。もっとやってほしいことはいろいろあるけど。ではまた三週間後。

translate to Japanese to Azure DevOps release not…

Build 2025で公開されたFoundry Localを試す

Foundry Localとは

今まで、Azure AI Foundryを使ってAI開発をしている人多かったと思います。しかし、いろいろ問題もありました。

  • LLMおよび、関連リソースのお金がかかる(あたりまえ)
  • 常時ネットワーク必要
  • スロットリング(Token Per Minutes)がある

github.com

※ GitHubで公開されていますが、今のところオープンソースではありません。

いろいろ工夫することはできますが、特にTPMの制限回避するために前段にAPI Managementとか置くとお金的につらい。何とかならないかということできたのが、Foundry Localです(たぶん)。身もふたもなく、はっきり言えばMicrosoft版Ollamaです。圧倒的ないいところといえば、APIが既存のAzure OpenAI SDK互換です。今のAzure OpenAIのSDKのエンドポイント変えるだけで動きます(そのはず)。もちろん使えるものはローカルで動くモデルに限られます。 FoundryサービスがモデルはONIXランタイムを通じてモデルを呼び出します。

現在はチャットのみ対応です。マルチモーダルなんかはまだまだ先の話、かな。インストール後、foundry model listコマンドを使うと、Azureからモデルリストが取得できます。

Alias                          Device     Task               File Size    License      Model ID
-----------------------------------------------------------------------------------------------
phi-4                          CPU        chat-completion    10.16 GB     MIT          Phi-4-generic-cpu
--------------------------------------------------------------------------------------------------------
phi-3.5-mini                   CPU        chat-completion    2.53 GB      MIT          Phi-3.5-mini-instruct-generic-cpu
------------------------------------------------------------------------------------------------------------------------
deepseek-r1-1.5b               NPU        chat-completion    1.52 GB      MIT          deepseek-r1-distill-qwen-1.5b-qnn-npu
----------------------------------------------------------------------------------------------------------------------------
deepseek-r1-14b                NPU        chat-completion    7.12 GB      MIT          deepseek-r1-distill-qwen-14b-qnn-npu
---------------------------------------------------------------------------------------------------------------------------
deepseek-r1-7b                 NPU        chat-completion    3.71 GB      MIT          deepseek-r1-distill-qwen-7b-qnn-npu
--------------------------------------------------------------------------------------------------------------------------
phi-4-mini-reasoning           NPU        chat-completion    2.78 GB      MIT          Phi-4-mini-reasoning-qnn-npu
                               CPU        chat-completion    4.52 GB      MIT          Phi-4-mini-reasoning-generic-cpu
-----------------------------------------------------------------------------------------------------------------------
phi-3-mini-128k                CPU        chat-completion    2.54 GB      MIT          Phi-3-mini-128k-instruct-generic-cpu
---------------------------------------------------------------------------------------------------------------------------
phi-3-mini-4k                  CPU        chat-completion    2.53 GB      MIT          Phi-3-mini-4k-instruct-generic-cpu
-------------------------------------------------------------------------------------------------------------------------
mistral-7b-v0.2                CPU        chat-completion    4.07 GB      apache-2.0   mistralai-Mistral-7B-Instruct-v0-2-generic-cpu
-------------------------------------------------------------------------------------------------------------------------------------
qwen2.5-0.5b                   CPU        chat-completion    0.80 GB      apache-2.0   qwen2.5-0.5b-instruct-generic-cpu
------------------------------------------------------------------------------------------------------------------------
qwen2.5-coder-0.5b             CPU        chat-completion    0.80 GB      apache-2.0   qwen2.5-coder-0.5b-instruct-generic-cpu
------------------------------------------------------------------------------------------------------------------------------
qwen2.5-1.5b                   CPU        chat-completion    1.78 GB      apache-2.0   qwen2.5-1.5b-instruct-generic-cpu
------------------------------------------------------------------------------------------------------------------------
qwen2.5-7b                     CPU        chat-completion    6.16 GB      apache-2.0   qwen2.5-7b-instruct-generic-cpu
----------------------------------------------------------------------------------------------------------------------
qwen2.5-coder-1.5b             CPU        chat-completion    1.78 GB      apache-2.0   qwen2.5-coder-1.5b-instruct-generic-cpu
------------------------------------------------------------------------------------------------------------------------------
qwen2.5-coder-7b               CPU        chat-completion    6.16 GB      apache-2.0   qwen2.5-coder-7b-instruct-generic-cpu
----------------------------------------------------------------------------------------------------------------------------
qwen2.5-14b                    CPU        chat-completion    11.06 GB     apache-2.0   qwen2.5-14b-instruct-generic-cpu
-----------------------------------------------------------------------------------------------------------------------
qwen2.5-coder-14b              CPU        chat-completion    11.06 GB     apache-2.0   qwen2.5-coder-14b-instruct-generic-cpu

使用できるモデルはFoundry Localが自動的に判断するので、例えばCopilot + PCではNPUに対応したモデルが表示されますし、GPU(GeForce 20xx以降)では、GPUに対応したモデルも表示されます。私の環境はGeForce 2060 Tiなのですが、8GiBのVRAMがほとんど使われたとしてもちょっと遅いなぁ、という感じです。NPU持っているSnapDragon DevKitのほうが早い印象です。もちろんGeForce 50xxだとまた変わるのでしょう。

How to use

インストールはHomebrew(macOS)とwingetに対応しているので楽ですね。Windowsは Copilot + PCのNPCモデルも対応しています。モデルのダウンロードはAliasを指定すればFoundryが自動的にCPU/GPU/NPUを切り分けてくれるので、心配しなくてもいいです。

foundry model download phi-4
foundry model load phi-4

これでサービスが起動してモデルが読み込まれます。ちなみにモデルはWindowsの場合、 %userprofile%\.aikというフォルダーの中に格納されます。ほかのストレージに移動したい場合、foundry cache cd <path>で指定したところに移動できます。

一定時間(確か10分)アクセスがないと、サービスはモデルを開放します。その際は再度読み込んでください。もしくは後述のSDKを使えばプログラム内で読み込めます。

アクセス

(Azure) OpenAI SDKでアクセスできます。私が試した範囲ではなぜか.NET SDKでは404になってしまいました…なんか動かないんだよなぁ。.NET AspireでPythonオーケストレーションとかは普通に動きました。

SDK

Python用ですが、SDKもあります。コマンドでモデルロードして、とかではなくてPythonのSDK使ってFoundry Localの実行ができます。

from foundry_local import FoundryLocalManager

manager = FoundryLocalManager()

# List available models in the catalog
catalog = manager.list_catalog_models()

# Download and load a model
manager.download_model("phi-4-mini-reasoning")
manager.load_model("phi-4-mini-reasoning")

# List models in cache
cached_models = manager.list_cached_models()

# List loaded models
loaded = manager.list_loaded_models()

# Unload a model
manager.unload_model("phi-4-mini-reasoning")

モデルダウンロードして、ロードして、キャッシュの場所を取得して、アンロードして、というしょうもない感じのやつですが、例えばAspireにPythonオーケストレーションで参加して、動的にモデルをダウンロードして切り替えるなんてこともできます。もちろんメモリはそれなりにいるので、割とリッチな環境が必要になります。メモリ32GBは必須な感じですね。

from foundry_local import FoundryLocalManager
from openai import OpenAI

alias = "phi-4-mini-reasoning"

manager = FoundryLocalManager()
manager.load_model(alias)

# Windowsは5273/tcp。macOSでは5272/tcpが既定
client = OpenAI(
    api_key="not-needed-for-local",
    base_url="http://localhost:5272/v1"
)

client = openai.OpenAI(
    base_url=manager.endpoint,
    api_key=manager.api_key  # API key is not required for local usage
)

# Set the model to use and generate a streaming response
stream = client.chat.completions.create(
    model=manager.get_model_info(alias).id,
    messages=[{"role": "user", "content": "What is the golden ratio?"}],
    stream=True
)

# Print the streaming response
for chunk in stream:
    if chunk.choices[0].delta.content is not None:
        print(chunk.choices[0].delta.content, end="", flush=True)

組み合わせてみるとこんな感じです。

まとめ

大多数の人にはOllamaから乗り換える理由はないのかもしれませんが、AI Foundry使っている人は便利だと思いますので、使ってみてください。

devblogs.microsoft.com

IaCオーケストレーションを支援するSymphony

はじめに

IaCはBicepなりTerraformなりなんでもいいですが、ソースコードを管理するレポジトリをアプリケーションと一緒にするか、インフラのみのレポジトリにするのか悩ましいと思っています。格納したとしても、デプロイするときのパイプラインを作るのもなかなかめんどくさいと思っていました。

そんなある日、GitHubのレポジトリあさっていたら、MicrosoftからSymphonyというツールがあることを知りました。宣伝を見かけたことはないですが、そこそこコミットもされているようです。

github.com

これで万事解決というものではないですが、デプロイ用のスクリプト作るのめんどくさいなーとか思っている人は使ってもいいと思います。

  • GitHub/Azure DevOpsサポート
  • チームプロジェクトもしくはレポジトリは存在しない状態で作らなくてはならない
  • Terraformの場合Azure Storageアカウント必要(Bicepでもなぜかつくられるけど)
  • GitHubの場合はPull Request検証してくれるが、Azure Pipelinesはやってくれない(自分で登録して工夫しないといけない)
  • サポートはLinux(Ubuntu)のランナーのみ
  • クライアントはWSL内での実行を推奨(bash/zsh)
  • git remoteはちゃんと書き換えよう
  • Service Principal作成権限が必要です

初期構築

実行してみます。azure cliでログインして、symphonyのレポジトリをクローンした後、git remoteを消してください(git remote remove orgin)。そのままではMicrosoftのレポジトリを指してしまいます。

symphony provisionで初期化を行い、Azureリソースを作成します。

Service Principalが作成されるまでしばらく待ちます。

作成されたらsymphony pipeline config azdo | github bicep | terraformでパイプラインを準備します。あとはIACフォルダー配下にiacファイルをコミットしていくだけになります。

最初にサンプルも追加すると、すぐビルドできるようになっています。

CIしてみる

サンプルをビルドしてみましょう。Gitleaks、Linterといった準備を実行して問題なければリリースになります。私の場合Defender for DevOpsでCSPMも有効にしているので、エージェントレススキャンも実行されます。

learn.microsoft.com

フォルダー構成はパラメータファイルとIaCファイルは完全に別のフォルダー配下に置くことになります。

bicepparamファイルの階層。

bicepの階層。サンプルの場合01_, 02_というフォルダーの順番に自動的にデプロイされます。パイプラインの中やbicepでの依存関係は最小限になると思います。

フォルダーパスの階層を作る側がちょっと意識しないといけないとか、フォルダーの数慎重に考えないといけないだろうかなとか考えるところは割とありますが、フォルダーやごちゃっとしたIaCのファイルが散らばっていて、どの順番でリリースすればいいのかわかりづらい点に悩んでいるとかという人はちょっと試してもらってもいいかなと思います。

問題点

問題点としては、Azure CLI 2.71のバグのようで、bicepのvalidationでエラーになります。az config set bicep.use_binary_from_path=Falseをbicep実行前につけないと失敗するはずです。

あと、もう一つ、本家のスクリプトが更新されたとき、知る方法がわからないことでしょうか😅。リリースされるわけでもないですしね。

よかったら試してみてください。私も自分の環境をこれで管理していこうかなと思っています。