Back to OSS
Swift Package LLM / AI

swift-research-agent

Web 検索・取得ツールと引用ゲートを備えたリサーチャー・エージェント

Swift
agentresearchweb-search

title: swift-research-agent README created: 2026-06-27 tags: [swift, spm, agent, research, llm] status: active

swift-research-agent

出典検証ゲート付きの Web リサーチエージェント — 検索・取得・引用検証を 3 層構造で提供する Swift Package。

LLM エージェントが「記憶や検索スニペットだけに基づいた引用」を生成するのを構造的に防ぐ。 web_searchfetch ツールが観測したソースを SourceRegistry に記帳し、ResearchCitationGate が「fetch 済みページしか引用できない」規約を決定論的に検証する。 違反があれば是正メッセージを積んで自動再試行し、合格した回答に構造化出典データを添付する。

アーキテクチャ概要

ResearchStore          Layer 0 — ソース台帳(UI / LLM / ネットワーク非依存)
    └── SourceRegistry     観測した URL・fetch 本文の SSOT(actor)

ResearchAgentTools     Layer 1 — Web 調査ツール群
    ├── ResearchToolKit    web_search / fetch ツールを提供、SourceRegistry に記帳
    ├── SerperSearchProvider   Google SERP (serper.dev)
    ├── BraveSearchProvider    Brave Search API
    ├── FallbackSearchProvider 複数プロバイダーの自動フォールバック
    └── ResilientSearchProvider レート制限 / サーキットブレーカー / LRU キャッシュ

ResearchAgent          Layer 2 — エージェント組立
    ├── ResearchAgentExecutor  AgentLoop + 出典検証リトライ + artifact 出力
    ├── ResearchCitationGate   fetch 済み確認・URL 実在確認の決定論的ゲート
    └── ResearcherAgent        system prompt / AgentCard の自己記述

提供モジュール

ResearchStore

ネットワーク・LLM に依存しない純粋なデータ層。タスク中に観測した全ソースを管理する。

役割
SourceRegistry 観測ソースの台帳(actor)。registerSearchResult / registerFetch で記帳し、record(citing:) / references(citedURLs:) で照会する
SourceRecord 1 ソースの記録(URL・タイトル・スニペット・fetch 済みフラグなど)

ResearchAgentTools

Web 調査ツールと検索プロバイダーを提供する。SourceRegistry への記帳まで担い、引用の検証はしない。

役割
ResearchToolKit web_search / fetch ツールを LLM に提供する ToolKitSourceRegistry と共有して記帳する
ResearchToolID ツール ID の列挙(.webSearch / .fetch)。有効化するツールセットを ToolKit・system prompt・AgentCard で一致させるための SSOT
WebSearchProvider 検索バックエンドの抽象プロトコル
SerperSearchProvider Serper API 経由の Google SERP 検索
BraveSearchProvider Brave Search API 検索
FallbackSearchProvider 複数プロバイダーの順番試行チェーン
ResilientSearchProvider レート制限 + サーキットブレーカー + LRU キャッシュを統合したラッパー
SearchResilienceConfiguration レジリエンス設定(RPS・失敗閾値・キャッシュ TTL など)

ResearchAgent

エージェントの組立と出典検証ロジック。

役割
ResearchAgentExecutor AgentLoop を回し、ResearchCitationGate で回答を検証。違反時は是正メッセージを積んで再試行(上限 maxRetries)。合格した回答に research.references メタデータを添付する
ResearchCitationGate 「出典 URL が台帳に存在し fetch 済みか」を決定論的に検証。ネットワーク・LLM 不要
ResearcherAgent system prompt / AgentCard の自己記述。有効ツール構成に応じて内容を剪定する

インストール

Swift Package Manager

Package.swiftdependencies に追加:

.package(url: "https://github.com/no-problem-dev/swift-research-agent.git", from: "1.0.0")

使用するターゲットに追加:

.target(
    name: "MyApp",
    dependencies: [
        .product(name: "ResearchAgent", package: "swift-research-agent"),
        .product(name: "ResearchAgentTools", package: "swift-research-agent"),
        .product(name: "ResearchStore", package: "swift-research-agent"),
    ]
)

使用例

基本: ResearchToolKit の組み立て

import ResearchStore
import ResearchAgentTools

// SourceRegistry はセッションスコープの actor — ToolKit とゲートで共有する
let registry = SourceRegistry()

// Serper プロバイダー付き(レジリエンスあり)
let toolKit = ResearchToolKit.serper(
    registry: registry,
    apiKey: "YOUR_SERPER_API_KEY",
    gl: "jp",
    hl: "ja"
)

// 利用可能なツール ID を確認
print(toolKit.availableToolIDs)  // [.webSearch, .fetch]

検索プロバイダーのフォールバックチェーン

import ResearchAgentTools

// Brave が失敗したら Serper にフォールバック
let provider = FallbackSearchProvider(providers: [
    BraveSearchProvider(apiKey: "BRAVE_KEY", searchLang: "ja", country: "JP"),
    SerperSearchProvider(apiKey: "SERPER_KEY", gl: "jp", hl: "ja"),
])

