◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Beta が 09/15 から。stable の案内はまだなく、Expo Go でのプロジェクト実行は 09/03 からログインが必要です●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Beta が 09/15 から。stable の案内はまだなく、Expo Go でのプロジェクト実行は 09/03 からログインが必要です●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/開発ツール
⬡ 開発ツール/2026-05-06初級

Rorkで作るAIアプリにオフラインエッジモデルを組み込む — ネット接続不要のAI機能実装入門

Rorkで開発するアプリに、ネット接続なしで動くエッジAIを組み込む入門です。オフラインAIを使うべきシチュエーション、Ollamaなどローカルサーバーへの接続、接続できない場合のフォールバック、シンプルなチャット実装までを初心者向けに紹介します。

Rork585エッジAI4オフラインAIローカルLLMOllamaモバイルアプリ12AIアプリ開発7

Rorkで「AIチャット機能付きアプリを作りたい」というとき、最初に思い浮かぶのはOpenAIやGeminiのAPIでしょう。でも、「地下鉄の中でも使える」「センシティブなデータを送信したくない」「APIコストをゼロにしたい」という要件が加わると、話が変わります。

こういったケースで選択肢になるのが、デバイス上またはローカルサーバー上で動く「エッジAI」です。RorkアプリにOllamaベースのローカルAI機能を組み込む基本的な考え方と実装のポイントを順番にご紹介します。

エッジAIを使うべきシチュエーション

全てのAIアプリにエッジAIが適しているわけではありません。どんなケースで検討すべきかを整理します。

向いているケース:

  • 日記・メモアプリなど、プライバシーを最優先するアプリ
  • 登山・キャンプガイドなど、電波が届かない場所で使うアプリ
  • 医療・法律など、データを外部に送信できないアプリ
  • APIコストを抑えたい無料アプリ

向いていないケース:

  • 最新のニュースや時事情報が必要なアプリ
  • 高い創造性や汎用的な知識が必要なアプリ
  • デバイスのスペックが低い環境をターゲットにするアプリ

Rorkアプリからローカルサーバーへの接続

最も現実的な構成は「デバイス内ではなく、同じLAN内のPCでOllamaを動かし、アプリからHTTPで接続する」アプローチです。デバイスのスペック制限を回避しながら、ネット環境なしで動作します。

Rorkアプリ側からは、OpenAI互換のAPIエンドポイントとして呼び出せます:

// Rorkアプリ内でのAPI呼び出し(Expo/React Native)
const callLocalAI = async (userMessage) => {
  // OllamaはOpenAI互換のエンドポイントも提供
  const response = await fetch('http://192.168.1.10:11434/api/generate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      model: 'gemma4:9b',
      prompt: userMessage,
      stream: false
    })
  });
  
  const data = await response.json();
  return data.response;
};

192.168.1.10 はOllamaを動かしているPCのローカルIPアドレスに変更してください。同じWiFiに繋がっていれば、スマートフォンからアクセスできます。

接続できない場合のフォールバック実装

ローカルサーバーに接続できない状況(外出先など)のためのフォールバックも実装しておきましょう:

// フォールバックありのAI呼び出し
const callAI = async (userMessage) => {
  // まずローカルを試みる
  try {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 3000); // 3秒でタイムアウト
    
    const response = await fetch('http://192.168.1.10:11434/api/generate', {
      method: 'POST',
      signal: controller.signal,
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        model: 'gemma4:9b',
        prompt: userMessage,
        stream: false
      })
    });
    
    clearTimeout(timeoutId);
    const data = await response.json();
    return { text: data.response, source: 'local' };
    
  } catch (localError) {
    // ローカルが使えない場合はクラウドAPIにフォールバック
    console.log('ローカルAI接続失敗、クラウドにフォールバック');
    return await callCloudAI(userMessage);
  }
};

Rorkでのシンプルなチャット実装

Rorkを使ってこの仕組みを組み込んだ基本的なチャット画面の構成:

// ChatScreen.js(Rorkで生成されるコンポーネントに組み込む)
import React, { useState } from 'react';
import { View, TextInput, FlatList, TouchableOpacity, Text } from 'react-native';
 
