技術約12分で読めます

APIで呼んでいるQwenに、天気とニュースを調べて答えさせた

いけさん目次

StackChanを音声チャットにつないだ記事の最後で「今の天気は。」と聞いたときは、外の様子はわからないと返された。
このころの音声チャットサーバーは、ModelScopeのAPIでQwenを呼んでいるだけで、調べものをする仕組みは入れていなかった。

10月20日の0時過ぎ、フォトフレームにしたXperiaのタブレットから同じサーバーに「今日の東京の天気は。」と聞くと、やはり「ごめん、今日の東京の天気はわからないんだよね。」と返ってきた。
ところがこのサーバーは、Qwenを呼ぶときにModelScopeの内蔵のWeb検索を有効にする enable_search: True を付けている。検索を付けているのに、なぜ天気がわからないのかを調べた。

調査と変更は、音声チャットサーバーのPCで動いているClaudeに頼んで進めてもらった。この記事の数字はその報告と、一緒に送られてきた試験スクリプトの出力から取っている。

音声チャットサーバーの構成

部分中身
文字起こしQwen3-ASR-0.6B(サーバーのGPUで動かす)
返事を作るLLMQwen3.8-Max(ModelScopeのAPI-Inference経由)
読み上げ別のTTSサーバー(文章から音声を作る)
話し相手StackChan、Xperiaのタブレット

返事は「かな」というキャラクターの話し言葉で、読み上げるので2文以内にしている。
LLMに最初に渡す指示(システムプロンプト)には、現在の日時、StackChanが測ったCO₂・温度・湿度、長期記憶から探した記憶を入れている。

検索は動いていた

まず、内蔵の検索が本当に動いているかを確かめた。
日時だけを書いた短いシステムプロンプトで、enable_search の有無だけを変えて同じ質問を送り、APIの応答に入っている入力のトークン数を比べた。入力のトークン数はLLMに渡した文章の長さを表す。

質問検索ありの入力トークン数検索なしの入力トークン数
今日の東京の天気は。3,42749
今日のニュースは。4,63947
今のドル円は?4,66348

検索ありでは入力が3,000トークン以上増えているので、検索結果はプロンプトに足されている。

それでも天気の返事は「2026年10月20日(今日)の東京のリアルタイム天気データが含まれていない」だった。
システムプロンプトを付けずに聞くと、「知识库(中国語で知識ベース)の情報によると、今日(2026年10月16日)の東京の天気は」と、4日前の日付の天気を答えた。ドル円を聞くと、2022年4月ごろのレートが返ってきた。

つまり、検索で取ってきた中に当日の東京の天気が入っていなかったので、Qwenは「わからない」と答えていた。

キャラクター設定は関係なかった

システムプロンプトには「書かれている内容は具体的に答え、書かれていないことは知らないと言う。」という一文がある。これが検索結果を使わせていない可能性も考えた。
そこで0時3分の質問を、実際のプロンプトのまま、この一文を外したもの、記憶の部分を外したものの3通りで4回ずつ送った。

プロンプト「わからない」と答えた回数
実際のプロンプトのまま4回中4回
「知らないと言う」の一文を外した4回中4回
記憶の部分を外した4回中4回

12回とも「わからない」で、プロンプトの書き方を変えても返事は変わらなかった。

内蔵の検索がどこから取ってきているか

次に、内蔵の検索がどこから情報を取ってきているのか、出典のURLとサイト名を全部書いて、と頼んでみた。
返事に書かれた出典は「中国天气网(中央气象台データ)」で、URLは「提供テキスト内にURLは含まれていません」。LLMが自分で書いた出典なので、本当にそこから取ってきたかは確かめようがない。
返事の中には10月16日から19日までの、日付の違う東京の天気も混ざっていた。

AlibabaのDashScopeには、検索の出典を返させる search_options という指定がある。これがModelScope経由でも使えるかを試した。

