ローカルAIで背景除去アプリを作る①|PyQt6と3枚の純関数レイヤで「壊れない土台」を作る【背景除去Studio制作】

アプリの作り方

ローカルAIで背景除去アプリを作る①|PyQt6と3枚の純関数レイヤで「壊れない土台」を作る【背景除去Studio制作】

背景除去アプリで最初に固めるべきは、UIの見た目でも採用するAIモデルでもない。「抜き方(エンジン)」をUIから引き剥がすことだ。AI・四隅FloodFill・クロマキー・指定カラー透過と、背景を消す手段はいくつもある。それを全部UIのボタン処理に直書きすると、手段を1つ足すたびにUIのコードが膨らみ、どこが「抜く計算」でどこが「画面の都合」なのか混ざって、やがて自分でも触れなくなる。だからこの連載の第1回では、全エンジンの出力を「uint8アルファマスク(255=被写体・0=背景)」という1種類のデータに統一し、UIはその1種類だけを相手にする土台を先に組む。PyQt6 + OpenCV + ONNX で、完全オフラインで動く Background Removal Studio Pro を、実際のコードで1から作っていく。

▶ この連載の全体像と完成アプリの使い分けガイドは連載ハブ(背景除去アプリの作り方)から。完成アプリの使い方だけ知りたい人はハブだけで足りる。作りたい人はここからだ。

目次
  1. この連載で作るもの(全7回の地図)
  2. 第1回のゴール
  3. Step 1. なぜ4ファイルに分けるのか
  4. Step 2. 共通通貨は uint8 アルファマスク
  5. Step 3. モードで被写体を切り替える思想
  6. Step 4. QMainWindow と左パネル
  7. Step 5. 2段階更新の設計
  8. Step 6. 完全オフライン方針
  9. まとめ:第1回の要点

この連載で作るもの(全7回の地図)

  1. ① 土台と全体設計 — PyQt6と3枚の純関数レイヤ(この記事)
  2. ② 古典CVエンジン — FloodFill・クロマキー・Color-to-Alpha
  3. ③ 2つの難所 — 囲まれた背景ポケットとアイコンの穴
  4. ④ AIをローカルで動かす — ONNX+BiRefNetとDirectMLフォールバック
  5. ⑤ 仕上げ — 5スライダー後処理・ブラシ・20段Undo
  6. ⑥ UI/UX — ズーム・スポイト・非同期・D&D一括処理
  7. ⑦ 完結 — 品質を数値で担保して配布する

第1回のゴール

この記事を読み終えると、QMainWindow の骨格が立ち、「ベースマスク生成 → スライダー後処理 → 合成 → プレビュー」という一方向のデータフローが動くところまでが手元に出来上がる。以降の回でエンジンを1つずつ差し込んでいくが、そのとき土台側は書き換えない。差し込み口だけ用意しておく。

コードそのものより先に、全体の骨組みを頭に入れてほしい。アプリは大きく4ファイルで構成する。うち3枚は画面もPyQtも知らない「純関数」の層で、残り1枚がその3枚を呼び出すPyQtのUIだ。下の図がその関係を表している。

図0:3枚の純関数レイヤ+1枚のUI。返り値はすべて uint8 アルファ removal_engines.py 古典CV(FloodFill/ クロマキー/カラー透過) BGR画像 → uint8アルファ ai_engine.py ONNXローカル推論 BiRefNet/ISNet/U2Net BGR画像 → uint8アルファ mask_pipeline.py 後処理・ブラシ合成 (純関数) アルファ → アルファ bg_remover_ui_ux.py(PyQt6・QMainWindow) 画像状態を持ち、モードに応じて上の3枚を呼び分け、結果を画面に映すだけ rebuild_base_mask(ベースマスク生成)→ postprocess_mask(後処理)→ compose_final_alpha(合成)→ update_preview(表示) データは左から右へ一方向。逆流させない。

Step 1. なぜ4ファイルに分けるのか

結論から書く。このアプリは役割ごとに4ファイルへ割る。removal_engines.py(古典CVのベースマスク生成)・ai_engine.py(ONNX推論)・mask_pipeline.py(後処理とブラシ合成)・bg_remover_ui_ux.py(PyQt6のUI)。README にもこの構成をそのまま書いてある。

