メインコンテンツまでスキップ
もみじーな
個人開発者
すべての著者を見る

Project AIRIをエージェント化するメモ1

· 約3分
もみじーな
個人開発者

Project AIRIっていうAIキャラクターに萌えを求めてエージェント化を実施中・・・

実装した機能とか使い方忘れるのでメモも兼ねてます。

Githubに公開してます。

昨日実装したやつ

追加機能1(ウェブ検索)

とりあえずウェブ検索機能を追加しました。

明日の天気は?
○○について調べて

とかあとはAIがわからない検索する必要があると考えたやつを勝手に検索します。

ウェブ検索はブリッジサーバーと拡張機能をChromeに登録しておく
browserというフォルダを拡張機能として取り込みが必要
start-bridge-server.batで検索サーバーが起動

追加機能2 (アラーム、タイマー)

あったら使いそうな機能としてタイマーを追加

3分タイマーをお願い
今,タイマーどれくらい経過した?
タイマー取り消し
アラート止めて

アラームは正直な話 ヘッドセットつけてる人がほとんどなので寝てたら聞こえないのでいらんですよね

明日の7時にアラームをセットして
アラーム止めて

などに対応

今日追加したやつ

カレンダー機能を追加

カレンダー機能を追加してます。
Airiの予定と自分自身の予定を追加可能

Airiの予定は指定の時間になったら自動で実行されます。

今日の私の予定は?

今日のAiriの予定は?
明日の7:00時に天気を調べる予定を追加して
カレンダーを開いて

今後の計画

Qwen3-tts 0.6bあたりを組み込みたいかな?
DBもSQLiteに変更はいずれしたいけどテストするのがめんどくさいので後かな

容量増えるのでどっちも少し機能追加してからだけど

コーディング機能とかは絶対不要だしAIキャラクターって何に使うか迷いますね。
TODOとかスケジュールはあってもいいかなと思って実装してます。

NZXT H5 FlowでPC組んだぞ

· 約3分
もみじーな
個人開発者

この前買ったNZXT H5 Flow NieR SQUARE ENIX 15th Anniversary EditionでPCを組みました。

今後、購入するGPUのテスト用に組みたいと思ってたらドスパラでセールとかしてたので適度な性能のパーツを購入しました。

今回のPC構成

今回のPC構成は以下となりました。

項目製品名
CPURyzen5 5600
MemoryCorsair DDR4 32GB(8GBx4)
MBGIGABYTE B550 GAMING X V2
PSUCorsair RM750e 2025
SATA SSD500GBx2(なんか余ってた)
CPUファン純正
PCケースNZXT H5 Flow
GPU余ってるやつをとりあえず

Ryzen5 5600を選んだのはDDR5が高騰してるのが理由ですね・・・
DDR4ですら驚異の価格
PCIe 4.0ならなんでもいいやとドスパラで3000円オフで売っていたRyzen5に決定
物足りなかったらNVMeは買います

NZXTに感動したこと

だいぶ久しぶりにNZXTのPCケースで組んだのですが組みやすい・・・・

本当に組みやすいです。

HyteとAntecでこの前に組んでますがNZXTはすべてが分かりやすいです。

ATX電源を入れたあとも結構余裕があって手を入れてケーブルさしたり普通にできてやっぱNZXTいいなと・・・

このSATA SSDを固定するやつもいいですよね

もし最近組んだPCケースを初心者におすすめするとしたら
NZXT > Hyte > Antec になりますかね

Hyteも組みやすいですがファンが最近は別売りだったりするのでここですね
AntecはATX電源をいれたあと手が入る隙間がほぼなかったです引きずり出しながら確か作業してました。(ケースとしては問題ない)

今後、買うGPUを決めたい(たぶんIntel Arc Pro B70)

Intel Arc B70っていつでるんですかね欲しいのですが
Radeon AI Pro R9700 32GB は私の中でAIに対してRADEONが微妙なのでどうしようか検討中

AIならNvidia > Intel > AMD が使いやすいです。

