ข้ามไปที่เนื้อหา

LLM-Guided Spec Search

LLM-guided spec search (Saad-Falcon et al., 2026) คือความร่วมมือระหว่าง local กับ cloud: teacher บน cloud ระดับ frontier อ่าน trace จากเอเจนต์ในเครื่อง ที่ deploy อยู่ แล้วเสนอการแก้ไขแบบมี type ครอบคลุมการตั้งค่าทั้งหมดของ เอเจนต์ ฮาร์ดแวร์ในเครื่องรัน configuration ที่ได้โดยไม่มีต้นทุน API เพิ่มเติม ตอน inference gate แบบ held-out จะยอมรับเฉพาะการแก้ไขที่ปรับปรุงกลุ่มความ ล้มเหลวเป้าหมาย โดยไม่มีการถดถอยที่ยอมรับไม่ได้ในส่วนอื่น

หน้านี้เป็น tutorial แบบ copy-paste เมื่อจบแล้วคุณจะมี:

  • SpecSearchOrchestrator จริงกำลังรันอยู่บนเครื่องของคุณ
  • ลูปแบบหลาย session ตามกฎ stagnation ของ paper (Algorithm 1)
  • ความเข้าใจว่าตัวปรับตั้งไหนที่ต้องปรับสำหรับการ deploy จริง

สรุปสั้น — รันเลย

python examples/openjarvis/spec_search_quickstart.py

สคริปต์นี้สมบูรณ์ในตัวเอง (ไม่ต้องมี API key ไม่ต้องมี Ollama) — มันต่อสาย orchestrator จริง + ลูปหลาย session + โมดูล composite-reward เข้ากับ teacher/student/judge แบบ stub เพื่อให้คุณเห็นหนึ่ง session แบบเต็มและลูป stagnation จบลง จุดที่สลับเป็นโปรดักชันมี comment กำกับไว้ในโค้ด ดู ไปสู่โปรดักชัน ด้านล่าง

ทำงานอย่างไร

หนึ่ง search session ทำซ้ำ 4 เฟส (paper §3.3):

เฟส สิ่งที่เกิดขึ้น
Diagnose Teacher อ่าน trace ที่เข้าเงื่อนไข และจัดกลุ่มความล้มเหลวเป็นคลัสเตอร์ แต่ละคลัสเตอร์มี annotation (student_failure_rate, teacher_success_rate, skill_gap)
Plan Teacher เสนอการแก้ไขแบบมี type ครอบคลุม primitive ที่แก้ไขได้สี่ตัว (Intelligence, Engine, Agents, Tools & Memory) หนึ่งข้อเสนออาจแก้หลาย slot พร้อมกัน
Execute การแก้ไขแต่ละตัวจะถูกนำไปใช้ gate ให้คะแนน spec ที่ได้บน subsample แบบ held-out ยอมรับก็ต่อเมื่อ GateOK เป็นจริง (ดูด้านล่าง)
Record การแก้ไขที่ยอมรับจะ commit เข้า checkpoint store การแก้ไขที่ถูกปฏิเสธจะ rollback session จะถูกบันทึกไว้ใน SessionStore

SpecSearchOrchestrator.run(trigger) รัน หนึ่ง session แบบครบวงจร SpecSearchLoop (paper Algorithm 1) ห่อ orchestrator และทำซ้ำ session จนกว่าจะ เจอ gate-score stagnation (ค่าเริ่มต้น k = 5 session) หรืองบประมาณหมด

GateOK — เงื่อนไขการยอมรับ

ให้ G_c(S) เป็นคะแนน gate แบบ held-out ของ spec S จำกัดเฉพาะคลัสเตอร์ความ ล้มเหลว c สำหรับการแก้ไข e ที่เล็งเป้าไปที่คลัสเตอร์ c โดยที่ S' = apply(S, e):

