Skip to content

Solo Mode

Potatoでアノテーションパイプライン全体を1人で実行します。1人のエキスパートとLLMが、プロンプト作成、並行アノテーション、不一致の解消、最終検証を含むフェーズのステートマシンに沿って作業を進めます。

v2.3.0の新機能

従来のアノテーションプロジェクトでは、複数のアノテーター、アノテーター間一致度の計算、裁定ラウンド、そして大きな調整コストが必要です。多くの研究チームにとって、これが最大のボトルネックです。問題はアノテーションインターフェースではなく、チームの雇用、トレーニング、管理にかかる手間です。

Solo Modeは、複数アノテーターによる方式を、1人の人間のエキスパートとLLMの協働に置き換えます。人間は、戦略的に選ばれた小さなサブセットに高品質なラベルを付けます。LLMはそのラベルから学習して残りのラベルを提案し、人間はLLMが確信を持てないケースや誤っている可能性が高いケースだけをレビューします。この流れはフェーズのステートマシンが制御します。設定した閾値に達すると先へ進み、プロンプト自体に問題があるとわかった場合はプロンプトに戻ります。

社内ベンチマークでは、Solo Modeは人間によるラベル総数のわずか10〜15%で、複数アノテーターによる完全なパイプラインと95%以上の一致を達成しました。

ワークフロー

Solo Modeはステートマシンです。SoloPhaseControllerは、プロジェクトが現在どのフェーズにあり、次にどのフェーズへ移れるかを管理します。そのため経路は一直線ではありません。プロンプトにまだ手直しが必要な場合、いくつかのフェーズは前に戻ることができます。

セットアップ

タスクの説明とデータ。Generate promptは、リビジョンモデル(revision_models)に説明とアノテーションスキーマから最初のプロンプトを書かせます。リビジョンモデルがない場合や、何も返さなかった場合は、説明そのものが最初のプロンプトになり、プロンプトの履歴にその旨が記録されます。セットアップから移れるのはプロンプトレビューだけです。

プロンプトレビュー

Potatoがタスクの説明からプロンプトを合成し、あなたがそれを編集します。プロンプトが問題だとわかったとき、後続のいくつかのフェーズはここに戻ってきます。そのため、このフェーズは一度きりのステップではなく、ワークフロー全体の中心になります。

ここからプロジェクトはエッジケースのループを経由するか、直接並行アノテーションへ進みます。

エッジケースの合成、続いてエッジケースのラベリング

LLMが境界上の例を生成し、あなたがそれにラベルを付けます。例からプロンプトの誤りがわかった場合、ラベリングはプロンプトレビューに戻ることがあります。

プロンプト検証

エッジケースに付けたあなたのラベルを、プロンプトが出力する結果と照合します。ここから並行アノテーションへ進むか、プロンプトレビューに戻ります。

並行アノテーション

あなたとLLMが同じデータを同時にアノテーションします。以降のすべての判断の基準となる一致度の数値を出すのがこのフェーズです。

不一致の解消

あなたとLLMの判断が分かれた箇所は、あなたが決着をつけます。その後は並行アノテーションに戻るか、不一致が個々のアイテムではなくプロンプトに原因があることを示している場合はプロンプトレビューに戻ります。

アクティブアノテーション

メインのアノテーションフェーズです。インスタンスはinstance_selectionの重み付きプールによって選ばれます。ここからPotatoは定期レビュー、ルールレビュー、または自律ラベリングへ進みます。

定期レビュー

確信度の低いLLMラベルを、thresholds.periodic_review_interval件ごとに提示します。その後はアクティブアノテーションに戻るか、ルールレビューへ進むか、プロンプトレビューまで戻ります。

ルールレビュー

Potatoがあなたのラベリングから抽出したエッジケースルールを、承認のために提示します。その後はアクティブアノテーションかプロンプトレビューに戻ります。

自律ラベリング

残りをLLMがラベリングします。このフェーズから移れるのは最終検証だけです。

最終検証

LLMだけが付けたラベルのサンプルをあなたが確認します。thresholds.end_human_annotation_agreementを上回ればプロジェクトは完了し、下回ればアクティブアノテーションに戻ります。

完了

終了状態です。


設定

クイックスタート

Solo Modeの最小構成:

yaml
annotation_task_name: "Sentiment Classification"
task_dir: "."
 
data_files:
  - "data/reviews.jsonl"
 
item_properties:
  id_key: id
  text_key: text
 
