こんにちは。
ソーイ株式会社、入社2年目の村上です。
AIをアプリに組み込もうとすると、APIやクラウドサービス(AWSなど)を経由するケースが多く、継続的に利用する場合はコストが気になるところです。
そこで今回は、以前から気になっていた Tauri と、ローカルでLLMを実行できる Ollama を組み合わせて、簡単なニュース収集&要約アプリを作ってみました。
- ニュースの要約
- 重要ポイントの抽出
- 結論の生成
以上3点を機能とし、API経由でLLMを利用するとコストがかかるため、ローカル環境だけでどこまで実用的に使えるのかという点も一つのテーマにしています。
実行環境
今回の開発・実行環境は以下の通りです。
| 項目 | 内容 |
|---|---|
| OS | Windows + WSL |
| 開発環境 | VSCode |
| フレームワーク | Tauri |
| フロントエンド | TypeScript |
| LLM実行環境 | Ollama |
| 使用モデル | gemma3:27b |
| GPU | RTX 5070Ti VRAM 16GB |
| メモリ | 32GB |
Tauriのプロジェクト作成や実装はWSL上で行っています。
一方で、ローカルLLMはPCの性能に依存するため、使用するモデルによって動作速度や安定性が変わります。
今回はVRAM 16GB / メモリ32GBの環境で gemma3:27b を使用しました。
低スペック環境では gemma3:4b や llama3.2:3b など、軽量モデルを選択する方が現実的です。
作ったもの
今回作ったものはボタン一つで米国のニュースを引っ張ってきて要約した形で出力するアプリです。
ニュースを取得し、その要約と結論を出力、その元URLも表示するといったものです。
Tauriの触り心地とOllamaの性能を確かめるうえで簡単に作成したものなので修正、追加できる機能はたくさん存在します。今回はそこまで踏み込みません。
構成
本アプリでは、ニュース取得から要約・分析・可視化までを段階的に処理する構成としています。
主な処理の流れは以下の通りです。
- NewsAPI を利用してニュースを取得
- Ollama を用いてニュース内容を要約・分析
フローチャート
Tauriの導入
Tauriは、Web技術(HTML / CSS / JavaScript)を使ってデスクトップアプリを開発できるフレームワークです。
Electronと同様にWebベースでアプリを構築できますが、内部でブラウザエンジンを同梱するのではなく、OS標準のWebViewを利用する構成になっています。
そのため、アプリサイズやメモリ使用量を抑えやすく、軽量に動作する点が特徴です。
Tauriのプロジェクト作成とインストール
Tauri導入のためには以下2つがダウンロードされている必要があります。
- Node.js (v22以上推奨)
- Rust (TauriはバックエンドがRustなので)
以下コマンドで確認しましょう。
# nodeバージョン確認コマンド
node --version
# Rustバージョン確認コマンド
rustc --version(-Vでも可能)
まずTauriを用いた開発の雛形となるプロジェクトを作成します。
以下コマンドを入力してください。
npm create tauri-app@latest
ダウンロード開始時に以下のような表示が出ます。
ここで名前を入力すると、プロジェクト名として認識し作成していただけます。
ノリでいうとlaravelでいうところのlaravel newと行っていることは同じです。
名前を入力すると、次はidentifierという項目が出てきます。
こちらはアプリの内部的な識別子として存在しています。
今回は個人で使う用なので適当にcom.example.news-appと名付けました。
のちに作ったアプリをexe化するためには、identifierを以下の条件に沿って登録してください。
英字(a-z, A-Z),数字(0-9),ハイフン(-),ドット(.)
作成したものをApp Storeで公開する際にはこの設定を詳細に行う必要がありますが、今回は個人で使用するものなので無視します。
詳しくは以下リンクから情報を確認してください。
最後に実行環境の選択を迫られます。
バックエンドはRust固定なのでフロント側を何で作るのか決めてください。
一般的なWeb技術を使いたい場合はTypeScript / JavaScriptが無難だと考えます。
フロントもRustで組みたい場合はRustを選択。
C#でUI作りたいのであれば.NETを選択してください。
パッケージマネージャーはnpmでいきます。
ここから先驚いたのですがなんとテンプレートを選択できます。
無難なものを選択したい場合はVanillaを選択してください。
次は悩みどころですが型エラーを教えてくれるTypeScriptを今回は選択します。
設定を終えて作成が完了したら最後に起動を確認します。
tauriとつけることで、デスクトップアプリとして開きます。
cd 作成したプロジェクトのパス
npm install
npm run tauri dev
コマンドを実行して以下のようなデスクトップアプリが起動したら確認完了です。
導入の流れとしては以上です。
詳しい流れや解説は以下ドキュメントを参考に
Linux環境に導入する際の注意点
今回はWSL環境で導入の作業を行ったところ、開発用ツールが足りなかったり、Tauriの特性によりウェブレンダリングエンジンが追加で必要になったりしたので記述しておきます。
1. Tauriのデスクトップ実行に必要なパーツが足りないといわれる
Tauriのプロジェクト作成時に下記のエラーが出ました。
Your system is missing dependencies:
webkit2gtk & rsvg2
これはTauriの仕様にかかわる話で、
Tauriは内部的に OSのWebViewを使って画面表示 するようになっています。
そのため、ウェブレンダリングエンジンがないと表示できないよー!という警告を受けている状況ですね。
以下のコマンドでダウンロードしてください。
sudo apt update # 念のため
sudo apt install -y libwebkit2gtk-4.1-dev librsvg2-dev
2. npm installは通ったけどrun devでエラーする
npm run tauri devしたときに以下のエラーが出ました。
error: linker cc not found
ccは C Compilerの略で、ソースコードをオブジェクトファイルに変換する過程で必要なものが足りずエラーしているという状況です。
以下のコマンドで対応可能です。
sudo apt update # 念のため
sudo apt install build-essential
NewsAPIの導入
ニュース取得には NewsAPI を使用します。
NewsAPIでは、top-headlines エンドポイントを利用することで、指定した国の主要ニュースを取得できます。今回は国別のニュースを取得し、その内容をローカルLLMに渡して要約・分析する構成にしています。
まずはNewsAPIに登録し、API Keyを取得します。
取得は簡単で、以下ホームページの Get API Key からメールアドレスとパスワードを入力するだけで発行されます。
ホーム画面でプランがDeveloperになっていることを確認できれば準備完了です。
取得したAPI Keyはコードに直接書かず、環境変数として管理します。
VITE_NEWS_API_KEY=取得したAPIキー
無料で使えるNewsAPIのDeveloperプランは開発・テスト用途向けです。
公開アプリや配布アプリとして利用する場合は、利用規約やプラン条件を確認してください。
Ollamaの導入
ニュースの要約や分析には、ローカルでLLMを実行できる Ollama を使用します。
通常、LLMを利用する場合はAPI経由での利用が一般的ですが、リクエスト回数に応じてコストが発生します。
今回は検証用途ということもあり、 ローカル環境でどこまで実用的な処理が可能か を確認するため、Ollamaを採用しています。
Ollamaは公式サイトからインストール可能です。
Linux / WSL環境の場合は以下コマンドでダウンロードできます。
curl -fsSL https://ollama.com/install.sh | sh
インストール後、以下のコマンドで正常に動作するか確認します。
ollama --version
モデルの取得
Ollamaでは、使用するモデルを個別にダウンロードする必要があります。
今回は文章生成・要約に適したモデルとして gemma3:27b を使用します。
ollama run gemma3:27b
ローカルLLMを作動させるためには個人のPCスペックがかかわってきます。
自分のPCは余裕があるのでgemma3:27bを選択しましたが、環境によっては動作が重くなる可能性も考えられます。
その場合はgemma3:1bなどサイズが小さい軽量なモデルに切り替えを行ってください。
モデルは以下から確認できます。
ダウンロードが完了したらプロンプトの入力を求められるので試しに挨拶してみましょう。
このように返答があったら導入に成功しています。
アプリ実装
導入が済んだので実装を行っていきます。
NewsAPI取得処理実装
先にNewsAPIからニュースを取得する処理を実装します。
今回は米国のニュースを取得するということで、国コードを固定した状態でtop-headlines エンドポイントを使用します。
導入時に環境変数として実装したAPIキーを使用します。
const API_KEY = import.meta.env.VITE_NEWS_API_KEY;
export type Article = {
title: string;
description: string;
url: string;
publishedAt: string;
source: string;
};
export const fetchNews = async (): Promise<Article[]> => {
const res = await fetch(
`https://newsapi.org/v2/top-headlines?country=us&pageSize=5&apiKey=${API_KEY}`
);
if (!res.ok) {
throw new Error("ニュースの取得に失敗しました");
}
const data = await res.json();
return data.articles.map((article: any) => ({
title: article.title,
description: article.description,
url: article.url,
publishedAt: article.publishedAt,
source: article.source?.name ?? "",
}));
};
NewsAPIのレスポンスには多くの情報が含まれますが、今回は以下の項目のみ使用しています。
- タイトル
- 概要
- URL
- 公開日時
- ソース
// フロントから国コードを送信する
const articles = await fetchNews();
Ollama処理の実装
次に、取得したニュースをローカルLLMに渡して、要約・重要ポイント・結論を生成する処理を実装します。
OllamaはローカルでAPIサーバーとして動作しており、以下のエンドポイントにリクエストを送ることでモデルにプロンプトを渡せます。
http://localhost:11434/api/generate
import { buildNewsPrompt } from "../prompts/newsPrompt";
export const summarizeNews = async (
article: NewsArticle
): Promise<string> => {
const response = await fetch(OLLAMA_API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
model: MODEL_NAME,
prompt: buildNewsPrompt(article),
stream: false,
options: {
temperature: 0.2,
},
}),
});
if (!response.ok) {
throw new Error("Ollamaによる要約に失敗しました");
}
const data = await response.json();
return data.response;
};
プロンプトはLLMの出力品質に大きく影響するため、今回ロジックとは分離して管理しています。
あとはUIに組み込んで完成です。
API処理の分離
ここでちょっと工夫を入れました。
ニュース取得、Ollamaの処理はUI側に直接書かず、専用のファイルに分離します。
src/services/newsApi.ts
src/services/ollamaApi.ts
このように分離することで、NewsAPI処理とLLM処理と独立して扱うことができ、デバッグや拡張をしやすくする目的があります。
実行してみた感じ
導入が終わったので動作の確認を行います。
プロンプトの動作検証
動作検証1
簡単にUIとプロンプトを作って出力してみました。
プロンプトは以下の通りです。
以下のニュース記事を日本語で分析してください。
記事の情報だけを根拠にし、不明な点は推測しないでください。
# ニュース
タイトル: ${article.title}
概要: ${article.description}
# 出力形式
以下の形式で出力してください。
要約:
- 3行以内で要約
結論:
- このニュースから読み取れる内容を1行で断言
3記事の要約にかかる時間はおよそ1分前後というところで、なかなかかかるなという認識です。
表示してもらったものはこのような形で表示されます。
確認すると、要約の部分に関してはニュースを読んで重要な内容をピックアップしているなと感じますが、結論の出力に少し疑問が残る形になりました。
試しにChatGPTへ同じプロンプトで聞いてみました。
両者を比較すると、今回の出力には、「要約」と「結論」の間にズレがあります。
本来、要約は記事の重要ポイントを抽出し、その流れを踏まえて結論が導かれるべきです。しかし、今回の要約には クルーズ船での感染事例 といった具体的なトピックが含まれている一方で、 結論はハンタウイルスの一般的な危険性 にフォーカスしています。
その結果、要約で触れている内容と、結論で述べている内容が直接つながっておらず、全体として一貫したストーリーになっていません。
一方で、ChatGPTの出力は、 感染経路、初期症状、治療法の有無
といった記事の本質的な情報を軸に要約しており、そのまま「危険性が高く予防が重要」という結論につながっています。
動作実験2 プロンプトの追加
上記の点を踏まえてプロンプトに以下を追加しました。
重要:
- 要約は、結論を導くための根拠となる情報のみを選んでください
- 要約と結論の内容は必ず一貫させてください
- 要約に含めた内容が、結論に反映されている状態にしてください
- 具体例(事例)は、結論に関係しない場合は含めないでください
その結果が以下です。
要約と結論の一貫性は改善されたものの、出力全体としては依然として具体性に欠け、抽象的な表現が目立ちます。
本来であれば、「危険なウイルスである」といった一般的な表現ではなく、感染経路や症状、治療法の有無といった具体的な情報に基づいて、「何がどのように危険なのか」まで踏み込んで説明されるべきです。しかし今回の出力では、そうした根拠となる情報が十分に含まれておらず、結果として内容の解像度が低くなっています。
このような差は、単なるプロンプトの問題というよりも、モデルが情報の重要度を判断し、適切に具体化する能力の違いに起因していると考えられます。情報を要約する際に「抽象化」に寄りやすく、詳細な説明や因果関係の整理が不十分になりがちな点が出てしまったという印象です。
今回の話はプロンプトを調整すると出力が改善するが、情報の具体性や表現の解像度については、モデルの性能差が顕著に表れてしまうという例を確認できました。
Tauriのメモリ使用量について
Tauriは軽量であることが特徴とされていますが、今回のアプリを実行した際のメモリ使用量は、おおよそ数十MB〜100MB程度でした(環境や起動状況に依存します)。
Electronと比較した厳密な検証は行っていませんが、ElectronはChromiumを内包する構成のため、一般的にメモリ使用量が大きくなりやすいと言われています。
実際、Electronアプリは最低でも 80MB 程度、軽量なものでも 130〜250MB 程度のメモリを使用するケースが多く、アプリによってはさらに大きくなることもあります。
これはElectronが各アプリごとにChromiumエンジンとNode.jsランタイムを同梱する構造になっているためです。
結果として多少軽量化されていると結論付けられますが、こういうものは大規模なものを作った場合にもっと差が広がるものだと認識しています。よって断言するのはまだ早いというのが軽く触ってみた感想になります。
今後の改良点
以下は自分のメモ書きのようなものです。
実装を試してみて再度記事にするかも...?
- 取得したニュースを要約後SQLiteに保存して更新するまで内容を保持する
- 作成したアプリをexe化してタスクスケジューラーに設定。起動時アプリを開くようにする
- マインドマップなどの図を出力させる
- ジャンルを取得させ、ソート機能を追加する
- 評価を追加し、出力結果に対する良し悪しを記録。データから出力の改善を行う(強化学習的なノリ)
まとめ
今回はAPI課金ゼロで、米国ニュース要約デスクトップアプリを作成しました。
実際に使えるものにするには、まだ改良の余地があると感じています。
特にローカルLLMの出力については、精度は問題ないレベルに感じますが、具体性や表現力に物足りなさがあり、少し前の生成AIのような印象を受ける場面もありました。
しかし、多少の精度差はあるものの、文章の要約や結論を手軽に生成できるモデルが、ローカル環境、無料で動作するという点を考えると、十分に実用的であり、それ以上の価値があると感じています。
Tauriを使った開発は今回そこまで大きいものを作ったわけではありませんが、それでも軽量である利点を実感できたと感じています。特に起動後の要素の表示が早く感じました(プラシーボかも)。プロジェクト作成時に環境やテンプレートを柔軟に選択できる点も含め、性能が高く、自由度の高いフレームワークであると感じました。
AIの進歩によってWeb開発は大きく変化していますが、デスクトップアプリの作成も容易になってきたことで、既存のツールを探すだけでなく、自分に必要なツールを自作するという選択肢がより現実的になってきていると感じます。
ローカルLLMについても今後さらに進化していくことが予想されるため、今のうちから積極的に触れていき、活用していきます。
ソースコードについて
今回作成したアプリについては、途中でもお話しした通り、NewsAPIの利用規約との兼ね合いもあり、現時点では公開を見送っています。
APIキー自体は環境変数で管理していますが、配布形態や利用条件によっては規約に抵触する可能性もあるため、慎重に判断しています。
今後、API部分を切り離すなど構成を見直したうえで公開する可能性はあります。
お知らせ
技術ブログを週1〜2本更新中、ソーイをフォローして最新記事をチェック!
https://qiita.com/organizations/sewii












