Grafana Alloy で独自メトリクスを扱う2つのパターンを Go で書く

こんにちは。ジェダイパンくず🍞 です。 以前の記事 自前開発した Prometheus Exporter で自宅ルータのメトリクス監視運用している話 や Prometheus Exporter 開発で Discord サーバ運営の健全化を図る では、Go で Prometheus Exporter を書いて Prometheus Server に scrape させていました。 最近は Grafana のエージェントとして Grafana Alloy を使う構成が増えています。Alloy は Prometheus の scrape や remote_write、OpenTelemetry、ログ収集などを 1 つのバイナリで扱えるテレメトリコレクタで、prometheus.exporter.* という形で多くの exporter を内蔵しています。そこで「自作 exporter を Alloy の世界に持ち込むにはどうするのか」「Alloy の component そのものを Go で書けるのか」を調べて、実際に手を動かしてみました。 コードは下記に置いてあります。 https://github.com/jedipunkz/alloy-playground 結論 自作のメトリクスを Alloy で扱う方法は大きく 2 つあります。 パターン 内容 A: 独立 exporter + prometheus.scrape 従来通り Go で exporter を書き、公式の Alloy から HTTP で scrape する B: Alloy を fork して component を追加 Alloy のソースツリーに internal/component/prometheus/exporter/<name> を足してビルドする Alloy の component は Go で書けます。ただし Alloy にはプラグイン機構が無く、exporter 系 component が使う API は internal/ 配下にあるため、パターン B は Alloy を fork してビルドするしかありません。自分のサービス向けの独自メトリクスであれば、運用コストの低いパターン A を選ぶのが良さそうだと感じました。 ...

2026-10-22 · 6 分 · jedipunkz

自作 Go ツールに導入したガードレール策

こんにちは。ジェダイパンくず🚀 です。 以前の記事 Go によるコマンド履歴・ファイル・Git ブランチ/Worktree/コミットを検索する Fish Plugin 開発 で紹介した fuzz.fish は、Fish shell のコマンド履歴・ファイル・Git ブランチなどをファジー検索する Fish plugin です。plugin ですが、検索と TUI の本体は Go で実装していて、Fish 側のスクリプト(conf.d/fuzz.fish)はキーバインドの登録と Go バイナリの呼び出しだけを担っています。このツールを自分で毎日使いながら一人で開発・運用しています。最近はコードの大半を Claude Code や opencode といった AI agent に書いてもらっていて、自分はレビューとマージが中心です。 この開発スタイルで一番気になっていたのは、LLM に何を意識させ続けられるかでした。具体的には次の 4 つです。 セキュリティ: シェルに文字列を渡して実行する箇所や、サプライチェーンに穴を開けないこと バグ混入: 既存の挙動を壊さないこと、テストを減らさないこと パフォーマンス: キー入力のたびに走るフィルタ処理を遅くしないこと 設計思想: パッケージの依存方向や Bubble Tea の Model / Update / View の分担を崩さないこと これらは CLAUDE.md に書いてあります。ただ、プロンプトに書いたルールは守られる保証がありませんし、一度守られても次の変更では忘れられます。チームであれば他のメンバーのレビューで拾えることも、一人だと自分が見落とした時点で main に入ってしまいます。 そこで「ルールは CLAUDE.md で宣言し、違反は linter と CI で機械的に落とす」という方針でガードレールを 17 件 issue 化して実装しました。この記事はその記録です。 方針: 宣言 → 可視化 → gate 化 ガードレールは次の 3 段階で考えました。 ...

2026-10-12 · 8 分 · jedipunkz

Google ADK Go 2.x の Dynamic Workflow とその為のガードレール