solo_mode:
  enabled: true
 
  # The models that label. A list: the first is used, the rest are fallbacks.
  labeling_models:
    - endpoint_type: openai
      model: "gpt-4o"
      api_key: ${OPENAI_API_KEY}
      temperature: 0.1
      max_tokens: 1024
 
  # When the project may stop asking the human
  thresholds:
    end_human_annotation_agreement: 0.92
    minimum_validation_sample: 50
    confidence_low: 0.5
 
  state_dir: "solo_state_sentiment"
 
annotation_schemes:
  - annotation_type: radio
    name: sentiment
    description: "What is the sentiment of this text?"
    labels:
      - Positive
      - Neutral
      - Negative
 
output_annotation_dir: "output/"
export_annotation_format: "jsonl"

設定リファレンス(全項目)

yaml
solo_mode:
  enabled: true
 
  # Models that label, and models that revise. Both are lists; revision_models
  # defaults to labeling_models when omitted.
  labeling_models:
    - endpoint_type: openai       # openai, anthropic, ollama, vllm, ...
      model: "gpt-4o"
      api_key: ${OPENAI_API_KEY}
      temperature: 0.1
      max_tokens: 1024
  revision_models:
    - endpoint_type: anthropic
      model: "claude-sonnet-4-20250514"
      api_key: ${ANTHROPIC_API_KEY}
      max_tokens: 1024
 
  # Sentence embeddings, used for diversity and neighbour lookups
  embedding:
    model_name: "all-MiniLM-L6-v2"
 
  # How the LLM's uncertainty is measured
  uncertainty:
    strategy: direct_confidence
    sampling_diversity:
      num_samples: 5
      temperature: 1.0
 
  # When the human can stop, and what counts as agreement per scheme type
  thresholds:
    end_human_annotation_agreement: 0.90
    minimum_validation_sample: 50
    confidence_low: 0.5
    confidence_high: 0.8
    periodic_review_interval: 100
    likert_tolerance: 1
    multiselect_jaccard_threshold: 0.5
    textbox_embedding_threshold: 0.7
    span_overlap_threshold: 0.5
 
  # Weights for the pools that choose what the human sees next
  instance_selection:
    low_confidence_weight: 0.3
    disagreement_weight: 0.1
    diversity_weight: 0.2
    llm_predicted_weight: 0.3
    random_weight: 0.1
    cartography_weight: 0.0
    edge_case_rule_weight: 0.0
 
  batches:
    llm_labeling_batch: 50
    max_parallel_labels: 200
 
  labeling_functions:
    enabled: true
    auto_extract: true
    max_functions: 50
    min_confidence: 0.85
    min_coverage: 3               # instances, not a fraction
    vote_threshold: 0.5
 
  edge_case_rules:
    enabled: true
    auto_extract_on_labeling: true
    confidence_threshold: 0.75
    min_rules_for_clustering: 10
    target_cluster_size: 15
    reannotation_enabled: true
    reannotation_confidence_threshold: 0.60
    max_reannotations_per_instance: 2
 
  confusion_analysis:
    enabled: true
    min_instances_for_pattern: 3
    max_patterns: 20
    auto_suggest_guidelines: false
 
  prompt_optimization:
    enabled: true
    find_smallest_model: true
    target_accuracy: 0.85
    optimization_interval_seconds: 300
    accuracy_weight: 0.7
    length_weight: 0.2
 
  # Send cheap cases to a small model and hard ones to a larger one
  confidence_routing:
    enabled: false
    tiers:
      - name: cheap
        confidence_threshold: 0.8
        model:
          endpoint_type: ollama
          model: "llama3.2"
 
  refinement_loop:
    enabled: true
    trigger_interval: 50
    min_improvement: 0.02
    max_cycles: 5
    patience: 2
 
  state_dir: "solo_state_sentiment"

主な機能

混同分析

各ラベリングラウンドの後、Potatoは人間とLLMのラベル間の混同行列を作成します。管理ダッシュボードには以下が表示されます:

  • LLMの観点から見たクラスごとの適合率、再現率、F1
  • 最も多い混同ペア(例:「neutralをpositiveと誤分類:23件」)
  • 各混同ペアのインスタンス例
  • 改善ラウンドを通じた改善を示すトレンドチャート

Solo Modeにはコマンドラインインターフェースがありません。サーバー内で動作し、実行中のインスタンス内にある専用ページから操作するため、分析結果はCLIではなくダッシュボードで確認します。表示される内容は次のとおりです:

