ข้ามไปยังเนื้อหา
Hugging Face เปิดวิธีเทรน coding agent ด้วย RL แบบ "loop-owning" — ปล่อยให้ OpenCode รันลูปของตัวเอง แล้วเทรนบนโทเคนจริงที่มันพ่นออกมา
เครื่องมือนักพัฒนา

Hugging Face เปิดวิธีเทรน coding agent ด้วย RL แบบ "loop-owning" — ปล่อยให้ OpenCode รันลูปของตัวเอง แล้วเทรนบนโทเคนจริงที่มันพ่นออกมา

อ่าน 7 นาที ที่มา: Hugging Face Blog

เมื่อ 5 สิงหาคม 2026 Sergio Paniego จาก Hugging Face เผยแพร่บทความที่แก้ปัญหาเงียบ ๆ ข้อหนึ่งของการเทรน coding agent ด้วย reinforcement learning — ปัญหาที่ว่า สิ่งที่คุณเทรน ไม่ใช่สิ่งที่คุณรันจริง

วิธีมาตรฐานของการ RL-train agent คือให้ trainer เป็นคนขับลูปเอง: สุ่มหนึ่ง turn, แกะ tool call ออกมา, รันเครื่องมือ, ป้อนผลกลับเข้าไป วนแบบนี้ไปเรื่อย ๆ ปัญหาคือบทความชี้ชัดว่านั่นเท่ากับ “trains a copy of the loop, not the harness you actually run” — คุณกำลังเทรนโมเดลให้เก่งกับลูปจำลองที่ trainer เขียนขึ้น ไม่ใช่ลูปจริงของ agent ที่จะไปรันบนเครื่องผู้ใช้ ที่มีการจัดการ context, การบีบอัดประวัติ, และลำดับเครื่องมือเป็นของตัวเอง

Loop-owning: กลับด้านว่าใครเป็นเจ้าของลูป

แนวทางใหม่ที่เรียกว่า loop-owning พลิกความสัมพันธ์นี้ — harness รันลูปของตัวเองเหมือนตอนที่มันชิปจริงทุกประการ TRL ไม่แตะการเดิน turn เลย มันแค่อ่านย้อนกลับว่า harness ทำอะไรไปบ้าง แล้วเทรนบนสิ่งนั้น ประโยคสรุปของบทความคือ “The agent runs to completion on its own, and TRL trains on the exact tokens the harness produced”

ตัวอย่างในบทความใช้ OpenCode เป็น agent จริง — coding agent โอเพนซอร์สที่เราเคยเขียนถึงตอนแตะ 160k ดาว — แต่สถาปัตยกรรมนี้ไม่ผูกกับ agent ตัวใดตัวหนึ่ง ตราบใดที่ agent รันใน sandbox ได้และคุยกับโมเดลผ่าน API ที่ดักจับได้ (ตอนนี้คือ OpenAI-compatible chat completions) ก็เทรนด้วยวิธีเดียวกันได้

การเปิดโหมดนี้ในโค้ดคือการตั้ง harness_adapter=None:

from trl.experimental.async_grpo import AsyncGRPOConfig, AsyncGRPOTrainer
from trl.experimental.async_grpo.openenv_harness import HarnessRolloutWorker, has_tool_call

worker = HarnessRolloutWorker(
    harness_session_factory=build_factory(...),  # OpenEnv ResourceSessionFactory
    harness_adapter=None,          # loop-owning: agent รันลูปของตัวเอง
    rollout_reward_fn=opencode_reward,
    train_turn_fn=has_tool_call,   # reinforce เฉพาะ turn ที่ลงมือ ไม่ใช่ turn ที่พูด
)

บรรทัด train_turn_fn=has_tool_call คือรายละเอียดเล็กที่สำคัญ — มันเลือก reinforce เฉพาะ turn ที่มีการเรียกเครื่องมือจริง ไม่ใช่ turn ที่โมเดลแค่บรรยายว่าจะทำอะไร

