Skip to main content

エージェント指向 RAG: AI エージェントでリトリーバル強化型生成を強化する

本記事では、エージェント駆動型の検索拡張生成(RAG)が、AIエージェントによって検索戦略を動的に洗練し、複数のデータソースを連携させ、応答精度を高める仕組みを解説します。この記事はAIによる翻訳です。誤訳の可能性があればコメント欄でお知らせください。
Created on August 27|Last edited on September 3
検索拡張生成(RAG) 事前学習済みの知識に外部の情報検索を組み合わせることでAIシステムを強化し、より正確で文脈に即した応答を生成できるようにする。従来の検索拡張生成(RAG)は二段階で動作する。まずクエリに基づいて関連文書を検索し、その情報を言語モデルの応答に統合する。単純な照会には有効だが、この静的な検索手法は複雑な推論やマルチステップのクエリ、あいまいな要求への対応で苦戦し、高度なタスクへの適用に限界がある。
Agentic RAGは、検索を能動的に管理し、検索戦略を洗練し、複数の情報ソースを統合的に調整する知的なエージェントを導入することで、これらの課題に対処する。固定的な検索プロセスではなく、クエリに応じて動的に適応することで、精度とスケーラビリティを高める。さらに活用することで マルチエージェントアーキテクチャ、リアルタイムのツール活用、W&B Weave のようなモニタリングフレームワークや Weave Agentic RAGは、AIシステムが複雑なクエリをより効率的に処理できるようにする。
本記事では、エージェント駆動型の検索拡張生成(RAG)を取り上げ、単一エージェントとマルチエージェントの各アーキテクチャを比較し、その強みやトレードオフ、AI駆動の検索を強化するうえでの実用的な適用例を分析する。
W&B Weave によるエージェントのツール使用のモニタリング

目次

目次検索拡張生成(RAG)とは何か?従来型RAGとエージェント駆動型RAGエージェント駆動型RAGのアーキテクチャ:単一エージェント vs. マルチエージェントシステム単一エージェントシステム:簡潔さの限界クエリルーティング向け単一エージェントシステムマルチエージェントシステム:専門化とスケーラビリティ高度な推論と協調のためのマルチエージェントシステムマルチエージェントRAGを使うべき場面単一エージェント対マルチエージェントを越えて:その他のRAGアーキテクチャステップバイステップチュートリアル:単一エージェントRAGシステムの構築ソフトウェアエンジニアリングにおけるユーザークエリの理解実装の概要1. データの収集と前処理2. テキストを埋め込み(ベクトル表現)に変換する3. クエリ分類と検索を実装する4. GPT-4o で応答を生成する次のステップ:スクリプトの作成クエリルーティングエージェントの構築クエリルーターの仕組みコード由来のシグナルで精度を高める曖昧なクエリへの対処実装アプローチ次のステップ:クエリルーティングエージェントをコードで実装するデータベースのクエリ実行ステップ1:クエリ分類手順 2:ベクトルデータベースを検索する手順 3:応答を生成する次のステップ:コードを書く今後の改善領域(クリックで展開)Claude 3.7 のツール使用の統合ベクトル検索とツールベースの検索Claude 3.7 のツール選択監視と最適化のための Weave動的なクエリ処理と反復実運用のユースケースと応用例法律調査:判例検索の精度と適応性を強化財務分析:リアルタイムの市場インサイトエージェント駆動型RAGシステムにおける課題と緩和策1. 複雑な検索によるレイテンシーの増加2. ツール選定の信頼性3. 倫理的懸念:プライバシーとバイアスのリスク結論


検索拡張生成(RAG)とは何か?

バニラ版のRAGは「検索」と「生成」の2段階で動作する。ユーザーがクエリを送信すると、それは埋め込み(意味を数値で表現したベクトル表現)に変換される。この埋め込みは ベクトルデータベースと比較して 前処理済みドキュメントの埋め込みと照合し、最も関連性の高いものを特定する。システムは類似度スコアに基づいてこれらのドキュメントを検索・取得する。
取得したドキュメントは生成モジュール(LLM)に渡され、回答に取り込まれる。これにより、モデルは事前学習済みの知識と最新の外部情報を組み合わせた回答を生成できる。
バニラ版のRAGはクエリごとに1回だけ検索を実行し、その結果を洗練・反復しない。単純な参照には有効だが、複雑な推論や複数ステップのクエリには弱い。ここで、エージェント駆動型のRAGは反復的な検索と自律的なクエリ処理を加えることで改善をもたらす。

従来型RAGとエージェント駆動型RAG

前述のとおり、従来型RAG静的に前処理されたデータベースからクエリごとに1回だけ情報を検索し、そのまま回答に取り込む方式である。単純な参照には有効だが、クエリが複雑・曖昧、または解決に複数のステップを要する場合には苦戦する。検索戦略を適応的に変えることも、結果を洗練することも、異種の複数ソースから情報を統合することもできない。
エージェント駆動型RAG は、検索プロセスを能動的に管理・強化するインテリジェントなエージェントを導入することで、これらの制約に対処する。あらかじめインデックス化されたデータのみに依存するのではなく、エージェントは多様なツールやデータベースと動的に連携できる。たとえば、ウェブサイトのようなリアルタイムソース、SQL などの高度なクエリでアクセスする構造化データベース、外部 API などが含まれる。エージェントはクエリの性質に応じて検索戦略を適応させ、反復的な検索を行い、自己評価を通じて検索を洗練し、必要に応じてアプローチを調整する。この能力により、 自律的に推論する複数の情報ソースを横��的に調整し、クエリと検索手法の双方を継続的に洗練することで、精度が大幅に向上し、ハルシネーションが減少し、関連性と文脈理解に優れた応答が得られる。

エージェント駆動型RAGのアーキテクチャ:単一エージェント vs. マルチエージェントシステム

エージェント駆動型RAGのアーキテクチャは、LLMを活用したエージェントが多様なソースから情報を検索・処理することで成り立つ。これらのシステムは単一エージェントまたはマルチエージェントとして構成でき、それぞれ効率性、モジュール性、スケーラビリティの観点で異なるトレードオフがある。
本セクションでは、検索拡張生成(RAG)システムにおける単一エージェントおよびマルチエージェントのアーキテクチャについて、その機能、トレードオフ、活用シーンを詳しく解説する。

単一エージェントシステム:簡潔さの限界


単一エージェントシステムでは、1つのエージェントが検��パイプライン全体を管理する。具体的には次を行う。
  • クエリを処理する
  • 関連するデータソースを特定する
  • 必要な情報を取得する
  • LLM に渡す前に結果を統合する
このアプローチは簡潔で効率的だが、検索タスクが複雑になるにつれて制約が生じる。単一エージェントは次を担う必要がある。
  • 複数の検索戦略(例:ベクトル検索、キーワードマッチング)
  • 多様なクエリ形式(例:構造化データと非構造化データ)
  • さまざまなランキング手法関連性の高い情報を優先する
検索の複雑性が増すにつれて、単一エージェントがボトルネックとなり、システムのスケールと保守が難しくなる。

クエリルーティング向け単一エージェントシステム

これらの課題はあるものの、単一エージェントアーキテクチャは、構造化され比較的単純な検索タスクに対しては依然として高い効果を発揮する。中央集約型のクエリルーターとして機能し、クエリ処理と応答生成を効率的に管理できる。
単一エージェントRAGシステムの主な機能は次のとおり。
  • クエリ前処理検索前のクエリの洗練と再構成
  • 多段階の検索検索を反復的に洗練し、複数回の検索結果を統合して要約する
  • 検証とランキング低信頼なソースを除外し、取得データを相互参照する
  • 外部ツール統合ベクトルデータベース、API、ウェブ検索モジュール、構造化ナレッジベースとの接続
単一エージェントRAGを使うべき場面
  • 〜を伴うタスク一貫したクエリ構造(例:構造化データベースのルックアップ)
  • 低遅延迅速かつ集中的な意思決定が求められるアプリケーション
  • 〜なシステムマルチステップ推論を必要としないまたは複雑な相互参照