text
Confusion Analysis (Round 2)
============================
Overall Accuracy: 0.87 (target: 0.92)

Top Confusion Pairs:
  neutral -> positive:  23 instances (15.3%)
  negative -> neutral:  11 instances (7.3%)
  positive -> neutral:   5 instances (3.3%)

Per-Class Performance:
  Positive:  P=0.91  R=0.94  F1=0.92
  Neutral:   P=0.78  R=0.71  F1=0.74
  Negative:  P=0.93  R=0.88  F1=0.90

自動改善ループ

改善ループは、LLMによるラベリング、混同分析、ガイドラインの更新を繰り返します。各イテレーションでは次の処理が行われます:

  1. LLMが現在のガイドラインでデータセット全体にラベルを付ける
  2. Potatoが利用可能なすべての人間のラベルと比較する
  3. 精度が閾値を下回っている場合、混同分析を実行する
  4. LLMがエラーパターンに基づいてガイドラインの修正を提案する
  5. 人間が修正をレビューして承認する
  6. サイクルを繰り返す(最大max_cycles回)
yaml
solo_mode:
  revision_models:
    - endpoint_type: anthropic
      model: "claude-sonnet-4-20250514"
      api_key: ${ANTHROPIC_API_KEY}
 
  refinement_loop:
    enabled: true
    trigger_interval: 50           # run a cycle every 50 new human labels
    min_improvement: 0.02          # stop if a cycle gains less than 2%
    max_cycles: 5
    patience: 2                    # cycles below min_improvement before stopping

ラベリング関数(ALCHEmistに着想)

Potatoは、人間のアノテーションに見られるパターンから軽量なラベリング関数を生成します。これらはLLMの呼び出しではなく、高速で決定的なルールです。

生成されるラベリング関数の例:

python
# Auto-generated labeling function 1
# Precision: 0.96, Coverage: 0.08
def lf_strong_positive_words(text):
    positive = {"excellent", "amazing", "fantastic", "outstanding", "perfect"}
    if any(w in text.lower() for w in positive):
        if not any(neg in text.lower() for neg in {"not", "never", "no"}):
            return "Positive"
    return None  # abstain
 
# Auto-generated labeling function 2
# Precision: 0.93, Coverage: 0.05
def lf_explicit_negative(text):
    negative = {"terrible", "awful", "horrible", "worst", "disgusting"}
    if any(w in text.lower() for w in negative):
        return "Negative"
    return None

ラベリング関数の動作を設定します:

yaml
solo_mode:
  labeling_functions:
    enabled: true
    auto_extract: true
    max_functions: 50
    min_confidence: 0.85
    min_coverage: 3          # minimum instances a function must cover
    vote_threshold: 0.5      # agreement needed when functions disagree

不一致エクスプローラー

不一致エクスプローラーは、異なるシグナル同士が食い違うインスタンスを提示します。各インスタンスについて、アノテーターには以下が表示されます:

  • LLMの予測ラベルとその確信度
  • ラベリング関数の投票(ある場合)
  • 埋め込み空間で最も近いラベル付き近傍
  • 元のテキストやコンテンツ

各ラベルが実際の曖昧さを解消するため、これは最も価値の高いアノテーション作業です。

yaml
solo_mode:
  # Disagreement is a phase, not a configurable block. What you control is how
  # heavily disagreement weighs when Potato picks the next instance.
  instance_selection:
    disagreement_weight: 0.3
    low_confidence_weight: 0.3
    diversity_weight: 0.2
    llm_predicted_weight: 0.1
    random_weight: 0.1

段階的な確信度エスカレーション

データセットの大部分をLLMがラベリングした後、PotatoはLLMがラベルを付けたすべてのインスタンスを確信度順に並べ、確信度の最も低いものを人間にエスカレーションします。品質が安定するまで、これをバッチ単位で続けます。

yaml
solo_mode:
  thresholds:
    confidence_low: 0.5           # below this, the human sees it
    confidence_high: 0.8          # above this, the LLM's label stands
    periodic_review_interval: 100 # surface low-confidence labels this often
 
  batches:
    llm_labeling_batch: 50
    max_parallel_labels: 200

意思決定モデル

