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 จริง
สรุปสั้น — รันเลย¶
สคริปต์นี้สมบูรณ์ในตัวเอง (ไม่ต้องมี 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):
ค่าเริ่มต้น (α, β, γ, δ) = (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) หากต้องการเพิ่ม
ตัวใหม่:
- สร้าง
src/openjarvis/evals/datasets/<corpus>.pyที่ implementDatasetProvider(adp.pyเป็นตัวอย่างอ้างอิงขนาดเล็ก)load(max_samples, seed, split)ของ provider ต้องเคารพsplitผ่านapply_splitจากopenjarvis.evals.core.splits - ลงทะเบียน:
@DatasetRegistry.register("<corpus>") - ป้อนให้ 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 ทิ้งไป สร้างความแม่นยำ
สูงปลอมๆ จากตัวอย่างที่สำเร็จเพียงหยิบมือ
การแก้ไขแบบบรรทัดเดียว:
ผู้เรียกที่เคยคาดหวังว่า 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. 1src/openjarvis/learning/spec_search/gate/benchmark_gate.py— เงื่อนไขGateOKsrc/openjarvis/learning/spec_search/external_adapter.py— adapter จาก corpus เป็น tracetests/learning/spec_search/test_multi_session.py,test_composite_reward.py— unit testtests/learning/spec_search/test_orchestrator.py— เทสต์แบบ full-session พร้อม mock