GateOK(S', S, c, eps) ⟺
    G_c(S')  >  G_c(S)            # target cluster improves, AND
    G_c'(S') >= G_c'(S) − eps     # every other cluster regresses by ≤ eps

ค่าเริ่มต้น eps = 0.01 (1%) ตาม paper คลาส BenchmarkGate implement สิ่งนี้ ตัวปรับตั้ง max_regression คือ eps

Composite Reward (เฉพาะการ Train แก้ไข Intelligence)

เมื่อการแก้ไข Intelligence trigger การ train แบบ LoRA / GRPO ในเฟส execute คำตอบผู้เข้าแข่งขัน y ต่อคำถาม q จะถูกให้คะแนนด้วย (paper Eq. 1):

R(q, y) = α · R_acc(q, y)
       − β · Ê(q, y)        # energy
       − γ · L̂(q, y)        # latency
       − δ · Ĉ(q, y)        # cost

ค่าเริ่มต้น (α, β, γ, δ) = (0.5, 0.1, 0.1, 0.3) ปริมาณด้านประสิทธิภาพ (E, L, C) จะถูก z-score ภายใน batch ก่อนถ่วงน้ำหนัก ดังนั้น reward จึงเทียบความ เบี่ยงเบนแบบไม่มีหน่วย แทนที่จะเป็นจูล/วินาที/ดอลลาร์ดิบ (paper Appendix C.6) Implementation: openjarvis.learning.spec_search.composite_reward.score_batch

Gate แบบ held-out ประเมิน spec ที่ได้แบบครบวงจร ไม่ได้รับผลกระทบจากน้ำหนัก เหล่านี้

Configuration

config สำเร็จรูปอยู่ที่ configs/openjarvis/examples/spec-search-quickstart.toml คัดลอกไปที่ ~/.openjarvis/config.toml (หรือตั้งค่า OPENJARVIS_CONFIG ให้ชี้ไปที่ไฟล์นี้) แล้ว loader ปกติจะอ่านมันเอง:

from openjarvis.core.config import load_config
cfg = load_config().learning.spec_search   # SpecSearchLearningConfig

ตาราง [learning.spec_search] map แบบ 1:1 กับ dataclass SpecSearchLearningConfig และถูกอ่านโดยทั้ง SpecSearchOrchestrator.from_config และ SpecSearchLoop:

[learning.spec_search]
enabled = true
teacher_model = "claude-opus-4-6"
teacher_engine = "cloud"
autonomy_mode  = "tiered"             # auto | tiered | manual

# Per-session bounds
min_traces                   = 20
max_cost_per_session_usd     = 5.0
max_tool_calls_per_diagnosis = 30

# Multi-session loop (paper Algorithm 1)
stagnation_k        = 5               # paper default
stagnation_eps      = 0.001
max_total_cost_usd  = 50.0

# Gate (GateOK)
max_regression           = 0.01       # paper default: epsilon = 1%
min_improvement          = 0.0
benchmark_subsample_size = 50
benchmark_version        = "personal_v1"

[learning.spec_search.composite_reward]
alpha = 0.5    # accuracy
beta  = 0.1    # energy
gamma = 0.1    # latency
delta = 0.3    # cost

ไปสู่โปรดักชัน

Quickstart ใช้ fake สำหรับ teacher engine, student runner และ judge เพื่อให้ รันได้โดยไม่ต้องพึ่งบริการภายนอก หากต้องการรัน session จริง สลับแต่ละ fake เป็นส่วนประกอบโปรดักชันที่ตรงกัน:

Slot Quickstart โปรดักชัน
teacher_engine FakeTeacherEngine EngineRegistry.get(cfg.teacher_engine)(model=cfg.teacher_model) — ตั้งค่า ANTHROPIC_API_KEY ฯลฯ
trace_store MagicMock openjarvis.traces.store.TraceStore(home / "traces.db")
student_runner MagicMock openjarvis.learning.spec_search.student_runner.VLLMStudentRunner(host=..., model=...)
judge MagicMock openjarvis.evals.core.scorer.LLMJudgeScorer(...) (หรือ scorer แบบ deterministic ถ้า benchmark ของคุณมีให้)
session_store MagicMock openjarvis.learning.spec_search.storage.session_store.SessionStore(home / "learning" / "sessions.db")
checkpoint_store MagicMock openjarvis.learning.spec_search.checkpoint.store.CheckpointStore(home / "learning" / "checkpoints")
scorer fake ที่ปีน plateau callable Scorer จริง (โดยทั่วไปคือ adapter ของ BenchmarkGate.score)

Orchestrator ขึ้นอยู่กับ interface ของแต่ละ slot เท่านั้น ไม่ใช่ concrete class — อะไรก็ตามที่ implement protocol ที่ตรงกันใช้งานได้

เพิ่ม Corpus ภายนอกใหม่

เฟส Diagnose รับข้อมูลจาก corpus ภายนอกที่อิง HuggingFace ได้ มี provider สามตัวมาให้ในตัว (adp, toolorchestra, generalthoughts) หากต้องการเพิ่ม ตัวใหม่:

  1. สร้าง src/openjarvis/evals/datasets/<corpus>.py ที่ implement DatasetProvider (adp.py เป็นตัวอย่างอ้างอิงขนาดเล็ก) load(max_samples, seed, split) ของ provider ต้องเคารพ split ผ่าน apply_split จาก openjarvis.evals.core.splits
  2. ลงทะเบียน: @DatasetRegistry.register("<corpus>")
  3. ป้อนให้ proposer ผ่าน trace store:
from openjarvis.evals.datasets.adp import ADPDataset
from openjarvis.learning.spec_search.external_adapter import (
    write_external_records_as_traces,
)
from openjarvis.traces.store import TraceStore

records = list(ADPDataset().load(max_samples=200, seed=42, split="all"))
store = TraceStore("~/.openjarvis/traces.db")
n = write_external_records_as_traces(store, records, source_name="adp")
# proposer can now filter on metadata["source"] == "adp"

อะไรรันอยู่ที่ไหน

ตอน inference spec ที่ได้จะรันทั้งหมดบนเครื่อง (on-device) — การอนุมานโมเดล, การรันเอเจนต์, การเรียก tool การเรียก teacher API เกิดขึ้นเฉพาะตอน search (diagnose + plan) เท่านั้น และมีเพียง trace ที่ผ่านการ scrub และเข้าเงื่อนไข เท่านั้นที่ถูกส่งออกไป (ตามกฎความเข้าเงื่อนไขของ trace ใน config ของคุณ)

ผู้ใช้ที่ต้องการทำงานแบบ local-only อย่างเข้มงวด สามารถสลับโมเดลในเครื่องที่ ใหญ่กว่ามาเป็น teacher ได้ วิธีนี้แลกคุณภาพการ search กับการไม่เปิดเผยข้อมูล สู่ cloud เลย

การแก้บั๊กที่มาพร้อม Release นี้

src/openjarvis/evals/backends/jarvis_agent.py เดิม hardcode builder.telemetry(telemetry).traces(True).build() ไว้ ทำให้ไม่สนใจ พารามิเตอร์ telemetry เลย สิ่งนี้ทำให้การประเมิน agent-backend ทุกครั้ง เขียนลง ~/.openjarvis/traces.db โดยไม่รู้ตัว ไม่ว่าผู้เรียกจะตั้งใจหรือไม่ ก็ตาม traces.db ที่เสียหายจึงทำให้ agent eval ทุกครั้งกลายเป็น error "database disk image is malformed" ที่ eval scorer ทิ้งไป สร้างความแม่นยำ สูงปลอมๆ จากตัวอย่างที่สำเร็จเพียงหยิบมือ

การแก้ไขแบบบรรทัดเดียว:

self._system = builder.telemetry(telemetry).traces(telemetry).build()

ผู้เรียกที่เคยคาดหวังว่า trace จะถูกเขียนเสมอ ควรส่ง telemetry=True แบบ ชัดเจน

ดูเพิ่มเติม

  • examples/openjarvis/spec_search_quickstart.py — demo แบบครบวงจรที่รันได้จริง
  • configs/openjarvis/examples/spec-search-quickstart.toml — config สำเร็จรูป
  • src/openjarvis/learning/spec_search/orchestrator.py — SpecSearchOrchestrator (session เดียว)
  • src/openjarvis/learning/spec_search/multi_session.py — SpecSearchLoop (Algorithm 1)
  • src/openjarvis/learning/spec_search/composite_reward.py — paper Eq. 1
  • src/openjarvis/learning/spec_search/gate/benchmark_gate.py — เงื่อนไข GateOK
  • src/openjarvis/learning/spec_search/external_adapter.py — adapter จาก corpus เป็น trace
  • tests/learning/spec_search/test_multi_session.py, test_composite_reward.py — unit test
  • tests/learning/spec_search/test_orchestrator.py — เทสต์แบบ full-session พร้อม mock