單人模式(Solo Mode)
在 Potato 中由一個人跑完整條標註流水線:一位專家和一個 LLM 沿著階段狀態機推進,涵蓋提示詞編寫、並行標註、分歧解決和最終驗證。
v2.3.0 新增
傳統標註項目需要多名標註者、標註者間一致性計算、仲裁輪次以及大量協調開銷。對許多研究團隊來說,主要瓶頸不是標註介面,而是招聘、培訓和管理團隊這些後勤工作。
Solo Mode 用一位人類專家與 LLM 的協作取代了多標註者範式。人類在一個精心挑選的小子集上提供高品質標籤。LLM 從這些標籤中學習,為其餘資料提出標籤,人類只稽核 LLM 不確定或可能出錯的案例。整個過程由一個階段狀態機編排,按你設定的閾值向前推進,並在發現問題出在提示詞本身時繞回提示詞環節。
在內部基準測試中,Solo Mode 與完整的多標註者流程達到了 95% 以上的一致性,而只需要 10-15% 的人工標籤總量。
工作流程
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 配置:
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"完整配置參考
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
- 最常見的混淆對(例如"中性被誤分為正面:23 個實例")
- 每個混淆對的示例實例
- 展示各最佳化輪次改進情況的趨勢圖
Solo Mode 沒有命令列介面。它執行在伺服器內部,由執行實例下它自己的頁面驅動,所以分析結果是從面板而不是 CLI 讀取的。它報告的內容大致是這樣:
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 標註、混淆分析和指南更新之間迭代。每次迭代:
- LLM 用當前指南標註完整資料集
- Potato 與所有可用的人工標籤做對比
- 如果準確率低於閾值,執行混淆分析
- LLM 根據錯誤模式提出指南修改建議
- 人類審查並批准這些修改
- 迴圈重複(最多
max_cycles次)
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 呼叫,而是快速、確定性的規則。
生成的標註函式示例:
# 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配置標註函式的行為:
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 預測的標籤和置信度
- 標註函式的投票(如果有)
- 嵌入空間中最近的已標註鄰居
- 原始文本或內容
這是價值最高的標註活動:每個標籤都解決一處真實的歧義。
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 標註的實例排序,把置信度最低的升級給人類。這個過程按批次持續進行,直到品質趨於穩定。
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 可以用決策模型來標註。決策模型用一次前向計算回答一道固定選項的題目,併為每個選項返回一個機率,所以它標註起來比 LLM 更快、更便宜,但它寫不了提示詞、規則或解釋。有兩種端點類型可以接入決策模型。laya 在 Potato 程序內用 CPU 執行 Laya,需要 pip install 'potato-annotation[decision]';systemone 則連線任何提供 /v1/systemone API 的伺服器,例如託管的 Jev 或 OpenJev。
決策模型通常放在置信度路由的第一層。它為所有條目打標籤,只有它沒把握的條目才會繼續交給 LLM:
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/v1revision_models 必須是生成式模型。它預設等於 labeling_models,所以唯一模型是決策模型的研究必須單獨設定它。決策模型能標註 radio、select、likert 和 slider 方案;如果把決策模型設為標註 multiselect、片段或自由文本方案,Solo Mode 會拒絕啟動。在評分量表上,Laya 的分數幾乎跟不上量表,所以在讓 likert 或 slider 方案經過它之前,先用 Try on 10 items 檢查幾個條目。置信度是模型給它所選選項的機率,它的校準程度因領域而異,所以請根據你自己的資料設定該層的閾值。config-decision-model.yaml 是一個可以直接執行的版本。
盲態建議
預設情況下,標註頁面會在你作答之前顯示 LLM 的標籤、它的置信度和一個 Accept 按鈕。這樣很快,也會把你的答案拉向建議。當你與 LLM 的一致性超過 end_human_annotation_agreement 時,Solo Mode 會把剩餘語料交給 LLM,所以如果一致性是在你看過建議之後給出的標籤上測得的,你那部分工作就可能過早結束。從 Potato 2.11 起,solo_mode.suggestions 控制建議何時出現:
solo_mode:
suggestions:
display: blind_sample # before_label | after_label | blind_sample
blind_rate: 0.2
minimum_blind_sample: 20blind_sample 在 blind_rate 比例的條目上隱藏建議,after_label 則從不先顯示建議。盲態比較達到 minimum_blind_sample 次之後,交接時會拿盲態一致性與 end_human_annotation_agreement 比較。面板上的 Blind Agreement 卡片把盲態一致性和顯示建議時的一致性並排列出,如果兩者的正差距很大,說明你在能看到 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 用一套加權池系統挑選資訊量最大的實例。七個池匯入一個統一的優先順序佇列:
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 生成針對已知弱點的合成示例:
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: 2LLM 生成在指定標籤對之間存在歧義的示例。人類標註這些示例,這些標籤隨後被加入後續 LLM 標註輪次的少樣本上下文。
提示詞最佳化(受 DSPy 啟發)
Potato 會執行自動化的提示詞最佳化,為 LLM 找出最好的指令格式,並會尋找仍能守住目標準確率的最小模型:
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:整個工作流都在伺服器自己的路由背後,因此進度是從面板讀取的。它報告的內容是:
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,何時使用傳統多標註者
適合使用 Solo Mode 的情況:
- 你有一位能提供高品質標籤的領域專家
- 預算或後勤條件不允許僱用多名標註者
- 任務的類別清晰、定義明確
- 你需要標註大型資料集(1,000 個以上實例)
- 速度比測量標註者間一致性更重要
適合使用傳統多標註者的情況:
- 你需要標註者間一致性統計用於發表
- 任務高度主觀(例如冒犯性、幽默感)
- 你需要研究標註者的分歧模式
- 法規要求多名獨立標註者
- 標籤空間複雜或仍在演變(標註指南還在制定中)
混合做法: 先用 Solo Mode 完成初始的批次標註,再指派第二位標註者標註隨機的 10-20% 樣本來計算一致性統計。這樣既有 Solo Mode 的效率,又有多標註者驗證帶來的品質保證。
Solo Mode 自己沒有驗證塊。把第二位標註者當作一次普通的 Potato 流程跑在匯出的標籤上,然後像對待任意兩位標註者那樣,計算他們的標籤與 Solo Mode 標籤之間的一致性。(icl_labeling 確實有 verification 塊,但那屬於上下文內標註,不屬於 Solo Mode。)
延伸閱讀
- Solo Mode 教程:標註 10,000 個樣本 -- 分步演練
- 主動學習 -- 底層的主動學習系統
- AI 支援 -- LLM 整合配置
- 品質控制 -- 標註的品質保證
- MACE -- 能力估計(對混合模式的驗證有用)
有關實現詳情,請參閱源文件。