NvidiaとIntelですら結構な差がでますがIntelの強みはXPUですCUDA対応のコードを書き換えれば対応させることができますのでこの位置です。

Radeon君はZLUDAっていうので動かなさにといけないのでめんどくさい。

なのでIntel Arc Pro B70を買いたいではありますね・・・・

来年,Rubinを買いたいのでとりあえず今年はGPUを1枚だけ買ってみます。

Geminiに発売予定日、予約がでたら通知してと送ったらGoogleカレンダーに色々と追加してくましたがこんな機能あったんですね・・・

Playwrightはもう古い?時代はAIが直接操作みたい

· 約7分
もみじーな
個人開発者

作成したものはGithubに公開しています。


そもそもPlaywrightってどう使うのが正解なんですかね?
Playwrightを使っていると私はロボットではありませんがでて使えないしボットのログがおそらくサーバー側に残ります。(もう私はボットですがほしいです)
playwright stealthも微妙

ってことで現在はブラウザの直接操作が主流見たいですね。

Manusとかいい例ですね。

ってことでエージェントに組み込むべく拡張機能を作成してみました。
もちろんバイブコーディングですよ?
一部、手動で修正したりもしますがClaude Opus 4.6は本当にいいですね・・・

もう戻れないですよ使ったら・・・

作成したコードとかはGithubみてください。
Claude Opusとかなら独自のやつも普通につくれますので試してください。

拡張機能とLM Studio連携による操作

スクショとるのも面倒なので動画です。

エージェントによる操作

ウェブサーチをエージェントと呼んでいいかはしらないけどこっちも動画

サーバーのログ

全部AIエージェントのログですがこんな感じ

xxx.xxx.xxx.xxx - - [20/Mar/2026:18:20:32 +0900] "GET /img/mylogo.webp HTTP/2.0" 304 0 "https://momijiina.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:20:32 +0900] "GET /assets/css/styles.337dd95d.css HTTP/2.0" 304 0 "https://momijiina.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:20:37 +0900] "GET /assets/js/7463.6cbf5ab1.js HTTP/2.0" 200 8961 "https://momijiina.com/blog" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:20:37 +0900] "GET /assets/js/acecf23e.b95306be.js HTTP/2.0" 304 0 "https://momijiina.com/blog" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:20:37 +0900] "GET /assets/js/ccc49370.7f76acc6.js HTTP/2.0" 304 0 "https://momijiina.com/blog" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:20:37 +0900] "GET /assets/images/20260315-06662a2c5816e1ef7a4e3d0568dfb7ce.webp HTTP/2.0" 200 179974 "https://momijiina.com/blog" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:20:37 +0900] "GET /assets/images/20260315-2-4361c14c9a3ace2b365296cd1dd82a08.webp HTTP/2.0" 200 30936 "https://momijiina.com/blog" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:20:37 +0900] "GET /assets/images/20260315-4-cfa7123d1af9115600fbe73818a8f1ac.webp HTTP/2.0" 200 154000 "https://momijiina.com/blog" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:20:37 +0900] "GET /assets/js/9e4087bc.4d156f5f.js HTTP/2.0" 200 887 "https://momijiina.com/blog" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:20:37 +0900] "GET /assets/js/f81c1134.69d99465.js HTTP/2.0" 200 67612 "https://momijiina.com/blog" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:20:37 +0900] "GET /assets/js/33fc5bb8.1e9b670b.js HTTP/2.0" 200 3560 "https://momijiina.com/blog" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:20:37 +0900] "GET /assets/js/129538fd.2ad13eed.js HTTP/2.0" 200 356 "https://momijiina.com/blog" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:22:20 +0900] "GET /blog HTTP/2.0" 301 239 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:22:20 +0900] "GET /blog/ HTTP/2.0" 200 22294 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:22:21 +0900] "GET /img/mylogo.webp HTTP/2.0" 304 0 "https://momijiina.com/blog/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:22:21 +0900] "GET /assets/css/styles.337dd95d.css HTTP/2.0" 304 0 "https://momijiina.com/blog/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:22:21 +0900] "GET /assets/images/20260315-2-4361c14c9a3ace2b365296cd1dd82a08.webp HTTP/2.0" 304 0 "https://momijiina.com/blog/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:22:21 +0900] "GET /assets/images/20260315-06662a2c5816e1ef7a4e3d0568dfb7ce.webp HTTP/2.0" 304 0 "https://momijiina.com/blog/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:22:21 +0900] "GET /assets/js/7463.6cbf5ab1.js HTTP/2.0" 304 0 "https://momijiina.com/blog/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:22:21 +0900] "GET /assets/images/20260315-4-cfa7123d1af9115600fbe73818a8f1ac.webp HTTP/2.0" 304 0 "https://momijiina.com/blog/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:22:21 +0900] "GET /assets/js/129538fd.2ad13eed.js HTTP/2.0" 304 0 "https://momijiina.com/blog/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:22:21 +0900] "GET /assets/js/33fc5bb8.1e9b670b.js HTTP/2.0" 304 0 "https://momijiina.com/blog/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:22:21 +0900] "GET /assets/js/9e4087bc.4d156f5f.js HTTP/2.0" 304 0 "https://momijiina.com/blog/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
xxx.xxx.xxx.xxx - - [20/Mar/2026:18:22:22 +0900] "GET /img/myfavicon.ico HTTP/2.0" 200 67646 "https://momijiina.com/blog/20260315" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "

"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"として表示されているので素晴らしいです。
これは人が検索したものと区別がつかないですね。(playwright stealthもこんな感じ)
ふるまい検知とか実装されればもしかしたら検出されるのかもしれないですが

最後に

ボット検出はされないですが個人利用の範囲で使ってください・・・
こうやってAIしかサイトは見なくなるのかもしれないですね

ローカルAIとクラウドAIで弾幕ゲームを作って比べる

· 約4分
もみじーな
個人開発者

※公開を忘れていた記事

Qwen3.5 27bとコーディング最高峰のClaude Opus 4.6だとやっぱり圧倒的な差がでます・・・

そもそもパラメータ数的に比べるのが間違っているのですがこれから本当にローカルGPUでコーディングを行おうとする人へのメッセージは本当にGPUを買うのか?
クラウドで良くないかとだけ書いておきます。

※一部の画像生成とか動画生成目的だけはありです。

弾幕ゲーム作成による比較

以下のGemini3.1 Proが生成したプロンプトから派生させていきました。

あなたは優秀なゲームプログラマーです。HTML5のCanvasとJavaScriptを使用して、東方Projectにインスパイアされた「弾幕シューティングゲーム」のプロトタイプを作成してください。
以下の要件を満たすコードを、1つのHTMLファイル(CSSとJSを含む)として出力してください。

【ゲームの基本要件】
1. 画面サイズ: 幅600px、高さ800pxの黒背景のCanvas。
2. メインループ: requestAnimationFrameを使用して滑らかに描画と更新を行うこと。

【プレイヤーの要件】
1. 操作: 矢印キーで上下左右に移動。
2. 東方風の特殊操作: Shiftキーを押している間は「低速移動」になり、自機の中心に小さな円(当たり判定の可視化)が表示されるようにすること。自機自体のグラフィックは適当な図形でよい。

【ボスと弾幕の要件】
1. ボス: 画面上部の中央に配置し、左右にゆっくりと移動させる。
2. 弾幕パターン: Math.sin() と Math.cos() を使用して、ボスから美しい幾何学模様の弾幕を発射する。
- 例として「全方位弾(円形に広がる)」と「渦巻き弾(回転しながら発射される)」を一定間隔で切り替えながら撃たせること。
3. パフォーマンス: 画面外(Canvas領域外)に出た弾は必ず配列から削除し、処理落ちを防ぐこと。

【当たり判定】
1. プレイヤーの中心の小さな当たり判定と、敵の弾(円)との間で円の衝突判定(ピタゴラスの定理)を行う。
2. 衝突した場合はコンソールに「Game Over」と出力し、ゲームの進行を停止させる。