しかし、より複雑な検索ニーズに対しては、マルチエージェントシステムがスケーラブルな選択肢となる。ここで登場するのが、マルチエージェントシステムだ。

マルチエージェントシステム:専門化とスケーラビリティ



マルチエージェントシステムは、検索タスクを特化エージェントに分散し、各エージェントが特定の検索手法やデータタイプに最適化されることで、単一エージェントの制約を克服する。すべての検索ロジックを単一エージェントが担うのではなく、タスクを委譲することで、モジュール式でスケーラブルな検索フレームワークを実現する。
たとえば:
  • ベクトル検索エージェントセマンティック検索を用いてベクトルデータベースから情報を取得する
  • ウェブ検索エージェント外部ソースからリアルタイムデータを取得する
  • 構造化データエージェントクエリデータベース、エンタープライズ記録、メールシステム
マルチエージェントシステムの利点
  • スケーラビリティの向上既存のシステムを壊すことなく、新しい検索エージェントを追加できる
  • より高度な最適化各エージェントは特定のデータ型に最適化されており、検索精度を高める。
  • デバッグと開発の容易化パイプライン全体に影響を与えずに、個々のエージェントだけを変更できる
しかし、マルチエージェントシステムには課題もある。
  • 調整のオーバーヘッド複数のエージェントを管理するには、高度なクエリルーティングと集約の手法が必要になる
  • 計算コストの増大複数のエージェント呼び出しによりLLMの処理オーバーヘッドが増え、パフォーマンスに影響する

高度な推論と協調のためのマルチエージェントシステム

マルチエージェントアーキテクチャは、次のような状況で複雑な検索ワークフローを処理するのに優れている。
  • 相互参照より的確な応答のための複数データソース活用
  • マルチステップ推論タスク取得した情報を反復的に洗練させる必要がある場合
  • 適応型検索エージェントがクエリの複雑さに応じて検索戦略を調整する場面
構造化された協調により、これらのエージェントは検索戦略を反復的に洗練し、より正確で文脈に即し、説明可能な応答を実現する。

マルチエージェントRAGを使うべき場面

  • 高重要度の検索タスク(例:法務調査、財務分析)
  • 動的クエリリアルタイムの相互参照を要する場合
  • 知識集約型アプリケーション複数ソースでの検証が必要な場合
マルチエージェントシステムは複雑さこそ増すものの、情報を動的に処理・洗練できるため、高度な推論タスクで強力に機能する。

単一エージェント対マルチエージェントを越えて:その他のRAGアーキテクチャ

これらのアーキテクチャ以外にも、次のようなRAGのバリエーションがある。
  • GraphRAG知識グラフを活用し、構造化された関係駆動型の検索を行うもの
  • モジュール型RAG:異なるデータソースに対して検索コンポーネントをプラグアンドプレイで差し替え可能に設計した方式
これらのアーキテクチャのより包括的な概要については、次を参照。 エージェント駆動型検索拡張生成:Agentic RAGに関するサーベイ Aditi Singh ほか。RAG 全体像を文脈化する詳細な解説と図解を提供している論文。
この先の数セクションで彼らの優れた図版を使用することを許可してくださった論文の著者の皆さまに、心より感謝申し上げます。
💡

ステップバイステップチュートリアル:単一エージェントRAGシステムの構築

このチュートリアルでは、Weights & Biases(W&B)の2つの主要ツールを対象に、複数のリポジトリ横断で検索する単一エージェントの検索拡張生成(RAG)システムを実装します。
  • W&B 実験トラッキング機械学習の実験を追跡・可視化・管理するためのフレームワーク。モデル性能のモニタリング、ハイパーパラメータの管理、開発ライフサイクル全体にわたるデータセットの整理を支援する。
  • W&B Weave大規模言語モデル(LLM)を活用したアプリケーションを開発するためのツールキット。LLMのプロンプトや対話を追跡・評価・デバッグするためのプログラマティックなツールを提供し、ワークフローの洗練を容易にする。
両方のツールにまたがる検索を統合することで、次の目的に対して関連情報を提供できるようにします。
  • 一般的な機械学習実験(W&B のドキュメントおよび Issue 追跡経由)
  • LLM の開発とデバッグ(Weave のドキュメントおよびディスカッション経由)

ソフトウェアエンジニアリングにおけるユーザークエリの理解

ユーザークエリは、一般的に次の二つのカテゴリに分類されます。
  1. 「やり方」と API の使い方に関する質問ユーザーが機能・関数・API エンドポイントの使い方について案内を必要としている
  2. トラブルシューティングと問題解決ユーザーがエラーや想定外の動作、バグを報告する
これらのクエリに対応するため、単一エージェントの検索拡張生成(RAG)システムは次を行う。
  • クエリが API の使い方に関するものか、デバッグに関するものかを分類する
  • 操作手順に関するクエリ向けにドキュメントを検索する
  • トラブルシューティングのクエリに関連する GitHub の Issue を取得する
  • 必要に応じて追加の GitHub のディスカッションも取り込みつつ、GPT-4o を用いて回答を生成する

実装の概要

本システムは次の4つの主要ステップに従う。

1. データの収集と前処理

  • 最新の W&B および Weave のリポジトリをクローンする
  • ドキュメント(Markdown ファイル)を抽出し、索引付け用にクレンジングする
  • GitHub の Issue を取得し、タイトル、説明、メタデータを取得する(現時点ではコメントを除外)

2. テキストを埋め込み(ベクトル表現)に変換する

  • OpenAI を使用する text-embedding-3-small すべてのドキュメントに対してベクトル埋め込みを生成するモデル
  • 高性能なベクトルデータベースである ChromaDB に埋め込みを保存する
  • 検索の関連性を高めるために、ドキュメントと Issue レポートを区別する

3. クエリ分類と検索を実装する

  • ユーザーのクエリが API ドキュメント関連かデバッグ関連かを判定する
  • API 関連の場合は、ドキュメントの埋め込みを検索して関連結果を取得する
  • Issue 関連の場合は、GitHub の Issue を検索してランク付けする
  • 追加の文脈が必要な場合は、GitHub のコメントをさらに取得する

4. GPT-4o で応答を生成する

  • 取得した結果を文脈として用い、GPT-4o で応答を生成する
  • 透明性を確保するため、応答は必ず元のドキュメントまたは Issue の議論を参照するようにする

次のステップ:スクリプトの作成

次のセクションでは、データ収集とベクトル化から始めて、実装を進めていきます
import os
import glob
import re
import requests
import time
from langchain_core.documents import Document
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma

# GitHub repository details
REPOS = {
"weave": "wandb/weave",
"wandb": "wandb/docs", # For documentation
}

# GitHub repositories for issues
ISSUE_REPOS = {
"weave": "wandb/weave",
"wandb": "wandb/wandb", # Correct repo for WandB issues
}

# GitHub issues URL
ISSUES_ENDPOINT = "/issues?state=all&per_page=100"

# Initialize OpenAI Embeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

# Function to download repo docs using manual git commands
def download_docs(repo_name, repo_url, path):
if os.path.exists(path):
print(f"{repo_name} documentation already exists, skipping download.")
return
print(f"Downloading {repo_name} documentation...")
# Simple git clone without sparse-checkout (more reliable)
clone_cmd = f"git clone --depth 1 https://github.com/{repo_url}.git {path}"
result = os.system(clone_cmd)
if result != 0 or not os.path.exists(path):
print(f"❌ Failed to clone {repo_name}. Check if the repository URL is correct.")
return
print(f"✅ {repo_name} documentation downloaded successfully.")

# Function to clean markdown content
def clean_markdown(text):
if not text:
return ""
text = re.sub(r'!\[.*?\]\(.*?\)', '', text) # Remove images
text = re.sub(r'\[([^\]]+)\]\((.*?)\)', r'\1', text) # Keep link text but remove URLs
return text.strip()

