🐡

テックブログの下書きを自分の特徴に合わせて生成、をAIを絡めて割とお手軽・安価に実現できました

に公開

概要

テックブログを書き続けていますが、慣れてきても書く手間はそこそこにかかります。この手間をAIなどを使って軽減できないかなと思って、「ブログの元ネタの情報を自然言語で入力すると、LLMが自分のブログの特徴に合わせて下書きを作ってくれる」という仕組みを検討してみたら、割とお手軽・安価に実現できました。それを紹介します。(「軽減」程度です、過度に期待しないで)

最初に結論まとめ

次のような構成で実現できました。

  1. C#アプリで、Web API を呼び出して、ブログの過去記事をファイルへエクスポート
  2. 代表的な記事を適当な LLM に渡して、自分のブログの書き方の特徴を要約した「文体カード」的なものを作成
  3. 全記事を Azure AI Search に渡して RAG として使えるようにベクトルDB化
  4. C#アプリで、入力した記事の元ネタ文章に対して、「文体カード」とRAGを使用して下書き案を作る

C#アプリを使うところは GitHub Copilot に書いてもらったので、人間がかけた手間はかなり少ないです。今回はツールの用途から見て少々バグがあっても困らないので、AIのコーディングで動けば十分、コードレビューもほとんどしていません。

費用としても、自分のブログ記事の量など大した量ではなく高度な機能も使わないので、 Azure AI Search の Free プランで十分。かかる費用は Blob ストレージと、実行の度にベクトル検索のための Embedding と推論で LLM を動かすところくらいです。今のところのコストは3円です。色々な要素を考えても月に数円~数十円くらいですみそうであり、十分に安いです。

この記事を書くにあたって与えたネタと、出てきた下書き案を、gistに置きました。どの程度の成果なのかに興味があれば見てみてください。

下書き案をそのまま使うのは厳しいですが、話の流れなどはこれをベースにして細部を正しい内容へ書き換えていく、という使い方ができます。体感では、無から書き出すよりもだいぶ楽に書けた気がします。

説明

上のまとめで話はほぼ終わりなので、この後は詳しい説明をして行きます。

記事をエクスポートしてJSON化する

ブログサービスのAPIは仕様が明確なので、GitHub Copilotにスクリプトを書いてもらいました。Qiita向けだとこんな感じです。

上のコードだと出力がテキストファイルになっていますが、ファイル名・日付などのメタデータを個別に読ませるならばJSONにした方が良いです。出力処理を変えるだけなので大差ありませんが、すみませんが上のコードには実装がありません。(今回は取り急ぎ、あとから別の処理でJSON変換して実現しました)

代表記事から文体カードを作る(LLMに要約させる)

代表的な記事を数本選んで、ChatGPT に「文体・語彙・構成の特徴を抽出して要約した文体カード」を作成してもらいました。

選んだファイルを添付し、次の程度のプロンプトを渡すだけです。

添付したファイルは、1つずつがブログの記事です。
これらブログ記事から"文体・語彙・段落構成・見出しの癖"を要約した文体カードを、Markdown記法で作成してください。 これと類似した記事をLLMで作成するための、ヒント(コンテキスト)となる情報として使うことが目的です。

最近のGPT-5にもなると実に優秀なもので、その程度で十分な結果を得られました。

Azure側の構成を用意する

Azureで使うものを用意します。ブログ書きの補助にそれほど大金は払えないので、できるだけ低コストで。

  • Azure OpenAI Service:推論用のGPT-5 miniをデプロイ
  • Azure Blob Storage:エクスポートしたブログ記事を全て置く
  • Azure AI Search:Blobにあるファイルを取り込んでベクトルDB化。(チャンク分割・Embeddingも全部おまかせ)

※注意:Azure AI Searchは有料プランだと料金が跳ね上がるので、この用途ならFreeプランにしましょう。

Azure AI Searchにブログ記事を取り込み

「データのインポートとベクター化」のメニューで Blob を選択して実行するだけです。 Azure の使い方解説は主題ではないので、操作の詳しい説明はしません。

ただ、ここで意外な問題に引っかかったのでその説明だけします。

インデクサの「Keyが日本語パスで失敗する」問題の回避

うっかり罠:ファイル名に日本語が含まれているとインデクサが失敗するようです。上のスクリプトだと日本語が入ります。

デフォルトではidフィールドにファイルのURLを使う動きをするようなのですが、ファイル名に日本語が入っているとURLエンコードされて文字に%が入ります。しかしidフィールドには%を使えない制限があるので、処理が失敗します。

対処:インデクサのマッピングを修正して、 Key を Base64 でエンコードするようにして再実行しました。

インデクサJSONの該当箇所を書き換える例

"fieldMappings": [
  {
    "sourceFieldName": "metadata_storage_path",
    "targetFieldName": "index_key",
    "mappingFunction": {
      "name": "base64Encode"
    }
  },
  {
    "sourceFieldName": "metadata_storage_path",
    "targetFieldName": "original_path"
  }
]


ポイントは mappingFunction を追加したところです。Base64エンコードならURLエンコードと違って%が入らないので、idに使用することができます。

C#アプリ(最小例)で検索→プロンプト生成→下書き生成

  • 流れ:UIから記事の元ネタを入力→RAG検索→上位N件を結合→Systemメッセージ部に文体カード+固定指示を入れる→ Semantic Kernel の ChatCompletions で下書きを生成。
  • ここはGitHub Copilotにコード生成を任せました

RAG検索については、Embeddingの処理もAzure AI Searchにおまかせすることにしました。このアプリは Embedding 処理はしていません。VectorizableTextQuery で入力テキストをそのまま投げているだけです。

コード全体は次のGitHubリポジトリです。元々Foundry Localとチャットするために書いたコードを改造したので、それ専用のコードが混入しています。この記事の処理での要はおおむね ChatModel.SendAsync() の部分です。

https://github.com/suusanex/blog_draft_learn_and_suggest

これで使える状態になったので、今後はこのアプリを使えば下書き作成ができます。

まとめ

そこそこの実用性のものがお手軽・安価に作れたので、紹介しました。だいぶ便利な時代になりましたね!

今回は具体的なコードやJSONなどの材料を入力しなかったので、割と要修正範囲が広かったですが・・・たぶんその辺を改善すればもっと良いものが出てくると思います。今後のブログ書きにも使っていってみるつもりです。

そんなに難しい話でもないので、興味が湧いた人はこの記事を参考にしてやってみるとよいと思います。

Discussion