Skip to content

AI 支援

接入 OpenAI、Anthropic、Gemini、Ollama、vLLM 或 HuggingFace,讓標註者按需獲得提示、關鍵詞高亮和標籤理由。

開啟 AI 支援後,標註者工作時會有一個大語言模型寫提示、高亮關鍵詞、解釋每個標籤為什麼可能適用。它不會替你填表單。決定權仍在標註者手裡,每個助手都藏在一個需要他們按下的按鈕後面。

安裝

各家提供商的 SDK 都是可選依賴。基礎安裝不需要其中任何一個,只有當配置選中某個 endpoint_type 時才會匯入對應的 SDK:

bash
pip install "potato-annotation[ai]"

這覆蓋了常用的那幾個。如果配置選了一個 SDK 缺失的端點,伺服器會告訴你該裝哪個包,而不是在匯入時就失敗。

支援的提供商

雲端: openai、anthropic、gemini、huggingface

本地: ollama、vllm

視覺: ollama_vision、openai_vision、anthropic_vision、yolo

視覺端點以及影像和影片助手請見視覺 AI 支援。

配置

yaml
ai_support:
  enabled: true
  endpoint_type: "openai"
  ai_config:
    model: "gpt-4o-mini"
    api_key: "${OPENAI_API_KEY}"
    temperature: 0.7
    max_tokens: 800
    include:
      all: true

關於這個塊有兩點值得直說,因為執行時哪一點都不會被發現:

模型設定放在 ai_config 裡面,比想當然的位置深一層。ai_support.model 和 ai_support.base_url 會被忽略,不過 potato validate --strict 會把它們指出來。

include.all 預設是關閉的。沒有它,頁面上任何地方都不會出現助手按鈕,AI 面板渲染成空的,校驗乾淨通過,也沒有任何警告。

配置選項

選項類型描述
enabledboolean開啟 AI 支援
endpoint_typestring使用哪個提供商
ai_config_filestring存放端點細節的外部配置檔案路徑
ai_config.modelstring模型名稱,預設為該提供商自己的預設模型
ai_config.api_keystring雲服務商必填
ai_config.base_urlstring用某個相容 OpenAI 的伺服器替代廠商自家的,例如 vLLM、SGLang、LM Studio 或 LiteLLM。設定它之後 api_key 變為可選
ai_config.temperaturefloat回覆的隨機程度,0.0 到 2.0,預設 0.7
ai_config.max_tokensinteger回覆的最大長度,預設 800
ai_config.include.allboolean在所有方案上啟用助手,預設 false
ai_config.include.special_includeobject按頁面、按方案分別控制
cache_config.disk_cache.enabledboolean把回覆快取到磁碟,預設 false
cache_config.disk_cache.pathstring快取檔案路徑,開啟快取時必填
cache_config.prefetch.warm_up_page_countinteger啟動時預先生成的實例數
cache_config.prefetch.on_nextinteger向前翻頁時預取的實例數
cache_config.prefetch.on_previnteger向後翻頁時預取的實例數

openai 和 openai_vision 接受三種提供 API 金鑰的方式,校驗三種都認:api_key 的值、環境中的 OPENAI_API_KEY,或指向一臺不需要金鑰的自託管伺服器的 base_url。anthropic、huggingface 和 gemini 只讀第一種,所以對這三個來說金鑰確實是必需的。

沒有理由就讓 max_tokens 保持預設值。理由助手和關鍵詞助手會在一次回覆裡為所有標籤作答,所以幾百個 token 會把回覆從物件中間截斷,助手渲染成空的,而且不報錯。

幾個助手

標註者在每個方案上會拿到三個按鈕,外加第四種隨提示一起出現的行為。

提示給出關於如何思考這條資料的指引,可以附帶一個建議標籤。

關鍵詞標出與每個標籤相關的詞和短語,畫成文本上的方框覆蓋層,而不是背景高亮。