Qwen3.5 27bが生成したもの

まぁ悪くはないですよね。

Claude Opus 4.6が生成したもの

Github Copilotで生成してます。
一応,もう何年か忘れましたかがPro+という39ドルにずっと入ってます。

これがClaudeです・・・
少なくともQwen3.5 397bでも使わないと現状はこのレベルは厳しいです。

コーディングをするならクラウド一択

上の比較の通りコーディングに関してはローカルでやらないといけない事情がない限りはクラウドを使ってください。
現在のローカルでクラウドに勝つには少なくともKimi K2などの超大型モデルをロードできる環境をよういできない限り不可能です。

勝てたとしてその予算は見合っているのかですがKimiやDeepSeekの量子化モデルで無理やりMacやRTX Proとかにロードするとして数百万円かかります。
Github Copilot+は39ドルです。
12か月使っても390ドル(年間だとお得)です。

Qwen3.5 27bを動かすためだけIntel Arcなどの安めのグラボを買ったとしても価格には見合っていません。
作りたいものがあるだけならGemini,Claude,ChatGPTを契約することおすすめだけします。

おまけ(Gemini 3.1で作ってみた)

これがGemini 3.1 Proの力だ!
ほぼ東方弾幕ゲームです。
素材さえ用意すればさらにいい感じにおそらくなります
Claudeよりやっぱりキャラデザインは強いきがします。

AI生成で東方の弾幕ゲームが完璧に作れる日はおそらく近いです。
SVGもそれっぽいですよね

プレイはこちら

エージェント(Qwen3.5 27b)のテストとAIに実行計画させるのは大事?

· 約3分
もみじーな
個人開発者

自分用のAIエージェントアプリを作成しているのですがGithub CopilotとかManusみたいに作業中の項目が見たいので実行計画をまず立てさせてから開発させるようにしたら生成の精度がよくなりました。

実行計画の実装前・実装後どうだったか

エラーがなくてもバグが多かったりAI自体がループに入ったりしましたが計画させて作業を分割したらなぜ1回でそこそこ動くアプリを作成してくれるようになりました。
ループもあんまりしなくなった。(なぜだ?むしろループを見ていない気も)
一度に色々と考えて生成させるのが原因なのか?
ある程度制限すれば減るのか?

生成テスト

テトリスの作成テスト

画像生成はStable Diffusionを起動していないのでエラーです
立ててれば自動で画像も生成して画像確認してつかってくれます。
あとこれは生成後に背景がなかったのでテストも兼ねておしゃれにしてと命令してます。

エージェントが勝手にエラーチェックもして修正してから報告してくるので基本的にはエラーがでるとかもないです。
スタートボタンなども勝手に押してテストしてくれます。(自分でもここまで機能実装して何を目指しているのかですが)

リバーシの作成テスト

エージェントの1回の完了報告でエラーやバグ、UIがずれているかもなさそう?
このリバーシとかもそうですがQwen3.5 27bとかQwen3.5シリーズが作成したものってなんか似たり寄ったりのUIと機能になりますよね。

TODOアプリの作成テスト

TODOアプリはなんとなくテストしただけです。 普通に動くし普通に使えなくはない。

最後に

Qwen3.5 27bいいですよねやっぱ 9bとかで開発テストしてますが使うときに27bを使うとここまで違うのかと実感します。(特にUIとか)
実行計画を立てさせるのがどれくらい効果的かはわかりませんが去年、Github CopilotでClaude 3.7(記憶が曖昧)とかの時に生成させたものに近いものが作れる気もしますのでQwen4.5に期待してます。
あと1bit量子化まだか?
富士通のQEPどうなったんですかね?3月になったので公開はされてるかもしれないですが精度89%がどれくらい響くのかもありますがはやくそこそこ精度の100bレベルが32GBくらいのVRAMで動いてくれると面白いですよね。

