รัน Stanford STORM บน custom OpenAI-compatible endpoint
Updated 2026-07-29
STORM สร้าง language model ทุกตัวเป็น LitellmModel และ litellm รับ api_base ใส่ https://api.apisrouter.com/v1 ใน openai_kwargs ที่แชร์กัน เติม prefix openai/ ให้ model id แล้วทั้งห้าช่อง LM ของ pipeline บทความจะ route ผ่าน endpoint เดียวและ key เดียว
คำตอบสั้น ๆ: api_base ใน openai_kwargs, prefix openai/ บน id
LitellmModel ของ STORM เก็บ kwargs ที่คุณสร้างมันมาไว้และรวมเข้าไปในทุกการเรียก litellm.completion() พารามิเตอร์ api_base ของ litellm คือวิธีที่คุณชี้ provider openai ไปที่ host อื่น ดังนั้นการเพิ่ม api_base เข้าไปใน openai_kwargs dict ที่ตัวอย่างของ STORM เองใช้อยู่แล้วก็คือ override ทั้งหมด เติม prefix openai/ ให้ model id แต่ละตัวเพื่อให้ litellm พูดโปรโตคอล chat-completions กับ base นั้น และสตริงหลัง slash จะถูกส่งผ่านไปยัง gateway เพราะตัวอย่างสร้าง openai_kwargs dict เดียวและใช้ซ้ำกับทุกโมเดล key ที่เพิ่มเข้าไปหนึ่งตัวจะ reroute ทั้ง pipeline ไม่ต้องแก้โค้ด STORM ไม่ต้อง fork นี่คือพฤติกรรม knowledge_storm มาตรฐานที่วางอยู่บน routing ที่ litellm มีเอกสารรองรับ
openai_kwargs = {
"api_key": os.getenv("APISROUTER_API_KEY"),
"api_base": "https://api.apisrouter.com/v1",
"temperature": 1.0,
"top_p": 0.9,
}
fast = LitellmModel(model="openai/deepseek-v4-flash", max_tokens=500, **openai_kwargs)
strong = LitellmModel(model="openai/claude-sonnet-4-6", max_tokens=3000, **openai_kwargs)STORM แบ่งบทความออกเป็นห้าช่อง LM อย่างไร
STORM (stanford-oval บน GitHub มีดาวประมาณ 30K) เขียนรายงานสไตล์ Wikipedia จากศูนย์: มันวิจัยหัวข้อผ่านบทสนทนาจำลองหลายมุมมอง สร้าง outline จากสิ่งที่มันเรียนรู้ สร้างบทความเต็มทีละ section แล้วขัดเกลาในขั้นสุดท้าย STORMWikiLMConfigs เปิด pipeline นั้นเป็นโมเดลห้าตัวที่ตั้งค่าแยกกันได้: conv_simulator_lm และ question_asker_lm ขับเคลื่อนบทสนทนาวิจัย outline_gen_lm จัดโครงบทความ article_gen_lm เขียนมัน และ article_polish_lm ทำรอบสุดท้าย README ต้นทางชัดเจนเรื่องเศรษฐศาสตร์: ตัวจำลองบทสนทนารันปริมาณการเรียกสูงที่สุด ดังนั้นมันแนะนำโมเดลที่เร็วกว่าตรงนั้นและโมเดลที่ทรงพลังกว่าสำหรับการสร้างบทความ คำแนะนำนั้นสมมติว่าเลือกระหว่างโมเดลของ OpenAI; อยู่หลัง endpoint หลาย vendor มันขยายไปเป็นอะไรที่มีประโยชน์กว่า แต่ละช่องเป็น LitellmModel ของตัวเองที่มี model string ของตัวเอง ดังนั้น research chatter รันบน DeepSeek id ที่เร็วได้ในขณะที่ outline และ article generation รันบน Claude และ polish รันบนโมเดลใดก็ตามที่คุณเชื่อเรื่อง tone ทั้งหมด authenticate ด้วย key เดียวกันเทียบกับ api_base เดียวกัน ฝั่ง retrieval เป็นกลไกแยกต่างหาก: runner ของ STORM รับ RM module (You.com, Bing และ search backend อื่น ๆ อีกหลายตัว) พร้อม API key ของตัวเอง การเปลี่ยนว่า language model ชี้ไปที่ไหนไม่แตะวิธีที่ source ถูกดึงมาเลย
ตั้งค่าแบบเต็ม: ห้าช่อง หนึ่ง kwargs dict
แพทเทิร์นที่ใช้จริงสะท้อน run script ของ repo เอง: สร้าง kwargs ที่แชร์กันครั้งเดียว สร้าง LitellmModel หนึ่งตัวต่อบทบาท แล้วกำหนดผ่าน setter ของ STORMWikiLMConfigs api_key จะเป็นชื่อใดก็ได้เพราะคุณส่งมันอย่างชัดเจน ตัวอย่างใช้ตัวแปรของตัวเองเพื่อให้ชัดว่านี่ไม่ใช่ credential บัญชี OpenAI litellm ยังเคารพ environment variable ระดับ provider ด้วย และ provider openai อ่าน OPENAI_API_BASE ดังนั้นการ override แบบ environment เท่านั้นก็ทำได้ path ผ่าน kwargs อย่างชัดเจนยังคงเป็นตัวเลือกที่ควรใช้: มันมองเห็นได้ในโค้ดที่สร้างบทความนั้นขึ้นมา มันอยู่รอดได้บนเครื่องที่มี environment state ต่างกัน และมันเปิดทางให้ยกเว้นเป็นต่อช่องได้ถ้าคุณต้องการให้ขั้นตอนหนึ่งไปที่ endpoint อื่น
import os
from knowledge_storm import STORMWikiRunnerArguments, STORMWikiRunner, STORMWikiLMConfigs
from knowledge_storm.lm import LitellmModel
from knowledge_storm.rm import YouRM
openai_kwargs = {
"api_key": os.getenv("APISROUTER_API_KEY"),
"api_base": "https://api.apisrouter.com/v1",
"temperature": 1.0,
"top_p": 0.9,
}
fast = LitellmModel(model="openai/deepseek-v4-flash", max_tokens=500, **openai_kwargs)
strong = LitellmModel(model="openai/claude-sonnet-4-6", max_tokens=3000, **openai_kwargs)
lm_configs = STORMWikiLMConfigs()
lm_configs.set_conv_simulator_lm(fast)
lm_configs.set_question_asker_lm(fast)
lm_configs.set_outline_gen_lm(strong)
lm_configs.set_article_gen_lm(strong)
lm_configs.set_article_polish_lm(strong)
engine_args = STORMWikiRunnerArguments(output_dir="./results")
rm = YouRM(ydc_api_key=os.getenv("YDC_API_KEY"), k=engine_args.search_top_k)
runner = STORMWikiRunner(engine_args, lm_configs, rm)
runner.run(topic="Small modular reactors")เลือกโมเดลต่อขั้นตอนของ pipeline
ปฏิบัติต่อ setter ทั้งห้าเป็นปุ่มปรับงบประมาณ ไม่ใช่ boilerplate คำแนะนำต้นทางบอกไว้แล้วให้แบ่งโมเดลเร็วและแข็งแกร่งข้ามขั้นตอน endpoint หลาย vendor แค่ขยายเมนูต่อขั้นตอนให้กว้างขึ้น เปลี่ยนทีละช่องระหว่างการรันบนหัวข้อเดียวกันแล้ว diff ผลลัพธ์ โดย usage log ต่อ key ตั้งราคาแต่ละการตั้งค่า
- conv_simulator_lm และ question_asker_lm คือขั้นตอนปริมาณ: บทสัมภาษณ์จำลองหลายเทิร์นข้ามหลายมุมมองต่อหัวข้อ deepseek-v4-flash หรือ id เร็วตัวอื่นทำให้เฟส research ไม่ครองบิล และบทสนทนาที่ไม่สมบูรณ์แบบก็รับได้เพราะมันป้อนโน้ต ไม่ใช่ร้อยแก้ว
- article_gen_lm คือช่อง flagship มันเขียน section ที่ยาว มีโครงสร้าง และมีการอ้างอิงจากงานวิจัยที่สะสมมา ซึ่งเป็นงาน generation ต่อเนื่องที่ claude-sonnet-4-6 หรือ gpt-5.5 เหนือกว่าโมเดลเล็กอย่างเห็นได้ชัด
- outline_gen_lm คือการเรียกน้อยครั้งแต่มีอิทธิพลสูง รูปแบบเดียวกับช่องวางแผน: outline ที่อ่อนแอจำกัดบทความไม่ว่านักเขียนจะเก่งแค่ไหน มันคือที่ที่ควรทดสอบ claude-opus-4-7
- article_polish_lm เขียนใหม่เพื่อความลื่นไหลและลบความซ้ำซ้อนข้ามบทความที่ประกอบแล้ว ซึ่งได้ประโยชน์จาก id แบบ context ยาว gemini-3.1-pro-preview คุ้มค่า benchmark ที่นี่
จ่ายตามการใช้งาน · ถูกกว่าราคาทางการ
Selected models are priced below official list prices. Exact input, output, cache, and per-request prices are shown for each model.
| โมเดล | ราคาทางการ | ราคาของเรา |
|---|---|---|
| DeepSeek V4 Flash | $0.14 / $0.28 per M | $0.10 / $0.30 per M |
| Claude Sonnet 4.6 | $3.00 / $15.00 per M | $2.40 / $12.00 per M |
| Claude Opus 4.7 | $5.00 / $25.00 per M | $4.00 / $20.00 per M |
| GPT-5.5 | $5.00 / $30.00 per M | $4.00 / $24.00 per M |
| Gemini 3.1 Pro Preview | $2.00 / $12.00 per M | $1.60 / $9.60 per M |
รูปแบบความล้มเหลวเฉพาะของ STORM
model id เปล่า ๆ จะ route ตามการเดา ไม่ใช่ตาม api_base ของคุณ litellm อ่าน prefix เพื่อเลือก provider และ id ของ Claude ที่ไม่มี prefix จะถูกเดาเป็นการเรียกแบบ Anthropic-native ซึ่งต้องการ ANTHROPIC_API_KEY และเมิน gateway ของคุณไปเลย ทุก id ที่มุ่งไปที่ gateway ต้องพก prefix openai/ prefix นี้ระบุ protocol ไม่ใช่ vendor ช่องหนึ่งที่ถูกทิ้งไว้ข้างหลัง LitellmModel แต่ละตัวเก็บ kwargs ของมันตอนสร้าง ถ้าสี่ช่องแชร์ openai_kwargs และช่องที่ห้าถูกสร้างเฉพาะกิจโดยไม่มี api_base ช่องนั้นจะโพสต์ไปที่ vendor ค่าเริ่มต้นอย่างเงียบ ๆ และล้มเหลวด้วย auth error และ traceback จะระบุชื่อขั้นตอนของ pipeline แทนที่จะเป็นบรรทัด config สร้างทุกช่องจาก dict เดียวกันแล้วบั๊กประเภทนี้จะหายไป ความล้มเหลวของ retriever ที่ถูกโทษว่าเป็นเพราะ endpoint เฟส research ต้องการ search backend ที่ทำงานได้ retriever key ที่ผิดหรือหมดโควตา (YDC_API_KEY, BING_SEARCH_API_KEY หรือ RM ตัวไหนก็ตามที่คุณเลือก) จะล้มเหลวระหว่างการรวบรวมข้อมูล เฟสนั้นสลับกับการเรียก LM ดังนั้นอ่าน traceback ว่า client ตัวไหนที่ raise ก่อนจะไปแตะการตั้งค่า LM secrets.toml ของ demo ไม่ใช่ config ของสคริปต์คุณ Streamlit demo อ่าน secrets.toml การรันแบบ programmatic อ่านสิ่งที่สคริปต์ของคุณส่งเข้าไป การแก้อันหนึ่งขณะรันอีกอันคือความไม่ตรงกันแบบคลาสสิก max_tokens ก็เป็นค่าต่อช่องเช่นกัน ตัวอย่างของ STORM ตั้ง limit เล็กในช่องเร็ว (500) และใหญ่กว่าในช่อง generation (3000) การชี้ช่องไปที่โมเดล long-form โดยไม่ยก max_tokens ให้ตรงกันจะตัด section อย่างเงียบ ๆ ซึ่งดูเหมือนปัญหาคุณภาพโมเดลแต่จริง ๆ เป็นตัวเลข config
ใครที่ route STORM ผ่าน gateway
- ทีมที่สร้างรายงานความรู้ในปริมาณมาก (brief, เอกสารภายในสไตล์ wiki, primer หัวข้อ) ที่การแบ่งห้าช่องทำให้การปรับต้นทุนต่อขั้นตอนคุ้มค่าเงินจริง
- นักวิจัยที่ศึกษาองค์ประกอบของ pipeline: ขั้นตอนไหนได้ประโยชน์จากโมเดลที่แข็งแกร่งกว่าเป็นคำถามเชิงประจักษ์ และ endpoint เดียวทำให้ตารางของ combination ช่อง-โมเดลง่ายที่จะลิสต์
- builder ที่รัน Claude หรือ Gemini ในช่องเขียนของ stack ทรง OpenAI โดยไม่ต้องเพิ่ม SDK ของ vendor ต่อตระกูลโมเดล
- ใครก็ตามที่รันรายการหัวข้อแบบ batch ที่ปริมาณเฟส research คูณข้ามหัวข้อ และ usage log กลายเป็นบัญชีต้นทุนต่อหัวข้อ
- นักพัฒนาที่ไม่มีสิทธิ์เข้าถึงระบบเก็บเงินของ vendor รายหนึ่ง การเข้าถึงแบบเติมเงินโดยไม่ต้องใช้บัตรตัดการพึ่งพาการสมัครต่อ provider ออกไป
ตรวจสอบ endpoint และ debug บทความแรก
ลิสต์โมเดลของ gateway ก่อน: สตริงหลัง openai/ ในแต่ละช่องต้องตรงกับ id ที่เสิร์ฟจริงเป๊ะ ความล้มเหลวในการรันครั้งแรกตามลำดับของ pipeline auth error ที่พูดถึง Anthropic หรือ Google หมายความว่า id ที่ไม่มี prefix ถูก route ไปที่ provider native เพิ่ม openai/ 401 จาก gateway หมายความว่า api_key ใน kwargs ของคุณไม่ใช่ gateway key error model-not-found ระบุชื่อช่องที่ id พิมพ์ผิด ความล้มเหลวระหว่างเฟส research ที่พูดถึง search backend ของคุณคือ credential ของ retriever ไม่ใช่ LM routing และ section บทความที่ถูกตัดหรือสั้นผิดปกติมักเป็นเพราะ max_tokens ตระหนี่ในช่อง generation มากกว่าอะไรก็ตามที่อยู่เหนือมัน การรัน STORM หนึ่งครั้งเต็มคือ burst ขนาดใหญ่: บทสนทนาจำลองข้ามหลายมุมมอง แล้วตามด้วย outline, generation และ polish เมื่อรันหนึ่งครั้งเสร็จแล้ว console ของ APIsRouter แสดงโมเดลต่อ request, จำนวน token และค่าใช้จ่าย ซึ่ง map เข้ากับห้าช่องได้ชัดเจนและบอกคุณได้แม่นยำว่าควรปรับขั้นตอนไหนก่อน batch หัวข้อถัดไป
curl -s https://api.apisrouter.com/v1/models \
-H "Authorization: Bearer $APISROUTER_API_KEY" | head -50คำถามที่พบบ่อย
STORM รองรับ custom OpenAI-compatible endpoint อย่างไร?
ผ่าน litellm STORM สร้าง LM ทุกตัวเป็น LitellmModel ซึ่งรวม kwargs ตอนสร้างเข้าไปในทุกการเรียก litellm.completion() และ litellm รับ api_base สำหรับ provider openai เพิ่ม api_base เข้าไปใน openai_kwargs dict แล้วทุกช่องที่สร้างจากมันจะ route ไปที่ gateway
ทำไม model id ต้องมี prefix openai/?
litellm เลือก provider จาก prefix openai/claude-sonnet-4-6 หมายความว่า "พูดโปรโตคอล chat-completions ของ OpenAI กับ api_base ของฉันด้วย model claude-sonnet-4-6" ถ้าไม่มี prefix litellm จะเดา vendor จากชื่อและ route แบบ native ข้าม endpoint ของคุณไป
ขั้นตอนต่างกันของ STORM ใช้โมเดลของ vendor ต่างกันได้ไหม?
ได้ แต่ละช่องในห้าช่องเป็น LitellmModel อิสระจากกัน ดังนั้นตัวจำลองบทสนทนารัน DeepSeek id ในขณะที่ article generation รัน Claude และ polish รัน GPT ได้ ทั้งหมดผ่าน api_base และ key เดียวกัน คำแนะนำต้นทางแนะนำให้แบ่งโมเดลเร็วและแข็งแกร่งข้ามขั้นตอนอยู่แล้ว
search retriever เปลี่ยนไปด้วยไหมเมื่อฉันเปลี่ยน api_base?
ไม่เปลี่ยน retrieval รันผ่าน RM module ที่คุณส่งให้ STORMWikiRunner (You.com, Bing และ backend ที่รองรับอื่น ๆ) พร้อม key ของตัวเอง LM routing และการดึง source เป็นระบบอิสระที่ล้มเหลวคนละเฟสของการรัน
มีทาง environment-variable แทน kwargs ไหม?
litellm เคารพตัวแปรระดับ provider และ provider openai อ่าน OPENAI_API_BASE มันใช้ได้ แต่ kwarg api_base ที่ชัดเจนทำซ้ำได้มากกว่า: มันเดินทางไปกับสคริปต์ อยู่รอดบนเครื่องที่มี environment state ต่างกัน และยอมให้มีข้อยกเว้นต่อช่องได้
บทความ STORM หนึ่งบทความใช้ token กี่ตัว?
เฟส research ครองสัดส่วนหลัก: บทสนทนาจำลองหลายมุมมองคูณการเรียกก่อนที่บทความจะมีสักตัวอักษร แล้ว generation และ polish ก็เพิ่ม output แบบ long-form เข้ามาอีก การรันเต็มมักอยู่ในหลักแสน token และมุมมอง usage ต่อ key แสดงการแบ่งต่อขั้นตอนที่แน่นอน