理由解釋每個可選標籤為什麼可能適用,對所有標籤給出平衡的推理。在培訓標註者或判斷確實困難時,這個最值得開啟。

標籤建議不是單獨的按鈕。當提示帶有建議標籤時,那個標籤會帶上琥珀色邊框和一個閃游標記。

各端點能做什麼

端點文本視覺方框關鍵詞理由
openai、anthropic、ollama是否否是是
openai_vision、anthropic_vision、ollama_vision是是否僅文本條目是
yolo否是是否否

「僅文本條目」是按條目判定的,而不是按端點判定。視覺模型仍然是語言模型,所以在文本條目上它提供全部三個助手,而在影像條目上關鍵詞按鈕會被過濾掉,因為沒有詞可標。

圖文都有的條目

帶截圖的客服工單,或配著評論的產品照片,應該圖文一起送到模型。Potato 從 item_properties.text_key 取文本,圖片則取下面第一個能解析出結果的來源:

  1. ai_support.image_key
  2. item_properties.image_key
  3. instance_display 中 type: image 的欄位,或第一個取值看起來像圖片的展示欄位

所以已經通過 instance_display 聲明瞭照片的配置,不需要再寫一遍:

yaml
instance_display:
  fields:
    - {key: body, type: text, label: Ticket}
    - {key: screenshot, type: image, label: Screenshot}

只把圖片發過去,提示就會信心十足地描述這條資料裡錯誤的那一半。給模型一張通用配圖,再讓它處理「匯出按鈕一直轉圈」,它會回說自己沒有拿到上下文。

這個欄位接受 http(s) URL、data URI,或 media_directory 下的檔名。本地檔案會從磁碟讀出並以 base64 傳送。既不是 URL、Potato 也找不到對應檔案的值會被記進日誌,然後不帶圖片去問模型,所以一條只針對文本推理的提示,就是去查那行日誌的理由。

按方案啟用助手

include.all 在所有地方開啟所有助手。另一種做法是 special_include,它把頁面索引對映到標註 id,再對映到該方案應帶的助手列表:

yaml
ai_support:
  enabled: true
  endpoint_type: "openai"
  ai_config:
    model: "gpt-4o-mini"
    api_key: "${OPENAI_API_KEY}"
    include:
      all: false
      special_include:
        "0":
          "0": ["hint", "keyword"]
        "1":
          "1": ["hint"]
        "2":
          "0": ["hint", "keyword"]
          "1": ["hint", "keyword"]

內層的鍵是標註 id,也就是該方案在 annotation_schemes 中從零開始數的位置。

當表單按網格排布時,這一點比看上去更要緊。助手那一行是按方案渲染的,所以三個方案各三個助手就是九個按鈕,每個單元格一行。單元格寬度低於約 260px 時,這一行會摺疊成單行圖示,標籤保留為按鈕的無障礙名稱並放在它的 title 上。如果要把 layout.grid 和 AI 支援一起用,請按方案指定想要的助手,而不是在所有地方全部開啟。

快取與預取

一次大語言模型呼叫會給頁面載入添上好幾秒,而等待的標註者就不再按那個按鈕了。快取還能讓你不必為別人重新訪問過的條目付兩次錢:

yaml
ai_support:
  enabled: true
  endpoint_type: "openai"
  ai_config:
    model: "gpt-4o-mini"
    api_key: "${OPENAI_API_KEY}"
    include:
      all: true
  cache_config:
    disk_cache:
      enabled: true
      path: "annotation_output/ai_cache.json"
    prefetch:
      warm_up_page_count: 20
      on_next: 10
      on_prev: 3

預熱在啟動時為前 N 條實例預先生成回覆。預取則在標註者瀏覽時於游標前方生成,on_next 比 on_prev 大,反映的是大部分移動都是向前的。磁碟快取在重啟後依然保留。

外部 AI 配置檔案