Qwen3.5のエージェントにWikipediaのDBを与えてみた

· 約8分
もみじーな
個人開発者

※3/11に追加でベクトル化もしましたが大変よかったです。

通常のAIモデルだと学習以前のデータは間違った答えを渡してくるので少しでもデータの正確性を保つためにWikipediaのデータを自動で検索するようにしてみました。

本当はベクトル化したいですけど時間かかるぽいので今回はテストとしてやってます。
来週か再来週あたりベクトル化したいですね。

画像のように現在の総理大臣も前のも大丈夫です。
なぜか私だけさんがつかないのがこのAIへの毎回の疑問ですが・・・

ちなみLM Studioだけだとこんな感じです。

ウェブリサーチさせる方法もありますけどネットを挟むとローカルだけだと動かないですしボットは嫌われる(企業ならやめたほうがいい)ので今回はWikipediaがデータベースを配布していることもあるのでダウンロードしてSQLite FTS5にデータを変換してAIが検索できるようにしてみました。

SQLite FTS5のデータはAPIでAIが参照するようにしているので割愛しますがデータベース化の仕方だけ残しておきます。

データベース作成方法

Wikipediaのデータ自体はhttps://dumps.wikimedia.org/jawiki/ から最新をダウンロードするだけです。
jawiki-20260301-pages-articles-multistream.xml.bz2 という4.4 GBを今回はダウンロードしました。

適当に学習させるフォルダに入れておいてください。
最終的にこうなります。

準備

学習に使ったコードは以下です。

requirements.txt

mwparserfromhell

build_wiki_db.py

"""
jawiki XML.bz2 → SQLite FTS5 (wiki.db) 変換スクリプト

出力:
wiki.db (同フォルダに作成)
"""

import bz2
import re
import sqlite3
import os
import sys
import time
import gc
import xml.sax
import xml.sax.handler

DB_PATH = "wiki.db"
BATCH_SIZE = 500 # 一度にINSERTする記事数
MAX_ARTICLES = 0 # 0 = 無制限。テスト時は例えば 10000 に設定すること
CHUNK_SIZE = 1024 * 1024 # 1MB ずつ bz2 展開

def strip_markup(text: str) -> str:
if not text:
return ""

# コメント除去
text = re.sub(r'<!--.*?-->', '', text, flags=re.DOTALL)
# <ref> タグ除去
text = re.sub(r'<ref[^>]*>.*?</ref>', '', text, flags=re.DOTALL)
text = re.sub(r'<ref[^/]*/>', '', text)
# その他HTMLタグ除去
text = re.sub(r'<[^>]+>', '', text)
# {{テンプレート}} 除去(ネスト対応・最大5段)
for _ in range(5):
prev = text
text = re.sub(r'\{\{[^{}]*\}\}', '', text)
if text == prev:
break
# [[File:...]] [[Image:...]] 除去
text = re.sub(r'\[\[(?:File|Image|ファイル|画像):[^\]]*\]\]', '', text, flags=re.IGNORECASE)
# [[リンク|表示テキスト]] → 表示テキストのみ残す
text = re.sub(r'\[\[[^\]|]*\|([^\]]*)\]\]', r'\1', text)
# [[リンク]] → テキストのみ残す
text = re.sub(r'\[\[([^\]]*)\]\]', r'\1', text)
# [URL 表示] → 表示テキスト
text = re.sub(r'\[https?://\S+\s+([^\]]+)\]', r'\1', text)
# 単独URL除去
text = re.sub(r'\[https?://\S+\]', '', text)
# 見出し記号除去
text = re.sub(r'={2,}(.+?)={2,}', r'\1', text)
# 太字・斜体
text = re.sub(r"'{2,3}", '', text)
# 表・wikitable
text = re.sub(r'^\s*[|!{][^\n]*', '', text, flags=re.MULTILINE)
# 余白整理
text = re.sub(r'\n{3,}', '\n\n', text)
return text.strip()