# Function to fetch GitHub issues without comments
def fetch_issues(repo_name, repo_url, max_pages=5):
all_issues = []
page = 1
# Paginate through the results
while page <= max_pages:
url = f"https://api.github.com/repos/{repo_url}{ISSUES_ENDPOINT}&page={page}"
headers = {"Accept": "application/vnd.github.v3+json"}
try:
print(f"Fetching page {page} of issues from {repo_name}...")
response = requests.get(url, headers=headers)
if response.status_code == 200:
issues = response.json()
# If no more issues are returned, break the loop
if not issues:
break
# Process each issue without fetching comments
for issue in issues:
# Add basic issue data to the list
all_issues.append({
"id": issue["id"],
"number": issue["number"],
"title": issue["title"],
"body": issue.get("body", ""),
"state": issue["state"],
"created_at": issue["created_at"],
"comments_count": issue.get("comments", 0),
"github_url": issue["html_url"], # URL to the GitHub issue page
"comments_url": issue["comments_url"], # Direct API URL for comments
"repo": repo_url # Store the repo info for later reference
})
print(f"Fetched {len(issues)} issues from {repo_name} (page {page})")
page += 1
time.sleep(1) # Small delay between pages
elif response.status_code == 403 and "rate limit exceeded" in response.text.lower():
print("⚠️ GitHub API rate limit exceeded. Waiting for 60 seconds...")
time.sleep(60) # Wait for rate limit to reset
continue # Retry the same page
else:
print(f"Error fetching issues from {repo_name}: {response.status_code}")
break
except Exception as e:
print(f"Exception fetching issues for {repo_name}: {e}")
break
print(f"Total issues fetched from {repo_name}: {len(all_issues)}")
return all_issues

# Function to create documents from issues (without comments)
def create_issue_documents(issues):
docs = []
for issue in issues:
# Create a document for the main issue
issue_text = f"Title: {issue['title']}\nIssue #{issue['number']}\nState: {issue['state']}\nComments: {issue['comments_count']}\n"
# Add a note about comments and where to find them
if issue['comments_count'] > 0:
issue_text += f"\nThis issue has {issue['comments_count']} comments. View at: {issue['github_url']}\n"
issue_text += f"\nBody:\n{issue['body']}"
# Create the document object with rich metadata
docs.append(Document(
page_content=clean_markdown(issue_text),
metadata={
"id": issue["id"],
"number": issue["number"],
"created_at": issue["created_at"],
"state": issue["state"],
"comments_count": issue["comments_count"],
"github_url": issue["github_url"],
"comments_url": issue["comments_url"],
"repo": issue["repo"],
"type": "issue"
}
))
return docs

# Function to process Markdown docs
def create_markdown_documents(repo_name, doc_path):
if not os.path.exists(doc_path):
print(f"⚠️ {doc_path} does not exist, skipping doc processing.")
return []
# Find markdown files recursively
md_files = glob.glob(f'{doc_path}/**/*.md', recursive=True)
if not md_files:
print(f"No markdown files found in {doc_path}.")
return []
docs = []
for md_file in md_files:
try:
with open(md_file, 'r', encoding='utf-8') as f:
content = clean_markdown(f.read())
# Create a proper Langchain Document object
docs.append(Document(
page_content=content,
metadata={
"file": md_file,
"type": "documentation"
}
))
except Exception as e:
print(f"Error processing {md_file}: {e}")
return docs

# Function to query the vector store
def search_docs(query, db_directory, k=5):
"""Search a Chroma vector store for documents similar to the query."""
try:
# Load the Chroma database
db = Chroma(persist_directory=db_directory, embedding_function=embeddings)
# Search for similar documents
results = db.similarity_search_with_score(query, k=k)
if not results:
print(f"No results found for '{query}'")
return []
print(f"Found {len(results)} results for '{query}':")
for i, (doc, score) in enumerate(results):
print(f"\nResult {i+1}:")
print(f"Score: {score}")
# Print metadata
print("Metadata:")
for key, value in doc.metadata.items():
if key in ["id", "number", "state", "comments_count", "type"]:
print(f" {key}: {value}")
# For issues with comments, show the GitHub URL
if doc.metadata.get("type") == "issue" and doc.metadata.get("comments_count", 0) > 0:
print(f" GitHub URL: {doc.metadata.get('github_url')}")
# Print content (truncated if too long)
content = doc.page_content
if len(content) > 500:
content = content[:500] + "... [truncated]"
print(f"\nContent: {content}\n")
print("-" * 40)
return results
except Exception as e:
print(f"Error searching database: {e}")
return []

# Main execution
if __name__ == "__main__":
# Create directory for vector stores
os.makedirs("db", exist_ok=True)
for repo_name, repo_url in REPOS.items():
print(f"\nProcessing {repo_name.upper()}...")

# Fetch issues from the correct repository
issue_repo_url = ISSUE_REPOS[repo_name]
print(f"Fetching issues from {issue_repo_url}...")
issues = fetch_issues(repo_name, issue_repo_url)

# Download docs
doc_path = f"{repo_name}_docs"
download_docs(repo_name, repo_url, doc_path)

# Process issues
if issues:
print(f"Creating vector store for {repo_name} issues...")
issue_docs = create_issue_documents(issues)
# Create Chroma vector store for issues
issues_db = Chroma.from_documents(
documents=issue_docs,
embedding=embeddings,
persist_directory=f"db/{repo_name}_issues"
)
print(f"✅ Saved {repo_name} issues vector store ({len(issue_docs)} documents)")
else:
print("No issues to process.")

# Process docs
print(f"Creating vector store for {repo_name} documentation...")
md_docs = create_markdown_documents(repo_name, doc_path)
if md_docs:
# Create Chroma vector store for documentation
docs_db = Chroma.from_documents(
documents=md_docs,
embedding=embeddings,
persist_directory=f"db/{repo_name}_docs"
)
print(f"✅ Saved {repo_name} documentation vector store ({len(md_docs)} documents)")
else:
print("No documentation to process.")

print("\n✅ Data ingestion completed successfully.")
# Example search to verify everything works
print("\nTesting search functionality...")
search_docs("authentication issues", "db/weave_issues", k=2)
ベクトルデータベースの準備が整えば、システムはユーザーのクエリを処理し、関連するドキュメントや GitHub の Issue における過去の議論を取得できるようになります。次のステップでは、ユーザーのクエリとの類似度に基づいてデータベースを検索する検索メカニズムを実装します。

クエリルーティングエージェントの構築

クエリルーティングエージェントは、RAG パイプラインにおける検索を最適化するうえで不可欠です。利用可能なすべての情報源を無差別に検索するのではなく、ルーターはユーザーのクエリを分類し、最も関連性の高いナレッジベースへ振り分けることで、効率と精度を向上させます。

クエリルーターの仕組み

ルーティングエージェントは、2つの主要な分類タスクを実行する。
  1. 関連する��ールの特定このクエリは Weights & Biases の Experiment Tracking または W&B Weave に関係していますか?
  2. クエリ種別の判定ユーザーはドキュメントの要求をしていますか、それとも問題のトラブルシューティングを求めていますか?
  • ドキュメントに関するクエリ通常はユーザーガイド、チュートリアル、API リファレンスを含む。
  • トラブルシューティングに関するクエリGitHub のディスカッション、バグ報告、Issue の解決内容を検索して回答するのが最適です。
クエリが曖昧な場合、エージェントは無理に単一の(誤りの可能性がある)選択に絞らず、複数のナレッジベースを返す。

コード由来のシグナルで精度を高める

分類精度を高めるために、エージェントは各ツールに特有の構文や頻出するコードスニペットを活用する。
  • W&B に関連するクエリにはしばしば含まれる wandb.init() やログ指標への参照
  • Weave に関連するクエリには含まれる可能性がある weave.op() またはメトリクスの記録への言及
クエリ内にこれらのパターンが現れた場合、エージェントはそれらを追加のルーティングシグナルとして利用し、検索精度を高める。

曖昧なクエリへの対処

