SGLang 编程模型与约束解码
SGLang 不仅是一个推理引擎,还提供了一套编程模型来精确控制大语言模型的生成过程。通过约束解码和结构化生成,可以确保模型输出严格符合预定义的格式。
SGLang 编程原语
SGLang 提供以下核心编程原语:
gen:生成文本,支持采样参数控制select:从候选选项中选择fork:创建并行分支image:多模态输入
python
import sglang as sgl
@sgl.function
def multi_choice_question(s, question, options):
"""结构化多选题生成"""
s += question + "\n"
s += "选项:\n"
for i, opt in enumerate(options):
s += f"{chr(65+i)}. {opt}\n"
s += "正确答案是:" + sgl.gen("answer", max_tokens=1, regex="[A-D]")
s += "\n解释:" + sgl.gen("explanation", max_tokens=200)
# 执行
runtime = sgl.Runtime(model_path="meta-llama/Llama-2-7b-hf")
result = multi_choice_question.run(
runtime,
question="以下哪个不是 Python 的内置数据类型?",
options=["list", "dict", "array", "tuple"],
)
print(result["answer"]) # C
print(result["explanation"]) # array 是 numpy 的...约束解码
约束解码通过正则表达式或 JSON Schema 限制生成空间:
python
# 正则约束
@sgl.function
def extract_info(s, text):
s += "从以下文本中提取信息:\n" + text + "\n"
s += "姓名:" + sgl.gen("name", regex="[\\u4e00-\\u9fa5]{2,4}") + "\n"
s += "电话:" + sgl.gen("phone", regex="1[3-9]\\d{9}") + "\n"
s += "邮箱:" + sgl.gen("email", regex="[\\w.]+@[\\w.]+\\.com")
# JSON Schema 约束
from pydantic import BaseModel
class PersonInfo(BaseModel):
name: str
age: int
city: str
@sgl.function
def json_extract(s, text):
s += "提取为JSON:" + text + "\n"
s += sgl.gen("result", schema=PersonInfo.schema_json())分支与并行
SGLang 支持 fork 原语实现并行推理:
python
@sgl.function
def debate(s, topic):
"""正反方辩论生成"""
s += f"辩论主题:{topic}\n\n"
# 创建两个并行分支
forks = s.fork(2)
forks[0] += "正方观点:" + sgl.gen("pro", max_tokens=200)
forks[1] += "反方观点:" + sgl.gen("con", max_tokens=200)
# 合并分支
s += "正方:" + forks[0]["pro"] + "\n"
s += "反方:" + forks[1]["con"] + "\n"
s += "总结:" + sgl.gen("summary", max_tokens=300)约束解码的性能
约束解码通过限制词表空间,不仅保证输出格式正确,还能加速生成过程。正则约束可将无效 Token 的概率直接置零,减少采样步数。
与其他方案对比
| 方案 | 约束类型 | 性能开销 | 灵活性 |
|---|---|---|---|
| SGLang | 正则/Schema | 低 | 高 |
| Guidance | 语法树 | 中 | 高 |
| Outlines | FSM | 低 | 中 |
| JSON Mode | JSON Schema | 低 | 低 |
正则复杂度
过于复杂的正则表达式可能导致约束解码的 DFA 状态数爆炸,影响推理速度。建议将复杂约束拆分为多个简单步骤。