class WikiHandler(xml.sax.handler.ContentHandler):
"""
XML要素をツリーに蓄積せず、文字列バッファのみ使用。
メモリ使用量は記事1件分のテキストのみ (数KB〜数百KB)。
"""

def __init__(self, on_article):
super().__init__()
self._on_article = on_article
self._in_page = False
self._in_revision = False
self._tag = None
self._title_buf = []
self._ns_buf = []
self._text_buf = []

def startElement(self, name, attrs):
if name == 'page':
self._in_page = True
self._in_revision = False
self._title_buf = []
self._ns_buf = []
self._text_buf = []
self._tag = None
elif name == 'revision' and self._in_page:
self._in_revision = True
if name in ('title', 'ns') and self._in_page:
self._tag = name
elif name == 'text' and self._in_page and self._in_revision:
self._tag = name

def endElement(self, name):
if name in ('title', 'ns', 'text'):
self._tag = None
elif name == 'revision':
self._in_revision = False
elif name == 'page':
self._in_page = False
ns = ''.join(self._ns_buf).strip()
title = ''.join(self._title_buf).strip()
text = ''.join(self._text_buf)
self._title_buf = []
self._ns_buf = []
self._text_buf = []
if ns == '0' and title and text \
and not text.lstrip().startswith('#REDIRECT') \
and not text.lstrip().startswith('#転送'):
self._on_article(title, text)

def characters(self, content):
if not self._in_page or self._tag is None:
return
if self._tag == 'title':
self._title_buf.append(content)
elif self._tag == 'ns':
self._ns_buf.append(content)
elif self._tag == 'text':
self._text_buf.append(content)


class _StopBuild(Exception):
pass


# ── DB構築 ───────────────────────────────────────────────
def build_db(dump_path: str):
print(f"入力ファイル : {dump_path}")
print(f"出力DB : {DB_PATH}")
print("─" * 50)

con = sqlite3.connect(DB_PATH)
cur = con.cursor()
cur.executescript("""
PRAGMA journal_mode = WAL;
PRAGMA cache_size = -32000;
PRAGMA temp_store = FILE;
PRAGMA mmap_size = 0;
""")

cur.executescript("""
DROP TABLE IF EXISTS wiki;
CREATE VIRTUAL TABLE wiki USING fts5(
title,
body,
tokenize = 'unicode61'
);
""")
con.commit()

batch = []
count = [0]
t0 = time.time()

def on_article(title: str, text: str):
body = strip_markup(text)
if len(body) < 50:
return
batch.append((title, body))
count[0] += 1
if len(batch) >= BATCH_SIZE:
cur.executemany("INSERT INTO wiki(title, body) VALUES (?, ?)", batch)
con.commit()
batch.clear()
elapsed = time.time() - t0
rate = count[0] / elapsed
print(f"\r{count[0]:,} 件処理済 ({rate:.0f} 件/秒)", end='', flush=True)
if count[0] % 5000 == 0:
gc.collect()
if MAX_ARTICLES and count[0] >= MAX_ARTICLES:
raise _StopBuild()

handler = WikiHandler(on_article)
parser = xml.sax.make_parser()
parser.setFeature(xml.sax.handler.feature_namespaces, False)
parser.setContentHandler(handler)

opener = bz2.open if dump_path.endswith('.bz2') else open
try:
with opener(dump_path, 'rb') as f:
while True:
chunk = f.read(CHUNK_SIZE)
if not chunk:
break
parser.feed(chunk)
parser.close()
except _StopBuild:
pass
except xml.sax.SAXParseException as e:
print(f"\n[警告] XML解析終了: {e}")

if batch:
cur.executemany("INSERT INTO wiki(title, body) VALUES (?, ?)", batch)
con.commit()

elapsed = time.time() - t0
print(f"\n{'─'*50}")
print(f"完了: {count[0]:,} 記事 所要時間: {elapsed/60:.1f} 分")
if os.path.exists(DB_PATH):
print(f"DBサイズ: {os.path.getsize(DB_PATH) / 1024**3:.2f} GB")
con.close()