エージェントは、曖昧なクエリを賢く扱うよう設計されている。
たとえば、ユーザーが次のように尋ねた場合:
AI ワークフローのエラーはどうデバッグすればよいですか?
この問題が W&B に関係するのか、Weave に関係するのかをシステムは確実には判定できない。そのため、該当する両方のナレッジベースを返し、漏れのない検索を保証する。

実装アプローチ

このロジックを実装するために、エージェントへ次を指示するシステムプロンプトを定義する。
  • ユーザーのクエリを分析するそのうえで適切に分類する
  • 構造化された JSON を返す検索対象のナレッジベースを指定して出力する
  • 複数ソース検索をサポートするあいまいさや重複のあるクエリに対して
このルーティング機構を実装することで、次のことが可能になる。
  • 不要な検索を減らす
  • 回答の関連性を高める
  • 構造化され効率的な検索プロセスを構築する

次のステップ:クエリルーティングエージェントをコードで実装する

では、クエリルーティングエージェントをコードで実装し、検索パイプラインに統合します。
You are a query-routing assistant that determines whether a user's question is related to Weights & Biases (wandb) or Weave and directs them to the most relevant database. Users may ask about documentation, troubleshooting issues, or implementation details for either tool.

Your task is to analyze the query and route it to all relevant sources if uncertainty exists. If a query could apply to multiple areas, return multiple results rather than choosing just one.

The available knowledge bases are:
1. "Weave Issues" - Contains GitHub issues from the wandb/weave repository
2. "Weave Documentation" - Contains documentation for Weave
3. "WandB Issues" - Contains GitHub issues from the wandb/wandb repository
4. "WandB Documentation" - Contains documentation for Weights & Biases

Return a JSON object with the following format:
{
"knowledge_bases": ["name_of_kb1", "name_of_kb2"]
}

## Library Descriptions for Context

### Weights & Biases (wandb) – Experiment Tracking & ML Workflow Management
Weights & Biases (wandb) is a tool for tracking and visualizing machine learning experiments. It helps researchers and engineers:
* Log and monitor metrics like loss, accuracy, and learning rate.
* Track hyperparameters and model configurations for experiment reproducibility.
* Store and visualize model artifacts and training progress.
* Integrate seamlessly with deep learning frameworks like PyTorch, TensorFlow, and Hugging Face Transformers.
* Collaborate via dashboards that display real-time and historical training runs.
It is widely used in research and production to optimize models, compare runs, and fine-tune hyperparameters efficiently.

### W&B Weave – Generative AI & LLM Debugging Toolkit
Weave is a toolkit designed for developing, debugging, and evaluating Generative AI applications, particularly those using Large Language Models (LLMs). It enables users to:
* Log inputs and outputs from LLMs for better debugging.
* Build evaluations to compare different model responses.
* Organize information across LLM workflows from experimentation to production.
* Track interactions within multi-step AI pipelines to analyze decision-making processes.
Weave is essential for understanding and improving the performance of AI agents, LLM-based chatbots, and retrieval-augmented generation (RAG) systems.

## Routing Logic
* If the user asks about experiment tracking, ML metrics, logging, hyperparameters, dashboards, or integrations, route to W&B Docs.
* If they report an error, unexpected behavior, or API issue related to W&B, route to W&B Issues.
* If the user asks about LLM evaluation, function tracking, Weave UI development, or Weave's role in ML workflows, route to Weave Docs.
* If they report errors related to Weave tracking or data storage, route to Weave Issues.
* If uncertain, include all possibly relevant sources instead of picking only one.

## Code Samples for Context
If the query is vague or unclear, match it against the functionality of each tool using these sample code snippets:

### W&B Experiment Tracking Example (wandb Docs/Issues)
```python
import wandb
wandb.init(project="my_project", config={"learning_rate": 0.001, "epochs": 10})
for epoch in range(wandb.config.epochs):
train_loss = 0.01 * (wandb.config.epochs - epoch)
train_accuracy = 0.1 * epoch
wandb.log({'epoch': epoch, 'train_loss': train_loss, 'train_accuracy': train_accuracy})
```
If the query involves logging, training loops, metrics, dashboards, or hyperparameters, it should likely be routed to W&B Docs or W&B Issues if errors are involved.

### Weave Function Tracking Example (Weave Docs/Issues)
```python
import weave
weave.init(project_name="my_weave_project")
@weave.op()
def add_numbers(a: int, b: int) -> int:
return a + b
result = add_numbers(5, 7)
print(f'Result: {result}')
```
If the query involves tracking function calls, logging LLM interactions, debugging AI workflows, or analyzing execution steps, route it to Weave Docs or Weave Issues if errors occur.


Focus on understanding:
- Is the query about Weave or WandB?
- Is the user looking for documentation or issues/bugs?
- If the query mentions "bug" or "issue" or describes a problem, prioritize the Issues knowledge base
- If the query is asking how to use a feature or understand concepts, prioritize Documentation
- If unclear whether the query is about Weave or WandB, include knowledge bases for both

Examples:
1. For "How do I track experiments in wandb?"
- Knowledge bases: ["WandB Documentation"]

2. For "Weave authentication failing"
- Knowledge bases: ["Weave Issues"]

3. For "How to visualize neural networks in Weights & Biases"
- Knowledge bases: ["WandB Documentation"]

4. For "LLM output tracking shows errors in production"
- Knowledge bases: ["Weave Issues", "Weave Documentation"]

Always include at least one knowledge base in your response.

Return a JSON object with the following format:
{
"knowledge_bases": ["name_of_kb1", "name_of_kb2"]
}

データベースのクエリ実行

では、ベクトルデータベースとクエリルーティングエージェントを用いてクエリの検索を実装する。手順は次の3段階からなる。
  1. クエリ分類AI駆動のルーターが、どのナレッジベースを検索すべきかを判断する
  2. ベクトル検索システムは Chroma データベースから最も関連性の高い結果を取得し、意味的類似度で順位付けする
  3. 応答生成 テキストがまだ提供されていません。翻訳する本文を送ってください。 GPT-4o 取得したソースへの参照を含めて透明性を確保しつつ、回答を統合して提示する

ステップ1:クエリ分類

クエリルーティングエージェントは、ユーザーのクエリを分類し、次の内容を指定する構造化されたJSON応答を返す。
  • 関連ナレッジベース(例:) W&B のドキュメント テキストがまだ提供されていません。翻訳する本文を送ってください。 Weave のドキュメント、GitHub の Issue)
  • 複数ソース検索クエリがあいまいな場合に、関連性を最大化すること
すべてのソースを手当たり次第に検索するのではなく、この手法は効率と精度を高める。

手順 2:ベクトルデータベースを検索する

ルーティングの判断が下されたら、システムは ChromaDB を用いてセマンティック検索を実行します。ChromaDB には次の内容が含まれます。
  • W&B の埋め込み(ベクトル表現)Weave のドキュメント
  • 過去の GitHub の Issueトラブルシューティング用のクエリに対して
取得されたドキュメントが GitHub の Issue の場合、システムは関連するコメントやディスカッションを追加取得して処理し、より豊富な文脈と解決策の候補を提供する。

手順 3:応答を生成する

取得したコンテンツは GPT-4o に渡され、次を行う。
  • 最も関連性の高いドキュメントに基づいて応答を統合生成する
  • 透明性を担保するため、ドキュメントおよび GitHub の Issue への参照を含める。
  • エッジケースの処理 ― 関連する結果が見つからない場合、システムは次を行う。
    • ユーザーに明確化を求める。
    • 検索を精緻化するための代替クエリを提案する。
クエリ分類、セマンティック検索、知的な応答生成を統合することで、このパイプラインは正確性、透明性、文脈適合性の高い回答を実現する。

次のステップ:コードを書く

では、次のスクリプトでクエリ処理ロジックを実装しましょう。
import sys
import os
import json
from litellm import completion
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
import weave; weave.init("agentic_rag")
import requests
import os
import json
import time

# Create cache directory if it doesn't exist
os.makedirs("comment_cache", exist_ok=True)

# Initialize OpenAI Embeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