端點細節往往正是不能提交進倉庫的那部分:API 金鑰、內部伺服器 URL、那臺機器上裝的是哪個模型。ai_config_file 把它們移進自己的檔案:

yaml
# config.yaml, committed, no secrets
ai_support:
  enabled: true
  ai_config_file: ai-config.yaml
 
  ai_config:
    temperature: 0.7
    max_tokens: 800
    include:
      all: true
yaml
# ai-config.yaml, gitignored
endpoint_type: ollama
model: qwen3:0.6b
base_url: http://localhost:11434

外部檔案負責設定 endpoint_type,它的其他值會合並進 ai_config 並優先於內聯的值。合併是扁平的:外部檔案在自己的頂層直接放 model、base_url 和 api_key。把它們包在該檔案裡的 ai_config: 塊中會產生 ai_config.ai_config,沒有任何程式碼讀它,啟動隨後會因缺少 API 金鑰而失敗,報錯指向的還是錯的鍵。

如果設定了 ai_config_file 但檔案不存在,AI 支援會給出警告並自行關閉,伺服器照常啟動。potato validate --strict 把那條警告當成錯誤,所以一項以 AI 輔助為研究物件的研究,在 AI 關著的情況下過不了自己的檢查。請在配置旁邊附一份 ai-config.yaml.example 供人複製。ai-config.yaml 和 ai-config*.yaml 這兩個模式已被 gitignore。

自定義提示詞

提示詞以 JSON 檔案的形式放在 potato/ai/prompt/ 裡,每種標註類型一個:

標註類型提示詞檔案
Radioradio.json
Likertlikert.json
Multiselectmultiselect.json
Spanspan.json
Sliderslider.json
Selectselect.json
Numbernumber.json
Free texttextbox.json

每個檔案為每個助手儲存一個模板,包含 name、prompt、output_format 和一個圖示:

json
{
  "hint": {
    "name": "Hint",
    "prompt": "TASK: Generate annotation guidance...\n- Text to annotate: \"${text}\"\n- Annotation task: ${description}\n- Available labels: ${labels}",
    "output_format": "default_hint",
    "img": "/static/ai_assistant_img/blub.svg"
  }
}

可用的變數是 ${text}、${description}、${labels}、${min_label}、${max_label}、${size}、${min_value} 和 ${max_value}。

output_format 指明渲染器期待的回覆結構。default_hint 返回 {hint, suggestive_choice},default_keyword 返回 {label_keywords: [{label, keywords}]},default_rationale 返回 {rationales: [{label, reasoning}]}。

各提供商的配置

OpenAI

yaml
ai_support:
  enabled: true
  endpoint_type: "openai"
  ai_config:
    model: "gpt-4o-mini"
    api_key: "${OPENAI_API_KEY}"
    temperature: 0.7
    max_tokens: 800
    include:
      all: true

Anthropic

yaml
ai_support:
  enabled: true
  endpoint_type: "anthropic"
  ai_config:
    model: "claude-sonnet-4-20250514"
    api_key: "${ANTHROPIC_API_KEY}"
    temperature: 0.7
    max_tokens: 800
    include:
      all: true

Google Gemini

yaml
ai_support:
  enabled: true
  endpoint_type: "gemini"
  ai_config:
    model: "gemini-2.0-flash-exp"
    api_key: "${GOOGLE_API_KEY}"
    max_tokens: 800
    include:
      all: true

Ollama

yaml
ai_support:
  enabled: true
  endpoint_type: "ollama"
  ai_config:
    model: "llama3.2"
    base_url: "http://localhost:11434"
    max_tokens: 800
    include:
      all: true

vLLM

yaml
ai_support:
  enabled: true
  endpoint_type: "vllm"
  ai_config:
    model: "meta-llama/Llama-3.2-3B-Instruct"
    base_url: "http://localhost:8000"
    max_tokens: 800
    include:
      all: true

HuggingFace