こんにちは。ジェダイパンくず🚀 です。 前回の記事 ADK Go 2.0 のグラフワークフローを読んで SRE Agent への応用を考える の最後に、こんな宿題を残していました。 インシデント対応は「調べて、分からなかったらもう少し調べる」という反復が本質なので、完全に静的なグラフでは表現しきれない部分があります。NewDynamicNode で調査ループを書き、確定した対処だけを静的グラフに戻す、というハイブリッドが現実解な気がしていますが、そうすると動的ノードの中身が結局ブラックボックスになるジレンマがあります。 その後、Claude Agent SDK の agent loop や、Claude Code の dynamic workflows を眺めていて、「自律的に、動的に回るループ」を Google の Agent Development Kit(ADK)でやるとどうなるのかが気になりました。結論を先に書くと、ループそのものは ADK でも回せます。ただし止める仕組みは自分で書く必要があります。この記事はその「止める仕組み」、つまりガードレールの話です。 記事中のコードは jedipunkz/adk-playground の investigation-loop にあり、ADK Go v2.5.0(2026-09-30 リリース)で go vet とテストを通しています。 「ループ」は 2 種類ある 最初に整理しておきたいのは、「自律的なループ」と言ったときに 2 つの別のものが混ざりがちだという点です。 Claude 側 ADK Go 側 agent loop(1 エージェントの中で、モデル → ツール → モデルを繰り返す) Claude Agent SDK の query() llmagent オーケストレーションのループ(エージェントを何度も呼び、結果を見て次を決める) Claude Code の dynamic workflows(loop until done パターン) workflow.NewDynamicNode Claude Code の dynamic workflows は、Claude 自身が JavaScript のオーケストレーション用スクリプトを書き、ランタイムがそれをバックグラウンドで実行する機能です。スクリプトの中では agent() でサブエージェントを起動し、parallel() / pipeline() で fan-out します。ループ・分岐・途中結果はスクリプトの変数が持つので、Claude のコンテキストには最終結果しか入りません。 ...

2026-10-12 · 7 分 · jedipunkz

[Go 再学習] Go のテストを理解する 後編: 性能・堅牢性を確かめる

前編 では、テーブル駆動テストからカバレッジまで、コードが正しく動くことを確かめるテストの書き方を整理しました。後編では、正しさ以外のことを確かめるテストを扱います。 性能: ベンチマーク、サブベンチマーク 堅牢性: Fuzzing 時間: testing/synctest 前編と同じく Go 1.27 を前提にしていて、実行結果は Go 1.27.1 で実行したものです。ベンチマークの数値は実行環境によって変わります。 コードは以下のレポジトリにあります。 https://github.com/jedipunkz/go-playground/tree/main/testing パターン9: ベンチマーク func BenchmarkXxx(b *testing.B) という関数を書き、go test -bench で処理時間とメモリ割り当てを計測するパターンです。 使い所 「+ で文字列を連結するより strings.Builder の方が速い」とよく言われますが、自分のユースケースでどれくらい差があるのかは、測ってみないと分かりません。ベンチマークを書いておけば、実装を選ぶときや性能改善をしたときに、効果を数値で確認できます。 テスト対象として、文字列を連結する 2 つの実装を用意しました。 package concat import "strings" // Plus は + 演算子で文字列を連結する func Plus(words []string) string { s := "" for _, w := range words { s += w } return s } // Builder は strings.Builder で文字列を連結する func Builder(words []string) string { var sb strings.Builder for _, w := range words { sb.WriteString(w) } return sb.String() } ベンチマークは for b.Loop() { ... } の中に計測したい処理を書きます。 ...

2026-10-11 · 9 分 · jedipunkz

[Go 再学習] Go のテストを理解する 前編: 正しさを確かめる