# Configure which databases to search (can be modified to include/exclude databases)
DATABASES_TO_SEARCH = [
{"name": "Weave Issues", "path": "db/weave_issues"},
{"name": "Weave Documentation", "path": "db/weave_docs"},
{"name": "WandB Issues", "path": "db/wandb_issues"},
{"name": "WandB Documentation", "path": "db/wandb_docs"}
]

def load_router_prompt():
"""Load the router prompt from router_prompt.txt"""
try:
with open("router_prompt.txt", "r") as f:
return f.read()
except FileNotFoundError:
print("❌ Error: router_prompt.txt not found.")
print("Please create this file with your router prompt before running the script.")
sys.exit(1)

def search_vector_store(query, db_directory, k=5):
"""Search a vector store and return results."""
try:
# Load the Chroma database
db = Chroma(persist_directory=db_directory, embedding_function=embeddings)
# Search for similar documents
results = db.similarity_search_with_score(query, k=k)
return results
except Exception as e:
print(f"❌ Error searching database at {db_directory}: {e}")
return []


def extract_json_from_response(response_text):
"""Extract JSON from response text, handling code blocks."""
# Try to find JSON in code blocks
import re
json_pattern = r'```(?:json)?\s*(\{.*?\})\s*```'
match = re.search(json_pattern, response_text, re.DOTALL)
if match:
# Extract JSON from code block
json_str = match.group(1)
else:
# Assume the entire response is JSON
json_str = response_text
# Remove any non-JSON content
json_str = json_str.strip()
return json.loads(json_str)


def route_query(user_query):
"""Use LiteLLM to route the query to the appropriate databases."""
router_prompt = load_router_prompt()
try:
# Call LiteLLM with the router prompt
response = completion(
model="openai/gpt-4o",
messages=[
{"role": "system", "content": router_prompt},
{"role": "user", "content": user_query}
]
)
# Extract the content from the response
router_response = response.choices[0].message.content
# Parse the JSON response
try:
# parsed_response = json.loads(router_response)
parsed_response = extract_json_from_response(router_response)
return parsed_response
except json.JSONDecodeError:
print("❌ Error: Router didn't return valid JSON. Using all knowledge bases.")
print(f"Router response: {router_response}")
return {
"knowledge_bases": [db["name"] for db in DATABASES_TO_SEARCH],
"search_query": user_query,
"reasoning": "Fallback due to JSON parsing error"
}
except Exception as e:
print(f"❌ Error calling LiteLLM: {e}")
return {
"knowledge_bases": [db["name"] for db in DATABASES_TO_SEARCH],
"search_query": user_query,
"reasoning": f"Fallback due to error: {str(e)}"
}

def get_kb_by_name(name):
"""Get the knowledge base by name."""
for db in DATABASES_TO_SEARCH:
if db["name"] == name:
return db
return None

def fetch_comments_for_issue(repo, issue_number):

# Check cache first
cache_file = f"comment_cache/{repo.replace('/', '_')}_issue_{issue_number}_comments.json"
if os.path.exists(cache_file):
try:
with open(cache_file, 'r') as f:
comments = json.load(f)
print(f"Loaded {len(comments)} comments for issue #{issue_number} from cache")
return comments
except Exception as e:
print(f"Error reading cache: {e}")
# Continue to fetch if cache read fails
# Fetch from GitHub API
url = f"https://api.github.com/repos/{repo}/issues/{issue_number}/comments"
headers = {"Accept": "application/vnd.github.v3+json"}
# Add GitHub token if available
if os.environ.get("GITHUB_TOKEN"):
headers["Authorization"] = f"token {os.environ.get('GITHUB_TOKEN')}"
try:
print(f"Fetching comments for issue #{issue_number} from {repo}...")
response = requests.get(url, headers=headers)
if response.status_code == 200:
comments = response.json()
# Cache the comments
with open(cache_file, 'w') as f:
json.dump(comments, f, indent=2)
print(f"✅ Fetched and cached {len(comments)} comments for issue #{issue_number}")
return comments
elif response.status_code == 403 and "rate limit exceeded" in response.text.lower():
print("⚠️ GitHub API rate limit exceeded. Waiting 60 seconds...")
time.sleep(60)
return fetch_comments_for_issue(repo, issue_number) # Retry after waiting
else:
print(f"Error fetching comments: {response.status_code}")
return []
except Exception as e:
print(f"Exception fetching comments: {e}")
return []

def format_comments_for_context(comments):

if not comments:
return ""
comments_text = "\n\nCOMMENTS:\n"
for i, comment in enumerate(comments):
user = comment.get('user', {}).get('login', 'anonymous')
created_at = comment.get('created_at', 'unknown date')
body = comment.get('body', '').strip()
comments_text += f"\n[Comment #{i+1} by {user} on {created_at}]\n{body}\n"
return comments_text

def generate_response(user_query, relevant_docs, query_analysis):
"""Generate a response using LiteLLM based on the retrieved documents."""
# Prepare context from documents
context = ""
github_links = []
for i, (doc, score) in enumerate(relevant_docs):
# Add the document to context
context += f"--- Document {i+1} (Relevance: {score:.4f}) ---\n"
# Add source information
if doc.metadata.get('type') == 'issue':
issue_num = doc.metadata.get('number', 'Unknown')
repo = doc.metadata.get('repo', 'Unknown')
context += f"Source: GitHub Issue #{issue_num} in {repo}\n"
# If issue has comments, fetch and add them to context
if doc.metadata.get('comments_count', 0) > 0:
github_url = doc.metadata.get('github_url')
if github_url:
github_links.append(github_url)
# Fetch and append comments
print(f"Fetching comments for issue #{issue_num}...")
comments = fetch_comments_for_issue(repo, issue_num)
if comments:
comments_text = format_comments_for_context(comments)
context += doc.page_content + comments_text + "\n\n"
continue # Skip the standard content addition below
else:
context += f"Source: {doc.metadata.get('file', 'Unknown')}\n"
# Add content (only reached if the issue doesn't have comments or isn't an issue)
context += doc.page_content
context += "\n\n"
# Create the prompt for the response
response_prompt = f"""You are an AI assistant for Weights & Biases (wandb) and Weave.
You've been given several documents retrieved from a search based on the user's query.
Use the information in these documents to answer the user's question.

If the documents don't contain the necessary information to answer the question, admit that you don't know
rather than making up an answer. If appropriate, suggest what the user might search for instead.

User Query: {user_query}

Retrieved Documents:
{context}

Based on these documents, provide a helpful response to the user's query.
Pay special attention to issue comments as they often contain solutions to the problems described in the issues.
"""

try:
# Call LiteLLM with the response prompt
response = completion(
model="openai/gpt-4o",
messages=[
{"role": "system", "content": response_prompt},
{"role": "user", "content": user_query}
]
)
# Extract the content from the response
return response.choices[0].message.content
except Exception as e:
print(f"❌ Error generating response: {e}")
return f"I encountered an error while generating a response: {str(e)}"


def agentic_rag(user_query):
"""Main function for the agentic RAG system."""
print(f"\n🔍 Processing query: '{user_query}'")
print("-" * 80)
# Step 1: Route the query
query_analysis = route_query(user_query)
print(f"✅ Query Analysis:")
print(f"- Selected Knowledge Bases: {', '.join(query_analysis['knowledge_bases'])}")
# print(f"- Refined Search Query: '{query_analysis['search_query']}'")
# print(f"- Reasoning: {query_analysis['reasoning']}")
# Step 2: Search the selected knowledge bases
all_results = []
for kb_name in query_analysis["knowledge_bases"]:
kb = get_kb_by_name(kb_name)
if kb and os.path.exists(kb["path"]):
# print(f"\n🔍 Searching '{kb['name']}' for: '{query_analysis['search_query']}'")
results = search_vector_store(user_query, kb["path"], k=3)
if results:
print(f"Found {len(results)} results in {kb['name']}")
# Print out GitHub URLs for issues with comments
for doc, _ in results:
if doc.metadata.get('type') == 'issue' and doc.metadata.get('comments_count', 0) > 0:
github_url = doc.metadata.get('github_url')
if github_url:
print(f" - Issue with comments: {github_url}")
all_results.extend(results)
else:
print(f"No results found in {kb['name']}")
else:
print(f"⚠️ Knowledge base '{kb_name}' not found or not available.")
# Step 3: Sort results by score (ascending since lower score = more relevant in Chroma)
all_results.sort(key=lambda x: x[1])
# Take top results (up to 5)
top_results = all_results[:5]
if not top_results:
print("❌ No relevant documents found across any knowledge base.")
return "I couldn't find any relevant information to answer your question. Could you please rephrase or provide more details?"
# Step 4: Generate a response
print(f"\n💬 Generating response based on {len(top_results)} documents...")
response = generate_response(user_query, top_results, query_analysis)
return response

