ローカルモデルでDjango予約サイトを生成

Qwen3.5-122B-A10BのQ5_K_Mを使い、Django 5の旅行予約サイトを生成しました。クラウドAPIは使っていません。MCPエージェントでファイルを操作し、初期生成後に手修正と2回の機能追加を行いました。Webアプリ・業務システム開発で、ローカルLLMを使える範囲を確認します。

量子化MoEのローカル推論で、モデル、ビュー、テンプレート、管理画面を揃えられるかを見ました。

ローカル推論環境

項目内容
モデルQwen3.5-122B-A10B(MoE、アクティブ 10B / 総パラメータ 122B)
量子化Q5_K_M(GGUF、3分割シャード)
推論エンジンik_llama.cpp(OpenAI 互換 API サーバー)
MCP ツール(自作)ctree(コードシンボル解析)、pathfinder(パス解決)
MCP ツール(OSS)serena(セマンティックコード操作)、filesystem(ファイル読み書き)、ripgrep(検索)
コンテキスト使用量約 77K プロンプトトークン

推論はすべてローカルです。serenaでシンボル、filesystemで読み書き、ripgrepで検索、ctreeで構造、pathfinderでパスを扱います。外部APIとトークン課金はありません。

検証の意図

クラウドではなくローカルで試す理由

クラウドのClaude SonnetやGPT-4oを使う方法には、次の条件があります。

  • 繰り返すほどトークン費用が増える
  • コードを外部へ送る
  • APIの稼働とレート制限に依存する

122B MoEをQ5_K_Mに量子化し、ik_llama.cpp上でコーディングエージェントに使えるかを検証しました。クラウド同等の品質を確認したものではありません。

ワンショット生成のアプローチ

仕様を一度に渡して全ファイルを生成します。モデルとビュー、フォームとテンプレート、シードデータ、管理画面の対応を揃える狙いです。

仕様設計

技術スタック

要素選定理由
バックエンドDjango 5.xPython 3.13 互換、ORM 完備、管理画面自動生成
フロントエンド JSAlpine.js v3 (CDN)Django テンプレート内で動作、ビルド不要
CSSTailwind CSSMaterial Design 3 インスパイアのユーティリティファースト
パッケージ管理uv高速な Python パッケージマネージャー
データベースSQLite開発環境のみ、設定不要

初期ドメインモデル

初期仕様の3モデルです。

  TravelPackage  1 ──── N  Tour
Tour           1 ──── N  Reservation
  

TravelPackage(旅行商品):

  • タイトル、スラグ、リージョン、期間、画像 URL
  • is_published フラグで公開制御
  • min_price プロパティで最安ツアー価格を動的取得

Tour(出発日程):

  • 特定の出発日・帰着日・価格・残席数
  • ステータス管理(available / soldout / cancelled)
  • is_reservable プロパティで予約可否判定

Reservation(予約):

  • 顧客情報(氏名、メール、電話、人数、備考)
  • Tour への FK で紐付け
  • 決済なし(DB 保存のみ)

ビジネスルール

予約と公開の条件です。

  1. is_published=True のパッケージのみフロントエンドに表示
  2. status="available" のツアーのみ予約可能
  3. 価格表示はパッケージ内の最安ツアー価格(Min 集約)
  4. 予約フローはフォーム → 確認プレビュー → 完了の3ステップ
  5. 確認画面での POST で初めて DB に保存(セッション経由)

UI ワイヤーフレーム

ASCIIアートのレイアウトに、Tailwindクラスの指定を組み合わせました。

  ┌─────────────────────────────────────────────────┐
│  HERO SECTION: bg-gradient-to-r from-blue-600   │
│  [SVG airplane animation flying across]         │
│  "Discover Your Next Adventure"                 │
│  [ Browse All Packages → ]                      │
├─────────────────────────────────────────────────┤
│  FEATURED PACKAGES (card grid)                  │
│  ┌──────┐  ┌──────┐  ┌──────┐                  │
│  │Card 1│  │Card 2│  │Card 3│                   │
│  └──────┘  └──────┘  └──────┘                  │
└─────────────────────────────────────────────────┘
  

Material Design 3 デザイン指定

役割Tailwind クラス用途
Primarybg-blue-600ボタン、リンク、アクティブ状態
Surfacebg-whiteカード、モーダル、フォーム背景
Elevation Level 2shadow-mdメインカード、ヘッダー
Border Radiusrounded-2xlカード、rounded-xl ボタン

ホバーエフェクト:hover:shadow-lg hover:-translate-y-1 transition-all duration-200

ローカル LLM が生成したコード

Alpine.js フィルタリング

Alpine.jsの指定に沿ったクライアント側フィルターです。

  x-data="{
    region: '',
    maxPrice: ''
}"
  
  x-show="(region === '' || $el.dataset.region === region) &&
        (maxPrice === '' || parseInt($el.dataset.minPrice) <= parseInt(maxPrice))"
  

サーバー側APIを使わない処理です。

セッションベース予約フロー