Go の再学習を続けています。今回はテストです。テーブル駆動テストやサブテスト、ベンチマークは普段から書いているのですが、それ以外にどんな書き方や機能があるのかを網羅的には把握できていませんでした。そこで標準の testing パッケージ周りを一通り調べ直し、前編・後編の 2 本にまとめることにしました。 前編 (この記事): 正しさを確かめる。テーブル駆動テスト、サブテスト、並列実行、テストヘルパー、テストダブル、httptest、Example テスト、カバレッジ 後編: 性能・堅牢性を確かめる。ベンチマーク、サブベンチマーク、Fuzzing、testing/synctest Go 1.27 を前提にしていて、古いバージョンとの違いには触れません。実行結果は Go 1.27.1 で実行したものです。 コードは以下のレポジトリにあります。 https://github.com/jedipunkz/go-playground/tree/main/testing Go のテストの基本 Go のテストは標準の testing パッケージと go test コマンドだけで書けます。ルールは次の 3 つです。 テストコードは xxx_test.go という名前のファイルに書く テスト関数は func TestXxx(t *testing.T) という形にする 失敗を報告するには t.Error / t.Errorf か t.Fatal / t.Fatalf を呼ぶ t.Error と t.Fatal の違いは重要です。t.Error は失敗を記録してテスト関数の実行を続けますが、t.Fatal は失敗を記録した時点でそのテスト関数を終了します。この違いがパターン 2 の話につながります。 この記事では、次の割り算の関数をテスト対象にして説明を始めます。 package calc import "errors" var ErrDivideByZero = errors.New("divide by zero") // Div は a を b で割った結果を返す func Div(a, b int) (int, error) { if b == 0 { return 0, ErrDivideByZero } return a / b, nil } よく使う go test のオプションは以下です。 ...

2026-10-11 · 12 分 · jedipunkz

ADK Go 2.0 のグラフワークフローを読んで SRE Agent への応用を考える

こんにちは。ジェダイパンくず☁️ です。 2026年6月30日に ADK (Agent Development Kit) for Go の 2.0 がリリースされました。目玉は「グラフベースのワークフローエンジン」で、エージェントアプリケーションをノードとエッジのグラフとして記述するという方向に大きく舵を切っています。 自分がこのリリースで一番興味を持ったのは、公式サンプルの examples/workflow/routing/llm/ に書かれていたこのコメントでした。 // This is the canonical "LLM as the brain, engine does the routing" pattern. LLM は「脳」であって「ルータ」ではない、分岐そのものはエンジン(=決定的なコード)が担う、という設計思想です。これは SRE のインシデント対応エージェントを考える上でかなり本質的な話に見えたので、リポジトリを実際に clone して公式サンプルを読み、さらに自分でインシデントトリアージのワークフローを書いてビルド・実行するところまでやってみました。 この記事は以下の三本柱で構成します。 ADK の基本(そもそも ADK でエージェントをどう書くのか) 2.0 の新機能(グラフワークフローエンジンを中心に) SRE Agent における応用(実際に動くコードを書いてみた) なお、記事中のコードはすべて google/adk-go の実際のサンプルか、自分で書いて go vet とビルド・実行を通したものです。 ADK の基本 インストール 2.0 からモジュールパスが google.golang.org/adk/v2 に変わりました。Go 1.25 以上が必要です。 go get google.golang.org/adk/v2 最小のエージェント ADK の世界観は「エージェント = モデル + 指示 + ツール」です。公式の examples/quickstart/main.go がその最小形です。 ...

2026-07-31 · 10 分 · jedipunkz

RAG を動かして基本を理解する