yaml
ai_support:
  enabled: true
  endpoint_type: "huggingface"
  ai_config:
    model: "meta-llama/Llama-3.2-3B-Instruct"
    api_key: "${HF_API_KEY}"
    max_tokens: 800
    include:
      all: true

OpenRouter 不在 ai_support.endpoint_type 接受的十個取值之內,在這裡寫它,配置載入不起來。它能用在聊天側欄:chat_support.endpoint_type: openrouter,注意這個拼寫沒有下劃線。

給影像單獨配一個端點

文本模型和視覺模型可以並排執行,讓每種內容類型都用上更合適的那個:

yaml
ai_support:
  enabled: true
  endpoint_type: "ollama"
  visual_endpoint_type: "ollama_vision"
  ai_config:
    model: "llama3.2"
    include:
      all: true
  visual_ai_config:
    model: "qwen2.5-vl:7b"

完整示例

yaml
annotation_task_name: "Sentiment Analysis with AI Support"
task_dir: "."
 
data_files:
  - "data/reviews.json"
 
item_properties:
  id_key: id
  text_key: text
 
annotation_schemes:
  - annotation_type: radio
    name: sentiment
    description: "What is the sentiment of this text?"
    labels: ["positive", "negative", "neutral"]
 
  - annotation_type: multiselect
    name: topics
    description: "What topics are discussed?"
    labels: ["product", "service", "price", "quality"]
 
ai_support:
  enabled: true
  endpoint_type: "openai"
  ai_config:
    model: "gpt-4o-mini"
    api_key: "${OPENAI_API_KEY}"
    temperature: 0.7
    max_tokens: 800
    include:
      all: true
  cache_config:
    disk_cache:
      enabled: true
      path: "annotation_output/ai_cache.json"
    prefetch:
      warm_up_page_count: 20
      on_next: 10
      on_prev: 3
 
output_annotation_dir: "output/"
export_annotation_format: jsonl
 
user_config:
  allow_all_users: true

環境變數

把金鑰留在配置之外:

bash
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
export GOOGLE_API_KEY="..."

用 ${VARIABLE_NAME} 引用它們,主配置和外部 AI 配置檔案裡都能用。

故障排查

提示框是空的,但伺服器確實回覆了

模型回覆了,請求返回 200,而提示框什麼都不顯示。有四件事會產生同樣的症狀,按這個順序檢查:

  1. include.all 缺失。 頁面上根本沒有助手按鈕,而且沒有任何警告。
  2. max_tokens 太低。 理由助手和關鍵詞助手會一次性為所有標籤作答,所以回覆從物件中間被截斷。日誌裡會寫模型觸到了 max_tokens。
  3. 伺服器忽略了 schema。 檢查回復是 JSON 而不是散文。沒有約束解碼的伺服器會按提示詞暗示的任何結構作答。
  4. 鍵放錯了地方。 所有跟模型有關的東西都屬於 ai_config。validate --strict 會指出它不認識的任何鍵。

一個按鈕都沒有,而啟動日誌看著正常

在日誌裡找 "AI endpoint ready"。如果它說端點啟動失敗,原因就在那一行上方的警告裡。

Ollama 或 vLLM 連線失敗

確認伺服器正在執行、模型已經拉取,Ollama 用 ollama list 檢視,並檢查配置的 base_url 可以訪問。

最佳實踐

  1. 當成輔助來用 - 決定由標註者做出,一項預設接受建議的研究測的是模型
  2. 在生產環境啟用快取 - 它能降低延遲和重複開銷
  3. 使用預熱 - 預先生成開頭那幾條實例,第一位標註者就不用等
  4. 留意賬單 - 雲服務商按請求收費,每個方案都配一個助手會在整個語料上成倍放大
  5. 考慮本地端點 - 大批次工作,或語料不能離開本機時,用 Ollama 或 vLLM
  6. 把金鑰放在環境變數裡 - 絕不要提交它們

延伸閱讀

有關實現細節,請參閱源文件。