- bg_remover_ui_ux.py — メインUI(エントリポイント)
- removal_engines.py — 古典CVエンジン(純関数)
- mask_pipeline.py — マスク後処理・ブラシ合成(純関数)
- ai_engine.py — ONNX推論(モデル別前処理、DirectML→CPU自動フォールバック)
- scripts/ — チューニング/評価ハーネス(アプリ実行時には不要)
- tests/ — pytest(プリセット回帰・AI推論・エンジン単体)

肝は、上の3枚(removal_enginesai_enginemask_pipeline)がPyQtもウィンドウも一切知らないという点だ。各ファイル冒頭のコメントが、その約束を宣言している。

# removal_engines.py
# 古典CVのベースマスク生成(純関数)
# 戻り値はすべて uint8 アルファ(255=被写体, 0=背景)

# mask_pipeline.py
# マスク後処理 + ブラシレイヤー合成(純関数)
# 入出力はすべて uint8 のアルファマスク
# (255=被写体/不透明, 0=背景/透明)

この分け方には、あとで効いてくる理由がある。

  • 画面なしでテストできる:純関数の層はNumPy配列を入れてNumPy配列が返るだけなので、ウィンドウを開かなくても pytest が一瞬で回る。実際このプロジェクトのテストは、プリセット回帰・AI推論・エンジン単体の3系統に分かれていて、ロジックの正しさを目視でなく自動テストで守っている(数値での担保は第7回で扱う)。
  • UIの都合がロジックに漏れない:スライダーの範囲やボタンの並びをどう変えても、抜く計算のコードは1行も動かない。逆に抜き方のアルゴリズムを改良しても、UI側は差し込み口を通じて受け取るだけで済む。
  • 1ファイルずつAIに任せられる:層の境界がはっきりしていると、各ファイルは小さく独立するので、生成AIに「このファイルだけ直して」と安全に頼める。境界がないと、1か所の修正が思わぬ場所を壊し、原因も追えなくなる。

💡 設計の勘所:バイブコーディングでAIにコードを書かせるほど、この「層の境界」を人間が握る価値は上がる。境界さえ守られていれば、中身の実装をどれだけAIに任せても全体は崩れない。

Step 2. 共通通貨は uint8 アルファマスク

4ファイルに割っただけでは、まだ土台にならない。3枚の純関数レイヤがてんでバラバラの形でデータを返したら、UI側が結局それぞれを場合分けして扱うはめになり、分けた意味が消える。そこでこのアプリの中心に、1本の約束を通す。背景の抜き具合は、つねに「uint8アルファマスク」1種類で表す。形は画像と同じ縦横の2次元配列で、各画素が0から255の1バイト。255なら被写体(残す)、0なら背景(透明にする)、中間はその割合の半透明。

これを「共通通貨」と呼んでいる。円やドルのように、価値をやりとりする共通の単位だ。古典CVで抜こうがAIで抜こうが、出てくるのは同じ規格のアルファマスク。だからUIは、どのエンジンを使ったかを気にせず、返ってきたマスクを同じ後処理・同じ合成・同じ表示にそのまま流せる。

例えば古典CVの四隅FloodFillエンジンは、docstring で返り値の規格をこう明記している。

def flood_background_mask(bgr, tolerance):
    """四隅FloodFillで「外側から到達できる背景色」だけを透明にする。
    ...
    Args:
        bgr: uint8 (H, W, 3)。
        tolerance: 0-100。FloodFill の loDiff/upDiff に使う色差。
    Returns:
        uint8 (H, W) アルファ。
    """

入力は BGR(OpenCVの色順)の3チャンネル画像、出力は1チャンネルの uint8 (H, W) アルファ。第4回で扱うAI推論も、モデルの生の出力を最終的にこの同じ uint8 (H, W) に揃えて返す。共通通貨を最初に決めておくと、あとから増えるエンジンが全部この型に「両替」して合流する形になる。

💡 ここが土台の本体:抜き方が5通りあっても、UIが覚える型は1つだけ。「複数の入口・1つの通貨・1本の処理パイプライン」。この形にしておくと、6回目・7回目でエンジンや後処理が増えても、UIの構造はほぼそのまま使い回せる。

Step 3. モードで被写体を切り替える思想

共通通貨を決めたら、次は「入口」を用意する。ユーザーが直接選ぶのは被写体と背景の関係で、抜き方のアルゴリズムはその選択からアプリが決める。白い机に置いた商品なのか、グリーンバックの人物なのか、クリックで背景を指定したいのか。それをアプリでは5つの「モード」として持つ。定数はメインファイルの先頭でこう定義している。

