第三者のOpenAI互換APIでは、公式OpenAIのWeb検索ツールがそのまま使えるとは限りません。[5] 汎用的な構成は、Tavilyで検索し、検索結果とTXTデータをまとめてモデルへ送る方法です。[1][3] Chat Completionsは、同じクライアントやモデルを使うだけでは過去の入力を自動的に記憶しません。[2][6]
研究の答え

Create a landscape editorial hero image for this Studio Global article: 我想让其在分析数据的时候连接网络搜索,帮我改一下代码,另外它会有记忆吗?我之前喂给它的东西会记得吗. Article summary: 可以,但你当前使用的是第三方 OpenAI 兼容接口,无法确定 deepseek v4 flash 是否原生支持联网工具。官方 OpenAI 的内置网页搜索主要通过 Responses API 提供,而第三方 base url 不一定兼容该功能。[5] 因此,下面采用更通用的方案:程序先调用 Tavily 搜索互联网,再把搜索结果连同 TXT 数据一起交给模型分析;Tavily 支持通过 API Key 调用搜索接口并返回结构化结果。[. Topic tags: deepresearch, general web, openai, agents, prompt engineering. Style: premium digital editorial illustration, source-backed research mood, clean composition, high detail, modern web publication hero. Use reference image context only for broad subject, composition, and topical grounding; do not copy the exact image. Avoid: logos, brand marks, copyrighted characters, real person likenesses, fake screenshots, UI text, readable text, watermarks, charts with fake numbers, clickbait thumbnails, icons, and tiny thumbnail layout
第三者のOpenAI互換APIを使っているなら、まず押さえておきたいのは「モデル自身に検索機能があるか」と「API経由で検索ツールを呼び出せるか」は別問題だという点です。
公式OpenAIのWeb検索はResponses APIの組み込みツールとして提供されていますが、独自のbase_urlを指定した互換APIが同じ機能に対応しているとは限りません。5 そのため、今回のコードではモデルに直接検索させるのではなく、プログラム側でTavilyを呼び出し、その結果をTXTデータと一緒にモデルへ渡す構成にするのが安全です。TavilyはAPIキーで認証し、検索結果を構造化データとして取得できます。
1
3
TXTファイルを読み込む
↓
検索キーワードを作る
↓
TavilyでWeb検索
↓
検索結果を整理し、[W1]などの番号を付ける
↓
TXTデータ+Web検索結果をモデルへ送る
↓
分析レポートを保存する
この方法なら、利用しているモデルや互換APIが独自の検索ツールに対応していなくても、検索機能を外付けできます。
提示されたコードには、レスポンスの本文を取得する箇所に修正が必要です。choicesは通常リストなので、次のように先頭要素を指定します。
def call_model(client, system_prompt, user_prompt):
last_error = None
for attempt in range(1, AI_MAX_RETRIES + 1):
try:
response = client.chat.completions.create(
model=MODEL_NAME,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
],
temperature=TEMPERATURE,
)
if not response.choices:
raise RuntimeError("APIレスポンスにchoicesがありません。")
content = response.choices.message.content
if not content or not content.strip():
raise RuntimeError("モデルから空の応答が返されました。")
return content.strip()
except Exception as error:
last_error = error
print(
f"AIリクエスト失敗 {attempt}/{AI_MAX_RETRIES}: "
f"{type(error).__name__}: {error}"
)
if attempt < AI_MAX_RETRIES:
time.sleep(AI_RETRY_WAIT_SECONDS * attempt)
raise RuntimeError(
f"最大リトライ回数に達しました。最後のエラー: {last_error}"
)
APIキーはコードに直接書かず、環境変数に保存するのがおすすめです。
import os
import requests
TAVILY_API_KEY = os.getenv("TAVILY_API_KEY", "")
TAVILY_SEARCH_URL = "https://api.tavily.com/search"
def search_tavily(query):
if not TAVILY_API_KEY:
raise ValueError(
"TAVILY_API_KEYが設定されていません。"
)
response = requests.post(
TAVILY_SEARCH_URL,
headers={
"Authorization": f"Bearer {TAVILY_API_KEY}",
"Content-Type": "application/json",
},
json={
"query": query,
"search_depth": "advanced",
"max_results": 5,
"include_answer": False,
"include_raw_content": False,
"include_images": False,
},
timeout=60,
)
response.raise_for_status()
data = response.json()
return data.get("results", [])
PowerShellでは、たとえば次のように設定します。
$env:AI_API_KEY="あなたのAI APIキー"
$env:AI_BASE_URL="あなたのBase URL"
$env:TAVILY_API_KEY="あなたのTavily APIキー"
検索結果は、モデルが参照しやすいようにURLと要約をまとめます。検索結果をそのまま無制限に渡すのではなく、件数と文字数に上限を設けることが重要です。
def format_web_context(results, max_chars=18000):
parts = []
current_length = 0
for index, result in enumerate(results, start=1):
title = str(result.get("title", "無題")).strip()
url = str(result.get("url", "")).strip()
content = str(result.get("content", "")).strip()[:1200]
block = (
f"[W{index}]\n"
f"タイトル: {title}\n"
f"URL: {url}\n"
f"要約: {content}\n"
)
if parts and current_length + len(block) > max_chars:
break
parts.append(block)
current_length += len(block)
return "\n".join(parts) or "利用可能なWeb検索結果はありません。"
モデルへの指示には、検索情報と現在のデータを混同しないためのルールを明記します。
web_context = format_web_context(search_results)
user_prompt = f"""
以下のTXTデータを分析してください。
重要なルール:
- xまたはXは欠損・無効値であり、0として扱わない。
- 現在のTXTデータに存在しない数値を作らない。
- Web情報を使った文には、対応する[W1]などの引用を付ける。
- TXTデータから直接分かる事実には、Web引用を付けない。
- Web情報と現在のデータが一致しない場合は、その違いを明記する。
- 根拠が足りない場合は「証拠不足」と書く。
================ DATA BEGIN ================
{chunk}
================= DATA END =================
================ WEB BEGIN =================
{web_context}
================= WEB END ==================
"""
現在のchat.completions.create()を呼び出しただけでは、同じclientや同じモデルを使っても、過去のリクエストを自動的に覚えるわけではありません。会話の文脈は、アプリケーション側で保存し、次のリクエストに再び渡す必要があります。2
6
つまり、次のような挙動です。
1回目: データAを送信
2回目: データBだけを送信
結果: モデルは通常、データAを前提にできない
過去の内容も使いたい場合は、次のいずれかを実装します。
messagesを保存して、次回も送信する今回の用途なら、まずは最終レポートをanalysis_memory.txtへ追記する方法で十分です。
from datetime import datetime
from pathlib import Path
MEMORY_FILE = Path("analysis_memory.txt")
ENABLE_PERSISTENT_MEMORY = False
MEMORY_INPUT_MAX_CHARS = 12000
MEMORY_FILE_MAX_CHARS = 50000
def load_memory():
if not ENABLE_PERSISTENT_MEMORY or not MEMORY_FILE.exists():
return ""
text = MEMORY_FILE.read_text(encoding="utf-8")
return text[-MEMORY_INPUT_MAX_CHARS:]
def save_memory(final_report, data_file_name="data.txt"):
if not ENABLE_PERSISTENT_MEMORY:
return
old_text = ""
if MEMORY_FILE.exists():
old_text = MEMORY_FILE.read_text(encoding="utf-8")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
record = (
f"\n\n{'=' * 60}\n"
f"分析日時: {timestamp}\n"
f"データファイル: {data_file_name}\n"
f"{'=' * 60}\n"
f"{final_report}\n"
)
new_text = (old_text + record)[-MEMORY_FILE_MAX_CHARS:]
MEMORY_FILE.write_text(new_text, encoding="utf-8")
分析前にload_memory()で読み込み、プロンプトへ追加します。分析が終わったらsave_memory(final_report)を呼び出します。
memory_text = load_memory()
memory_context = memory_text or "過去の記憶はありません。"
user_prompt = f"""
過去の分析レポートは補助情報としてのみ使用してください。
現在のデータと矛盾する場合は、現在のデータを優先してください。
過去のレポートに含まれる数値を、現在のデータの数値として扱わないでください。
================ MEMORY BEGIN ================
{memory_context}
================= MEMORY END =================
"""
提示コードでは、ENABLE_PERSISTENT_MEMORY = Falseが初期値になっています。記憶を使う場合はTrueに変更します。
ただし、異なるプロジェクトや異なる半導体デバイスを同じ記憶ファイルに保存すると、以前の結論が現在の分析へ混入する可能性があります。プロジェクトごとに記憶ファイルを分けるか、ファイル名にプロジェクト名を含める設計が安全です。
また、現在の実装が保存するのは「過去の最終レポート」であり、元のTXTデータそのものではありません。以前の生データを正確に再利用したい場合は、データベースやベクトル検索を使った文書管理の方が適しています。
結論として、今回の構成では、Tavilyを検索担当、OpenAI互換モデルを分析担当、analysis_memory.txtを簡易的な記憶領域として分担させます。第三者APIの検索ツール対応状況に依存しにくく、既存のTXT分析コードにも組み込みやすい方法です。
Studio Global AI
このページにはソースに裏付けされた回答が含まれており、Studio Global 内で続行できます。
第三者のOpenAI互換APIでは、公式OpenAIのWeb検索ツールがそのまま使えるとは限りません。[5]
第三者のOpenAI互換APIでは、公式OpenAIのWeb検索ツールがそのまま使えるとは限りません。[5] 汎用的な構成は、Tavilyで検索し、検索結果とTXTデータをまとめてモデルへ送る方法です。[1][3]
Chat Completionsは、同じクライアントやモデルを使うだけでは過去の入力を自動的に記憶しません。[2][6]