let toolKit = ResearchToolKit(registry: registry, searchProvider: provider)

レジリエンス設定のカスタマイズ

import ResearchAgentTools

let resilience = SearchResilienceConfiguration(
    maxRequestsPerSecond: 2.0,   // レート制限: 2 req/sec
    failureThreshold: 3,          // 3 回失敗でサーキットブレーカー open
    resetTimeout: 30,             // 30 秒後に half-open
    cacheTTL: 600,                // 10 分キャッシュ
    maxCacheEntries: 200,
    maxRetries: 2
)

let toolKit = ResearchToolKit.serper(
    registry: registry,
    apiKey: "YOUR_API_KEY",
    resilience: resilience
)

ResearchAgentExecutor の組み立て(swift-agent-runtime 連携)

import ResearchStore
import ResearchAgentTools
import ResearchAgent
import AgentRuntime
import LLMClient

let registry = SourceRegistry()
let toolKit = ResearchToolKit.serper(registry: registry, apiKey: "SERPER_KEY")

// ツール構成(有効化するツール ID を 1 箇所で決めて全層に反映)
let enabledTools: Set<ResearchToolID> = ResearchToolID.allTools

// ToolSet 構築
let toolSet = ToolSet { toolKit.tools(enabled: enabledTools) }

// executor 構築
let executor = ResearchAgentExecutor(
    client: myLLMClient,
    model: myModel,
    tools: toolSet,
    systemPrompt: ResearcherAgent.systemPrompt(
        outputConstraint: "Reply concisely in Japanese.",
        tools: enabledTools
    ),
    maxSteps: 16,
    registry: registry,
    maxRetries: 2,
    cachePolicy: .default,
    history: myHistoryStore
)

ResearchCitationGate の単体検証

import ResearchStore
import ResearchAgent

let registry = SourceRegistry()

// ツールが fetch 成功を記帳した後
await registry.registerFetch(
    url: "https://example.com/article",
    title: "Example Article",
    content: "..."
)

// 回答テキストを検証
let issues = await ResearchCitationGate.validate(
    text: "詳細は https://example.com/article を参照。",
    registry: registry
)

if issues.isEmpty {
    print("出典検証 OK")
} else {
    // 是正メッセージを生成して LLM に再入力
    let corrective = ResearchCitationGate.corrective(issues: issues)
    print(corrective)
}

Artifact から References を取り出す

import ResearchAgent
import ResearchStore

// ResearchAgentExecutor が合格した回答に添付するキー
let key = ResearchAgentExecutor<MyClient>.referencesMetadataKey  // "research.references"

if let json = artifact.metadata?[key],
   case .string(let jsonString) = json,
   let data = jsonString.data(using: .utf8),
   let references = try? JSONDecoder().decode([SourceRecord].self, from: data) {
    for ref in references {
        print("\(ref.title ?? ref.url)  fetched=\(ref.fetched)")
    }
}

出典検証の仕組み

ResearchCitationGate は 3 つの規約を順に検証する。

規約 内容
出典必須 回答に URL が 1 件以上引用されていること(ツールを使わない回答の排除)
実在 引用 URL が SourceRegistry に記帳されていること(記憶・捏造 URL の排除)
取得済み 引用 URL が fetch 成功済みであること(スニペットのみを根拠にした引用の排除)

URL は正規化(トラッキングパラメータ・フラグメント・www 畳み込み)して照合するため、表記ゆれによる偽陰性が起きない。 検証はネットワークも LLM も使わない決定論的処理。

エラーハンドリング

import ResearchAgentTools

do {
    let results = try await provider.search(query: "Swift 6", maxResults: 5)
} catch WebSearchError.providerNotConfigured {
    // ResearchToolKit に searchProvider が注入されていない
} catch WebSearchError.circuitBreakerOpen {
    // 連続失敗によりサーキットブレーカーが open 状態
} catch WebSearchError.httpError(let statusCode) {
    // HTTP エラー(429: レート制限, 403: アクセス拒否 など)
}

do {
    // fetch ツールの内部エラー
} catch ResearchToolError.domainNotAllowed(let domain, let allowed) {
    // allowedDomains を設定した場合のドメイン制限違反
} catch ResearchToolError.contentTooLarge(let size, let maxSize) {
    // PDF・バイナリなど変換不可コンテンツ
}

対応プラットフォーム

プラットフォーム 最小バージョン
macOS 14.0+
iOS 17.0+

Swift 6 / strict concurrency 対応(SourceRegistry / RateLimiter / CircuitBreaker / SearchResultCache はすべて actor)。

関連パッケージ

パッケージ 役割
swift-llm-client Tool / ToolSet / SystemPrompt / AgentCapableClient の定義
swift-agent-runtime AgentExecutor / AgentLoop / TaskUpdater の実行環境
swift-http-transport HTTP トランスポート抽象(テスト時にモック差し替え可)

最終更新: 2026-06-27

© 2026 Kyoichi Taniguchi. All rights reserved.