指定結果
出典を返す(enable_source)応答に新しい項目は増えず、入力トークン数も指定なしと同じ
引用を付ける(enable_citation)同上
存在しない指定同上。エラーにもならない
検索方式 agent400エラー。このモデルでは使えない
検索方式 max入力トークン数も答えも変わった

agent でエラーが返り、max で結果が変わったので、指定自体はAlibaba側まで届いている。
それでも出典は返ってこず、検索先や地域を選ぶ指定もなかった。

同じ時間帯に日時だけのプロンプトで聞くと、「小雨、最高気温24〜26℃」と10月20日の天気として答えたこともあった。
気象庁の予報では、20日の東京は「くもり 所により夕方まで雨」で最高27℃なので、合っていない。検索方式を max にしたときは、「最高・最低ともに0℃、非常に寒い」という予報まで返ってきた。

内蔵の検索では、当日の天気が入っていないことがあり、入っていても気象庁の予報と合わず、出典も返ってこない。ドル円は古く、ニュースには中国語の訳が混ざっていた。
そこで内蔵の検索はやめて、検索をサーバー側で用意することにした。

Qwenにツールを渡す

関数呼び出し(function calling)では、LLMにツールの名前と説明を渡しておく。LLMは必要だと判断すると、「このツールをこの引数で呼んで」という返事を返す。
LLMが自分で検索するわけではなく、実際に検索するのはサーバー側のPythonのコードで、その結果をもう一度LLMに送ると、それを使って返事を作る。

ModelScopeのQwen3.8-Maxでこれが使えるか、web_search というツールを1つ渡して6つの質問で試した。

質問ツールを呼んだか検索語
今日のニュースは。呼んだ2026年10月20日 ニュース
今のドル円は?呼んだドル円 為替レート 現在
昨日の日本シリーズどうなった?呼んだ日本シリーズ 2026年10月19日 結果
眠くなってきた。呼ばない-
富士山の高さは?呼ばない-
鈴木さんのとこ出かけてくるね。呼ばない-

雑談や知っていることには検索を使わず、新しい情報がいる3問だけでツールを呼んでいた。検索語には日付も入っている。

検索に使うサービス

検索に何を使うかは使える検索APIを調べてから選んだ。
GoogleのCustom Search JSON APIは新規の受け付けを終えていて、既存の利用者も2027年1月1日で使えなくなる。MicrosoftのBing Search APIは2025年8月11日に廃止された。

そこで、追加の費用がかからないddgsから試した。
ddgsはDuckDuckGoなど複数の検索サービスの結果をまとめて取ってくるPythonのライブラリで、このサーバーではAPIキーを設定せずに使っている。問題が出たら、Braveの検索APIに差し替える方針にした。

ddgsで検索して答えさせる

日本・日本語の検索結果を5件取り、タイトルと抜粋とURLをそのままLLMに返した。

質問返事全体の時間
今日のニュースは。今日の詳しいニュースの中身まではよくわからなかった6.7秒
今のドル円は?1ドル158円40銭くらい4.7秒
昨日の日本シリーズどうなった?検索せずに「わからなかった」1.2秒

ドル円は検索結果の日経の抜粋にあった「158.41-158.43(10/19 23:59)」の数字を使っていた。
ニュースのほうは、検索結果がNHKの番組ページやニュースサイトの一覧ページばかりで、中身がなかった。

日本シリーズはツールを呼ばずに答えていた。
システムプロンプトに「最新の出来事・ニュース・相場・試合結果など知識に無いことは、推測せずweb_searchで調べてから答える」と書き加えると、検索するようになった。
返事は「今年の日本シリーズは10月24日開幕で、昨日はまだ試合が始まってないよ!」で、検索結果のプロ野球データのページと合っていた。

