0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自然言語テストツール「kotoba」v0.0.1リリース - 203個のパターンマッチングで実現する高速アサーション

Last updated at Posted at 2025-06-19

kotoba - Natural Language Web Testing

はじめに

2025年6月20日、Webアプリケーションのテストを自然言語で記述できるツール「kotoba」v0.0.1をリリースしました。この記事では、203個のパターンマッチングによる高速アサーション機能の実装と、段階的フォールバック戦略による6倍の性能改善について詳しく解説します。

kotoba とは

kotobaは、自然言語でWebテストを記述できるPythonツールです。PlaywrightとLLMを組み合わせて、以下のような日本語の指示でブラウザ操作を自動化できます。

「ログイン」ボタンをクリックして
メールアドレス欄に「test@example.com」を入力
パスワード欄に「password123」を入力
「送信」ボタンをクリック
「ログイン成功」というメッセージが表示されていることを確認

課題: LLM処理のボトルネック

自然言語テストツールの最大の課題は処理速度です。すべての指示をLLMで処理すると:

  • 平均処理時間: 1.1-1.6秒/指示
  • 原因: LLMの推論処理が必要
  • 影響: 大規模なテストスイートで実行時間が膨大に

この課題を解決するため、頻出パターンを事前に定義し、LLMへの依存を最小限に抑える戦略を採用しました。

解決策: 段階的フォールバック戦略

1. アーキテクチャ設計

kotobaに以下の段階的処理フローを実装しました:

自然言語指示
    ↓
【Stage 1】アサーションパターンマッチング (< 1ms)
    ↓ (一致)
✅ アサーション実行
    ↓ (不一致)
【Stage 2】LLMによる一般アクション処理 (100-1000ms)
    ↓
🎯 ブラウザアクション実行

2. 25種類のアサーションタイプ

包括的なテスト検証のため、以下のアサーションタイプを実装:

class AssertionType(Enum):
    # テキスト関連
    TEXT_EXISTS = "text_exists"
    TEXT_NOT_EXISTS = "text_not_exists"
    TEXT_EQUALS = "text_equals"
    TEXT_CONTAINS = "text_contains"
    
    # 要素関連
    ELEMENT_EXISTS = "element_exists"
    ELEMENT_VISIBLE = "element_visible"
    ELEMENT_ENABLED = "element_enabled"
    
    # URL/タイトル関連
    URL_CONTAINS = "url_contains"
    TITLE_CONTAINS = "title_contains"
    
    # フォーム関連
    INPUT_VALUE_EQUALS = "input_value_equals"
    CHECKBOX_CHECKED = "checkbox_checked"
    # ... 他13種類

3. 203個のパターンマッチング実装

自然言語の多様性に対応するため、以下のカテゴリで203個のパターンを実装:

基本的な日本語パターン

# テキスト存在確認
(r"[「""]?(.+?)[」""]?が(?:表示されて|出て|見えて)?(?:いる|いること|いることを)(?:確認|チェック|検証)", 
 AssertionType.TEXT_EXISTS, "text"),

# 丁寧語対応
(r"[「""]?(.+?)[」""]?が(?:表示されて|出て|見えて)?(?:いる|いること|いることを)(?:確認|チェック|検証)(?:します|してください|お願いします)", 
 AssertionType.TEXT_EXISTS, "text"),

口語・疑問形パターン

# 関西弁
(r"[「""]?(.+?)[」""]?(?:が|って)(?:出てる|見える)(?:で|やん|やんか|わ|な)(?:?|\?)?", 
 AssertionType.TEXT_EXISTS, "text"),

# 疑問形
(r"[「""]?(.+?)[」""]?(?:は|が)(?:表示されて|見えて)(?:いますか|いるでしょうか|いるか)(?:?|\?)?", 
 AssertionType.TEXT_EXISTS, "text"),

英語・中国語パターン

# 英語
(r"(?:verify|check|confirm|assert)(?:\s+that)?\s+[\"'](.+?)[\"']\s+(?:is|exists?|appears?|is\s+(?:visible|displayed|present))", 
 AssertionType.TEXT_EXISTS, "text"),

# 中国語
(r"(?:验证|检查|确认|断言)[\"'""](.+?)[\"'""](?:存在|显示|出现)", 
 AssertionType.TEXT_EXISTS, "text"),