def interactive_mode():
"""Run the RAG system in interactive mode."""
print("\n" + "=" * 80)
print("AGENTIC RAG SYSTEM")
print("=" * 80)
print("Type 'exit' to quit.")
print("To ask a question, type your question (can be multiple lines)")
print("When finished, press Enter on an empty line to submit.")
while True:
print("\nYour question (multi-line, empty line to submit):")
lines = []
while True:
line = input().strip()
# Check for exit command on single line
if not lines and line.lower() in ['exit', 'quit']:
return
# Empty line finishes input if we already have some content
if not line and lines:
break
# Otherwise add the line to our input
if line:
lines.append(line)
# Combine all lines into a single query
user_query = "\n".join(lines)
if not user_query:
print("Please enter a question.")
continue
print("\n" + "-" * 80)
print("Processing your query...")
response = agentic_rag(user_query)
print("\n" + "=" * 80)
print("ANSWER:")
print(response)
print("=" * 80)

if __name__ == "__main__":
# Check for API key
if not os.environ.get("OPENAI_API_KEY"):
api_key = input("Please enter your OpenAI API key: ").strip()
if api_key:
os.environ["OPENAI_API_KEY"] = api_key
else:
print("❌ No API key provided. Exiting.")
sys.exit(1)
interactive_mode()
Weave はシステムの挙動を可視化し、インタラクションを追跡するために用いられ、クエリのパターン、応答品質、検索効率を分析できる。これにより、システムのパフォーマンスと改善余地を把握できる。クエリ、取得結果、生成応答を記録することで、Weave は RAG パイプラインの洗練と検索戦略の最適化を支援する。また、クエリがどのようにルーティングされたかを追跡し、システムが最も関連性の高い知識ソースを選択していることを確認することで、潜在的な問題のデバッグにも役立つ。
この単一エージェント設計は、簡潔で一元化された検索ワークフローを保ちながら、効率的なクエリ処理を実現する。将来的に、構造化データやリアルタイム検索、反復的リファインメントをそれぞれ担当するマルチエージェントシステムへ拡張するための土台を築く。

今後の改善領域(クリックで展開)

Claude 3.7 のツール使用の統合

現行のシステムは機能しているものの、手作業が多く硬直的だ。プロンプトの慎重な調整や明示的なデータベース設定が必要で、情報の取得方法を変更したいときには相応の人的介入が発生する。新しいデータソース(追加のドキュメントセットや別の GitHub リポジトリなど)を組み込むには、ベクトルデータベースを手動で更新し、検索ロジックを改修し、すべてが正しく統合されるよう確認しなければならない。動作自体はするが、スケールしない。
システムをより柔軟かつ自律的にするために、統合できます Claude 3.7ツール使用を組み込むことで、取得プロセスそのものを推論し、知識ソースへ動的にクエリを投げられるようになる。あらかじめ定義した規則に基づいて検索先のデータベースを決めるのではなく、Claude がユーザーのクエリを解析し、最も関連性の高いソースを選択してリアルタイムに検索を実行できる。このアプローチは ReAct パラダイムに従っており、Claude は受動的に情報を取得するだけでなく、最も関連する結果を得るために「どのように検索すべきか」を能動的に推論する。
この戦略には、いくつもの利点がある。
  • 手作業による介入の削減プロンプトや検索ルールを絶えず調整する代わりに、検索の意思決定をClaudeに動的に任せられる。
  • スケーリングが容易新しいデータソースを追加しても、システムに大きな改修は不要だ。Claude は新しいツールを認識し、必要に応じて活用できる。
  • より優れたクエリ処理クエリが曖昧な場合、Claude は内容を言い換えたり、単一の不十分な結果を返すのではなく複数の検索を実行したりできる。
Weave は、インタラクションを追跡し、どの検索戦略が最も有効かを可視化し、時間の経過とともにクエリの処理方法を洗練するうえで重要な役割を果たす。
以下では、ツール使用を通じて Claude にベクトルデータベースへの直接アクセスを付与し、ドキュメント、GitHub の Issue、ユーザーの議論をリアルタイムで検索できるように実装する。
import os
import json
import sys
from anthropic import Anthropic
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
import requests
import time
import weave; weave.init("claude_agentic_rag")

# Create cache directory if it doesn't exist
os.makedirs("comment_cache", exist_ok=True)

# Initialize OpenAI Embeddings for vector search
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

# Configure knowledge bases
KNOWLEDGE_BASES = [
{"name": "weave_issues", "path": "db/weave_issues", "description": "GitHub issues related to Weave, containing bug reports and feature requests."},
{"name": "weave_docs", "path": "db/weave_docs", "description": "Documentation for Weave, explaining its features, APIs, and usage."},
{"name": "wandb_issues", "path": "db/wandb_issues", "description": "GitHub issues related to Weights & Biases (wandb), containing bug reports and feature requests."},
{"name": "wandb_docs", "path": "db/wandb_docs", "description": "Documentation for Weights & Biases (wandb), explaining its features, APIs, and usage."}
]

# Initialize Anthropic client
CLAUDE_API_KEY = os.environ.get("ANTHROPIC_API_KEY")
MODEL = "claude-3-7-sonnet-20250219"
client = Anthropic(api_key=CLAUDE_API_KEY)

# Define search tools
TOOLS = [
{
"name": "search_weave_issues",
"description": "Search for GitHub issues related to Weave. Use this for debugging, error messages, or when the user mentions bugs or problems with Weave.",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "The search query to find relevant Weave issues."}
},
"required": ["query"]
}
},
{
"name": "search_weave_docs",
"description": "Search Weave documentation. Use this for understanding Weave features, APIs, usage examples, or 'how-to' questions about Weave.",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "The search query to find relevant Weave documentation."}
},
"required": ["query"]
}
},
{
"name": "search_wandb_issues",
"description": "Search for GitHub issues related to Weights & Biases (wandb). Use this for debugging, error messages, or when the user mentions bugs or problems with wandb.",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "The search query to find relevant Weights & Biases issues."}
},
"required": ["query"]
}
},
{
"name": "search_wandb_docs",
"description": "Search Weights & Biases documentation. Use this for understanding wandb features, APIs, usage examples, or 'how-to' questions about wandb.",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "The search query to find relevant Weights & Biases documentation."}
},
"required": ["query"]
}
}
]
@weave.op
def search_vector_store(kb_name, query, k=3):
"""Search a vector store and return results, automatically fetching comments for issues."""
kb = next((kb for kb in KNOWLEDGE_BASES if kb["name"] == kb_name), None)
if not kb:
return {"error": f"Knowledge base '{kb_name}' not found"}
db_path = kb["path"]
if not os.path.exists(db_path):
return {"error": f"Knowledge base at '{db_path}' does not exist"}
try:
# Load the Chroma database
db = Chroma(persist_directory=db_path, embedding_function=embeddings)
# Search for similar documents
results = db.similarity_search_with_score(query, k=k)
# Format results
formatted_results = []
for i, (doc, score) in enumerate(results):
# Format document
result = {
"document_id": i + 1,
"relevance_score": float(score),
"content": doc.page_content,
"metadata": doc.metadata
}
# Add special handling for issues
if doc.metadata.get('type') == 'issue':
issue_number = doc.metadata.get('number')
repo = doc.metadata.get('repo')
result["issue_number"] = issue_number
result["repo"] = repo
result["github_url"] = doc.metadata.get('github_url')
result["comments_count"] = doc.metadata.get('comments_count', 0)
# Automatically fetch comments if available
if doc.metadata.get('comments_count', 0) > 0 and repo and issue_number:
print(f" Fetching {doc.metadata.get('comments_count')} comments for {repo}#{issue_number}")
comments_result = fetch_github_comments(repo, issue_number)
result["comments"] = comments_result
formatted_results.append(result)
return {
"results_count": len(formatted_results),
"results": formatted_results
}
except Exception as e:
return {"error": f"Error searching knowledge base: {str(e)}"}

