KORMo-Web-10B / README.md
jungsin3's picture
README: use a real KORMo-Web-10B output in the example
5636a20 verified
|
Raw History Blame Contribute Delete
11.2 kB
---
license: apache-2.0
base_model:
- KORMo-VL/KORMo-VL
language:
- ko
- en
pipeline_tag: image-text-to-text
library_name: transformers
tags:
- gui-agent
- web-agent
- computer-use
- browser
- korean
---
<style>
#kormo-web-card { font-size: 16px; line-height: 1.6; overflow-wrap: break-word; }
#kormo-web-card h1 { margin: 0 0 12px; font-size: 30px; line-height: 1.25; letter-spacing: -0.018em; }
#kormo-web-card h2 { margin: 30px 0 10px; font-size: 22px; line-height: 1.3; letter-spacing: -0.012em; }
#kormo-web-card h3 { margin: 22px 0 8px; font-size: 17px; font-weight: 700; line-height: 1.4; }
#kormo-web-card h2 + h3 { margin-top: 14px; }
#kormo-web-card p { margin: 0 0 10px; }
#kormo-web-card ul, #kormo-web-card ol { margin: 6px 0 12px; padding-left: 22px; }
#kormo-web-card li { margin: 4px 0; line-height: 1.55; }
#kormo-web-card table { width: 100%; margin: 6px 0 14px; border-collapse: collapse; font-size: 14px; line-height: 1.45; font-variant-numeric: tabular-nums; }
#kormo-web-card th, #kormo-web-card td { padding: 7px 10px; vertical-align: middle; }
#kormo-web-card blockquote { margin: 12px 0 16px; padding: 0 0 0 14px; font-size: 15px; }
#kormo-web-card blockquote p { margin: 0; }
#kormo-web-card pre { margin: 10px 0 14px; padding: 14px 16px; overflow-x: auto; line-height: 1.5; }
#kormo-web-card pre code { font-size: 13px; }
#kormo-web-card > p:has(> em:only-child) { margin: 4px 0 14px; font-size: 13px; }
#kormo-web-card .pills { margin: 4px 0 18px; }
#kormo-web-card .pill { display: inline-block; margin: 0 6px 6px 0; padding: 3px 10px; border-radius: 999px; border: 1px solid rgba(127,127,127,.35); font-size: 13px; }
@media (max-width: 640px) {
#kormo-web-card h1 { font-size: 26px; }
#kormo-web-card table { font-size: 13px; }
#kormo-web-card th, #kormo-web-card td { padding: 7px 6px; }
}
</style>
<div id="kormo-web-card">
# KORMo-Web-10B
**KORMo-Web-10B** is a Korean-first web agent built entirely on the **KORMo** model family, which KAIST MLP Lab trains **from scratch**. It reads a screenshot, thinks, and takes one browser action at a time (click, type, scroll, move between tabs and sites) until the task is done.
Among the open web agents we compared, it scores the **highest in Korean (30.1%)** on Online-Mind2Web, and **29.0% in English**.
## From a Korean LLM to a Korean web agent
KORMo-Web-10B is the third step of one Korean lineage, not a fine-tune of a foreign base model:
| Step | Model | What it adds |
|---|---|---|
| 1 | [KORMo-10B](https://huggingface.co/KORMo-Team/KORMo-10B-sft) | Koreanโ€“English LLM trained from scratch, with its own Korean tokenizer |
| 2 | [KORMo-VL](https://huggingface.co/KORMo-VL/KORMo-VL) | Vision: SigLIP encoder + KORMo-10B, trained on Korean document and UI images |
| 3 | **KORMo-Web-10B** | Web agent: large-scale web understanding data and browser trajectories, in Korean and English |
Because every stage was trained with Korean as a first language, the model handles Korean pages, Korean UI terms and natural Korean requests directly.
## Performance
All models below ran **the same 612 live-website tasks** (300 English, 312 Korean), each with its own prompt and action format, and were scored by **the same LLM judge**.
| Model | Size | Online-Mind2Web EN | Online-Mind2Web KO |
|---|---:|---:|---:|
| Qwen3.5-9B (base, no fine-tuning) | 9B | 24.7 | 16.7 |
| Fara-1.5-4B | 4B | 20.7 | 7.7 |
| UI-TARS-1.5-7B | 7B | 22.3 | 20.2 |
| OpenWebRL-4B | 4B | 32.7 | 15.4 |
| **KORMo-Web-10B (ours)** | **11B** | **29.0** | **30.1** |
| GPT-6-luna (proprietary) | โ€“ | 63.0 | 65.7 |
| Gemini-3.8-flash (proprietary) | โ€“ | 74.0 | 80.1 |
Success rate (%). The denominator is always the full task count, so crashed or empty runs count as failures.
KORMo-Web-10B has the best Korean score among the open web agents above, by about 10 points.
## Why KORMo-Web-10B
- **Korean from the ground up.** Built on a Korean LLM trained from scratch, not adapted from an English-first model.
- **Korean and English, natural requests.** Handles short commands as well as long, conversational instructions in either language.
- **Screenshot-only.** It needs no DOM or accessibility tree; it acts on what is on screen and checks each action's result on the next screenshot.
- **Thinks, then acts.** A short reasoning step precedes every action, so its behaviour is easy to inspect.
- **Open and ready to run.** Weights, model code for transformers 5, a vLLM path, a reference browser harness and the exact inference settings are all included.
## Quickstart
Tested with **transformers 5.12.1** and **vLLM 0.23.0**. The model runs on a single GPU.
transformers does not ship the KORMo text model, so this repo includes it (`_modeling_kormo.py`) together with a small loader, `kormo_load.py`:
- `kormo_load.register(path)` registers the KORMo text model. Call it **before** loading the processor or the model.
- `kormo_load.patch_vision(model)` feeds the SigLIP features *after* post-layernorm to the projector, as in training. Without it the model still runs but misreads screenshots.
### Inference example (one step, transformers)
```bash
pip install -U "transformers>=5.12" torch pillow huggingface_hub
huggingface-cli download KORMo-VL/KORMo-Web-10B --local-dir KORMo-Web-10B
```
```python
import json, sys, torch
from PIL import Image
from transformers import AutoModelForImageTextToText, AutoProcessor
M = "KORMo-Web-10B"
sys.path.insert(0, M)
import kormo_load
kormo_load.register(M)
processor = AutoProcessor.from_pretrained(M)
model = AutoModelForImageTextToText.from_pretrained(M, dtype=torch.bfloat16).to("cuda").eval()
kormo_load.patch_vision(model)
SYSTEM = open(f"{M}/system_prompt.md").read().strip()
TOOLS = json.load(open(f"{M}/tools.json"))
obs = ("<observation>\nscreen size: 1280 x 720\nCurrent tab: 0\nAvailable tabs:\n"
"- Tab 0 (active): https://www.aladin.co.kr/\n\nurl: https://www.aladin.co.kr/\nscreenshot:\n")
messages = [
{"role": "system", "content": SYSTEM},
{"role": "user", "content": [
{"type": "text", "text": "<user_request>\n์•Œ๋ผ๋”˜ ์‚ฌ์ดํŠธ์—์„œ 'ํ•ด์ปค์Šค ํ† ์ต RC'๋ฅผ ๊ฒ€์ƒ‰ํ•œ ๋’ค ๊ฐ€์žฅ ๋จผ์ € ํ‘œ์‹œ๋˜๋Š” ๋„์„œ์˜ ์ถœ๊ฐ„์ผ์„ ์•Œ๋ ค์ค˜.\n</user_request>\n" + obs},
{"type": "image", "image": Image.open("screen.png").convert("RGB")},
{"type": "text", "text": "\n</observation>"},
]},
]
inputs = processor.apply_chat_template(messages, tools=TOOLS, add_generation_prompt=True,
tokenize=True, return_dict=True, return_tensors="pt").to(model.device)
out = model.generate(**inputs, max_new_tokens=16384, do_sample=True, temperature=0.6, top_p=0.95, top_k=20)
print(processor.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=False))
```
A real first step from our Online-Mind2Web run (Aladin home page):
```text
<think>I see the Aladin homepage, with the search field labeled โ€œํ†ตํ•ฉ๊ฒ€์ƒ‰โ€ near the top. I have not yet searched for the book, so I still need to enter โ€œํ•ด์ปค์Šค ํ† ์ต RC,โ€ inspect the first resultโ€™s publication date, and report that date. My plan:
1. Click the search field
2. Enter โ€œํ•ด์ปค์Šค ํ† ์ต RCโ€ and submit the search
3. Read the publication date of the first listed book
This click targets the visible search field and is a safe, relevant first action.
</think>
<tool_call>
<function=click>
<parameter=x>
482
</parameter>
<parameter=y>
212
</parameter>
</function>
</tool_call>
```
Coordinates are normalized to 0โ€“1000. Convert them to pixels with `x / 1000 ร— width` and `y / 1000 ร— height`.
### Run the agent (vLLM)
vLLM does not include the KORMo architecture either. The KORMo text layers have the Llama structure, so `vllm/convert_to_vllm.py` renames two parameter names and writes a folder vLLM loads natively. `vllm/sitecustomize.py` turns SigLIP post-layernorm back on inside vLLM.
**1. Convert (once)**
```bash
pip install -U "vllm>=0.23.0" openai playwright safetensors
playwright install chromium
python KORMo-Web-10B/vllm/convert_to_vllm.py KORMo-Web-10B KORMo-Web-10B-vllm
```
**2. Start the model server**
```bash
PYTHONPATH=$PWD/KORMo-Web-10B/vllm vllm serve ./KORMo-Web-10B-vllm --served-model-name KORMo-Web-10B \
--max-model-len 65536 --limit-mm-per-prompt '{"image":1,"video":0}'
```
Check that the server log prints `[kormo-vllm] SigLIP post_layernorm enabled`.
**3. Run a browser task**
```bash
python KORMo-Web-10B/examples/agent_playwright.py --model KORMo-Web-10B \
--task "๋‹ค๋‚˜์™€์—์„œ 50๋งŒ ์› ์ดํ•˜, 10์ธ์น˜ ์ด์ƒ ํƒœ๋ธ”๋ฆฟ 3๊ฐœ๋ฅผ ์ฐพ์•„์„œ ๊ฐ€๊ฒฉ์„ ์•Œ๋ ค์ค˜"
```
The agent opens Chromium, prints each action as it goes, and finishes with the model's answer.
## How It Works
```
user <user_request> task </user_request> + observation (tabs, url, screenshot)
assistant <think> what I see ยท what is done ยท plan </think> <tool_call> one action </tool_call>
tool action result + new observation (newest screenshot only)
โ€ฆ repeat until finished(answer)
```
- **Action space:** 22 browser tools: click, type, hotkey, scroll, drag, goto, tab control, `finished`, and others ([`tools.json`](tools.json)).
- **Memory:** the full text history of thoughts, actions and observations is kept. Only the current screenshot is sent.
- **Recommended settings:** temperature 0.6, top_p 0.95, top_k 20, thinking on, up to 16k tokens per step and 50 steps. These are already set in `generation_config.json`.
The exact message format, parsing rules and feedback strings are in **[HARNESS.md](HARNESS.md)**. Follow it if you build your own harness.
## Evaluation Details
| | |
|---|---|
| Tasks | Online-Mind2Web: 300 English, 312 Korean |
| Judge | GPT-6 |
## Files
| File | Description |
|---|---|
| `model.safetensors` ยท `config.json` | Weights and config (LLaVA-OneVision: SigLIP + KORMo-10B text model) |
| `_configuration_kormo.py` ยท `_modeling_kormo.py` | KORMo text model code for transformers 5 |
| `kormo_load.py` | `register()` and `patch_vision()` |
| `chat_template.jinja` | Chat template (XML tool calls, thinking on) |
| `system_prompt.md` ยท `tools.json` | System prompt and 22-tool schema |
| `HARNESS.md` | Full harness specification |
| `examples/agent_playwright.py` | Reference browser agent (Playwright + OpenAI-compatible API). It does not execute `upload`. |
| `vllm/convert_to_vllm.py` ยท `vllm/sitecustomize.py` | Convert for vLLM and the post-layernorm fix |
| `generation_config.json` ยท `*processor_config.json` ยท `tokenizer*.json` ยท `special_tokens_map.json` | Sampling, processor and tokenizer configs |
## ํ•œ๊ตญ์–ด ์š”์•ฝ
**KORMo-Web-10B**๋Š” KAIST MLP ์—ฐ๊ตฌ์‹ค์ด ์ฒ˜์Œ๋ถ€ํ„ฐ ํ•™์Šตํ•œ ํ•œ๊ตญ์–ด ๋ชจ๋ธ **KORMo-10B โ†’ KORMo-VL**์„ ์ด์–ด ๋งŒ๋“  ํ•œ๊ตญ์–ด ์šฐ์„  ์›น ์—์ด์ „ํŠธ์ž…๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฐ์ƒท๋งŒ ๋ณด๊ณ  ํ•œ๊ตญ์–ดยท์˜์–ด ์›น ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
- **Online-Mind2Web:** EN 29.0, KO 30.1
- ๋น„๊ตํ•œ ๊ณต๊ฐœ ์›น ์—์ด์ „ํŠธ ์ค‘ ํ•œ๊ตญ์–ด ์ ์ˆ˜๊ฐ€ ๊ฐ€์žฅ ๋†’์Šต๋‹ˆ๋‹ค.
## Acknowledgements
Built on **[KORMo-VL](https://huggingface.co/KORMo-VL/KORMo-VL)** and **[KORMo-10B](https://huggingface.co/KORMo-Team/KORMo-10B-sft)** by KAIST MLP Lab. Evaluated on **Online-Mind2Web** and its Korean counterpart.
</div>