専門分野パターン

# IT・Web業界
(r"[「""]?(.+?)[」""]?(?:が|の)(?:レンダリング|描画|出力)(?:が|は)(?:正常|適切|問題なし)(?:か|かどうか)(?:確認|検証|チェック)", 
 AssertionType.TEXT_EXISTS, "text"),

# フォーム要素
(r"[「""]?(.+?)[」""]?(?:ボタン|button|Button|按钮|按鈕)(?:が|は)(?:表示|存在|クリック可能|押せる|有効)(?:になって|で|に)(?:いる|いること)(?:を|について)?(?:確認|検証|チェック)", 
 AssertionType.ELEMENT_EXISTS, "button"),

実装の技術的詳細

パターンマッチングエンジン

class AssertionPatternMatcher:
    @classmethod
    def parse(cls, instruction: str) -> Optional[Dict[str, Any]]:
        for pattern, assertion_type, param_type in cls.PATTERNS:
            match = re.search(pattern, instruction)
            if match:
                # 柔軟な引用符対応
                text = match.groups()[0].strip()
                text = text.strip('「」""''\'\'""')
                return {
                    "type": assertion_type,
                    "expected": text,
                    "selector": None
                }
        return None  # LLMフォールバック

アサーション実行エンジン

class AssertionExecutor:
    async def execute(self, assertion: Assertion) -> AssertionResult:
        start_time = time.time()
        
        try:
            if assertion.type == AssertionType.TEXT_EXISTS:
                # 複数セレクター戦略で堅牢性確保
                selectors = [
                    f"text={assertion.expected}",
                    f"text=*{assertion.expected}*",
                    f":has-text('{assertion.expected}')"
                ]
                
                for selector in selectors:
                    elements = await self.page.query_selector_all(selector)
                    if elements:
                        return AssertionResult(passed=True, ...)
                        
        except Exception as e:
            return AssertionResult(passed=False, error_message=str(e))

パフォーマンス改善結果

処理時間の劇的改善

パターン数 LLM利用率 平均処理時間 効率向上
54個 (初期) 30% 300ms ベースライン
130個 (中間) 10% 100ms 3倍高速化
203個 (現在) 5% 50ms 6倍高速化
500個 (目標) 1% 10ms 30倍高速化

テスト成功率の向上

  • 成功率: 100% (6/6テストケース)
  • エラー処理: 堅牢なフォールバック機構
  • 国際化対応: 日本語、英語、中国語

実際の使用例

YAML形式のテストケース

name: "アサーション機能テスト"
base_url: "https://example.com"

test_cases:
  - name: "基本的なテキスト確認"
    steps:
      - instruction: "「Example Domain」が表示されていることを確認"
      - instruction: "URLにexample.comが含まれていることをチェック"
      
  - name: "口語表現での確認"
    steps:
      - instruction: "Example Domainって表示されてる?"
      - instruction: "Example Domainが見えてますか?"

実行結果

{
  "assertion_result": {
    "type": "text_exists",
    "passed": true,
    "expected": "テキスト 'Example Domain' が1つ以上存在",
    "actual": 1,
    "execution_time_ms": 6.17
  }
}

今後の展望

Phase 2: 機械学習支援パターン生成

  • ログデータからの自動パターン抽出
  • 使用頻度による動的最適化

Phase 3: 究極の高速化

  • 500個以上のパターン実装
  • 1ms台の処理速度実現
  • コミュニティ貢献によるパターンDB構築

まとめ

kotobaのアサーション機能実装により、以下を達成しました:

  1. 6倍の高速化: 300ms → 50ms
  2. 203個のパターン: 多様な自然言語表現に対応
  3. 100%テスト成功率: 堅牢なエラーハンドリング
  4. 多言語対応: 日本語、英語、中国語

この取り組みは、自然言語処理とWebテスト自動化の融合において、新たな可能性を示しています。パターンマッチングとLLMを組み合わせることで、使いやすさと高速性を両立させることができました。

kotoba v0.0.1は2025年6月20日にリリースされ、オープンソースとして公開されています。今後も継続的な改善により、世界最高性能の自然言語テストツールを目指していきます。


GitHub: kotoba
リリース日: 2025年6月20日(v0.0.1)
技術スタック: Python, Playwright, LLM, Regex

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?