MODE_AI = "AI自動(どんな背景でも)"
MODE_FLOOD = "白/単色背景"
MODE_CHROMA = "クロマキー(任意色)"
MODE_WAND = "マジックワンド(クリック指定)"
MODE_COLOR = "指定カラー透過"

文字列をそのまま定数にしているのは、この値がコンボボックスの表示ラベルと内部の判定キーを兼ねるからだ。ユーザーが読むラベルと、コードが分岐に使うキーを二重管理しない。モードを増やすときは、この5行に1行足して、ベースマスク生成の分岐を1つ増やせばいい。

5つのモードは、それぞれ別のエンジンに対応する。AIは ai_engine、白/単色背景とマジックワンドとクロマキーと指定カラー透過は removal_engines の各関数だ。だが出口は全部同じ uint8 (H, W) のアルファなので、UIはモードに応じて呼ぶ関数を切り替えるだけで、その先の処理は共通化できる。第2回以降は、この5つの入口の中身を1つずつ実装していく。

Step 4. QMainWindow と左パネル

ここからPyQtのUIを組む。BgRemovalStudioQMainWindow を継承し、まず画像や作業状態を素直な属性として持つ。ウィンドウを作った直後の __init__ がこの状態宣言だ。

class BgRemovalStudio(QMainWindow):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("Background Removal Studio Pro v2")
        self.resize(1600, 950)

        # --- 画像状態 ---
        self.original_bgr = None      # uint8 (H, W, 3)
        self.original_alpha = None    # uint8 (H, W) or None
        self.base_mask = None         # モード別ベースマスク
        self.preview_rgba = None      # 保存用 BGRA
        ...
        self.setAcceptDrops(True)
        self.batch_thread = None
        self.setup_ui()

コメントに注目してほしい。base_mask は「モード別ベースマスク」、original_alpha は「uint8 (H, W) or None」。状態の1つひとつが、Step 2 で決めた共通通貨(uint8アルファ)で書かれている。UIが持つデータの単位まで、最初に統一しておく。

画面は左に操作パネル、右に画像ビューを2枚(元画像と結果)並べる横並びレイアウトにする。setup_ui の骨格を抜き出すとこうなる。

def setup_ui(self):
    self.setStyleSheet(""" ... """)   # ダークテーマのQSS

    central = QWidget()
    self.setCentralWidget(central)
    root = QHBoxLayout()
    central.setLayout(root)

    # ---------- 左パネル ----------
    left_panel = QFrame()
    left_panel.setMaximumWidth(360)
    left = QVBoxLayout()
    left_panel.setLayout(left)

    self.btn_load = QPushButton("画像を読み込む")
    self.btn_load.clicked.connect(self.load_image_dialog)
    left.addWidget(self.btn_load)
    ...
    # --- モード選択 ---
    mode_group = QGroupBox("除去モード")
    mode_lay = QVBoxLayout()
    self.mode_combo = QComboBox()
    self.mode_combo.addItems([MODE_AI, MODE_FLOOD, MODE_CHROMA,
                              MODE_WAND, MODE_COLOR])
    self.mode_combo.currentIndexChanged.connect(self.on_mode_changed)

左パネルは QVBoxLayout にグループを縦積みしていく。除去モードのコンボ、モード別パラメータのグループ、全モード共通の微調整、ブラシ、プリセット。モードのコンボには Step 3 の5定数をそのまま addItems で流し込む。ここでも定数を1か所で管理している効果が出ていて、モードを足せば選択肢に自動で並ぶ。

左パネルにはスライダーが十数本ある。同じ「ラベル+スライダー+値変更で再計算」を毎回手書きすると長くなるので、小さなヘルパーにまとめた。

def _slider(self, lo, hi, val, layout, label, update=True):
    layout.addWidget(QLabel(label))
    s = QSlider(Qt.Orientation.Horizontal)
    s.setRange(lo, hi)
    s.setValue(val)
    layout.addWidget(s)
    if update:
        s.valueChanged.connect(self.on_param_changed)
    return s

update=True のスライダーは、動かすと on_param_changed が呼ばれて再計算に入る。ブラシのサイズや硬さのように、動かしても即座に再計算したくないスライダーは update=False で配線を外す。この小さな引数1つで、「どのスライダーがパイプラインを起動するか」を宣言的に決められる。