export default function ChatScreen() {
  const [messages, setMessages] = useState([]);
  const [input, setInput] = useState('');
  const [loading, setLoading] = useState(false);
 
  const sendMessage = async () => {
    if (!input.trim() || loading) return;
    
    const userMessage = { id: Date.now(), role: 'user', text: input };
    setMessages(prev => [...prev, userMessage]);
    setInput('');
    setLoading(true);
    
    try {
      const response = await callAI(input);
      const aiMessage = {
        id: Date.now() + 1,
        role: 'assistant',
        text: response.text,
        isLocal: response.source === 'local'
      };
      setMessages(prev => [...prev, aiMessage]);
    } catch (error) {
      console.error('AI呼び出しエラー:', error);
    } finally {
      setLoading(false);
    }
  };
 
  return (
    <View style={{ flex: 1, padding: 16 }}>
      <FlatList
        data={messages}
        keyExtractor={item => item.id.toString()}
        renderItem={({ item }) => (
          <View style={{
            alignSelf: item.role === 'user' ? 'flex-end' : 'flex-start',
            backgroundColor: item.role === 'user' ? '#007AFF' : '#E5E5E5',
            borderRadius: 12,
            padding: 12,
            marginBottom: 8,
            maxWidth: '80%'
          }}>
            <Text style={{ color: item.role === 'user' ? 'white' : 'black' }}>
              {item.text}
            </Text>
            {item.isLocal && (
              <Text style={{ fontSize: 10, color: '#666', marginTop: 4 }}>
                🔒 オフライン処理
              </Text>
            )}
          </View>
        )}
      />
      
      <View style={{ flexDirection: 'row', marginTop: 8 }}>
        <TextInput
          value={input}
          onChangeText={setInput}
          placeholder="メッセージを入力..."
          style={{ flex: 1, borderWidth: 1, borderRadius: 8, padding: 8 }}
        />
        <TouchableOpacity
          onPress={sendMessage}
          disabled={loading}
          style={{ marginLeft: 8, backgroundColor: '#007AFF', borderRadius: 8, padding: 8 }}
        >
          <Text style={{ color: 'white' }}>送信</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
}

まずは試してみることから

Rorkで「Ollamaに繋がるチャットアプリ」を作ることは、難しくありません。PCでOllamaを起動し、Rorkで生成したアプリのAPIエンドポイントをローカルIPに向けるだけです。

プライバシー重視のアプリや、オフライン対応が必要なアプリの開発を考えているなら、まず実際に動かしてみることをお勧めします。より高度な実装(完全オフラインのオンデバイスAI、ファインチューニングモデルの配布、バックグラウンド推論)については、プレミアム記事で詳しく解説しています。

12年の個人開発で見えてきたこと

リリース直後にチェックしているもの

  • クラッシュレポートの上位エラーを24時間ごとに確認しているか
  • AdMob/StoreKit の収益ダッシュボードに異常検知が出ていないか
  • ユーザーレビューに技術的な指摘が含まれていないか
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

⬡ 開発ツール2026-10-15
EXPO_PUBLIC_ の値が、開発では読めて本番だけ undefined になる書き方を、変換結果で見分けます
API の URL が開発中は読めるのに、ビルドした途端に undefined になる。原因は process.env の書き方でした。babel-preset-expo の変換結果を開発・本番で並べ、置き換わる書き方と残ってしまう書き方を表にしました。
⬡ 開発ツール2026-10-13
リリース前に、書き出した Expo バンドルの中から自分の鍵を探してみる
ソースに鍵がなくても、出来上がったアプリの中に鍵が残ることがあります。Expo SDK 57 で実際に expo export を実行し、バンドルの中身を grep と 60 行ほどの Node スクリプトで調べて、見つかる鍵と見つからない鍵の違いを確かめました。
⬡ 開発ツール2026-10-07
日本語の見出しだけ太字にならない — Android で fontWeight が効かないときの切り分けと直し方
Rork で作ったアプリで、iPhone では太字なのに Android では日本語の見出しが細いままになる症状の切り分け記事です。原因の見分け方、fontFamily を重みごとに分ける直し方、確認用コードを載せています。