สี่ชิ้นส่วนที่ประกอบกันเป็นระบบ

  1. Harness ใน sandbox — ทุก rollout ได้ OpenEnv session แยกของตัวเอง เป็น container ที่มีไฟล์ระบบและโปรเซสของตัวเอง เริ่มจาก Docker image ที่มี harness ฝังมาแล้ว
  2. Transparent proxy ที่ดักโทเคน — นั่งคั่นระหว่าง harness กับ vLLM server บันทึกทุก model call พร้อม token ID และ logprob รายเทิร์น บทความเรียกจุดนี้ว่า key trick: ไม่มีการ re-tokenize และไม่มีการเดา คุณเทรนบนโทเคนจริงที่ policy พ่นออกมา
  3. Hidden-test verifier สำหรับ reward — คุณเขียน verify() ที่ตรวจ workspace สุดท้ายแล้วคืนค่า reward ในตัวอย่างใช้ชุดข้อมูล agentica-org/DeepCoder-Preview-Dataset โดยโจทย์คือ prompt ที่ agent ทำงาน ส่วน hidden test ของโจทย์เดียวกัน (ที่ agent ไม่เคยเห็น) คือ reward — agent เขียน solution.py แล้วได้คะแนนตามสัดส่วนเทสต์ที่ผ่าน
  4. Trainer — เมื่อ harness ทำงานจบ TRL ประกอบ training sample ขึ้นใหม่จากเทิร์นที่บันทึกไว้ แล้วรัน GRPO โดย reward กระจายไปทุกโทเคนผ่าน group-relative advantage

กับดักเดียวที่ต้องทำให้ถูก: vLLM สอง URL

การย้าย rollout ไปรันบน remote Hugging Face sandbox ตัวละ rollout ทำให้สเกลข้ามเครื่องเดียวได้ แต่มาพร้อมกับดักที่บทความบอกว่ามีข้อเดียวและเลี่ยงไม่ได้:

trainer กับ vLLM อยู่บนโหนดเดียวกันและ sync น้ำหนักผ่าน NCCL ลิงก์นั้นจึงต้องอยู่บน localhost — แต่ sandbox ที่อยู่ระยะไกล มองไม่เห็น localhost ของโหนดนั้น มันต้องเข้าถึง vLLM ตัวเดียวกันจากภายนอก

URLใครใช้ทำไม
vllm-urltrainer → vLLMlocalhost, sync น้ำหนักผ่าน NCCL
sandbox-vllm-urlsandbox → vLLMendpoint ที่เข้าถึงได้จริง (vLLM สาธารณะ หรือ tunnel)

การรันทั้งหมดบน Hugging Face Jobs ใช้ launcher ที่ห่อสามงาน (เสิร์ฟ vLLM, เปิด tunnel, รัน trainer) ไว้ใน job เดียว:

hf jobs uv run --flavor h200x2 --secrets HF_TOKEN -- timeout 7200s launcher.py

ข้างในเรียก:

python opencode_hf_sandbox.py --model Qwen/Qwen3-8B \
  --vllm-url http://localhost:8000 --sandbox-vllm-url <tunnel-url> \
  --n-prompts 32 --max-steps 10

เรื่องต้นทุน — remote sandbox ทุกตัวรันบน flavor cpu-basic ที่ราคาราว $0.01 ต่อชั่วโมงต่ออินสแตนซ์ ทำให้ rollout เป็นพัน ๆ ครั้งยังถูกอยู่ ต้นทุนจริงคือ GPU job ตัวเดียวที่รัน vLLM และเทรน

มันเรียนรู้จริงไหม — และสิ่งที่ล้มเหลวก่อนหน้า

ผลการรันสั้น ๆ: Qwen3-8B, 10 step, 32 โจทย์ reward ขยับจากราว 0.27 → 0.71 (แกว่ง แต่ทิศทางขึ้น) โดย reward_std อยู่ราว 0.4–0.5 ซึ่งแปลว่า GRPO มีสัญญาณต่างกันภายในกลุ่มให้เรียนรู้จริง โมเดลที่ได้เผยแพร่เป็น sergiopaniego/Qwen3-8B-opencode-deepcoder-grpo

ส่วนที่มีค่ากว่าตัวเลข คือ การเล่าสิ่งที่ล้มเหลว ตัวอย่างอ้างอิงเดิมเทรน Qwen3-4B-Instruct-2507 แบบรันในเครื่องและเรียนรู้ได้ดี (reward ~0.4 → ~0.6 ใน 110 step) แต่พอเอาโมเดล 4B ตัวเดียวกันไปรัน rollout บน sandbox ระยะไกล มันพัง — reward ขึ้นได้ไม่กี่ step แล้วร่วง โดย agent เอาแต่ยิง tool call รัว ๆ โดยไม่เคยแก้โจทย์ได้เลย