# ── 検索テスト ────────────────────────────────────────────
def test_search(query: str, limit: int = 5):
if not os.path.exists(DB_PATH):
print("wiki.db が見つかりません。先に build を実行してください。")
return
con = sqlite3.connect(DB_PATH)
cur = con.cursor()
rows = cur.execute(
"SELECT title, snippet(wiki, 1, '[', ']', '...', 20) FROM wiki WHERE wiki MATCH ? ORDER BY rank LIMIT ?",
(query, limit)
).fetchall()
con.close()
if not rows:
print(f"「{query}」に一致する記事が見つかりませんでした。")
return
for i, (title, snip) in enumerate(rows, 1):
print(f"\n[{i}] {title}")
print(f" {snip}")


# ── エントリーポイント ────────────────────────────────────
if __name__ == '__main__':
if len(sys.argv) >= 2:
cmd = sys.argv[1]
if cmd == 'search':
q = ' '.join(sys.argv[2:]) if len(sys.argv) > 2 else '機械学習'
test_search(q)
else:
# 引数をダンプファイルとして扱う
build_db(cmd)
else:
# 自動検索
candidates = [f for f in os.listdir('.') if f.endswith('.xml.bz2')]
if not candidates:
print("エラー: *.xml.bz2 ファイルが見つかりません。")
print("使い方: python build_wiki_db.py <ダンプファイル>")
sys.exit(1)
build_db(candidates[0])

私がbatファイルで起動したいので作成してます。
build.bat

@echo off
chcp 65001 > nul
echo ============================================
echo Wikipedia FTS5 DB ビルダー
echo ============================================
echo.

:: Python確認
python --version > nul 2>&1
if %errorlevel% neq 0 (
echo [エラー] Python が見つかりません。
echo Python 3.8以上をインストールしてください。
pause
exit /b 1
)

:: 依存ライブラリインストール
echo [1/2] 依存ライブラリをインストール中...
pip install -r requirements.txt
if %errorlevel% neq 0 (
echo [エラー] pip install に失敗しました。
pause
exit /b 1
)

echo.
echo [2/2] wiki.db を構築します(数十分かかります)...
echo 処理中はウィンドウを閉じないでください。
echo.

python build_wiki_db.py

if %errorlevel% neq 0 (
echo.
echo [エラー] ビルドに失敗しました。
pause
exit /b 1
)

echo.
echo ============================================
echo 完了! wiki.db が作成されました。
echo ============================================
echo.
pause

batファイル起動

batファイル実行したらこんな感じですね

完了

私の環境では20分程度で終わりました。

API実装などは自由に

12GB程度のデータベースが生成されますがデータベースなので自由に組み込めるはずです。
最新のデータがほしくなったら都度、DBを更新する必要がありますがある程度のデータには答えてくれるようになります。

最後に

そもそも現状のローカルAIに求めすぎな気がするんですよね所詮はローカルです。
物足りないならクラウドを使うべきだとは思いますがローカルで楽しみたいですよね・・・

もし時間に余裕があるならembedding,ベクトル化することをお勧めします。
精度が多分ですが全然違います。

追加でベクトル化もしました(2026/03/11)

結局、次の日にembeddingを作成しましたが更新してなかったので書いておきますが
embed + FTS5ハイブリッド検索に切り替えた方が確実に精度が上がります。
これは確実です。
FTS5だけだったときは時々違うデータが入っていたのでそれはなくなりました。

DB→embed作成で所要時間はRTX5090で50分かからなかったのでこの精度ならやる価値はあると思いますが最新データがどうしてもほしい場合だけです。

せっかくなのでコーディング用に次はBigCodeとか試して見ようと思いますがどれくらい精度があがってくれるか楽しみです。

Qwen3.5搭載のAIエージェントを作れば案外使える

· 約2分
もみじーな
個人開発者

Gensparkを契約しようか考えてたのですがManusと同じく一瞬でクレジットなくなるので微妙なんですよね。
あとGensparkは画像を集めろと命令しても著作物はあつめ・・なんでもないです。