右側の画像エリアは、元画像と結果を左右に並べる。左パネルとの間を QSplitter で区切り、2枚の ImageView を横に置くだけの素直な作りだ。

    # ---------- 画像エリア ----------
    splitter = QSplitter()
    self.original_view = ImageView(interactive=True)   # スポイト/ワンド用
    self.preview_view = ImageView(interactive=True)    # ブラシ用
    splitter.addWidget(self.original_view)
    splitter.addWidget(self.preview_view)
    root.addWidget(splitter)

2枚のビューを分けているのには意味がある。左(元画像)はスポイトやマジックワンドで背景色を指定する入力を受け、右(結果)はブラシで抜き結果を直接修正する。同じ画像を2つの役割で見せることで、「どこを指しているか」と「どう抜けたか」を並べて確認できる。ImageView はホイールで拡大縮小できる自作ビューで、その中身(ズームやクリック座標の通知)は第6回で開く。第1回では、左右2ビューの器を QSplitter で用意するところまでで足りる。

Background Removal Studio のメイン画面全体
Background Removal Studio のメイン画面。左パネルに除去モードの選択・モード別パラメータ・全モード共通の微調整スライダー・ブラシが縦に並び、右側は元画像(左)と背景除去の結果(右)を並べた2ビュー構成。左パネルの各グループが setup_ui で組んだ QGroupBox に対応する。

Step 5. 2段階更新の設計

土台の心臓部がここだ。ユーザーがスライダーを動かすたびに毎回AI推論やFloodFillをやり直していたら、重すぎて操作にならない。そこで更新を2段階に分ける。重い「ベースマスク生成」と、軽い「後処理+合成+表示」を切り離す。

どちらを走らせるかは、動かしたスライダーの種類で決める。on_param_changed が、送り主のスライダーを見て振り分ける。

def on_param_changed(self):
    # モード固有スライダーはベースマスク再生成、共通スライダーは合成のみ
    sender = self.sender()
    if sender in (self.flood_tol, self.hue_center, self.hue_width,
                  self.sat_min, self.wand_tol,
                  self.color_tol, self.color_soft):
        self.rebuild_base_mask()
    else:
        self.update_preview()

色の許容差や色相のように「抜き方そのもの」を変えるスライダーは rebuild_base_mask(重い側)へ。しきい値やぼかしのように「抜いた後の見せ方」を変えるスライダーは update_preview(軽い側)へ。境界のぼかしを微調整するたびにAIを再実行せずに済むのは、この振り分けのおかげだ。

重い側の rebuild_base_mask は、現在のモードを見てエンジンを呼び分け、結果を self.base_mask(共通通貨のアルファ)に格納して、最後に軽い側の update_preview を呼ぶ。骨格はこうだ。

def rebuild_base_mask(self):
    if self.original_bgr is None:
        return
    mode = self.current_mode()
    bgr = self.original_bgr

    if mode == MODE_AI:
        ...              # 推論はスレッドへ(第6回)
        return
    if mode == MODE_FLOOD:
        self.base_mask = flood_base_mask(bgr, { ... })
    elif mode == MODE_CHROMA:
        self.base_mask = chroma_key_mask(bgr, ... )
    elif mode == MODE_WAND:
        self.base_mask = magic_wand_mask(bgr, ... )
    elif mode == MODE_COLOR:
        self.base_mask = color_to_alpha_mask(bgr, ... )
    self.update_preview()

この関数が、Step 3 の「5つの入口」と Step 2 の「1つの通貨」が合流する場所だ。どの枝を通っても、行き着く先は self.base_mask という同じ変数。だから軽い側は、どのエンジンが埋めたマスクかを知らずに処理できる。

軽い側は、後処理と合成を1本のパイプラインで通す。UI用のまとめ関数 compute_final_alpha が、その2工程を順に呼ぶ。

def compute_final_alpha(self):
    """現在の全パラメータで最終アルファを計算する。"""
    processed = postprocess_mask(
        self.base_mask,
        threshold=self.threshold.value(),
        softness=self.softness.value(),
        edge_shift=self.edge_shift.value(),
        noise=self.noise.value(),
        blur=self.blur.value())
    return compose_final_alpha(
        processed, self.erase_layer, self.restore_layer,
        self.original_alpha)

postprocess_mask がスライダー5本ぶんの後処理(しきい値・ソフト幅・エッジ収縮膨張・ノイズ除去・ぼかし)をかけ、compose_final_alpha がそこにブラシの消去・復元レイヤーと、元画像がもともと持っていたアルファを合成する。合成の優先順位も docstring で固定してある。