こんにちは。ジェダイパンくず☁️ です。 RAG (Retrieval Augmented Generation) という言葉はよく聞く様になったものの最初はどこからどこまでが RAG システムの責務なのかが分かりにくいと感じていました。「検索含め AI が勝手に全部やってくれるのでは?」もしくは「あくまでの検索結果を返すだけなのか?」等。そこで RAG を段階的に理解するための学習用環境を2つ作って理解する作業を行いました。ここではそれを順に説明しようと思います。 コードは下記に置いてあります。 https://github.com/jedipunkz/rag-playground 構成名 フォルダ名 構成内容 検索のみ構成 retrieve-only LLM 無し構成。Go のハッシュベース埋め込み + PostgreSQL(pgvector) で検索だけを行う LLM を使った構成 full-rag-ollama Ollama (all-minilm / llama3.2) + PostgreSQL(pgvector) で検索からプロンプト化、回答生成まで行う 2つの構成図 まず両者の構成を図で示します。 検索のみの構成 (フォルダ名: retrieve-only) flowchart LR M[Markdown files] --> L[load command<br/>Go] L -->|1 chunk + embed| P[(PostgreSQL<br/>pgvector)] U[curl / browser] -->|2 /search| S[search app<br/>Go HTTP API] S -->|3 embed query + vector search| P P -->|4 top-k chunks| S S -->|5 JSON results| U load コマンドが Markdown をチャンクに分割し、ベクトル化して PostgreSQL(pgvector) に保存する ユーザーが curl で /search?q=... を呼び出す search アプリがクエリを同じ方法でベクトル化し、pgvector で cosine 距離の近いチャンクを検索する PostgreSQL が近い順に top-k チャンクを返す search アプリが検索結果を JSON で返す LLM を使った構成 (フォルダ名: full-rag-ollama) flowchart LR M[Markdown files] --> L[load command<br/>Go] L -->|1 embed chunks| O[Ollama<br/>all-minilm] L -->|1 save chunks + vectors| P[(PostgreSQL<br/>pgvector)] U[curl / browser] -->|2 /answer| A[app<br/>Go HTTP API] A -->|3 embed query| O A -->|4 Retrieve| P P -->|top-k chunks| A A -->|5 Augment| PR[/prompt/] PR -->|6 Generate| G[Ollama<br/>llama3.2] G -->|answer| A A -->|7 JSON answer + sources| U load コマンドが Markdown をチャンクに分割し、Ollama (all-minilm) でベクトル化して PostgreSQL(pgvector) に保存する ユーザーが curl で /answer?q=... を呼び出す app がクエリを Ollama (all-minilm) でベクトル化する app が pgvector で近いチャンクを検索し、top-k チャンクを受け取る (Retrieve) app が質問と検索結果からプロンプトを組み立てる (Augment) app がプロンプトを Ollama (llama3.2) に渡し、LLM が回答を生成する (Generate) app が回答と根拠チャンクを JSON で返す 見比べると、LLM を使った構成は検索のみの構成に Ollama を足し、検索結果をプロンプト化して LLM に渡す経路が増えているだけ、というのが分かると思います。この「増えた部分」が RAG の Augment と Generate に相当します。それらについて次の項目で説明します。 ...

2026-07-05 · 4 分 · jedipunkz

HackerNews を翻訳・要約して RSS 配信するパイプラインを完全無料で作った話

こんにちは。jedipunkz🚀 です。 HackerNews を毎日読みたいのですが、昔は狂ったように巡回していたものの最近は厳しく英語も何だか疲れてしまうし、そして1日に流れてくる記事数が多すぎて自分の関心領域(SRE / Platform Engineering / AI)に絞り込むのが面倒、という課題がありました。そこで「興味のある記事だけを日本語に翻訳し、AI でサマライズして RSS リーダーに流す」パイプラインを作りました。ポイントは、翻訳・要約・ホスティングまで含めてすべて無料サービスだけで完結している点です。 全体アーキテクチャ パイプラインは下記のように、クロール → 翻訳 → AI 要約 → RSS 配信という流れになっています。 (1) HackerNews をクロール (GitHub Actions + Go + Algolia API) (2) contents/YYYY-MM-DD/*.md として保存 (Google Tranalate) (3) AI 翻訳 (GitHub Models - gpt-4o-mini (4) summaries/YYYY-MM-DD.json 保存 (5) Astro ビルド -> rss.xml 配信 (Vercel) クロールと要約は2つの GitHub Actions ワークフローに分かれていて、前者の完了をトリガーに後者が走るようにしています。要約結果の JSON が main に push されると Vercel が自動でビルドし、RSS が更新されます。 1. クロールと翻訳(GitHub Actions + Go) 最初のステップは HackerNews からの記事取得です。これは Go で書いたツールが担当しています。HackerNews 公式 API ではなく Algolia の HN Search API を使うことで、期間指定や検索が簡単になります。API キーは不要です。 ...

2026-06-06 · 3 分 · jedipunkz