ニュースはddgsのニュース検索もあわせて使うようにした。
すると今度は、小林薫さん主演のドラマ「深夜食堂6」が7年ぶりに放送開始される日だ、と答えた。検索結果の先頭がこのドラマの放送開始を告知する記事で、今日のニュースとしては外れている。

天気はツールを呼ばずに作り話をした

天気には、気象庁の天気予報のページが読み込んでいる予報のJSONを使うことにした。都道府県ごとに、今日・明日・明後日の予報と1週間分の予報が入っている。
weather というツールを作り、都道府県を指定して予報を取れるようにした。

ところが「今日の東京の天気は。」と聞くと、ツールを呼ばずに「今日の東京は晴れで、最高気温は22度の予想だよ。降水確率は0パーセ」と答えた。繰り返し聞いても同じだった。
20日の東京の予報は「くもり 所により夕方まで雨」なので、これは作り話だ。

同じ条件で聞き方だけを変えると、次のようになった。

質問ツールを呼んだか
今日の東京の天気は。呼ばない
今日の東京の天気は?呼んだ
東京の今日の天気教えて呼んだ
今日の大阪の天気は。呼んだ
明日の大阪は雨降る?呼んだ

句点で終わる「今日の東京の天気は。」だけがツールを呼ばなかった。この試験では、句点そのものが原因かまでは確かめていない。

ModelScope側がこっそり検索している可能性も考えて、enable_search: False を明示した場合とも比べた。入力トークン数はどちらも1,176で変わらず、検索結果をプロンプトに足す形の検索はしていなかった。

ツールを呼ぶかどうかをLLMの判断だけに任せると、いちばんよく聞く質問で外れる。
そこで東京の予報のJSONは毎回システムプロンプトに入れておくことにした。ほかの都道府県はこれまでどおり weather で取る。
気象庁の予報は5時・11時・17時に更新されるので、取得した予報は30分の間は取り直さずに使い回す。

2回目の問い合わせで返事が空になる

ドル円を聞くと、返事が空のまま終わることがあった。

1回目の問い合わせでQwenが検索を呼び、その結果を送って2回目を問い合わせると、2回目でもまた検索を呼ぼうとしていた。
サーバー側は2回目の検索の呼び出しを処理していなかったので、何も出力せずに終わっていた。

そこで検索を繰り返せるようにして、1回の返事で検索は最大2回までにした。それでも答えが出ないときは、「ごめんね、うまく調べられなかった。」と返す。
検索は1日100回までに制限し、検索語、取ってきたURL、エラーを search.log に残すようにした。返事がおかしいときに、何をもとに答えたのかを後から調べられる。

タブレットから聞き直す

1時21分、タブレットから同じ「今日の東京の天気は。」を聞くと、今度は答えが返ってきた。

今日の東京はくもりで、所により夜に雨が降る見込みだよ。気温は20度から27度くらいで、夕方までは雨の可能性もあるから傘があると安心だね。

このとき取得した気象庁の東京地方の予報のJSONは19日17時発表の分で、このURLの中身は発表のたびに上書きされる。
それと比べると、20日の「くもり 所により夕方まで雨」と気温の20℃・27℃は合っていたが、「所により夜に雨」は19日の天気の文だった。

予報のJSONには、発表された時点から先の予報が入っている。
1時台はまだ次の5時の発表の前なので、先頭には19日の分が残っていて、その19日夜の文と20日の文が混ざっていた。

まず、予報の値と日時の対応を読むこと、今日より前の日の値は使わないことをプロンプトに書いた。
これで「今日の東京の天気は」「今日傘いるかな」は4回とも正しくなったが、「今夜は雨降る?」には4回とも19日夜の文で答えた。

そこで、期間が終わった予報を消してからLLMに送るようにした。
天気や降水確率のような時間帯ごとの予報は、次の時間帯が始まった時点で消す。気温は朝の最低と日中の最高という時点の値なので、日付が過ぎたものだけ消す。

1時台に「今夜は雨降る?」答えた予報
消す前4回とも19日夜の予報
消した後4回とも20日の予報