ผู้เขียนบอกตรง ๆ ว่า “we are not fully sure why” และเดาว่าเป็นส่วนผสมของ: รันน้อย step เกินไป, การรัน rollout แบบ remote/async เพิ่ม lag และมี sandbox ที่ล้มเป็นครั้งคราว, และโมเดล 4B ตั้งหลักกับโจทย์ระดับนี้ได้ยากตั้งแต่แรก การขยับไป 8B คือสิ่งที่ทำให้มันติด — มันแก้โจทย์ได้มากพอที่ reward จะมีอะไรให้ไต่ขึ้น

สองจุดที่ผู้เขียนเตือนเอง

  • Remote sandbox ต้องการการดูแลเชิงปฏิบัติการ — sandbox สตาร์ตช้าได้, พังกลางทางได้, และ ค้างรันทิ้งไว้หลังจบงานได้ ต้องวางแผนเก็บกวาดและจัดการตัวที่ล้มเอง ซึ่งเป็นภาระที่ subprocess ในเครื่องไม่มี
  • การเปิด vLLM ให้ sandbox เข้าถึง — launcher เปิด public tunnel แบบเร็ว ๆ ให้ ซึ่งสะดวกสำหรับเดโม แต่มันคือ endpoint เปล่า ๆ ที่ไม่มี authentication อะไรที่เกินกว่างานใช้แล้วทิ้ง ควรชี้ sandbox-vllm-url ไปที่ vLLM ที่มีการควบคุมการเข้าถึงจริง

ทิศทางถัดไปคือการปรับโครงสร้างรอบ ๆ Harbor เพื่อให้ coding agent หลายตัว (Claude Code, Pi, Codex, OpenCode, Cursor, …) และ sandbox backend หลายแบบ เทรนผ่านเส้นทางเดียวกันได้ แทนที่จะต้องต่อสายทีละตัว

มุมมอง AI Curator

ประโยค “trains a copy of the loop, not the harness you actually run” คือประโยคที่ควรติดไว้บนกำแพงของทุกทีมที่ทำ agent เพราะมันชี้ปัญหาที่ใหญ่กว่าเรื่อง RL — ช่องว่างระหว่างสภาพแวดล้อมตอนเทรนกับสภาพแวดล้อมตอนรัน ที่วงการ agent สะสมมานาน โมเดลที่เก่งบนลูปสังเคราะห์ของ trainer อาจเจอ context compaction, ลำดับเครื่องมือ และ prompt จริงของ harness แล้วทำได้แย่กว่า โดยที่ไม่มีเบนช์มาร์กไหนจับได้ การเทรนบนโทเคนที่ harness พ่นออกมาจริง ๆ เป็นการปิดช่องว่างนั้นตรงจุดที่สุด และน่าสนใจว่ามันสอดคล้องกับสิ่งที่ Meta พูดในวันเดียวกันตอนเปิด Muse Spark 1.2 ว่าโมเดลถูก co-train มากับ Muse Code — สองทีมที่ไม่เกี่ยวกันมาถึงข้อสรุปเดียวกันในวันเดียวกัน ว่าโมเดลกับ harness ต้องถูกฝึกด้วยกัน

สำหรับทีมไทยและ SEA นี่เป็นข่าวที่จับต้องได้กว่าที่เห็น ตัวเลขที่ควรจำคือ 10 step / 32 โจทย์ / Qwen3-8B บน h200x2 — นี่ไม่ใช่สเกลที่ต้องมีคลัสเตอร์ของแล็บใหญ่ มันคืองานที่มหาวิทยาลัยไทยหรือทีม R&D ขนาดกลางจ่ายไหว และ sandbox ที่ราคา $0.01/ชั่วโมงแปลว่าส่วนที่แพงคือ GPU ตัวเดียว ไม่ใช่ฟลีตของ worker กรณีใช้งานที่ชัดที่สุดสำหรับบ้านเราคือ การเทรน agent ให้เก่งกับโค้ดเบสหรือ toolchain ภายในองค์กร — verifier ไม่จำเป็นต้องเป็น hidden test ของ LeetCode มันคือฟังก์ชันที่คุณเขียนเองเพื่อตรวจ workspace สุดท้าย จะเป็นเทสต์ภายใน, linter ของทีม หรือกฎ compliance ก็ได้

แต่อย่ามองข้ามคำเตือนสองข้อท้ายบทความ โดยเฉพาะข้อ tunnel — การเปิด vLLM endpoint ที่ไม่มี auth ออกสู่อินเทอร์เน็ตเพื่อให้ sandbox เข้าถึง คือรูปแบบที่จะกลายเป็นข่าวความปลอดภัยได้ง่ายมากถ้าใครก๊อปคำสั่งจากบทความไปใช้กับโมเดลที่ fine-tune บนข้อมูลลูกค้า ผู้เขียนพูดชัดแล้วว่าอย่าทำเกินกว่างานใช้แล้วทิ้ง คนอ่านที่รีบมักข้ามบรรทัดนั้น