Potato 2.11から、Solo Modeで意思決定モデルにラベリングさせることができます。意思決定モデルは固定選択肢の質問に1回のフォワードパスで答え、すべての選択肢の確率を返します。そのためラベリングではLLMより高速かつ安価ですが、プロンプト、ルール、説明を書くことはできません。意思決定モデルには2つのエンドポイントタイプで接続できます。layaはLayaをPotatoのプロセス内でCPU上で実行し、pip install 'potato-annotation[decision]'が必要です。systemoneは、ホスト版のJevやOpenJevなど、/v1/systemone APIを持つ任意のサーバーと通信します。

意思決定モデルの一般的な使いどころは確信度ルーティングの最初の段です。そこですべてにラベルを付け、確信の持てないインスタンスだけがLLMに回ります:

yaml
solo_mode:
  labeling_models:
    - endpoint_type: laya
      model: convaiinnovations/laya
  revision_models:                     # required: decision models cannot write text
    - endpoint_type: vllm
      model: google/gemma-4-12B-it-qat-w4a16-ct
      base_url: http://localhost:8001/v1
  confidence_routing:
    enabled: true
    tiers:
      - name: laya
        confidence_threshold: 0.9
        model:
          endpoint_type: laya
          model: convaiinnovations/laya
          device: cpu
      - name: llm
        confidence_threshold: 0.5
        model:
          endpoint_type: vllm
          model: google/gemma-4-12B-it-qat-w4a16-ct
          base_url: http://localhost:8001/v1

revision_modelsは生成モデルでなければなりません。デフォルトはlabeling_modelsなので、意思決定モデルしか使わない研究ではこれを設定する必要があります。意思決定モデルがラベリングできるのはradio、select、likert、sliderのスキーマで、multiselect、スパン、自由記述のスキーマをラベリングするよう設定されている場合、Solo Modeは起動を拒否します。評定尺度では、Layaのスコアは尺度をほとんど追えないことがあるので、likertやsliderのスキーマをそこに回す前に、Try on 10 itemsでいくつかのインスタンスを確認してください。確信度はモデルが選んだ選択肢に与えた確率で、その較正の度合いはドメインによって異なるため、段のしきい値は自分のデータをもとに設定してください。実行可能な版はconfig-decision-model.yamlにあります。

ブラインド提案

デフォルトでは、アノテーションページはあなたが回答する前に、LLMのラベル、その確信度、Acceptボタンを表示します。これは速い一方で、あなたの回答を提案の方へ引き寄せます。Solo Modeは、LLMとの一致率がend_human_annotation_agreementを超えた時点でコーパスの残りをLLMに引き渡すので、提案を見た後に付けたラベルで測った一致率では、あなたの作業が早すぎる段階で終わってしまうことがあります。Potato 2.11からは、solo_mode.suggestionsで提案を表示するタイミングを制御できます:

yaml
solo_mode:
  suggestions:
    display: blind_sample      # before_label | after_label | blind_sample
    blind_rate: 0.2
    minimum_blind_sample: 20

blind_sampleはblind_rateの割合のインスタンスで提案を伏せ、after_labelは提案を決して先に表示しません。ブラインドの比較がminimum_blind_sample件そろうと、引き渡しの判定ではブラインドでの一致率をend_human_annotation_agreementと比較します。ダッシュボードのBlind Agreementカードはブラインドでの一致率と提案を表示したときの一致率を並べて示し、差が大きくプラスであれば、LLMの答えが見えているときのほうがあなたはLLMに同意しやすいということです。

Potato 2.11では、Solo Modeがラベリング開始前と各バッチの前にプロジェクトの支出上限(ai_budget.cap_usd)を確認するようになり、さらに応答キャッシュ(solo_mode.cache)、プロンプトの試行を伴うゴールドセット(solo_mode.gold)、プロンプトの感度チェック、ラベリング用のジュリー(solo_mode.jury)、カリキュラム順序付け(solo_mode.instance_selection.curriculum)が加わりました。それぞれについてはSolo Mode Advanced Featuresで説明しています。ラベリングエンジンが有効な場合、Solo Modeはモデルの予測をエンジンのストアにも書き込むので、エンジンはそれらをキャリブレーションし、証明できます。

複数シグナルによるインスタンスの優先順位付け

人間のラベリングを伴うすべてのフェーズで、Potatoは重み付きプールの仕組みを使って最も情報量の多いインスタンスを選びます。7つのプールが1つの優先度キューにまとめられます:

yaml
solo_mode:
  instance_selection:
    low_confidence_weight: 0.3
    diversity_weight: 0.2
    llm_predicted_weight: 0.3
    disagreement_weight: 0.1
    random_weight: 0.1
    cartography_weight: 0.0
    edge_case_rule_weight: 0.0
  • low_confidence:LLMの確信度がthresholds.confidence_lowを下回る
  • diversity:埋め込み空間で、すでにラベルを付けたものから遠い
  • llm_predicted:LLMのラベルがあり、まだ確認されていない
  • disagreement:そのインスタンスについて得られているシグナルが一致しない
  • random:推定の偏りを防ぎ、見落としを見つけるための小規模なランダム抽出
  • cartography:学習ダイナミクスのシグナル。デフォルトでは無効
  • edge_case_rule:以前のラベリングから抽出されたルールに一致する。デフォルトでは無効

エッジケースの合成

PotatoはLLMを使って、既知の弱点を狙った合成例を生成します:

yaml
solo_mode:
  edge_case_rules:
    enabled: true
    auto_extract_on_labeling: true   # derive rules as you label
    confidence_threshold: 0.75
    min_rules_for_clustering: 10
    target_cluster_size: 15
    reannotation_enabled: true       # revisit items a new rule contradicts
    reannotation_confidence_threshold: 0.60
    max_reannotations_per_instance: 2

LLMは指定されたラベルペアの間で曖昧になる例を生成します。人間がそれにラベルを付け、そのラベルは後続のLLMラベリングラウンドのfew-shotコンテキストに追加されます。

プロンプト最適化(DSPyに着想)

Potatoはプロンプトの自動最適化を実行してLLMに最適な指示形式を探し、目標精度を維持できる最小のモデルも探します:

yaml
solo_mode:
  prompt_optimization:
    enabled: true
    find_smallest_model: true          # prefer the cheapest model that holds up
    target_accuracy: 0.85
    optimization_interval_seconds: 300
    accuracy_weight: 0.7               # how the candidates are scored
    length_weight: 0.2

進捗のモニタリング

管理ダッシュボードには、Solo Modeの進捗がリアルタイムで表示されます:

  • 現在のフェーズと各フェーズ内の進捗
  • 完了した人間のラベル数と総予算の比較
  • LLMの精度の推移(ラウンドごと)
  • ラベリング関数のカバレッジと適合率
  • 確信度分布のヒストグラム
  • 完了までの推定時間

potato.soloモジュールもSolo ModeのCLIもありません。ワークフロー全体がサーバー自身のルートの背後で動作するため、進捗はダッシュボードで確認します。表示内容は次のとおりです:

text
Solo Mode Status
================
Current Phase: 6 (Active Labeling) - Batch 3/10
Human Labels: 142 / ~300 estimated total
LLM Accuracy: 0.89 (target: 0.92)
LF Coverage: 0.23 (labeling functions cover 23% of data)
Dataset Size: 10,000 instances
  - Human labeled: 142
  - LF labeled: 2,300
  - LLM labeled: 7,558
  - Unlabeled: 0

Solo Mode vs. 従来のマルチアノテーターの使い分け

Solo Modeを使用すべき場合:

  • 高品質のラベルを提供できるドメインエキスパートがいる
  • 予算やロジスティクスの制約で複数のアノテーターを雇えない
  • タスクのカテゴリが明確に定義されている
  • 大規模なデータセット(1,000件以上)にラベルを付ける必要がある
  • アノテーター間一致度の測定よりも速度が重要

従来のマルチアノテーターを使用すべき場合:

  • 論文発表のためにアノテーター間一致度の統計が必要
  • タスクが非常に主観的(例:攻撃性、ユーモア)
  • アノテーター間の不一致パターンを研究する必要がある
  • 規制要件で複数の独立したアノテーターが義務付けられている
  • ラベル空間が複雑、または変化している(アノテーションガイドラインがまだ策定中)

ハイブリッドアプローチ: 初期の一括ラベリングにSolo Modeを使い、その後ランダムに抽出した10〜20%のサンプルを2人目のアノテーターに割り当てて一致度の統計を計算します。これにより、Solo Modeの効率と、複数アノテーターによる検証の品質保証を両立できます。

Solo Modeには独自の検証ブロックはありません。2人目のアノテーターは、エクスポートしたラベルに対する通常のPotatoのアノテーション作業として実行し、そのラベルとSolo Modeのラベルの一致度を、任意の2人のアノテーターの場合と同じ方法で計算してください。(icl_labelingにはverificationブロックがありますが、これはSolo Modeではなくインコンテキストラベリングに属するものです。)


参考資料

実装の詳細については、ソースドキュメントを参照してください。