def fetch_github_comments(repo, issue_number):
"""Fetch comments for a GitHub issue."""
# Check cache first
cache_file = f"comment_cache/{repo.replace('/', '_')}_issue_{issue_number}_comments.json"
if os.path.exists(cache_file):
try:
with open(cache_file, 'r') as f:
comments = json.load(f)
return {
"source": "cache",
"comments_count": len(comments),
"comments": comments
}
except Exception as e:
# Continue to fetch if cache read fails
pass
# Fetch from GitHub API
url = f"https://api.github.com/repos/{repo}/issues/{issue_number}/comments"
headers = {"Accept": "application/vnd.github.v3+json"}
# Add GitHub token if available
if os.environ.get("GITHUB_TOKEN"):
headers["Authorization"] = f"token {os.environ.get('GITHUB_TOKEN')}"
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
comments = response.json()
# Cache the comments
with open(cache_file, 'w') as f:
json.dump(comments, f, indent=2)
# Format the comments
formatted_comments = []
for comment in comments:
formatted_comments.append({
"id": comment.get("id"),
"user": comment.get("user", {}).get("login", "anonymous"),
"created_at": comment.get("created_at"),
"body": comment.get("body", "")
})
return {
"source": "github_api",
"comments_count": len(formatted_comments),
"comments": formatted_comments
}
elif response.status_code == 403 and "rate limit exceeded" in response.text.lower():
return {
"error": "GitHub API rate limit exceeded",
"suggestion": "Set a GITHUB_TOKEN environment variable to increase rate limits"
}
else:
return {
"error": f"Error fetching comments: HTTP {response.status_code}",
"response": response.text
}
except Exception as e:
return {"error": f"Exception fetching comments: {str(e)}"}

def execute_tool(tool_name, tool_input):
"""Execute the appropriate tool based on the name and input."""
if tool_name == "search_weave_issues":
return search_vector_store("weave_issues", tool_input["query"])
elif tool_name == "search_weave_docs":
return search_vector_store("weave_docs", tool_input["query"])
elif tool_name == "search_wandb_issues":
return search_vector_store("wandb_issues", tool_input["query"])
elif tool_name == "search_wandb_docs":
return search_vector_store("wandb_docs", tool_input["query"])
else:
return {"error": "Unknown tool requested"}

def get_system_prompt():
"""Get the system prompt for Claude."""
return """You are an AI assistant for Weights & Biases (wandb) and Weave.

You have access to several searchable knowledge bases:
1. Weave Issues - Contains GitHub issues related to Weave
2. Weave Documentation - Contains documentation for Weave
3. WandB Issues - Contains GitHub issues related to Weights & Biases
4. WandB Documentation - Contains documentation for Weights & Biases

To assist the user effectively:
1. Analyze their question to determine if it's about Weave or WandB (or both)
2. Determine if they're asking about documentation or having an issue
3. Search the appropriate knowledge base(s) using the search tools
4. If an issue looks relevant but lacks context, fetch its GitHub comments
5. Synthesize the retrieved information to provide a detailed answer
6. Make sure to provide the full links to the github issue so the user can investigate further
7. For doc links- DO NOT hallucinate links ---> just provide a verbatim repeat of the docs you think could be helpful

For code-related queries or error messages, search the issues database first. For "how to" questions, search the documentation database first.

Example workflows:
- For "How do I track experiments in wandb?": Search wandb_docs
- For "Weave authentication failing": Search weave_issues, then fetch comments if needed
- For a query mentioning an error message: Search the relevant issues database with the error message

Aim to provide comprehensive answers based on the most relevant retrieved documents. If you don't find relevant information in your first search, try different search queries or additional knowledge bases.
"""

@weave.op
def claude_agentic_rag(user_query):
"""
Run the agentic RAG system using Claude with tools.
Args:
user_query (str): The user's query
Returns:
dict: The complete response, including search results and Claude's answer
"""
# Check for API key
if not CLAUDE_API_KEY:
raise ValueError("ANTHROPIC_API_KEY environment variable is not set")
system_prompt = get_system_prompt()
print(f"\n🔍 Processing query: '{user_query}'")
print("-" * 80)
# Initial request with the user's prompt
response = client.messages.create(
model=MODEL,
max_tokens=4000,
system=system_prompt,
thinking={"type": "enabled", "budget_tokens": 2000},
tools=TOOLS,
messages=[{"role": "user", "content": user_query}]
)
# Display thinking
thinking_blocks = [b for b in response.content if b.type == "thinking"]
for block in thinking_blocks:
print("\n🧠 THINKING:")
print(block.thinking[:300] + "..." if len(block.thinking) > 300 else block.thinking)
# Process tool use if needed
conversation = [{"role": "user", "content": user_query}]
search_results = []
# We might need multiple tool calls, so loop until we get a final answer
while response.stop_reason == "tool_use":
tool_block = next((b for b in response.content if b.type == "tool_use"), None)
if tool_block:
# Show which tool was selected
print(f"\n🔧 USING TOOL: {tool_block.name}")
print(f"Tool input: {json.dumps(tool_block.input, indent=2)}")
# Execute the appropriate tool
tool_result = execute_tool(tool_block.name, tool_block.input)
print(f"Tool found {tool_result.get('results_count', 0)} results" if 'results_count' in tool_result else f"Tool completed")
# Save search results for return value
search_results.append({
"tool": tool_block.name,
"input": tool_block.input,
"result": tool_result
})
# Save assistant's response (thinking + tool use)
assistant_blocks = thinking_blocks + [tool_block]
conversation.append({"role": "assistant", "content": assistant_blocks})
# Add tool result to conversation
conversation.append({
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": tool_block.id,
"content": json.dumps(tool_result)
}]
})
# Get next response
response = client.messages.create(
model=MODEL,
max_tokens=4000,
system=system_prompt,
thinking={"type": "enabled", "budget_tokens": 2000},
tools=TOOLS,
messages=conversation
)
# Update thinking blocks for next iteration
thinking_blocks = [b for b in response.content if b.type == "thinking"]
for block in thinking_blocks:
print("\n🧠 ADDITIONAL THINKING:")
print(block.thinking[:300] + "..." if len(block.thinking) > 300 else block.thinking)
# Get final text response
final_text = ""
for block in response.content:
if block.type == "text":
final_text += block.text
print("\n" + "=" * 80)
print("ANSWER:")
print(final_text)
print("=" * 80)
# Return the complete result with all context
return {
"query": user_query,
"answer": final_text,
"search_results": search_results,
"conversation_history": conversation
}

def interactive_mode():
"""Run the Claude agentic RAG system in interactive mode."""
print("\n" + "=" * 80)
print("CLAUDE 3.7 AGENTIC RAG SYSTEM")
print("=" * 80)
print("Type 'exit' or 'quit' to exit.")
while True:
print("\nYour question: ", end="")
user_query = input().strip()
# Check for exit command
if user_query.lower() in ['exit', 'quit']:
return
# Skip empty queries
if not user_query:
print("Please enter a question.")
continue
print("\n" + "-" * 80)
print("Processing your query...")
try:
# Process the query using Claude
claude_agentic_rag(user_query)
except Exception as e:
print(f"❌ Error: {str(e)}")


