はじめに
前回の記事では、OCI Enterprise AI Agents と Responses API を使って、MCP サーバー(DeepWiki)を呼び出すエージェント「GitHub DeepInsight」を作りました。たった数十行の Python コードで、サービス側がエージェントループを回してくれる体験を味わうことができました。今回は、「GitHub DeepInsight」にトレーシングの仕組みを追加してサービス側がエージェントループを回してくれている様子を見てみます。
エージェント・ループと Responses API
ところで、なぜループに注目しているかというとエージェントの本質は、推論とツール呼び出しのループだからです。
絵にかくとこんな感じです。
- クライアントから初期コンテキスト(システムプロンプトとユーザーの要求)が、AIモデルに送信される
- AI モデルは、ユーザーの要求に応えるために適切なツールを選択する
- ツールは、外部環境とインタラクションする
- ツールと外部環境のインタラクションの結果が返る(外部環境からの情報取得である場合もあれば、外部環境を操作した結果ということもある)
- ツールの応答が AI モデルへ戻される
- モデルは、ユーザーの要求の応えるための情報が十分かどうか判断して不十分であれば、再度、ツールを選択する
- 以後、モデルがユーザーの要求の応えるための情報が十分揃ったと判断するまで繰り返す
- 情報が十分揃ったら回答を生成してクライアントへ最終応答を返す
これが、エージェントの本質、エージェント・ループですね。
エージェント・アプリケーションを開発していると、Planning、Execution、Clarification/Approval、Verification、Function Calling、Sub-Agentなどの様々なループを作ることになります。
これを毎回、開発者が作っていては大変です。そこで、API 側でループを回してくれる Responses API の登場です。
つまり、こんな感じです。
コードも短くなりますが往復遅延が少なくなるのも大きなメリットです。
シーケンス図で見るとこの違いがより明確になります。
Chat Completions API の場合(従来)
エージェント・アプリケーションがループを管理し、ツール呼び出しのたびに API との往復が発生します。
Responses API の場合
エージェント・アプリケーションは 1 回 API を呼ぶだけで、ループはサービス側で完結します。
Chat Completions API では、ツール呼び出し 3 回のループなら API との往復が計 4 回(ツール呼び出し 3 回 + 最終推論 1 回)発生しますが、Responses API ではわずか 1 往復で済みます。
Responses API は、ループをAPI(サービス側)で回してくれるだけではなくて、会話履歴の管理もしてくれます。
上のコードの中で、messages にAPIやツールの呼び出しの都度、応答メッセージを append している部分を API 側で管理してくれます。
前回のコードでも、サービス側がエージェント・ループを回してくれているのは、OCI Enterprise AI Agents の Agentic API が OpenAI Responses API/Open Responses API 準拠だからでした。
しかし、実際にエージェントを作っていると、こんな疑問が出てきます。
- 1 回の API 呼び出しの裏で、MCP ツールは何回呼ばれたのか?
- 各ステップにどれくらい時間がかかっているのか?
- トークン消費量はどのくらいか?
- 期待どおりの順序でツールが呼ばれているか?
Responses API はエージェントループをサービス側で実行してくれるため、開発者のコードはシンプルになります。その反面、ループの内部で何が起きているかは API の戻り値だけでは把握しにくくなります。
そこで活躍するのが LangFuse や LangSmith によるトレーシングです。この記事では、前回のサンプルコードに LangSmith のトレーシングを追加して、エージェントの動作を可視化する方法を試してみます。
OCI Enterprise AI Agents の可観測性
本題の LangSmith 連携に入る前に、OCI Enterprise AI Agents が提供する可観測性の仕組みを整理しておきます。
Responses API の構造化トレース
Responses API の output 配列には、エージェントループの各ステップが構造化された形で含まれています。
| output タイプ | 内容 |
|---|---|
message |
アシスタントの応答テキスト |
mcp_call |
Remote MCP ツールの呼び出しと結果 |
mcp_list_tools |
MCP サーバの利用可能ツール一覧 |
file_search_call |
File Search (Vector Store) の呼び出しとヒット |
reasoning |
思考プロセスサマリ(Reasoning 有効時) |
function_call_output |
Function Tool 実行結果(クライアント返却) |
つまり、レスポンスの output を見れば、ループ内で何が起きたかをプログラム的に取得できます。しかし、これだけでは時系列の可視化やプロジェクト横断での分析は難しいため、外部の可観測性ツールとの連携が重要になります。
サードパーティの可観測性ツールとの連携
OCI Enterprise AI Agents は OpenAI Responses API 互換であるため、OpenAI SDK のエコシステムで利用できる可観測性ツールがそのまま使えます。代表的なものとして以下があります。
- LangSmith ー LangChain 社が提供するトレーシング・評価プラットフォーム
- LangFuse ー オープンソースの LLM 可観測性プラットフォーム
- OpenTelemetry ベースのツール
この記事では LangSmith を使った連携方法を紹介します。
LangSmith とは
LangSmith は、LLM アプリケーションの開発・テスト・監視を支援するプラットフォームです。主な機能として、トレーシング(LLM 呼び出しやツール実行の記録・可視化)、評価(出力品質の自動評価)、モニタリング(本番環境でのパフォーマンス監視)があります。
この記事ではトレーシング機能に焦点を当てます。LangSmith のトレーシングでは、エージェントの 1 回の実行(ラン)の中で発生した LLM 呼び出し、ツール呼び出し、それぞれの入出力、所要時間、トークン消費量などが、ネストされたスパン(span)として階層的に記録されます。
LangSmith トレーシングの設定方法
LangSmith のトレーシングを有効にするのはとても簡単です。たった 3 ステップで完了します。
ステップ 1. 環境変数を設定する
.env ファイルまたは環境変数に、以下を追加します。
# LangSmith トレーシング
LANGSMITH_TRACING=true
LANGSMITH_API_KEY=lsv2_pt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
LANGSMITH_PROJECT=my-deepwiki-agent
| 環境変数 | 説明 |
|---|---|
LANGSMITH_TRACING |
true にするとトレースの送信が有効になります |
LANGSMITH_API_KEY |
LangSmith の API キー。LangSmith のコンソールから取得できます |
LANGSMITH_PROJECT |
トレースの送信先プロジェクト名。未設定の場合は default プロジェクトに送信されます |
LANGCHAIN_TRACING_V2、LANGCHAIN_API_KEY、LANGCHAIN_PROJECT という旧名の環境変数も互換として認識されます。
ステップ 2. OpenAI クライアントを wrap_openai でラップする
langsmith パッケージの wrap_openai で OpenAI クライアントをラップします。これにより、client を経由するすべての API 呼び出しが自動的にトレースされるようになります。
from openai import OpenAI
from langsmith.wrappers import wrap_openai
# 通常の OpenAI クライアントを作成
_base_client = OpenAI(
base_url=os.environ["ENTERPRISE_AI_BASE_URL"],
api_key=os.environ["ENTERPRISE_AI_API_KEY"],
project=os.environ["ENTERPRISE_AI_PROJECT_ID"],
)
# LangSmith でラップしてトレースを有効化
client = wrap_openai(_base_client)
ポイントは、OCI Enterprise AI Agents は OpenAI API 互換なので、OpenAI クライアントの base_url を OCI のエンドポイントに向けるだけで接続できるという点です。LangSmith の wrap_openai もそのままこのクライアントに適用できます。
ステップ 3. 関数に @traceable デコレータを付与する
トレースの粒度を制御したい関数に @traceable デコレータを付けます。
from langsmith import traceable
@traceable(name="DeepWiki MCP ターン", run_type="chain")
def run_agent(user_input: str, conversation_id: str) -> None:
with console.status("[bold blue]お調べしています..."):
response = client.responses.create(
**agent,
conversation=conversation_id,
input=user_input,
)
console.print(Panel(Markdown(response.output_text)))
@traceable を付けた関数が親スパンとなり、その中で呼ばれる client.responses.create() などの API 呼び出しが子スパンとしてネストされます。これにより、LangSmith のトレース画面でエージェントの動作を階層的に確認できるようになります。
run_type="chain" は、この関数が複数のステップをオーケストレーションする「チェーン」であることを示しています。
完全なサンプルコード
前回の記事のコードに LangSmith トレーシングを追加した完全版です。変更箇所にはコメントで # [LangSmith] と記しています。
import os
import pyfiglet
from dotenv import load_dotenv
from rich.console import Console
from rich.markdown import Markdown
from rich.panel import Panel
from openai import OpenAI
from langsmith import traceable # [LangSmith] traceable をインポート
from langsmith.wrappers import wrap_openai # [LangSmith] wrap_openai をインポート
"""
このPython スクリプトは、OpenAI SDKを使用してインタラクティブなチャットを実行するデモです。
- Conversations APIによるマルチターンの対話
- ターミナルでのインタラクティブなチャット体験
- MarkdownとPanelによるリッチフォーマットの応答表示
- LangSmith へ OpenAI 呼び出し(Responses / Conversations 含む)をトレース
LangSmith を有効にするには .env などに次を設定してください。
- LANGSMITH_TRACING=true (トレースを送るとき必須)
- LANGSMITH_API_KEY=<LangSmith の API キー>
- LANGSMITH_PROJECT=<任意。未設定時は default プロジェクト>
LANGCHAIN_TRACING_V2 / LANGCHAIN_API_KEY / LANGCHAIN_PROJECT も互換として解釈されます。
"""
load_dotenv()
console = Console()
# OpenAIクライアントオブジェクトを作成(LangSmith でラップしてトレース)
_base_client = OpenAI(
base_url=os.environ["ENTERPRISE_AI_BASE_URL"],
api_key=os.environ["ENTERPRISE_AI_API_KEY"],
project=os.environ["ENTERPRISE_AI_PROJECT_ID"],
)
client = wrap_openai(_base_client) # [LangSmith] ラップ
# Agent を定義
agent = {
"model": "xai.grok-4-1-fast-reasoning",
"instructions": (
"あなたはDeepWiki MCPを使用して GitHub のリポジトリを分析するエージェントです。"
"DeepWiki MCPを使用してリポジトリを理解してユーザーのリクエストに応えます。"
"DeepWiki から得られた情報だけに基づいて回答します。"
"DeepWiki で情報が見つからない場合は、その旨を報告します。"
),
"tools": [
{
"type": "mcp",
"server_label": "DeepWiki",
"server_url": "https://mcp.deepwiki.com/mcp",
"require_approval": "never"
}
],
}
# ユーザーのメッセージに対してエージェントを実行
@traceable(name="DeepWiki MCP ターン", run_type="chain") # [LangSmith] デコレータ
def run_agent(user_input: str, conversation_id: str) -> None:
# Responses API でエージェントループを実行
with console.status("[bold blue]お調べしています..."):
response = client.responses.create(
**agent,
conversation=conversation_id,
input=user_input,
)
# レスポンスをMarkdown形式でパネル表示する
console.print(Panel(Markdown(response.output_text)))
# メインエントリーポイント
if __name__ == "__main__":
# 新しい会話を作成して、複数のターンをサポート
conv = client.conversations.create()
# ウェルカムメッセージを表示
console.print(pyfiglet.figlet_format("GitHub DeepInsight"))
console.print(
"GitHub DeepInsightは、GitHubのリポジトリを分析し、"
"その内容を深掘りすることができます。"
"調査対象リポジトリは、オーナー名/リポジトリ名形式で指定してください。"
"最大10個まで指定できます。"
)
console.print(
"LangSmith: LANGSMITH_TRACING=true と LANGSMITH_API_KEY を設定すると"
"トレースが送られます。"
)
console.print("'quit' や 'bye' で終了します")
# インタラクティブなチャットループ
while True:
# ユーザーの入力を取得
user_input = console.input("\n[bold green]メッセージ:[/] ").strip()
# 終了コマンドを処理
if user_input.lower() in (
"quit", "exit", "q",
"bye", "byebye",
"さようなら", "さよなら", "バイバイ",
"終わります", "終了", "終了します",
):
break
# 空の入力を処理
if not user_input:
continue
# エージェントを実行してレスポンスを表示
run_agent(user_input, conv.id)
前回のコードからの変更点は以下の 3 箇所だけです。
-
langsmithパッケージからtraceableとwrap_openaiをインポート -
OpenAIクライアントをwrap_openai()でラップ -
run_agent関数に@traceableデコレータを付与
追加の依存パッケージとして langsmith が必要です。
langsmith
LangSmith のトレース画面で見えること
コードを実行して LangSmith のコンソール( https://smith.langchain.com )を開くと、トレースが記録されているのを確認できます。
以下は、「kutsushitaneko/multimodal_retrieval について説明してください。また、どのような API プロバイダーに対応しているのかの一覧もください。」というタスクを実行したときのトレース画面の例です。
このトレースから、以下のことが読み取れます。
- 1 回の Responses API 呼び出し(
run_agent)の中で、サービス側で MCP ツールが 3 回呼び出されている - 各 MCP 呼び出しの入出力と所要時間
- トークン消費量(入力トークン・出力トークン)
Responses API はサービス側でエージェントループを回すため、開発者のコードからは「1 回の API 呼び出し」に見えますが、LangSmith のトレースを見ると、その裏で何が起きていたかが一目瞭然です。
なぜ可観測性が重要なのか
AI エージェントの開発において、可観測性は「あると便利」ではなく「なくてはならない」ものです。
開発フェーズ
エージェントが期待どおりに動作しないとき、トレースを見れば、どのステップで何が起きたかを特定できます。
運用フェーズ
本番環境では、レイテンシの変動、トークン消費量の増加、エラー率の上昇などを継続的にモニタリングすることが重要です。LangSmith や LangFuse のダッシュボードでこれらの指標をプロジェクト単位で追跡できます。
コスト最適化
トークン消費量を可視化することで、モデルの選択やプロンプトの最適化によるコスト削減の余地が見えてきます。
Responses API の構造化トレースとの使い分け
Responses API 自体が返す output 配列と、LangSmith のトレースは相補的な関係にあります。
| 観点 | Responses API の output | LangSmith/LangFuse トレース |
|---|---|---|
| 取得方法 | API レスポンスに含まれる | バックグラウンドで自動送信 |
| 用途 | プログラム的な後処理 | 可視化・分析・モニタリング |
| 時間情報 | なし | 各スパンの所要時間を記録 |
| トークン情報 | レスポンスに含まれる | ダッシュボードで集計・可視化 |
| 永続化 | 開発者が実装 | LangSmith/LangFuse が自動保存 |
| 複数ランの比較 | 開発者が実装 | ダッシュボードで横断分析 |
開発中のデバッグには LangSmith/LangFuse のトレース画面が直感的で便利です。一方、本番環境でレスポンスの内容に基づいて条件分岐するような場合は、Responses API の output 配列をプログラム的に処理するのが適しています。
まとめ
この記事では、OCI Enterprise AI Agents のエージェントに LangSmith トレーシングを追加してみた体験をご紹介しました。
ポイントを振り返ります。
- OCI Enterprise AI Agents は OpenAI API 互換のため、LangSmith の
wrap_openaiがそのまま使える - コード変更はインポート 2 行、ラップ 1 行、デコレータ 1 行の合計 4 行だけ
- Responses API はサービス側でエージェントループを回すため、トレーシングなしではループ内部の動作が見えにくい
- LangSmith のトレースにより、MCP ツール呼び出しの回数・順序・所要時間・トークン消費量が可視化される
OCI Enterprise AI Agents が OpenAI Responses API 互換であることの恩恵は、単にコードの書き方がシンプルになるだけではありません。LangSmith、LangFuse、OpenTelemetry といった OpenAI エコシステムの可観測性ツールをそのまま活用できることも、大きなメリットだということがわかりました。
エージェントの動作を「見える化」して、安心して開発・運用を進めていきたいですね。