3段階の予約フローを生成しました。

  1. フォーム入力(/reserve/<tour_id>/):バリデーション後、データをセッションに保存
  2. 確認プレビュー(/reserve/<tour_id>/confirm/):セッションから読み出し、サマリー表示
  3. 完了(/reserve/success/):確認 POST で DB 保存、セッションクリア
  # ReservationCreateView: POST でセッションに保存
request.session['reservation_data'] = form.cleaned_data
return redirect('reservation_confirm', tour_id=tour.id)

# ReservationConfirmView: POST で DB 保存
data = request.session.pop('reservation_data')
Reservation.objects.create(tour=tour, **data)
return redirect('reservation_success')
  

Django 管理画面

モデルに合わせ、インライン編集を含むadmin設定を生成しました。

  class TourInline(admin.TabularInline):
    model = Tour
    extra = 1

@admin.register(TravelPackage)
class TravelPackageAdmin(admin.ModelAdmin):
    list_display = ["title", "region", "duration_days", "is_published"]
    list_editable = ["is_published"]
    prepopulated_fields = {"slug": ("title",)}
    inlines = [TourInline]
  

シードデータ

seed_demoで5パッケージ、計14ツアーを生成します。地域別フィルターを確認するデータです。

エージェントによる段階的拡張

初期生成を手修正した後、2回の追加指示を出しました。既存コードを読み、新モデルとアプリを追加しています。各段階で手修正も必要でした。

追加アプリ主なモデルエージェントの作業内容
shopShop(店舗情報)models.py に Shop モデル追加、admin 登録、テンプレート作成
reviewsReview, Rating, ReviewPhotoツアーレビュー機能一式、承認フロー付き
searchSearchIndex, TrendingKeyword検索インデックスとトレンド管理
accountsUser, UserProfile, UserActivityユーザー認証とプロフィール管理

tours/models.pyのFKを確認して拡張しました。引用符のエスケープ、import漏れ、テンプレート間の不整合は人が直しています。作業量は生成8割、手修正2割ほどの印象でした。

生成結果

画面キャプチャ

トップページ:ヒーローセクションと Featured Products カードグリッド
トップページ — ヒーローセクション + Featured Products(ローカル推論で生成)
トップページ下部:ショップ情報と Available Tours セクション
エージェントが追加した Shop Locations + Available Tours セクション
パッケージ詳細ページ:Takayama & Japanese Alps のツアー一覧と価格ソート
パッケージ詳細ページ — ツアー一覧・価格表示・ソート機能
Django 管理画面:6アプリのモデル一覧
Django 管理画面 — Tours + Shop + Reviews + Search + Accounts の全モデル

初期生成ファイル構成

ファイル内容
tours/models.py3モデル + プロパティ + バリデーション
tours/admin.py3モデルの管理画面設定 + インライン
tours/forms.pyReservationForm (ModelForm)
tours/views.py6ビュー(Home, List, Detail, Form, Confirm, Success)
tours/urls.pyURL パターン定義
tours/management/commands/seed_demo.pyシードデータ
tours/templatetags/tour_filters.pyカスタムフィルター(multiply)
tours/templates/tours/*.html7テンプレート
static/css/custom.cssSVG アニメーション
config/settings.pyINSTALLED_APPS 追加

動作確認

手修正後に確認できた動作です。

  • マイグレーション後にサーバーが起動
  • シード投入でパッケージを表示
  • Alpine.jsのフィルターとソートが動作
  • 入力→確認→完了の予約フローが完走
  • 管理画面のインライン編集が動作

手修正が必要だった主な箇所:

  • パッケージ詳細ページのテンプレートで Django テンプレートタグの引用符エスケープ({{ tour.end_date|date:"M j, Y" }} が生文字列として表示)
  • 一部のインポート漏れや型の不整合
  • 機能拡張時のテンプレート間の整合性

SVG アニメーション

SVG + CSSで、飛行機の8秒ループと雲の15秒ループを生成しました。

生成と修正で分かったこと

1. ローカル MoE モデルのコーディング能力

Qwen3.5-122B-A10B(Q5_K_M)は、Djangoのモデル、ビュー、テンプレート、adminを生成し、77Kトークンの文脈で拡張できました。手修正は必要で、クラウド同等とまでは評価していません。

2. 仕様書の詳細度が成否を分ける

今回、仕様で役立った項目です。

  • フィールドの型、制約、初期値
  • URLとビューの対応
  • ASCIIアートのUI
  • Tailwindクラスの指定

曖昧な仕様は出力のぶれにつながる印象でした。クラウドとの定量比較はしていません。

3. 複数 MCP サーバーの連携

ctree(シンボル解析)、pathfinder(パス解決)、serena(意味に基づく操作)、filesystem、ripgrepを組み合わせました。エージェントはシンボル検索、読み書き、構造確認を実行できました。

4. テンプレートのエスケープ問題

{{ value|date:"M j, Y" }}のような引用符付きフィルター引数に、エスケープの誤りがありました。クラウドでも起こり得ますが、今回は修正対象として目立ちました。