def compose_final_alpha(processed, erase_layer, restore_layer, original_alpha):
    """後処理済みマスクにブラシレイヤーと既存アルファを合成する。

    final = clip(processed + restore - erase) を AND(既存アルファ)。
    消去 > 復元 > ベースの優先順位(erase を最後に減算)。
    ...
    """

これらの中身(後処理の各段・ブラシ合成・元アルファのAND)は第5回でじっくり開く。第1回で押さえるのは、「重い生成」と「軽い後処理」を分け、送り主のスライダーで振り分ける、という更新の骨格だ。update_previewcompute_final_alpha を呼び、結果を表示モード(完成画像・マスク表示・背景検出)に応じて右ビューへ映すだけの薄い関数になる。完成画像モードでは、透過部分をチェッカーボード(市松模様)の上に重ねて見せる。透明な部分がひと目で分かるようにするためで、この市松の生成は大画像でスライダーが重くならないようサイズ別にキャッシュしている。

Step 6. 完全オフライン方針

このアプリは一切ネットワークにアクセスしない。AI推論も、モデルさえ手元に置けばオフラインで完結する。ai_engine.py の冒頭がその方針を宣言している。

# ai_engine.py
# ONNX ローカル推論(完全オフライン)
# AiSession: onnxruntime のみに依存する純粋部分
# AiInferenceThread: PyQt6 QThread ラッパー(Task 6 で追加)

ここに、Step 1 の分離思想がAIレイヤーにも貫かれているのが見える。AiSession は onnxruntime だけに依存する純粋部分で、PyQtには触れない。PyQtと繋ぐ AiInferenceThread は別に切り出す。推論エンジンも、他の純関数レイヤと同じく画面から独立している。

オフラインで「GPUがあれば速く、無ければ動く」を両立させる仕掛けが、プロバイダの自動フォールバックだ。AiSession の初期化がこれを担う。

class AiSession:
    """1モデル分の ONNX セッション。DirectML 失敗時は CPU に自動フォールバック。"""

    def __init__(self, model_path):
        self.model_path = model_path
        self.cfg = _config_for(model_path)
        self.provider = "CPU"
        try:
            self.sess = ort.InferenceSession(
                model_path, providers=["DmlExecutionProvider",
                                       "CPUExecutionProvider"])
            if "DmlExecutionProvider" in self.sess.get_providers():
                self.provider = "DirectML(GPU)"
        except Exception:
            self.sess = ort.InferenceSession(
                model_path, providers=["CPUExecutionProvider"])

まず DirectML(Windowsの多くのGPUで動く推論プロバイダ)を試し、使えれば providerDirectML(GPU) に、ダメなら例外を捕まえてCPUで開き直す。配布先のPCにGPUがあるか無いかを、利用者が意識せずに済む。モデルは大きいので同梱せず、README の表に沿って利用者が models/ へ手動配置する形にしている(配置の詳細やモデル別の前処理は第4回で扱う)。

オフラインに振り切ると、AdSenseのようなクラウドAPIの従量課金も、画像を外部へ送るプライバシーの懸念も、そもそも発生しない。手元の画像は手元で処理して終わる。

まとめ:第1回の要点

  • 抜き方をUIから切り離す:4ファイルに割り、removal_enginesai_enginemask_pipeline の3枚はPyQtを知らない純関数にする。だから画面なしで pytest が回る。
  • 共通通貨は uint8 アルファ:全エンジンの出力を uint8 (H, W)(255=被写体・0=背景)に統一。入口は5モードあっても、UIが扱う型は1つ。
  • モードは被写体と背景の関係MODE_* の5定数が、表示ラベルと分岐キーを兼ねる。増設はここに1行足すだけ。
  • 2段階更新on_param_changed が送り主のスライダーで振り分け、重い rebuild_base_mask と軽い update_preview を分離。ぼかし調整でAIを再実行しない。
  • 完全オフライン:ONNXローカル推論、DirectML→CPUの自動フォールバック。画像を外へ出さない。

次回(第2回)は、この土台に最初のエンジンを差し込む。AIを使わない古典CVの4エンジンだ。四隅FloodFillで単色背景を外側から削り、HSVのクロマキーでグリーンバックを抜き、Color-to-Alphaでアンチエイリアスの境界まできれいに消す。単純な背景なら、十数秒かかるAIより古典CVのほうが速くて正確な場面が多い。その「BGR画像→uint8アルファ」の純関数を、実コードで1本ずつ組み立てて、白背景の商品写真とグリーンバックが実際に抜けるところまで進める。