なので自作してますが少なくとも作るのは難しくないです。
データベースも自分しか使わないのでSQLiteにしてるので楽ですね。

今は色んなオープンソースやライブラリがあるので結構いい感じに個人用なら作成が可能です。
Qwen3.5の9bと2bなどで切り分けて使えるようにしてますが27bとかでもいいですが速度がちょっとって感じですね。
gpt-oss-20bとかでもそりゃいいですけど画像が使えないです。

動作動画も張っておきます。

AIのエンドポイントはLM StudioとOpenRouterに対応させてるのでローカルでもクラウドでも可能にしてます。

ウェブリサーチもPlaywrightでいい感じになってますが精度があまりまだよくないですしエージェントに画像生成も組み込ませたいのでまだまだ理想のエージェントは遠そうです。

Qwen3.5 Small(0.8b,2b,4b,9b)の画像認識テスト(内容を訂正しました)

· 約2分
もみじーな
個人開発者

※3月4日にかなり修正してます


帰ってきて適当に作ってデバッグログみない自分が悪いのですが昨日作成した分は補助モードに勝手に入っており綺麗にできていただけでした。

補助モードを実装した理由は生成失敗によるミスをなくすためでしたがそれが悪さをしてましたね・・・

補助モードを削除したうえでOpenCVで画像加工処理するように変更しました。

結局,2Bが一番組み込むならいいです。

Thinkingもなんとかきれました。
9Bでも爆速です。

もうしばらくは満足です。

昨日(3月3日)までの分↓
動画はあれみて4bすごいと思われないように限定公開にしました。

小さくて画像認識精度が高いモデルを日頃さがしてるのですがQwen3.5 4Bはかなりよかったです。

テトリス作って画像認識操作テストした動画です。
Qwen3-VL-4Bから2Bまでは処理できてませんがQwen3.5 4Bから圧倒的です。
もちろん画像認識精度だけの問題ではないですが性能がここまであがるとこれからが楽しみです。
ロボットとかドローンとかに組み込めば面白そう?

このクラスが2Bにおさえられたら夢が広がりますがとりあえずQwen3.5 4Bでここまで動くならよさそうです。

ホールド機能つけてないので配置がおかしいところがありますが私がテトリスやるより早いですたぶん・・・・

NZXT H5 Flowを買ったから年内PCを組む

· 約1分
もみじーな
個人開発者

NZXT H5 Flow NieR SQUARE ENIX 15th Anniversary Editionを結局、買いましたね。

売り切れてたらいいやと思ってたんですが昼頃にビックカメラ見たらなぜか少し安く売ってました。
Xとかでも完売みたいな話だったのでビックカメラで売っていることに誰も気づいてなかったんですかね?

昨日,Hyte Y70届いたばっかだけど来年でるだろうRTX6090用にしたいので来年組む予定です。

こいつ何個PCケース買ってるんだって話なんですがね・・・

今見たらまだ在庫あるんですが価格は正常値になってました。

時間があるときにR9700を買おうと思いますがB70の販売があるならそっち買うかもしれないですね。

所詮、AIようなのでCPUは適当に買う予定です。

DDR6でるならそっちがいいんですが現状はDDR4とかでも別に・・・

HYTE Y70 Firefly Limited Editionが届いた

· 約1分
もみじーな
個人開発者

買ったのすら忘れていましたがHYTE Y70 Firefly Limited Editionが届いてしまいました・・・

ConoHaの抽選で当たったバレンタインキーホルダーと比較してますが・・

ケースでかすぎるんよ
A4との比較

一応,割れてないか確認で開封

銀狼版もまだ所持在庫があるので暫くは組まないですがそのうちなんか組みます。

明日,2BコラボのH5 FLOW NieR 15TH ANNIVERSARY EDITIONがでるらしいので狙ってたのですがどうしよかな・・・・

2BコラボケースにRadeon AI PRO R9700でも買って組もうかと思ってたのでちょっと考え中(ケースに入るかは不明)