if __name__ == "__main__":
# Check for API keys
if not os.environ.get("ANTHROPIC_API_KEY"):
api_key = input("Please enter your Anthropic API key: ").strip()
if api_key:
os.environ["ANTHROPIC_API_KEY"] = api_key
CLAUDE_API_KEY = api_key
else:
print("❌ No Anthropic API key provided. Exiting.")
sys.exit(1)
# Check GitHub token
if not os.environ.get("GITHUB_TOKEN"):
print("\n⚠️ No GitHub token found. You may hit rate limits when fetching comments.")
print("To set a token: export GITHUB_TOKEN=your_token_here\n")
# Run the interactive mode
interactive_mode()
ツール使用に対応した Claude 3.7 を用いた本実装では、手動のチューニングなしに、複数のナレッジベースから関連情報をどのように検索するかを動的に判断できます。さらに W&B Weave を統合することで、Claude の検索ツール選択や推論過程を可視化・追跡し、システム性能を反復的に改善できます。以下に、システムの中核コンポーネントを整理します。

ベクトル検索とツールベースの検索

このシステムの中核はベクトル検索の実装であり、単純なキーワード一致ではなくセマンティック検索によって、Claude が関連文書を取得できるようにする。効率的なベクトル格納には ChromaDB を使用し、OpenAI の text-embedding-3-small 埋め込み(ベクトル表現)を生成するモデル。関数 search_vector_store 次の方法でクエリを処理する:
  1. 関連するナレッジベースの特定(例:Weave の Issue、Weave のドキュメント、Weights & Biases の Issue、Weights & Biases のドキュメント)
  2. 保存済みのベクトルデータベースに対して類似度検索を実行する。
  3. 上位の結果を返し、GitHub の Issue 番号、関連リポジトリ、コメントなどのメタデータを含める。
検索結果が GitHub の Issue の場合は、コメントを自動取得します。 fetch_github_comments 関数により、解決策や関連する議論を含む可能性のある追加コンテキストをシステムが取得できるようにする。

Claude 3.7 のツール選択

Claude は、ユーザーのクエリに基づいて適切なツールを動的に選択できるため、システムを簡潔にします。システムには、4 つの検索ツールへのアクセス権が与えられています。
  • search_weave_issuesWeave 関連の GitHub の Issue を照会する。
  • search_weave_docs:Weave のドキュメントを検索する。
  • search_wandb_issues:Weights & Biases に関連する Issue を取得する。
  • search_wandb_docs:WandB ドキュメントの関連セクションを検索する。
その execute_tool 関数は Claude の判断に基づいて適切な検索関数を実行する。これにより静的な検索ロジックが不要になり、Claude がどのナレッジベースを検索すべきかを推論できるようになる。

監視と最適化のための Weave

ツールを併用する検索システムの課題の一つは、意思決定の過程と、その決定が最適な結果につながっているかを把握することです。私たちは W&B Weave を用いて、各クエリに対して Claude がどのツールを選択したか、その選択理由(推論)、そして取得結果が最終回答にどのように影響したかを監視します。
検索に関与する各関数はすべて包み込まれています @weave.op、すべての対話が記録・可視化・分析できることを意味します。これはデバッグやシステム挙動の改善に不可欠です。たとえば、特定のクエリに対して Claude が一貫して誤ったツールを選ぶ場合、システムプロンプトを調整したり、検索ヒューリスティクスを修正したりできます。以下は、スクリプト実行後に Weave 内で確認できる画面のスクリーンショットです。


動的なクエリ処理と反復

従来の RAG システムが単一ステップで文書を検索するのに対し、Claude は次のことが可能です。
  1. クエリを解析し、追加のコンテキストが必要かどうかを判定する。
  2. 不確実性がある場合は、複数のナレッジベースを検索する。
  3. 検索クエリを洗練する、または追質問を行う。
Weave はこれらのマルチステップの対話を記録し、クエリの再定式化が必要なタイミングや、検索戦略の改善が求められる局面を可視化します。

実運用のユースケースと応用例

エージェント駆動型 RAG は、従来の検索システムに比べて大きな利点があり、法律調査や財務分析のような知識集約領域で特に有効です。事前にインデックス化したベクトルデータベースに依存する静的な検索手法と異なり、エージェント駆動型のシステムは、ウェブサイト、構造化データベース、非ベクトル化ドキュメントなどのリアルタイムな情報源に能動的にクエリを投げます。これにより手動更新の負担が減り、常に最新の情報を反映した回答を提供できます。

法律調査:判例検索の精度と適応性を強化

エージェント駆動型 RAG により、法的リサーチを包括的に実施できるようになる:
  • 外部データベースへのクエリ実行まだインデックス化されていない可能性のある判例、法令、規制文書向けに。
  • 複雑な条件ベース検索への対応(例えば、特定の金額基準や和解金額を伴う訴訟)
  • 反復的なクエリ再構成による結果の洗練無関係な結果を減らし、幻覚を最小化する。
法律実務に携わる人々にとって、これは複雑な法的課題のリサーチにおける精度向上、適応性の強化、そしてより的確な意思決定につながります。

財務分析:リアルタイムの市場インサイト

Agentic RAG は次の観点で財務分析を強化します。
  • リアルタイムデータへのアクセス外部の金融ソース、決算報告、経済指標から。
  • クエリを動的に調整する市場イベントや大口取引などの微妙な数値パラメータを捉えるために。
  • 不正確さの低減自己修正と適応的な検索戦略を通じて。
これにより、アナリストは変化の激しい環境でも、より十分な根拠に基づいたリアルタイムの財務判断を下せるようになります。

エージェント駆動型RAGシステムにおける課題と緩和策

1. 複雑な検索によるレイテンシーの増加

エージェント駆動型RAGは複数のソースから検索するため、静的なLLM照会に比べて応答時間が遅くなる場合がある。
緩和策
  • キャッシュ機構を実装する頻繁に照会されるデータ向けに
  • 並列処理を最適化する複数の検索を同時に実行するために
  • 高速なソースを優先する検索範囲を拡大する前に

2. ツール選定の信頼性

不適切な検索ツールを選ぶと、無関係または不完全な回答につながる。
緩和策
  • ツール選定を監視するWeave を用いて体系的な誤りを特定し、リアルタイムで挙動を調整する。
  • 信頼度のしきい値を導入する低関連の結果が検出された場合は検索を拡大する。
  • 代替クエリを提案するより正確な検索へとユーザーを導くために。

3. 倫理的懸念:プライバシーとバイアスのリスク

実データベースとやり取りすることは、データ漏えいとバイアス増幅のリスクを伴う。
緩和策
プライバシーエージェントを導入して次を実施する:
  • 厳格なアクセス制御を実施する不正なデータ取得を防止するために。
  • 機微情報にフラグを立てる処理を開始する前に。
  • 順守を徹底する各種プライバシー規制(例:GDPR、HIPAA)に準拠すること。
    • 複数の情報源を相互参照して、誤情報のリスクを低減する。
    • 透明性と説明責任のために監査ログを維持する。
プライバシー重視の要件を組み込むことで セーフガード、エージェント型RAGは、自動化された検索においてセキュリティと信頼性を確保できる。

結論

検索拡張生成(RAG)は、単純で静的な検索プロセスから、エージェント型RAGのアーキテクチャによって、より動的かつ適応的なシステムへと進化してきた。従来のRAGが単一ステップの参照メカニズムを提供するのに対し、エージェント型RAGはマルチステップ推論、適応的なクエリ分解、モジュール化された検索エージェントを導入し、応答精度とスケーラビリティを高める。
単一エージェントシステムは構造化された検索において効率性とシンプルさを提供し、マルチエージェントアーキテクチャは複雑なクエリに対してより高い柔軟性をもたらす。Claude 3.7 のようなツールや Weave のような監視フレームワークを統合することで、検索の洗練、知識ソースの拡張、そしてリアルタイムでの精度向上が可能になる。AI駆動の検索が進化するにつれ、エージェント型RAGシステムは、情報アクセスの効率化を通じて法務調査や金融などの産業を強化していく。

本記事はAIによる翻訳です。誤訳の可能性があればコメント欄でご指摘ください。原文レポートへのリンクはこちらです。 原文レポートを表示
Iterate on AI agents and models faster. Try Weights & Biases today.