この連載について:Background Removal Studio Pro は、設計・実装の各工程でAIを併用して開発した個人プロジェクトです。掲載するコードは実際のソースからの抜粋で、動作は自動テスト(pytest)と実機で検証しています。© 2026 Cooliris(記事本文と掲載コードの権利は作者に帰属します)。


👉 ▶ この連載のハブ(全7回の地図)バイブコーディング開発実例いろんなアプリの作り方

アーティファクトがライブになった代わりに、配れなくなった — MCP コネクタ対応の実際アーティファクトがライブになった代わりに、配れなくなった — MCP コネクタ対応の実際前のページ

Kimi K3 の実費を見る — 表示価格より推論トークンとキャッシュヒットで決まる次のページKimi K3 の実費を見る — 表示価格より推論トークンとキャッシュヒットで決まる

ピックアップ記事

  1. 高精度OCRデスクトップアプリの作り方 — PaddleOCR-VLとPyIns…

  2. 競艇予想AIの作り方 — LightGBMで「当たる順位」を学習させる実装ガイド…

  3. New Eden Intelligence Hub の作り方 — EVE Onl…

  4. Claude Mission Control の作り方 — Tauri+Pyth…

関連記事

  1. ブラウザゲームを自動テスト&デプロイ⑥|puppeteerヘッドレス検証と静的配信【Archipelago Saga制作・完結】

    アプリの作り方

    ブラウザゲームを自動テスト&デプロイ⑥|puppeteerヘッドレス検証と静的配信【Archipel…

    1万行のゲームを手で全クリアせず「動いている」を機械に証明させる。pu…

  2. 都市開発シミュを作る⑥|静的ビルドをWordPressへ公開+連載総括【Aurum City制作・完結】

    アプリの作り方

    都市開発シミュを作る⑥|静的ビルドをWordPressへ公開+連載総括【Aurum City制作・完…

    完成した都市開発シミュを、バックエンド無しの静的ビルドにしてWordP…

  3. JavaScriptでブラウザゲームを作る①|ビルド不要(file://)で動くシーン制の土台と全体設計【Archipelago Saga制作】
  4. Grokで回すXメディア生成パイプラインの作り方 — 検索から画像・音声・動画まで自動化する
  5. Transcription Studio の作り方 — Whisper をローカルで走らせ、エンジンを切り替えて文字起こしする

    アプリの作り方

    Transcription Studio の作り方 — Whisper をローカルで走らせ、エンジン…

    音声/動画をドロップするとローカルの Whisper で文字起こしする…

  6. WorkSpace Explorer の作り方 — 増えた自作アプリを一画面で束ねる Electron 製の開発司令塔

    アプリの作り方

    WorkSpace Explorer の作り方 — 増えた自作アプリを一画面で束ねる Electro…

    作業フォルダのプロジェクトが増えて「どれが何で最後にいつ触ったか」を見…

注目

AIで、ここまで作れる

AIで作った2D RPGを、ブラウザでそのまま遊べます。その「作り方=最後まで完成させる進め方」も実例つきで公開中。

▶ ゲームを遊ぶやり方を読む

PR

ロリポップ!レンタルサーバー(PR)

レンタルサーバ:ロリポップ!(本サイトの稼働環境・PR)

  1. WorkSpace Explorer の作り方 — 増えた自作アプリを一画面で束ねる Electron 製の開発司令塔

    アプリの作り方

    WorkSpace Explorer の作り方 — 増えた自作アプリを一画面で束…
  2. アプリの作り方

    Claude Codeのトークン消費を減らす実践ガイド — 公式ドキュメントで裏…
  3. CodeQL 2.26.0 でプロンプトインジェクション検出 — LLMアプリをCIで守る

    AI・テック動向

    CodeQL 2.26.0 でプロンプトインジェクション検出 — LLMアプリを…
  4. アプリの作り方

    自作MCPサーバーの作り方 — 長い処理はジョブ方式で公開する
  5. 都市開発シミュを作る③|需要・成長・経済の決定論シミュレーション(MVP完成)【Aurum City制作】

    アプリの作り方

    都市開発シミュを作る③|需要・成長・経済の決定論シミュレーション(MVP完成)【…
PAGE TOP

TAG CLOUD

ドラッグで回転・クリックでそのタグの記事一覧へ