週間予報には、文章の天気がなく数字の天気コードしか入っていない日がある。
プロンプトには、コードの百の位を主な天気(1が晴れ、2がくもり、3が雨、4が雪)として扱うという説明と、今日から1週間分の日付と曜日を書き加えた。

「伝えるね」で終わったニュース

1時40分、タブレットから「ヤフーニュースから最新のニュース一覧取ってきて」と頼んだ。文字起こしでは「一覧」が「一見」になっている。
返事は「ヤフーニュースの最新記事は取得できないので、代わりに最新のニュースを検索して伝えるね。」の1文だけだった。

Qwenはサイトを開けないと断る途中で「検索して伝えるね」と言い、検索を呼ばないまま返事を終えていた。
同じ質問を何度か送ると、検索して記事の話題を2つ答えることもあれば、「最新記事一覧を取得する機能はないから、わからないな」と断ることもあった。

見出しはRSSから取る

RSSは、ニュースサイトなどが記事の見出しやURLの一覧を決まった形式で配信しているデータだ。
ニュースの一覧を聞かれたときに検索で探すのはやめて、news というツールを作り、Yahoo!ニュースとNHKのRSSを取ってくるようにした。

取得元ジャンル中身
Yahoo!ニュース主要・国内・国際・経済・エンタメ・スポーツ・IT・科学・地域見出し
NHK主要・社会・暮らし・科学文化・政治・経済・国際・スポーツ見出しと要約。新しい順に15件まで

RSSは0.3〜0.7秒で取れて、ニュースの返事は全体で約4秒だった。
特定の話題を詳しく聞かれたときは、これまでどおり web_search を使う。

ツールを呼ばずに古いニュースを答える

RSSを入れた後も、「何かニュースある?」と聞くと、3回中2回はツールを呼ばずに答えた。
話していたのは2021年のノーベル物理学賞などの過去のニュースで、それを今日のこととして答えていた。「今日のニュースは。」でも、ツールを呼ばずに過去のニュースを答えることがあった。

そこで、聞き取った文に「ニュース」が入っているときは、最初の問い合わせからツールの呼び出しを必須にした。APIの tool_choice="required" を付けると、LLMは必ずツールを呼ぶ返事を返す。
ほかの質問でも、返事の最後の文が「調べるね」「検索して伝えるね」のような予告で終わっていて、ツールを呼んでいなければ、呼び出しを必須にしてもう1回問い合わせる。
「検索して調べられるよ」のような申し出は、予告とは別に扱う。

ニュースの返事は全部読み上げると長いので、見出しは2本まで、全体で70字くらいまでにした。

質問返事(1文目までの時間)
何かニュースある?Yahoo!ニュースの主要から見出し2本(4.2秒)
スポーツのニュースある?Yahoo!ニュースのスポーツから見出し2本(3.6秒)
NHKのニュース教えてNHKの主要から見出し2本(3.8秒)
ヤフーニュースから最新のニュース一覧取ってきて。Yahoo!ニュースの主要から見出し2本(3.8秒)

作り直した後の返事

質問結果
今日の東京の天気、傘、週末、明日の大阪気象庁の予報どおり
昨日の日本シリーズどうなった?試合はなく、10月24日にDeNA対ソフトバンクで開幕
ニュースの一覧RSSの見出しから2本
今のドル円は?3回中1回だけ値(158円台前半)を答えた
富士山の高さ、眠くなってきた検索せずに答えた

1文目までの時間は検索する返事で5〜8秒、検索しない返事で1.5〜2秒だった。東京の天気はプロンプトに入れてあるので、検索せずに答えた。

ドル円は、検索結果の抜粋にレートが入っていないことが多かった。
ddgsでは、ニュースと通常の検索のどちらかが「結果なし」のエラーを返すことがときどきあり、そのときはもう片方の結果で答えていた。