ข้อสุดท้าย: บทความบอกเองว่านี่คือ เวอร์ชันแรกและอยู่ใน trl.experimental API จะเปลี่ยน ถ้าคุณจะลองตอนนี้ ให้ปักเวอร์ชัน TRL ไว้ และอ่านมันในฐานะ “เอกสารที่อธิบายว่าชิ้นส่วนทั้งหมดต่อกันยังไง” มากกว่าจะเป็นสูตรที่ก๊อปแล้วรันได้ตลอดไป — ซึ่งผู้เขียนก็ตั้งใจให้เป็นแบบนั้นตั้งแต่แรก

แหล่งข่าว

บทความนี้เรียบเรียงจากแหล่งข่าวต้นทาง — Hugging Face Blog

เนื้อหาบน blog นี้สร้างโดย AI โดยมีการกำกับบรรณาธิการจากมนุษย์

อ่านเพิ่มในหมวด เครื่องมือนักพัฒนา

Betelgeuse รัน Qwen3.8 Flash Next ขนาด 68GB บน RTX 3060 กับแรม 16GB ส่วน Basalt วัดให้เห็นว่าเอนจินที่เร่งด้วยแฟล็กลัดทำให้โมเดลลืมของ
เครื่องมือนักพัฒนา

Betelgeuse รัน Qwen3.8 Flash Next ขนาด 68GB บน RTX 3060 กับแรม 16GB ส่วน Basalt วัดให้เห็นว่าเอนจินที่เร่งด้วยแฟล็กลัดทำให้โมเดลลืมของ

Betelgeuse ฟอร์ก llama.cpp ที่เปิดเมื่อ 31 ต.ค. ดึง expert จาก NVMe เข้า VRAM ตามต้องการ อ้าง 22 tok/s แบบไม่ทิ้ง expert แต่ README เตือนเองว่ายังทดลองและตัวเลขมาจากคนละรอบ วันเดียวกันผู้พัฒนา Basalt เผยผลวัดว่าแฟล็กเร่งความเร็วทำคุณภาพ Q8 ตกไปใกล้ Q2

· อ่าน 5 นาที
กฎห้ามโคลนเกมของ Claude Code เริ่มกระทบงานจริง บริษัทรับทำเกมกระดานร้องงานลูกค้าหยุด แต่รายงานหลายชิ้นปนกับตัวกรองไซเบอร์คนละเรื่อง
เครื่องมือนักพัฒนา

กฎห้ามโคลนเกมของ Claude Code เริ่มกระทบงานจริง บริษัทรับทำเกมกระดานร้องงานลูกค้าหยุด แต่รายงานหลายชิ้นปนกับตัวกรองไซเบอร์คนละเรื่อง

ต่อจากที่เราพบกฎนี้ในไบนารี Claude Code ผู้ใช้เริ่มรายงานว่างานเชิงพาณิชย์ถูกปฏิเสธ และ Tom's Hardware กับ Mashable หยิบไปรายงาน แต่เมื่อไล่ issue ที่อ้างถึง พบว่าหลายรายการเกิดก่อนกฎจะมา จึงน่าจะเป็นตัวกรองด้านไซเบอร์ ส่วน Anthropic ยังไม่ชี้แจง

· อ่าน 4 นาที
Claude Code ส่ง transcript ของ background agent ให้โมเดลทุก ~30 วินาทีเพื่อเขียนสถานะ 3–5 คำ ผู้วัดรายงานกินราว 16% และยังปิดแบบทางการไม่ได้
เครื่องมือนักพัฒนา

Claude Code ส่ง transcript ของ background agent ให้โมเดลทุก ~30 วินาทีเพื่อเขียนสถานะ 3–5 คำ ผู้วัดรายงานกินราว 16% และยังปิดแบบทางการไม่ได้

รายงานอิสระพบว่าฟีเจอร์ agent_summary ของ Claude Code ส่ง transcript เต็มของ background agent ไปถามสถานะทุก ~30 วินาที คิดเป็น 16.4% ของค่าใช้จ่ายในระบบของผู้วัด Issue เปิดมาตั้งแต่ ส.ค. ยังไม่มีสวิตช์ปิดที่รองรับ เราตรวจพบโค้ดนี้ใน 2.1.314 และพบกฎใหม่เรื่องเกมที่ไม่อยู่ใน changelog

· อ่าน 4 นาที