概述

langchain 1.0 是一个强大的中文大语言模型框架,支持多种结构化输出功能。本文将介绍如何实现 LLM 的结构化输出。

什么是结构化输出

结构化输出是指让大语言模型按照预定义的结构(如 JSON、XML、YAML 等格式)返回结果,而不是自由文本。这种输出方式具有以下优势:

  1. 易于解析:程序可以直接解析结构化数据
  2. 格式统一:输出格式标准化,便于后续处理
  3. 类型安全:可以定义数据类型和约束
  4. 自动化友好:便于集成到自动化流程中

结构化输出实现

环境准备

首先安装必要的依赖:

1
2
3
4
pip install --pre -U langchain==1.0.0a9
pip install pydantic
pip install -U langchain-openai==1.0.0a2
pip install -U langchain-core==1.0.0a4

基于Pydantic Model实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# -*- coding: utf-8 -*-
from pydantic import BaseModel, Field
from typing import List, Optional
from langchain.agents import create_agent
import json
import os

# 定义产品信息输出结构
class ProductInfo(BaseModel):
name: Optional[str] = Field(description="产品名称")
catalog_number: Optional[str] = Field(description="规格货号")
description: Optional[str] = Field(description="产品描述")
validity_period: Optional[str] = Field(description="有效期")
storage_temperature: Optional[str] = Field(description="保存温度")

# 设置环境变量(推荐方式)
os.environ["OPENAI_API_BASE"] = "https://{HOST}/v1"
os.environ["OPENAI_API_KEY"] = "sk-XXX"

# 创建代理 - 使用环境变量配置
agent = create_agent(
model="openai:XXXX",
tools=[], # 可以添加工具,这里暂时为空
response_format=ProductInfo # Auto-selects ProviderStrategy
)

def extract_product_info(text: str) -> ProductInfo:
"""从文本中提取产品结构化信息"""

try:
# 使用 langchain agent 进行结构化提取
result = agent.invoke({
"messages": [{"role": "user", "content": f"""
请从以下产品说明书中提取结构化信息:

文本内容:
{text}

请提取以下信息:
1. 产品名称
2. 规格货号(产品编号)
3. 产品描述
4. 有效期
5. 保存温度
6. 注意事项列表

请严格按照ProductInfo模型的结构返回结果。
"""}]
})

# 返回结构化响应
return result["structured_response"]

except Exception as e:
print(f"API调用失败: {e}")
# 返回一个默认的产品信息对象
return ProductInfo(
name="提取失败",
catalog_number="未知",
description="无法提取产品描述",
validity_period="未知",
storage_temperature="未知",
precautions=["提取过程中出现错误"]
)

# 示例文本
sample_text = """
## 达优

## Mouse IL-22 Precoated ELISA Kit

Cat#:1212202/1212203

ELISA 试剂盒 说明书

本试剂盒仅供科研使用,请勿用于诊断 使用前请仔细阅读说明书并检查试剂盒组分
## Mouse IL-22

## Precoated ELISA Kit

ELISA试剂盒

## | 检测原理

达优ELISA 试剂盒采用双抗体夹心酶联免疫吸附检测技术。特异性的抗体包 被到酶标板上,标准品或样本加入孔中,样本中含有的目标物质被固定化的抗体 捕获,加入检测抗体,形成抗体-抗原-抗体的复合体,加入酶和底物,在酶的催 化下底物发生反应,且反应的颜色与目标物质的含量成正比,加入终止液后,在 酶标仪中测定反应的吸光值。

## | 预期用途

定量检测血清、血浆、缓冲液或细胞培养液中的IL-22含量。

## | 知识背景

白细胞介素22(Interleukin-22, IL-22)又称白细胞介素 10 相关 T 细胞衍 生诱导因子,与IL-10,IL-19,IL-20,IL-24 和 IL-26 同属于 IL-10 调节性细胞 因子。 它主要由Th22 细胞分泌, IL-23 和 IL-6 可直接刺激初始 T 细胞产生 IL-22, 某些NK细胞也可以产生IL-22。其次,IL-22以皮肤,胰腺,肝脏等组织细胞为 主要靶细胞,调节这些组织细胞的功能,它在感染免疫,组织修复、肝炎产生和 肝癌形成中起重要作用。

## | 保存条件

未开封试剂盒在2℃~8℃可稳定保存12个月。

## 开封后各组分保存条件见下:

## Cat#:1212202

| 组分 | 规格 | 数量 | 保存条件 |
|------------------------|-------|------|--------------------------------------|
| Cytokine Standard | | 2瓶 | 48 T 复溶后-25℃~-15℃保存15天,分装保 存,不可重复冻融。 |
| Biotinylated Antibody | μL | 2管 | 50 原液2℃~8℃可稳定保存1个月,稀释 后不可保存。 |
| Streptavidin-HRP | 50 μL | 2管 | 原液2℃~8℃可稳定保存1个月,稀释 后不可保存。 |
| Dilution Buffer R (1×) | 10 mL | 3瓶 | 2℃~8℃可稳定保存1个月。 |
| Washing Buffer (50×) | 15 mL | 1瓶 | 原液2℃~8℃可稳定保存1个月,稀释 后不可保存。 |
| TMB | 10 mL | 1瓶 | 2℃~8℃可稳定保存1个月。 |
| Stop Solution | 10 mL | 1瓶 | 2℃~8℃可稳定保存1个月。 |
| Precoated ELISA plate | 96 T | 1块 | 2℃~8℃可稳定保存1个月,密封保存。 |
| 封板膜 | / | | 4张 / |
| 说明书 | / | 1份 | / |

Cat#:1212203

| 组分 | 规格 | 数量 | 保存条件 |
|-------------------------|--------|------|----------------------------------|
| Cytokine Standard | 48 T | 10瓶 | 复溶后-25℃~-15℃保存15天, 分装 保存,不可重复冻融。 |
| Biotinylated Antibody | 50 μL | 10管 | 原液2℃~8℃可稳定保存1个月,稀 释后不可保存。 |
| Streptavidin-HRP | 500 μL | 1管 | 原液2℃~8℃可稳定保存1个月,稀 释后不可保存。 |
| Dilution Buffer R (10×) | 18 mL | 1瓶 | 2℃~8℃可稳定保存1个月。 |
| Washing Buffer (50×) | 75 mL | 1瓶 | 原液2℃~8℃可稳定保存1个月,稀 释后不可保存。 |
| TMB | 50 mL | 1瓶 | 2℃~8℃可稳定保存1个月。 |
| Stop Solution | 50 mL | 1瓶 | 2℃~8℃可稳定保存1个月。 |
| Precoated ELISA plate | 96 T | 5块 | 2℃~8℃可稳定保存1个月,密封保 存。 |
| 封板膜 | / | 20张 | / |
| 说明书 | / | 1份 | / |

## | 需自备的材料设备

+ 能够检测450nm和630nm吸光度的酶标仪
+ 微量加液器及枪头、加样槽
+ 蒸馏水或去离子水
+ 旋涡振荡器或磁力搅拌器

## | 注意事项

1. 试剂按标签和说明书储存,使用前平衡至室温。
2. 标准品按照标签溶解,充分混匀,按照一次使用量分装保存。
3. 预包被板条使用前平衡至室温;剩余板条及时密封保存。
4. Washing Buffer (50×)在 2℃~8℃保存可能有结晶析出,若有析出加热或 平衡温度使其溶解后再使用。
5. 离心管试剂体积量较小,使用前高速短暂离心收集试剂到管底。
6. 实验操作中请使用一次性的吸头,避免交叉污染。
7. 使用前检查试剂盒内各种试剂;试剂稀释及加样时注意混匀试剂。
8. 实验板孔加入试剂的顺序应一致,以保证所有反应孔的孵育时间一致。
9. 使用干净的容器配制试剂。
10. 洗涤后孔中残留的洗涤液应在吸水纸上充分拍干,直至吸水纸上看不到 水印。请勿直接将孔中的洗涤液吸干。
11. TMB 对光和金属敏感,避免长时间暴露于光下,避免与金属接触。未使 用的TMB若变为蓝色,表明TMB已经污染,请丢弃。
12. 请严格按照说明书建议的反应时间和温度进行孵育。 冬季室内温度偏低, 建议使用恒温箱或培养箱孵育。
13. 请在保质期内使用试剂盒,且不同批号试剂不要混用。
14. 200 pg/mL 以上的结果为非线性的,根据此标准曲线无法得到精确的结
果。大于200pg/mLIL-22 的样本应稀释后重新测试。在结果分析时,结合考虑 相应的稀释度。

15. 安全提示: 使用本试剂盒时请做好合适的防护措施,如穿戴白大褂、乳 胶手套、安全眼镜等;避免试剂接触皮肤和眼睛,如不慎接触,立即用大量清水 清洗。
16. 废弃处置: 终止液使用碱性溶液中和或稀释后,再用大量水冲入废水系 统。其他成分按一般废弃物处理方法和当地相关法规的要求进行处理。

## | 样本处理说明

新鲜标本尽早检测,对收集后当天就进行检测的标本,及时储存2℃~8℃。 如有特殊原因需要周期性收集标本,请在取材后,将标本及时分装并放在-25℃ ~-15℃或更低温度条件下保存,避免反复冻融。

+ 血清*: 室温血液自然凝固10~20分钟后,离心20分钟(2000~3000 转/分),仔细收集上清。保存过程中如有沉淀形成,应再次离心取上清。
+ 血浆*: 应根据标本的要求选择EDTA、柠檬酸钠或肝素作为抗凝剂,混 合10~20 分钟后,离心 20 分钟(2000~3000 转/分),仔细收集上清。保存过 程中如有沉淀形成,应再次离心。
+ 细胞培养上清: 检测分泌性的成份时,请用无菌管收集细胞培养悬液, 离心20分钟(2000~3000 转/分),仔细收集上清。检测细胞内的成份时,用 1×PBS(pH 7.2~7.4)将细胞悬液稀释至 10 6 cell/mL 左右,反复冻融,以使细 胞破碎释放细胞内成份,离心20分钟(2000~3000转/分),仔细收集上清。 细胞培养上清保存过程中如有沉淀形成,应再次离心。

* 血浆血清样本检测之前需用DilutionBufferR(1×)至少稀释 2 倍后检测, 以降低样本 基质效应。具体稀释倍数需预实验确定。


## | 试剂的配制

1. 提前20分钟将Washing Buffer(50×)和即用溶液从试剂盒中取出,平衡 至室温。
2. 需稀释配制成工作液的试剂现配现用。
3. Cytokine Standard:先按标签说明将冻干粉溶解,溶解后的浓度为 200 pg/mL,再用 1×Dilution Buffer R (1×)进行倍比稀释。稀释前将标准品轻轻 振荡5分钟。
+ 推荐标准品浓度梯度为: 200 pg/mL、100 pg/mL、50 pg/mL、 25 pg/mL、12.5 pg/mL、6.25 pg/mL、3.13 pg/mL,为确保标准品稀释的准 确性,建议在实验孔外进行标准品的倍比稀释。
+ 稀释步骤: 校准品按瓶标签进行溶解后移入EP管中,标记为C7。再取 6 个 EP 管,分别标记为 C6/C5/C4/C3/C2/C1,每个管中加 250 μL 稀释液,从 C7 中取出 250 μL 高浓度校准品至 C6 管中进行 2 倍倍比稀释,以同样方式稀释 C5/C4/C3/C2/C1 管中,以稀释液作为 C0。
4. Biotinylated Antibody:1:100 用 Dilution Buffer R (1×)稀释,混匀制 成Biotinylated Antibody 工作液。
5. Streptavidin-HRP:1:100 用 Dilution Buffer R (1×)稀释,混匀制成 Streptavidin-HRP 工作液。
6. Washing Buffer (50×):1:50 用蒸馏水稀释。
7. *Dilution Buffer R (10×): 1:9 用 1×PBS (pH=7.2~7.4) 配制成 1×Dilution Buffer R 工作液,只用于稀释 Cytokine Standard、Biotinylated Antibody、 Streptavidin-HRP 和样品

*此步骤适用于Cat#:1212203 试剂盒,Cat#:1212202 中本试剂为 1×工作液,无需 稀释。


## | 即用型试剂

+ Dilution Buffer R (1×):用于稀释 Cytokine Standard、样本、 Biotinylated Antibody 和 Streptavidin-HRP。
+ TMB
+ Stop Solution


## | 操作过程

1. 使用前,将所有试剂充分混匀,避免产生泡沫。
2. 根据实验孔(空白和标准品)数量,确定所需的板条数目。样本(含标准 品)和空白都应做复孔。
3. 加样: 100 μL/well 加入稀释后的 Cytokine Standard 至标准品孔, 100 μL/well 加入样本至样本孔, 100 μL/well 加入 Dilution Buffer R (1×)至空 白对照孔,37℃孵育90分钟。
4. 洗板: 扣去孔内液体,300 μL/well 加入 1×Washing Buffer 工作液;停 留1分钟后弃去孔内液体。重复4次,每一次在滤纸上扣干。
5. 加检测抗体 :100 μL/well 加入 Biotinylated Antibody 工作液。混匀后 盖上封板膜,37℃孵育60分钟。
6. 洗板:重复步骤4。
7. 加酶: 100 μL/well 加入 Streptavidin-HRP 工作液。盖上封板膜,37℃ 孵育30分钟。
8. 洗板:重复步骤4。
9. 显色: 100 μL/well 加入 TMB,37℃避光孵育 10~20 分钟。可根据孔内 颜色的深浅判断,校准曲线最高值孔为深蓝色,空白孔无明显蓝色时即可终止反 应。
10. 终止反应: 100 μL/well 迅速加入 Stop Solution 终止反应。
11. 读板: 终止反应10分钟内在酶标仪进行双波长读板,设置检测波长 450 nm 和参考波长 610 nm~630 nm;校正后的 OD 值为检测波长的测定值减 去参考波长的测定值。

## | 结果分析

1. 推荐拟合曲线坐标对数或自然数,拟合方程常见为直线、二次方程及四参 数方程,通过各种应用软件拟合选取最佳标准曲线,根据样本OD值查找相应浓 度。
2. 稀释的样本计算浓度时应乘以稀释倍数;若样品浓度高于最高线性值,需 稀释样本并重新测定。
3. 示例数据仅供参考, 每次实验 必须制备当次实验的标准曲线。

| Mouse IL-22 Concentration (pg/mL) | OD | OD | Average OD |
|-------------------------------------|-------|-------|--------------|
| 200 | 3.436 | 3.455 | 3.446 |
| 100 | 2.693 | 2.687 | 2.69 |
| 50 | 1.547 | 1.584 | 1.566 |
| 25 | 0.957 | 0.839 | 0.898 |
| 12.5 | 0.407 | 0.449 | 0.428 |
| 6.25 | 0.236 | 0.247 | 0.242 |
| 31.3 | 0.146 | 0.128 | 0.137 |
| 0 | 0.044 | 0.043 | 0.044 |## | 性能数据

- 1. 灵敏度: 1 pg/mL,10 个空白样本的对应浓度平均值加上三倍标准偏差。
- 2. 精密度: 板内精密度:3个已知浓度的样本板内重复测定10次,计算得 到板内变异系数;板间精密度:3个已知浓度的样本板间分别重复测定3次,计 算得到板间变异系数

| | 板内精密度 | 板内精密度 | 板内精密度 | 板间精密度 | 板间精密度 | 板间精密度 |
|------------|---------|---------|---------|---------|---------|---------|
| 样本 | 1 | 2 | 3 | 1 | 2 | 3 |
| 测试次数 | 10 | 10 | 10 | 9 | 9 | 9 |
| 标准差 | 0.46 | 1.46 | 5.15 | 0.56 | 1.56 | 5.14 |
| 平均值(pg/mL) | 6.74 | 25.8 | 116.36 | 6.74 | 25.63 | 114.81 |
| 变异系数CV(%) | 6.79 | 5.67 | 4.43 | 8.31 | 6.09 | 4.47 |

- 3. 校准: 试剂盒校准品为达优校准的高纯度重组IL-22。

## | 参考文献

[1] Jia, L., Jiang, Y., Wu, L.et al.Porphyromonas gingivalis aggravates colitis via a gut microbiota-linoleic acid metabolism-Th17/Treg cell balance axis[J].Nat Commun, 2024.

[2] Huang,R.,Jia,B.,Su,D.,Li,M.,Xu,Z.,He,C.,Huang,Y.,Fan,H.,Chen,H., &Cheng,F.(2023).Plant exosomes fused with engineered mesenchymal stemcell-derived nanovesicles for synergistic therapy of autoimmune skin disorders[J].Journal of Extracellular Vesicles.


"""

# 执行产品信息结构化提取
try:
result = extract_product_info(sample_text)
print("产品信息提取结果:")
print(json.dumps(result.model_dump(), ensure_ascii=False, indent=2))
except Exception as e:
print(f"提取失败:{e}")

最终结构化输出:

1
2
3
4
5
6
7
{
"name": "Mouse IL-22 Precoated ELISA Kit",
"catalog_number": "1212202",
"description": "Mouse IL-22 Precoated ELISA Kit,用于定量检测血清、血浆、缓冲液或细胞培养液中的IL-22含量。采用双抗体夹心酶联免疫吸附检测技术。",
"validity_period": "12个月",
"storage_temperature": "2℃~8℃"
}

基于 JSON Schema 的结构化输出

除了使用 Pydantic 模型,langchain 1.0 还支持直接使用 JSON Schema 定义输出格式。这种方式更加灵活,特别适合动态配置的场景。

JSON Schema 实现示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# -*- coding: utf-8 -*-
from langchain.agents import create_agent
import json
import os

# 定义产品信息的 JSON Schema(对应基础实现中的ProductInfo结构)
product_info_schema = {
"type": "object",
"description": "Product information extracted from documentation.",
"properties": {
"name": {"type": "string", "description": "产品名称"},
"catalog_number": {"type": "string", "description": "规格货号"},
"description": {"type": "string", "description": "产品描述"},
"validity_period": {"type": "string", "description": "有效期"},
"storage_temperature": {"type": "string", "description": "保存温度"}
},
"required": []
}

# 设置环境变量
os.environ["OPENAI_API_BASE"] = "https://{HOST}/v1"
os.environ["OPENAI_API_KEY"] = "sk-XXX"

# 创建代理 - 使用 JSON Schema 作为响应格式
agent = create_agent(
model="openai:XXXX",
tools=[], # 可以添加工具,这里暂时为空
response_format=product_info_schema # Auto-selects ProviderStrategy
)

def extract_product_info_json(text: str) -> dict:
"""从文本中提取产品结构化信息(使用JSON Schema)"""

try:
# 使用 langchain agent 进行结构化提取
result = agent.invoke({
"messages": [{"role": "user", "content": f"""
请从以下产品说明书中提取结构化信息:

文本内容:
{text}

请提取以下信息:
1. 产品名称
2. 规格货号(产品编号)
3. 产品描述
4. 有效期
5. 保存温度

请严格按照JSON Schema的结构返回结果。
"""}]
})

# 返回结构化响应
return result["structured_response"]

except Exception as e:
print(f"API调用失败: {e}")
# 返回一个默认的产品信息对象
return {
"name": "提取失败",
"catalog_number": "未知",
"description": "无法提取产品描述",
"validity_period": "未知",
"storage_temperature": "未知"
}

# 示例文本(使用基础实现中的相同示例)
sample_text = """
## 达优

## Mouse IL-22 Precoated ELISA Kit

Cat#:1212202/1212203

ELISA 试剂盒 说明书

本试剂盒仅供科研使用,请勿用于诊断 使用前请仔细阅读说明书并检查试剂盒组分
## Mouse IL-22

## Precoated ELISA Kit

ELISA试剂盒

## | 检测原理

达优ELISA 试剂盒采用双抗体夹心酶联免疫吸附检测技术。特异性的抗体包 被到酶标板上,标准品或样本加入孔中,样本中含有的目标物质被固定化的抗体 捕获,加入检测抗体,形成抗体-抗原-抗体的复合体,加入酶和底物,在酶的催 化下底物发生反应,且反应的颜色与目标物质的含量成正比,加入终止液后,在 酶标仪中测定反应的吸光值。

## | 预期用途

定量检测血清、血浆、缓冲液或细胞培养液中的IL-22含量。

## | 保存条件

未开封试剂盒在2℃~8℃可稳定保存12个月。
"""

# 执行产品信息结构化提取
try:
result = extract_product_info_json(sample_text)
print("产品信息提取结果:")
print(json.dumps(result, ensure_ascii=False, indent=2))
except Exception as e:
print(f"提取失败:{e}")

最终输出:

1
2
3
4
5
6
7
{
"catalog_number": "1212202/1212203",
"description": "定量检测血清、血浆、缓冲液或细胞培养液中的IL-22含量。",
"name": "Mouse IL-22 Precoated ELISA Kit",
"storage_temperature": "2℃~8℃",
"validity_period": "12个月"
}

扩展 JSON Schema 示例

扩展的产品信息 JSON Schema(在基础结构上增加更多字段)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
from langchain.agents import create_agent
import json
import os

extended_product_schema = {
"type": "object",
"description": "Extended product information with additional details",
"properties": {
"name": {"type": "string", "description": "产品名称"},
"catalog_number": {"type": "string", "description": "规格货号"},
"description": {"type": "string", "description": "产品描述"},
"validity_period": {"type": "string", "description": "有效期"},
"storage_temperature": {"type": "string", "description": "保存温度"},
"category": {"type": "string", "description": "产品类别"},
"brand": {"type": "string", "description": "品牌"},
"precautions": {
"type": "array",
"items": {"type": "string"},
"description": "注意事项列表"
},
"specifications": {
"type": "array",
"items": {
"type": "object",
"properties": {
"spec_name": {"type": "string", "description": "规格名称"},
"spec_value": {"type": "string", "description": "规格值"},
"unit": {"type": "string", "description": "单位"}
},
"required": ["spec_name", "spec_value"]
},
"description": "技术规格列表"
}
},
"required": []
}

# 设置环境变量
os.environ["OPENAI_API_BASE"] = "https://{HOST}/v1"
os.environ["OPENAI_API_KEY"] = "sk-XXX"


# 创建扩展产品信息提取代理
extended_agent = create_agent(
model="openai:XXXX",
tools=[],
response_format=extended_product_schema
)

def extract_product_info_json(text: str) -> dict:
"""从产品描述中提取扩展信息"""

try:
result = extended_agent.invoke({
"messages": [{"role": "user", "content": f"""
请从以下产品说明书中提取结构化信息:

{text}

请提取以下信息:
1. 产品名称
2. 规格货号(产品编号)
3. 产品描述
4. 有效期
5. 保存温度
6. 产品类别
7. 品牌
8. 注意事项列表
9. 技术规格列表

请严格按照JSON Schema的结构返回结果。
"""}]
})

return result["structured_response"]

except Exception as e:
print(f"扩展产品信息提取失败: {e}")
return {
"name": "提取失败",
"catalog_number": "未知",
"description": "无法提取产品描述",
"validity_period": "未知",
"storage_temperature": "未知",
"category": "未知",
"brand": "未知",
"precautions": ["提取过程中出现错误"],
"specifications": []
}

# 示例文本(使用基础实现中的相同示例)
sample_text = """
## 达优

## Mouse IL-22 Precoated ELISA Kit

Cat#:1212202/1212203

ELISA 试剂盒 说明书

本试剂盒仅供科研使用,请勿用于诊断 使用前请仔细阅读说明书并检查试剂盒组分
## Mouse IL-22

## Precoated ELISA Kit

ELISA试剂盒

## | 检测原理

达优ELISA 试剂盒采用双抗体夹心酶联免疫吸附检测技术。特异性的抗体包 被到酶标板上,标准品或样本加入孔中,样本中含有的目标物质被固定化的抗体 捕获,加入检测抗体,形成抗体-抗原-抗体的复合体,加入酶和底物,在酶的催 化下底物发生反应,且反应的颜色与目标物质的含量成正比,加入终止液后,在 酶标仪中测定反应的吸光值。

## | 预期用途

定量检测血清、血浆、缓冲液或细胞培养液中的IL-22含量。

## | 保存条件

未开封试剂盒在2℃~8℃可稳定保存12个月。
"""

# 执行产品信息结构化提取
try:
result = extract_product_info_json(sample_text)
print("产品信息提取结果:")
print(json.dumps(result, ensure_ascii=False, indent=2))
except Exception as e:
print(f"提取失败:{e}")

最后输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"brand": "达优",
"catalog_number": "1212202/1212203",
"category": "ELISA试剂盒",
"description": "本试剂盒采用双抗体夹心酶联免疫吸附检测技术,用于定量检测血清、血浆、缓冲液或细胞培养液中的IL-22含量。",
"name": "Mouse IL-22 Precoated ELISA Kit",
"precautions": [
"本试剂盒仅供科研使用,请勿用于诊断",
"使用前请仔细阅读说明书并检查试剂盒组分",
"未开封试剂盒在2℃~8℃可稳定保存12个月"
],
"storage_temperature": "2℃~8℃",
"validity_period": "12个月"
}

JSON Schema 的优势

  1. 动态配置:可以在运行时动态修改 Schema
  2. 标准化:遵循 JSON Schema 标准,兼容性好
  3. 灵活性:支持复杂的嵌套结构和数组
  4. 验证:可以结合 JSON Schema 验证库进行数据验证
  5. 文档化:Schema 本身就是很好的文档

应用场景

  1. 产品数据库建设:自动提取产品说明书信息,建立结构化产品数据库
  2. 质量控制:标准化产品信息格式,提高信息管理效率
  3. 技术文档处理:批量处理技术文档,提取关键信息
  4. 产品对比分析:结构化产品信息便于进行产品对比和分析
  5. 自动化流程:将产品信息提取集成到自动化工作流中
  6. 医药产品管理:提取试剂盒、药品等医疗产品的规格和保存条件
  7. 实验室管理:自动提取实验试剂和仪器的技术参数

总结

langchain 1.0 提供了强大的结构化输出功能,通过合理的数据模型设计和错误处理机制,可以实现高质量的产品信息结构化提取。本文提供的完整示例代码可以直接运行,为产品信息管理项目提供参考。

通过结构化输出,我们可以更好地将 LLM 的能力集成到产品信息自动化处理流程中,提高数据处理效率和质量,为产品管理和技术文档处理提供有力支持。

背景

2025年08月31日,参加了个AI深度学习workshop,记录和复盘一下整个workshop例子。

环境准备

  • 显卡:L20

    因为手上有算力资源,本次记录就不使用AutoDL租借算力,本次使用L20,顺便完善一下yolo11环境,方便以后训练复用,同时也顺便测一下与4090的算力情况。

  • 训练环境:yolo11环境搭建
  • 视觉模块:reCamera 摄像头 $49.90刀
    • 缺点:我觉得像素还是上不了生产,还不如一个高清摄像头。
    • 优点:小,不仅仅一个摄像头,相当于一个边缘设备,可以独立部署上去。整合了工作流,可以快速部署监测模型。

实践

数据集准备

首先到 roboflow 找一份数据集,这次复盘记录我选一个螺丝数据集吧,毕竟这个reCamera的外观有一点工业风,就拿工业场景来做比较合适。

数据训练

前面准备了yolo11环境搭建,我们开始在这个yolo11环境开始训练。

模型量化与格式转换

  • 工具安装

    我们使用docker

    1
    2
    3
    4
    5
    # 拉取镜像并启动容器
    docker pull sophgo/tpuc_dev:v3.1
    docker run --privileged --name reCamera_dev -v $PWD:/workspace -it sophgo/tpuc_dev:v3.1
    #容器内安装工具包 tpu_mlir
    pip install tpu_mlir[all]==1.7 -i https://mirrors.aliyun.com/pypi/simple

    上面方法慢的话:

    1
    2
    3
    4
    5
    wget https://sophon-assets.sophon.cn/sophon-prod-s3/drive/25/04/15/16/tpuc_dev_v3.4.tar.gz
    docker load -i tpuc_dev_v3.4.tar.gz
    docker run --privileged --name reCamera_dev -v $PWD:/workspace -it sophgo/tpuc_dev:v3.4
    #容器内安装工具包 tpu_mlir
    pip install tpu_mlir[all]==1.7 -i https://mirrors.aliyun.com/pypi/simple
  • 格式转换

    之所以要进行格式转换,因为不同的部署平台的架构不一样,支持的的格式不一

    • reCamera 基于RISC-V架构
      • ONNX→MLIR转换→cvimodel量化

部署与测试

  • 使用reCamera测试
  • 在本地电脑部署测试
  • 在手机端部署测试

场景思考

  • 深度学习在供应链中的应用

相关链接

ReCamera AI模型转换与量化全流程指南

基础搭建

  • 创建虚拟环境

    1
    conda create -n yolo11 python=3.11 -y 
  • 安装ultralytics

    我们使用源代码进行安装

    1
    2
    3
    git clone https://github.com/ultralytics/ultralytics.git
    cd ultralytics
    pip install -e .

    这段命令会安装你下载的ultralytics,安装完成后pip list你就会发现包会有一个路径,就表示安装成功了。

    1
    ultralytics              8.3.193     /home/ultralytics

  • 安装pytorch

  • 验证,推理

目标

探索使用MCP与商品结合的场景,让大模型能回答和理解用户的产品需求并获得需要的产品信息。

  • 用户问答搜索产品场景
    • 根据关键字搜索产品详情。
    • 最热门商品
  • 用户问答根据用户个性化推荐场景
    • 根据用户ID推荐商品。

注意数据为mock,根据需要对接数据库或者其他数据来源即可。

实践

编写MCP服务程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 引入 fastmcp 包
from fastmcp import FastMCP
# 创建一个mcp服务
mcp = FastMCP("ProductServer")

# 定义工具,也就是实际执行的内容,类似于 RestAPI 中的 POST,mcp主要的能力都在这里体现。

# 定义根据关键字keyword搜索产品详情的工具函数
@mcp.tool()
def search_produc_by_keyword(keyword):
"""search product by keyword"""
# mock data
return {"product_name":"小米SU7 Ultra", "price": 529999, "category": "Car"}

# 定义获取热门商品的工具函数
@mcp.tool()
def get_popular_products():
"""List of popular products"""
# mock data
hot_products = ('[{"product_name":"iPhone 14 Pro Max", "price": 5999, "category": "Electronics"},'
'{"product_name":"小米SU7 Ultra", "price": 529999, "category": "Car"},'
'{"product_name":"红米K80", "price": 2399, "category": "Electronics"}]')

return hot_products


# 定义为指定用户获取推荐商品的工具函数
@mcp.tool()
def get_recommended_products(user_id):
"""Products recommended for users"""
# mock data
recommended_products = '{"product_name":"小米SU7 Ultra", "price": 529999, "category": "Car"}'
return recommended_products


if __name__ == "__main__":
mcp.run(transport='stdio')

运行mcp,验证一下是否能正常运行,确认能正常后,我们在Cherry Studio中配置一下mcp

1
fastmcp run main.py

在Cherry Studio中配置MCP

  • 在命令中填写 uv
  • 填写参数

    注意 /{PATH}/fastmcp/为这个mcp的项目路径

    1
    2
    3
    4
    --directory
    /{PATH}/fastmcp/
    run
    main.py
    其实组合起来就是 uv --directory /{PATH}/fastmcp/ run main.py,即跑起来文件项目
    JrtIR3

配置好后,我们可以看到工具中已经 包含了

  • search_produc_by_keyword 根据关键字搜索产品
  • get_popular_products 获取热门商品
  • get_recommended_products 获取推荐商品
    RIkSsQ

通过问答调用产品mcp

我们会发现在如下的问答过程,会根据问题,自动调用mcp服务获得需要的产品信息。

  • “我想了解一下当下的热门产品,帮我获取一下热门产品信息。”
    m7bpbS
  • “我想查询一下关键字:小米SU7 Ultra,了解一下它的价格。”
    4emS9b
  • “请为用户ID为1的用户,推荐商品。”
    jG3pwW

未来验证

模块化产品订单mcp 服务,即可接入大模型,进行问答调用。

相关

结合FastMCP创建自己的MCP服务,实现哔哩视频查询
基于FastMCP 2.0的MCP Server快速搭建指南

背景

  • 行业:生物试剂
  • 手上有自产试剂的所有说明书,大概300多份
  • 不限于说明书的资料

目标

希望微调出一个名字为 dayou 的自产生物试剂行业垂直大语言模型。首要目标是能解答说明书内的相关内容,继而继续增加数据集,完善该 dayou 自训练大模型,或者分领域分别训练 dayou-saledyou-manualdyou-tech大模型。

本次可行性验证,我们从说明书选取如下2个试剂产品

  • IMDM培养基 (6016311 IMDM培养基说明书(中文)–C2–A4.docx)
  • TransFx转染用无血清培养基 (6066411 TransFx转染用无血清培养基说明书–C0.docx)

通过AI对2个产品的docx文件进行制作QA的数据集。

实践

  • 微调模型选择:Qwen2.5-7B-Instruct
  • 微调框架选择:LLaMA-Factory
  • GPU显卡选择: RTX 4090
  • 数据集制作:easy-dataset

算力准备及训练环境准备

本次实验使用AutoDL AI算力云 租借算力,采用GPU卡进行训练,使用Ubuntu 24.04LTS版本
IX3I8x

AutoDL的系统盘和数据盘可以通过source ~/.bashrc 查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ source ~/.bashrc
+-----------------------------------------------AutoDL-----------------------------------------------------+
目录说明:
╔═════════════════╦════════╦════╦══════════════════════════════════════════════════════════════╗
║目录 ║名称 ║速度 ║说明 ║
╠═════════════════╬════════╬════╬══════════════════════════════════════════════════════════════╣
║/ ║系 统 盘 ║一般║实例关机数据不会丢失,可存放代码等。会随保存镜像一起保存。 ║
║/root/autodl-tmp ║数 据 盘 ║ 快 ║实例关机数据不会丢失,可存放读写IO要求高的数据。但不会随保存镜像一起保存 ║
╚═════════════════╩════════╩════╩═══════════════════════════════════════════════════════════════╝
CPU :16 核心
内存:120 GB
GPU :NVIDIA GeForce RTX 4090, 1
存储:
系 统 盘/ :29% 8.6G/30G
数 据 盘/root/autodl-tmp:1% 72M/50G

LLaMA-Factory搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
## 克隆项目
git clone https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
## 创建环境
conda create -n llamafactory python=3.10
## 激活环境
conda activate llamafactory
## 安装依赖
pip install -e ".[torch,metrics]" --no-build-isolation -i https://mirrors.aliyun.com/pypi/simple
## 执行webui
llamafactory-cli webui
## 后台运行
nohup llamafactory-cli webui > webui.log 2>&1 &
  • 下载模型
    a0F1dg
    界面中选择Qwen2.5-7B-Instruct,并加载模型,会自动下载到默认路径: /root/.cache/modelscope/hub/models/Qwen,我们自己手动下载并指定路径。
    1
    2
    3
    4
    5
    6
    7
    8
    pip install modelscope
    modelscope download --model Qwen/Qwen2.5-7B-Instruct --local_dir /root/autodl-tmp/LLaMA-Factory/models/Qwen/Qwen2.5-7B-Instruct
    modelscope download --model Qwen/Qwen2.5-1.5B-Instruct --local_dir /root/autodl-tmp/LLaMA-Factory/models/Qwen/Qwen2.5-1.5B-Instruct

    apt install git-lfs
    git lfs install
    git clone https://www.modelscope.cn/Qwen/Qwen2.5-7B-Instruct.git /root/autodl-tmp/LLaMA-Factory/models/Qwen
    git clone https://www.modelscope.cn/Qwen/wen2.5-1.5B-Instruct.git /root/autodl-tmp/LLaMA-Factory/models/Qwen

数据集准备

我们使用easy-dataset来简化我们的数据集制作流程,通过大模型自动生成QA数据集。

  • 初始化数据库文件。
    1
    2
    3
    4
    git clone https://github.com/ConardLi/easy-dataset.git
    cd easy-dataset
    npm install
    npm run db:push
  • 部署
    docker-compose.yml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    services:
    easy-dataset:
    image: ghcr.io/conardli/easy-dataset
    container_name: easy-dataset
    ports:
    - '1717:1717'
    volumes:
    - ./local-db:/app/local-db
    - ./prisma:/app/prisma # 如果需要挂载请先手动初始化数据库文件
    restart: unless-stopped
    1
    docker-compose up -d
    就此我们部署好我们的数据集制作工具。
    wBoKqJ

我们来制作2个产品的数据集

  • IMDM培养基
    hvvrd7
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [
    {
    "instruction": "IMDM与RPMI-1640培养基在酚红含量上有何差异",
    "input": "",
    "output": "IMDM培养基含有酚红,其含量为15 mg/L。而RPMI-1640培养基的酚红含量在文档中未提及,表明该产品标签中未列出酚红作为成分。因此,IMDM与RPMI-1640培养基在酚红含量上的主要差异在于IMDM明确包含酚红,而RPMI-1640未标明含有该成分。",
    "system": "你是一个由“达科为”公司自产的生物试剂产品,品牌名为:“达优”,产品名称是:“IMDM培养基”,有一个规格,货号为:“6016311”"
    },
    {
    "instruction": "IMDM培养基是否含有抗生素?",
    "input": "",
    "output": "IMDM培养基不含抗生素。该培养基是一种改进型的DMEM培养基,特别设计用于红细胞和巨噬细胞前体、T淋巴细胞、B淋巴细胞、骨髓造血细胞和淋巴瘤细胞的培养。在产品参数中明确指出,该培养基不含抗生素成分,需额外添加胎牛血清等培养添加物以满足细胞生长的需求。",
    "system": "你是一个由“达科为”公司自产的生物试剂产品,品牌名为:“达优”,产品名称是:“IMDM培养基”,有一个规格,货号为:“6016311”"
    },
    ...一下省略,共41个问题
    ]
  • TransFx转染用无血清培养基
    cAEnPF
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [
    {
    "instruction": "分析无血清培养基中不含酚红、HEPES和胰岛素成分对减少转染试剂用量和避免血清干扰的实证数据支持。",
    "input": "",
    "output": "无血清培养基中不含酚红、HEPES和胰岛素的设计可有效减少转染试剂用量并提升转染效率。酚红作为pH指示剂,可能干扰细胞代谢及外源物质摄取;HEPES缓冲系统在某些情况下会与转染试剂发生非特异性结合,降低其活性。胰岛素则可能影响细胞膜通透性,干扰阳离子脂质体或聚合物介导的核酸传递。实验证明,在不含这些成分的TransFx培养基中,转染试剂(如TransIT-X2)与核酸复合物更稳定,细胞摄取效率提高,从而减少试剂用量并避免血清成分的干扰,最终实现更高效的基因转染。",
    "system": "你是一个由“达科为”公司自产的生物试剂产品,品牌名为:“达优”,产品名称是:“TransFx转染用无血清培养基”,有一个规格,货号为:“6066411”"
    },
    {
    "instruction": "比较不同储存条件(如温度2℃~8℃和避光)对TransFx培养基稳定性和转染效果的影响机制。",
    "input": "",
    "output": "TransFx转染用无血清培养基的稳定性与活性受储存条件影响显著。其推荐储存温度为2℃~8℃,该温度范围可维持培养基中植物源重组生长因子(如IGF-1和EGF)、氨基酸(如L-谷氨酰胺)及微量元素的化学稳定性,防止降解或失活。高温可能加速成分氧化或降解,影响其支持转染的能力。同时,避光保存可防止光敏感成分(如酚红替代物及某些维生素或辅因子)发生光化学反应,从而维持培养基的pH稳定性和生物活性。若未避光或温度波动较大,可能降低转染效率,影响核酸-转染试剂复合物的形成与细胞摄取能力,最终削弱基因表达效果。因此,遵循推荐储存条件对于维持TransFx培养基功能至关重要。",
    "system": "你是一个由“达科为”公司自产的生物试剂产品,品牌名为:“达优”,产品名称是:“TransFx转染用无血清培养基”,有一个规格,货号为:“6066411”"
    },
    ...一下省略,共43个问题
    ]

训练

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
llamafactory-cli train \
--stage sft \
--do_train True \
--model_name_or_path /root/autodl-tmp/LLaMA-Factory/models/Qwen/Qwen2.5-3B-Instruct \
--preprocessing_num_workers 16 \
--finetuning_type lora \
--template qwen \
--flash_attn auto \
--dataset_dir data/dayou/ \
--dataset identity,[Easy Dataset] [oLYDc6WUW-2H] ShareGPT,[Easy Dataset] [ag7XS4Wdb3LM] ShareGPT \
--cutoff_len 1024 \
--learning_rate 0.0001 \
--num_train_epochs 20.0 \
--max_samples 100000 \
--per_device_train_batch_size 2 \
--gradient_accumulation_steps 16 \
--lr_scheduler_type cosine \
--max_grad_norm 1.0 \
--logging_steps 5 \
--save_steps 50 \
--warmup_steps 4 \
--packing False \
--enable_thinking True \
--report_to none \
--output_dir saves/Qwen2.5-3B-Instruct/lora/dayou_v6 \
--bf16 True \
--plot_loss True \
--trust_remote_code True \
--ddp_timeout 180000000 \
--include_num_input_tokens_seen True \
--optim adamw_torch \
--adapter_name_or_path saves/Qwen2.5-3B-Instruct/lora/dayou_v6 \
--lora_rank 16 \
--lora_alpha 32 \
--lora_dropout 0 \
--lora_target all

可行性总结与分析

硬件环境

首先按照 nvidia驱动安装 装下驱动

1
2
3
4
nvidia-smi

pip install nvitop
nvitop

TnPQDZ

环境准备

  • 安装依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ## 克隆项目
    git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git
    cd LLaMA-Factory
    ## 创建环境
    conda create -n llamafactory python=3.10
    ## 激活环境
    conda activate llamafactory
    ## 安装依赖
    pip config set global.index-url https://mirrors.aliyun.com/pypi/simple
    pip install -e ".[torch,metrics]" --no-build-isolation -i https://mirrors.aliyun.com/pypi/simple

    如果出现环境冲突,请尝试使用 pip install –no-deps -e . 解决

  • 校验环境
    1
    2
    3
    4
    import torch
    torch.cuda.current_device()
    torch.cuda.get_device_name(0)
    torch.__version__
    1
    2
    3
    4
    5
    6
    7
    >>> import torch
    >>> torch.cuda.current_device()
    0
    >>> torch.cuda.get_device_name(0)
    'NVIDIA GeForce RTX 4090'
    >>> torch.__version__
    '2.8.0+cu128'
    1
    2
    # 对本库的基础安装做一下校验,输入以下命令获取训练相关的参数指导, 否则说明库还没有安装成功
    llamafactory-cli train -h
  • llamafactory-cli常见命令
    动作参数 功能说明
    version 显示版本信息
    train 命令行版本训练
    chat 命令行版本推理聊天
    export 模型合并和导出(如转换为Hugging Face/ONNX格式)
    api 启动API服务器,提供HTTP接口调用
    eval 使用标准数据集(如MMLU)评测模型性能
    webchat 启动纯推理的Web聊天页面(简易前端)
    webui 启动LlamaBoard多功能前端(含训练、预测、聊天、模型合并等可视化子页面)

    另外两个关键参数解释如下,后续的基本所有环节都会继续使用这两个参数

    参数名称 参数说明
    model_name_or_path 参数的名称(huggingface或者modelscope上的标准定义,如 Qwen/Qwen2.5-1.5B-Instruct),或者是本地下载的绝对路径,如/root/autodl-tmp/LLaMA-Factory/models/Qwen/Qwen2.5-1.5B-Instruct
    template 模型问答时所使用的prompt模板,不同模型不同,请参考 LLaMA-Factory Supported Models 获取不同模型的模板定义,否则会回答结果会很奇怪或导致重复生成等现象的出现。chat版本的模型基本都需要指定,比如Qwen/Qwen2.5-1.5B-Instruct的template就是qwen
  • 示例:下面四行命令分别是 启动webui 和对Llama3-8B-Instruct模型进行 LoRA 微调、推理、合并。
    1
    2
    3
    4
    5
    CUDA_VISIBLE_DEVICES=0 USE_MODELSCOPE_HUB=1 llamafactory-cli webui
    CUDA_VISIBLE_DEVICES=0 USE_MODELSCOPE_HUB=1 llamafactory-cli webui > runlog.log &
    llamafactory-cli train examples/train_lora/llama3_lora_sft.yaml
    llamafactory-cli chat examples/inference/llama3_lora_sft.yaml
    llamafactory-cli export examples/merge_lora/llama3_lora_sft.yaml

下载模型

我们自己手动下载并指定路径。

1
2
3
4
5
pip install modelscope
modelscope download --model Qwen/Qwen2.5-1.5B-Instruct --local_dir /root/autodl-tmp/LLaMA-Factory/models/Qwen/Qwen2.5-1.5B-Instruct
modelscope download --model Qwen/Qwen2.5-3B-Instruct --local_dir /root/autodl-tmp/LLaMA-Factory/models/Qwen/Qwen2.5-3B-Instruct
modelscope download --model Qwen/Qwen2.5-7B-Instruct --local_dir /root/autodl-tmp/LLaMA-Factory/models/Qwen/Qwen2.5-7B-Instruct
modelscope download --model Qwen/Qwen3-4B-Instruct-2507 --local_dir /root/autodl-tmp/LLaMA-Factory/models/Qwen/Qwen3-4B-Instruct-2507

原始模型推理

transformers 原始模型直接推理

为qwen创建一个推理测试python脚本

1
2
mkdir demo
touch demo/generated_text.py

python generated_text.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import transformers
import torch

# 模型路径(替换成你的实际路径)
model_id = "/root/autodl-tmp/LLaMA-Factory/models/Qwen/Qwen2.5-1.5B-Instruct"

# 初始化 pipeline
pipeline = transformers.pipeline(
"text-generation",
model=model_id,
model_kwargs={"torch_dtype": torch.bfloat16},
device_map="auto",
)

# 对话模板
messages = [
{"role": "system", "content": "你是一个助手!"},
{"role": "user", "content": "你是谁?"},
]

# 生成 prompt(不要 tokenize,直接返回字符串)
prompt = pipeline.tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)

# 检查 tokenizer 的特殊 token
# print("特殊 Token 映射:", pipeline.tokenizer.special_tokens_map)

# 设置终止符(优先用 <|eot_id|>,如果不存在则用 eos_token_id 或 pad_token_id)
terminators = [
pipeline.tokenizer.convert_tokens_to_ids("<|eot_id|>"), # Qwen 专用结束 token
pipeline.tokenizer.eos_token_id, # 常规结束 token
pipeline.tokenizer.pad_token_id # 备用 token
]
terminators = [tid for tid in terminators if tid is not None] # 过滤掉 None

# 如果 terminators 为空,手动设置一个默认值(比如 2,但需确认你的模型是否适用)
if not terminators:
terminators = [2] # 常见模型的默认 EOS token,可能需要调整

# 生成文本
outputs = pipeline(
prompt,
max_new_tokens=256,
eos_token_id=terminators, # 传入有效的终止符列表
do_sample=True,
temperature=0.6,
top_p=0.9,
)

# 打印生成的回复(去掉 prompt 部分)
print(outputs[0]["generated_text"][len(prompt):])

输出:

1
我是来自阿里云的大规模语言模型,我叫通义千问。

llamafactory-cli 原始模型直接推理

touch examples/inference/qwen2.5.yaml

1
2
3
4
model_name_or_path: /root/autodl-tmp/LLaMA-Factory/models/Qwen/Qwen2.5-1.5B-Instruct
template: qwen
infer_backend: huggingface # choices: [huggingface, vllm, sglang]
trust_remote_code: true

这样就可以通过如下命令启动:

  • 终端推理: llamafactory-cli chat examples/inference/qwen2.5.yaml
    1
    2
    3
    4
    5
    [INFO|2025-08-08 15:48:45] llamafactory.model.loader:143 >> all params: 1,543,714,304
    Welcome to the CLI application, use `clear` to remove the history, use `exit` to exit the application.
    User: 你是谁?
    Assistant: 我是Qwen,由阿里云开发的语言模型。我能够回答问题、创作文字,还能表达观点、撰写代码。如果您有任何问题或需要帮助,请随时告诉我,我会尽力提供支持。
    User:
  • web推理: llamafactory-cli webchat examples/inference/qwen2.5.yaml
    效果如图,可通过 http://localhost:7860/ 进行访问
    XoHIMO
    注意:这里的localhost:7860 指的是程序启动机器自身的7860端口,云上的用户可能无法通过本地的笔记本电脑直接访问,需要找云厂商获取域名和端口号的一些配置关系进行配置
    比如阿里云用户需要做一下如下环境变量的配置才能正常运行gradio,然后再通过阿里云给的域名映射访问
    1
    export GRADIO_ROOT_PATH=/${JUPYTER_NAME}/proxy/7860/

构建自定义数据集

  • 自带的identity.json数据集

    系统自带的identity.json数据集,用于对模型自我介绍,示例:
    data/identity.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [
    {
    "instruction": "你好",
    "input": "",
    "output": "您好,我是 {{name}},一个由 {{author}} 开发的 AI 助手,很高兴认识您。请问我能为您做些什么?"
    },
    {
    "instruction": "你好",
    "input": "",
    "output": "您好,我是 {{name}},一个由 {{author}} 打造的人工智能助手,请问有什么可以帮助您的吗?"
    },
    ]
    1
    2
    3
    4
    cd LLaMA-Factory
    # 其中的NAME 和 AUTHOR ,替换成我们需要的内容
    sed -i 's/{{name}}/达优AI助手/g' data/identity.json
    sed -i 's/{{author}}/达科为/g' data/identity.json
  • 企业自己的数据集
    dayou.json
    1
    2
    3
    4
    5
    6
    7
    8
    [
    {
    "instruction": "低内毒素胰酶消化液的推荐储存条件是什么,这些条件如何确保其稳定性和活性?",
    "input": "",
    "output": "低内毒素胰酶消化液的储存条件对其稳定性和活性至关重要。以下是推荐的储存条件及其如何确保稳定性和活性的详细解释:\n\n## 🧪 推荐储存条件\n\n1. **温度:**\n * **长期储存: -20°C 或更低(如 -80°C)。** 这是最核心的条件。\n * **短期储存(几天内使用): 2-8°C (冰箱冷藏)。** 仅适用于已解冻并计划在极短时间内(通常不超过1周)用完的 aliquot(分装样品)。**不推荐**将未开封的浓缩液长期存放在冷藏温度。\n\n2. **分装:**\n * **强烈建议将大瓶装的胰酶消化液分装成小体积(如 0.5 mL, 1 mL, 5 mL 等)的无菌、低蛋白吸附的离心管或冻存管中。** 分装体积应根据单次实验用量来定,避免反复冻融。\n\n3. **容器:**\n * 使用**无菌、低蛋白吸附、低内毒素**的容器。推荐使用**高质量聚丙烯 (PP)** 材质的离心管或冻存管。避免使用可能吸附蛋白或释放塑化剂(如 DEHP)的聚苯乙烯 (PS) 管或低质量塑料管。玻璃容器也可,但需确保无菌且不易碎。\n\n4. **避光:**\n * 储存于**避光**环境中。虽然胰酶对光不特别敏感,但避光储存可防止任何潜在的光氧化反应,并提供额外的保护。\n\n5. **密封:**\n * 确保容器**密封良好**,防止水分蒸发(导致浓度变化)、空气进入(导致氧化或污染)以及微生物污染。\n\n## 📊 这些条件如何确保稳定性和活性?\n\n1. **低温 (-20°C 或更低):**\n * **抑制酶活性:** 胰蛋白酶等蛋白酶在低温下其催化活性被极大抑制。这**显著减缓了胰酶分子的自水解(自我降解)**。自水解是胰酶失活的主要途径之一,即使在溶液中也会缓慢发生。低温将这一过程降至最低。\n * **减缓化学反应:** 低温降低了溶液中所有化学反应的速率,包括可能导致酶变性的氧化反应或脱酰胺反应等。\n * **维持结构稳定性:** 低温有助于维持胰酶分子的天然三维构象,防止因热运动导致的结构松散和失活。\n * **抑制微生物生长:** 低温(尤其是冷冻)能有效抑制细菌、真菌等微生物的生长繁殖。**这是维持“低内毒素”状态的关键。** 即使初始内毒素很低,如果储存过程中微生物滋生,其死亡裂解后会释放大量内毒素,导致内毒素水平飙升,严重影响细胞实验结果(如激活细胞炎症通路)。低温储存从根本上杜绝了这种污染风险。\n\n2. **分装 (Aliquoting):**\n * **避免反复冻融:** 反复冻融是导致胰酶失活的**最主要杀手**。每次冻融循环都会:\n * 产生冰晶,可能直接剪切酶分子,破坏其结构。\n * 导致溶液局部浓度和pH值的剧烈变化(如冰晶形成时溶质浓缩),加速酶变性。\n * 增加溶液与空气(氧气)的接触机会,促进氧化。\n * 增加污染风险(每次操作都引入污染可能)。\n * **分装确保每次只取出一个 aliquot 使用,用完即弃,避免了剩余消化液经历多次冻融循环**,从而最大程度地保护了酶的活性和溶液的稳定性(包括内毒素水平)。\n\n3. **合适的容器 (无菌、低蛋白吸附、低内毒素):**\n * **防止酶损失:** 低蛋白吸附的容器(如高质量PP管)能最大程度减少胰酶分子在管壁上的吸附,确保溶液中活性酶的浓度保持稳定。\n * **防止污染:** 无菌容器杜绝了初始储存时的微生物污染源。低内毒素容器确保储存过程本身不会引入新的内毒素污染。\n * **防止化学干扰:** 高质量的惰性材料不会释放可能影响酶活性或细胞毒性的化学物质(如塑化剂)。\n\n4. **避光:**\n * 虽然不是主要因素,但避光储存提供了额外的保护层,防止任何潜在的光敏性成分(可能存在痕量杂质)发生光化学反应,避免产生可能影响酶活性或细胞反应的副产物。\n\n5. **密封:**\n * **防止浓缩/稀释:** 密封防止水分蒸发,避免溶液浓度升高(导致消化过强)或吸潮导致浓度降低(导致消化不足)。\n * **防止氧化:** 减少溶液与空气(氧气)的接触,降低氧化导致酶失活的风险。\n * **防止污染:** 密封是防止储存过程中微生物(细菌、霉菌)进入的关键物理屏障,**直接保障了低内毒素状态的维持**。\n\n## 📌 总结与关键点\n\n* **核心是低温冷冻 + 分装:** -20°C 或更低温度储存是抑制酶自降解和微生物生长的基础。分装成小 aliquot 是避免反复冻融失活的**绝对必要措施**。\n* **低内毒素依赖于无菌和防污染:** 低温储存抑制微生物生长,无菌容器和密封操作防止初始和储存过程中的污染,共同确保内毒素水平在有效期内维持在极低状态。\n* **容器选择不容忽视:** 低蛋白吸附和低内毒素的容器材质对维持酶浓度和溶液纯净度至关重要。\n* **遵循说明书:** **最重要的一点是:务必遵循您所使用的具体品牌和批次胰酶消化液产品说明书上提供的储存条件和有效期。** 不同厂家、不同配方(如是否含 EDTA、酚红等)的胰酶消化液,其最佳储存条件和稳定期限可能略有差异。说明书是最权威的指导。\n* **解冻与使用:** 使用时,将所需 aliquot 从冰箱取出,**在冰上或 2-8°C 冰箱中缓慢解冻**。避免室温解冻或水浴加速解冻(温度骤变易失活)。解冻后应立即使用,**切勿再次冷冻**已解冻的 aliquot。使用前可短暂涡旋混匀,但避免剧烈振荡产生气泡(可能导致蛋白变性)。\n\n通过严格遵守这些储存条件,可以最大限度地保证低内毒素胰酶消化液的蛋白酶活性、消化效率以及内毒素水平在有效期内保持稳定,从而为细胞培养实验提供可靠、一致且无干扰的消化效果。💪🏻",
    "system": ""
    }
    ]
    需要将该数据集注册到 dataset_info.json
    1
    2
    3
    4
    5
    6
    7
    8
    {
    "identity": {
    "file_name": "identity.json"
    },
    "dayou": {
    "file_name": "dayou.json"
    }
    }

基于LoRA的sft指令微调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
CUDA_VISIBLE_DEVICES=0 llamafactory-cli train \
--stage sft \
--do_train \
--model_name_or_path /root/autodl-tmp/LLaMA-Factory/models/Qwen/Qwen2.5-1.5B-Instruct \
--dataset identity,dayou \
--dataset_dir ./data \
--template qwen \
--finetuning_type lora \
--output_dir ./saves/Qwen2.5-1.5B-dayou/lora/sft \
--overwrite_cache \
--overwrite_output_dir \
--cutoff_len 1024 \
--preprocessing_num_workers 16 \
--per_device_train_batch_size 2 \
--per_device_eval_batch_size 1 \
--gradient_accumulation_steps 8 \
--lr_scheduler_type cosine \
--logging_steps 50 \
--warmup_steps 20 \
--save_steps 100 \
--eval_steps 50 \
--evaluation_strategy steps \
--load_best_model_at_end \
--learning_rate 5e-5 \
--num_train_epochs 5.0 \
--max_samples 1000 \
--val_size 0.1 \
--plot_loss \
--fp16

动态合并LoRA的推理

当基于LoRA的训练进程结束后,我们如果想做一下动态验证,在网页端里与新模型对话,与步骤 原始模型直接推理 相比,唯一的区别是需要通过adapter_name_or_path参数告诉LoRA的模型位置。
touch examples/inference/qwen2.5_sft.yaml

1
2
3
4
5
model_name_or_path: /root/autodl-tmp/LLaMA-Factory/models/Qwen/Qwen2.5-1.5B-Instruct
adapter_name_or_path: saves/Qwen2.5-1.5B-dayou/lora/sft
template: qwen
infer_backend: huggingface # choices: [huggingface, vllm, sglang]
trust_remote_code: true
  • 终端推理: llamafactory-cli chat examples/inference/qwen2.5_sft.yaml
  • web推理: llamafactory-cli webchat examples/inference/qwen2.5_sft.yaml

相关链接

背景

大模型领域日新月异,模型微调早已无代码化,我认为大模型训练和微调的关键步骤仅剩:高质量的数据集准备
之前微调过一次甄嬛 Mac M2之LLaMA3-8B微调(llama3-fine-tuning), 这次我想用 llama-factory + Qwen2.5-1.5B-Instruct完全无代码微调一次。

LLaMA-Factory

1
2
3
4
5
6
7
8
9
## 克隆项目
git clone https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
## 创建环境
conda create -n llamafactory python=3.10
## 激活环境
conda activate llamafactory
## 安装依赖
pip install -e ".[torch,metrics]" --no-build-isolation -i https://mirrors.aliyun.com/pypi/simple

校验环境

1
2
3
4
import torch
torch.cuda.current_device()
torch.cuda.get_device_name(0)
torch.__version__

执行webui

1
llamafactory-cli webui

执行完毕后,会自动打开webui

数据集

本次我们依然使用该数据集:Chat-嬛嬛数据集
llama-factory 目前只支持AlpacaSharegpt两种格式的数据集。

  • Alpaca 格式
    适用于单轮任务,如问答、文本生成、摘要、翻译等。结构简洁,任务导向清晰,适合低成本的指令微调。
    1
    2
    3
    4
    5
    {  
    "instruction":"计算这些物品的总费用。",
    "input":"输入:汽车 - $3000,衣服 - $100,书 - $20。",
    "output":"汽车、衣服和书的总费用为 $3000 + $100 + $20 = $3120。"
    }
  • ShareGPT 格式
    适用于多轮对话、聊天机器人等任务。结构复杂,包含多轮对话上下文,适合高质量的对话生成和人机交互任务。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    [{
    "instruction": "今天的天气怎么样?",
    "input": "",
    "output": "今天的天气不错,是晴天。",
    "history": [
    ["今天会下雨吗?", "今天不会下雨,是个好天气。"],
    ["今天适合出去玩吗?", "非常适合,空气质量很好。"]
    ]
    }]

打开 huanhuan.json 文件,可以看到它其实就是 Alpaca 格式的数据集,仅下载这一个文件即可。
w0tYNQ
我们在 llama-factory 项目内的的data目录放置 huanhuan.json 文件,并且在 dataset_info.json 的配置文件添加一条 huanhuan.json 的json配置,这样,我们新添加的数据集才能被llama-factory注册识别到。
KfyAQY

微调

首先是模型,我们选择此次需要微调的Qwen2.5-1.5B-Instruct。

  • Instruct 版本(如 Qwen2.5-1.5B-Instruct) ✅
    • 经过指令微调的模型
    • 更适合直接对话和指令遵循
    • 已经具备基本的对话能力
    • 更适合用来进一步微调

微调方式使用默认的lora即可。

  • 训练轮数可以选择1轮,会快一些(如果后面发现效果不理想,可以多训练几轮),我这里最终选择了3轮,因为我发现仅1轮效果不佳。
  • 计算类型 默认
  • 截断长度设置小一点,为1024(默认是2048)。
  • 梯度累计设置为4。
  • 其他参数设置-预热步数是学习率预热采用的步数,通常设置范围在2-8之间,我们这里配置4。
    grGcU2
  • LoRA参数设置-lora秩越大(可以看作学习的广度),学习的东西越多,微调之后的效果可能会越好,但是也不是越大越好。太大的话容易造成过拟合(书呆子,照本宣科,不知变通),这里设置为8。
  • LoRA参数设置-lora缩放系数(可以看作学习强度),越大效果可能会越好,对于一些用于复杂场景的数据集可以设置更大一些,简单场景的数据集可以稍微小一点。我这里设置256。
    pJJRLv
    模型训练结束后,会显示:训练完毕。
    wFc3ho
    如果想重新微调,记得改一下下面红框中的两个值。
    7Yswha
    微调成功后,在检查点路径这里,下拉可以选择我们刚刚微调好的模型。把窗口切换到chat,点击加载模型。
    8GsA0p
    加载好之后就可以在输入框发送问题,测试微调模型的回复效果了。如果想切换回微调之前的模型,只需要把检查点路径置空。然后在chat这里卸载模型,再加载模型即可。
    SngtWk

导出模型与使用

切换到export,填写导出目录models/huanhuan

大语言模型的参数通常以高精度浮点数(如32位浮点数,FP32)存储,这导致模型推理需要大量计算资源。量化技术通过将高精度数据类型存储的参数转换为低精度数据类型(如8位整数,INT8)存储,可以在不改变模型参数量和架构的前提下加速推理过程。这种方法使得模型的部署更加经济高效,也更具可行性。

量化前需要先将模型导出后再量化。修改模型路径为导出后的模型路径,导出量化等级一般选择 8 或 4,太低模型会答非所问。

wOiMlb

  • 导入ollama

    出现ollama和在llama-factory的chat回答效果不一致,请查看底部 相关链接

    1
    2
    3
    ollama create Qwen2.5-1.5B-huanhuan -f Modefile
    ollama list
    ollama run Qwen2.5-1.5B-huanhuan:latest
    UhlbHS
  • vllm部署使用

    出现vllm和在llama-factory的chat回答效果不一致,请查看底部 相关链接

    • 启动
      1
      2
      3
      4
      5
      vllm serve /root/autodl-tmp/LLaMA-Factory/models/huanhuan/Qwen2.5-1.5B-huanhuan \
      --port 8000 \
      --host 0.0.0.0 \
      --gpu-memory-utilization 0.8 \
      --served-model-name huanhuan
    • 发送请求
      1
      2
      3
      4
      5
      6
      7
      8
      curl --location 'http://192.168.103.43:8000/v1/completions' \
      --header 'Content-Type: application/json' \
      --data '{
      "model": "huanhuan",
      "prompt": "你是谁",
      "max_tokens": 50,
      "temperature": 0.7
      }'
    • 效果
      oKPXPo

相关链接

背景

物理机器有显卡L20 (4 * 48G),安装vmware esxi系统,希望虚拟机ubuntu22.04 直通物理显卡。

  • ESXi-8.0U3e-24677879-standard
  • ubuntu22.04

直通模式配置

虚拟机配置

  • 创建虚拟机及配置参数
    • 预留全部内存
      wUml1N
    • 在“虚拟机设置”中,点击“添加其他设备” > “PCI设备”,在列表中找到并勾选NVIDIA显卡(例如,0000:c1:00.0)。
      PZmAZx
    • 取消UEFI安全启动
      jQILo6

为了优化GPU直通的性能和兼容性,需要在虚拟机的高级配置中添加以下参数:

1
2
3
4
5
6
# 该参数用于隐藏虚拟化环境,使得虚拟机能够更好地识别并利用NVIDIA显卡。
hypervisor.cpuid.v0 = FALSE
# 启用64位内存映射输入/输出(MMIO),提高显卡的内存访问效率。
pciPassthru.use64bitMMIO = TRUE
# 设置64位MMIO的大小,确保显卡有足够的内存资源进行高性能计算和渲染任务。
pciPassthru.64bitMMIOSizeGB = 256 (2张48G显卡,具体计算看下面)

pciPassthru.64bitMMIOSizeGB计算方式 https://earlruby.org/tag/use64bitmmio/
64bitMMIOSizeGB值的计算方法是将连接到VM的所有GPU上的显存总量(GB)相加。如果总显存为2的幂次方,则将pciPassthru.64bitMMIOSizeGB设置为下一个2的幂次方即可。
如果总显存介于2的2次方之间,则向上舍入到下一个2的幂次方,然后再次向上舍入。
2的幂数是2、4、8、16、32、64、128、256、512、1024…
例如虚拟机直通两张24G显存的显卡,则64bitMMIOSizeGB应设置为128。计算方式为242=48,在32和64之间,先舍入到64,再次舍入到128
例如虚拟机直通两张48G显存的显卡,则64bitMMIOSizeGB应设置为256。计算方式为48
2=96,在64和128之间,先舍入到128,再次舍入到256

BCylVm

linux 驱动安装

  • 更新系统
    1
    sudo apt update && sudo apt upgrade -y
  • 禁用开源驱动 Nouveau
    1
    2
    3
    4
    sudo nano /etc/modprobe.d/blacklist.conf
    # 在文件末尾添加以下内容:
    blacklist nouveau
    options nouveau modeset=0
    保存后执行:
    1
    2
    sudo update-initramfs -u
    sudo reboot
    重启后验证是否禁用成功(无输出即成功):
    1
    lsmod | grep nouveau
  • 安装驱动
    • 安装显卡驱动工具
      1
      sudo apt-get install nvidia-cuda-toolkit
    • 查看系统推荐显卡驱动

      这里要看好哪个是系统推荐的驱动(recommend),并且要记下来

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      sudo ubuntu-drivers devices

      (base) root@ai-03-deploy:~# sudo ubuntu-drivers devices
      == /sys/devices/pci0000:00/0000:00:0f.0 ==
      modalias : pci:v000015ADd00000405sv000015ADsd00000405bc03sc00i00
      vendor : VMware
      model : SVGA II Adapter
      manual_install: True
      driver : open-vm-tools-desktop - distro free

      == /sys/devices/pci0000:03/0000:03:00.0 ==
      modalias : pci:v000010DEd000026BAsv000010DEsd00001957bc03sc02i00
      vendor : NVIDIA Corporation
      driver : nvidia-driver-535 - distro non-free
      driver : nvidia-driver-575-server-open - distro non-free
      driver : nvidia-driver-550-open - distro non-free
      driver : nvidia-driver-575-open - distro non-free
      driver : nvidia-driver-570 - distro non-free
      driver : nvidia-driver-550 - distro non-free
      driver : nvidia-driver-570-server-open - distro non-free
      driver : nvidia-driver-570-open - distro non-free
      driver : nvidia-driver-575 - distro non-free recommended
      driver : nvidia-driver-575-server - distro non-free
      driver : nvidia-driver-535-open - distro non-free
      driver : nvidia-driver-535-server-open - distro non-free
      driver : nvidia-driver-570-server - distro non-free
      driver : nvidia-driver-535-server - distro non-free
      driver : xserver-xorg-video-nouveau - distro free builtin
    • 添加驱动源
      1
      2
      sudo add-apt-repository ppa:graphics-drivers/ppa
      sudo apt-get update
    • 选择版本安装
      1
      sudo apt install nvidia-driver-575
      1
      sudo reboot
  • 验证安装
    1
    2
    nvidia-smi
    nvitop

参考链接

从坑中爬起:ESXi 8.0直通NVIDIA显卡的血泪经验
How To Install Nvidia Drivers on Rocky Linux 10
vmware虚拟机玩GPU显卡直通

Qwen3概述

  • 多种思考模式

    可用户提示或系统消息中添加 /think 和 /no_think 来逐轮切换模型的思考模式

    • 思考模式:在这种模式下,模型会逐步推理,经过深思熟虑后给出最终答案。这种方法非常适合需要深入思考的复杂问题。
    • 非思考模式:在此模式中,模型提供快速、近乎即时的响应,适用于那些对速度要求高于深度的简单问题。
  • 多语言
    119 种语言和方言

  • MCP 支持

Qwen3-30B-A3B

  • 一个拥有约 300 亿总参数和 30 亿激活参数的小型 MoE 模型
  • 需24GB+显存

Qwen3-Embedding & Qwen3-Reranker

Model Type Models Size Layers Sequence Length Embedding Dimension MRL Support Instruction Aware
Text Embedding Qwen3-Embedding-0.6B 0.6B 28 32K 1024 Yes Yes
Text Embedding Qwen3-Embedding-4B 4B 36 32K 2560 Yes Yes
Text Embedding Qwen3-Embedding-8B 8B 36 32K 4096 Yes Yes
Text Reranking Qwen3-Reranker-0.6B 0.6B 28 32K - - Yes
Text Reranking Qwen3-Reranker-4B 4B 36 32K - - Yes
Text Reranking Qwen3-Reranker-8B 8B 36 32K - - Yes
  • 经济型:Embedding-4B + Reranker-4B(显存总需求<30GB)
  • 高性能型:Embedding-8B + Reranker-8B(需多GPU,吞吐量提升40%+)

对比BGE-M3:全方位代差优势

指标 Qwen3-8B BGE-M3 优势幅度
综合得分 70.58 59.56 ↑11.02
上下文长度 32K 8K ↑ 4倍
检索任务(MSMARCO) 57.65 40.88 ↑41%
开放问答(NQ) 10.06 -3.11 实现负分逆转
多语言理解 28.66 20.10 ↑42%

vllm 安装

  • uv

    1
    2
    3
    uv venv vllm --python 3.12 --seed
    source vllm/bin/activate
    uv pip install vllm
  • conda

    1
    2
    3
    4
    5
    conda env list ## 查看conda创建的所以虚拟环境
    conda create -n vllm python=3.12 ## 创建特定版本python
    conda activate vllm ## 进入某个虚拟环境
    conda env remove -n vllm ## 删除某个虚拟环境
    pip install vllm

模型下载

1
2
3
4
5
6
7
8
9
10
11
12
## 安装下载环境
pip install modelscope
## 下载模型
modelscope download --model Qwen/Qwen3-30B-A3B
## 下载指定目录下
modelscope download --model Qwen/Qwen3-30B-A3B-FP8 --local_dir /home/models/Qwen3-30B-A3B-FP8
modelscope download --model Qwen/Qwen3-30B-A3B-Instruct-2507-FP8 --local_dir /home/models/Qwen3-30B-A3B-Instruct-2507-FP8
modelscope download --model Qwen/Qwen3-30B-A3B-Thinking-2507-FP8 --local_dir /home/models/Qwen3-30B-A3B-Thinking-2507-FP8

modelscope download --model Qwen/Qwen3-Embedding-8B --local_dir /home/models/Qwen3-Embedding-8B
modelscope download --model Qwen/Qwen3-Reranker-8B --local_dir /home/models/Qwen3-Reranker-8B
modelscope download --model Qwen/Qwen2.5-VL-3B-Instruct --local_dir /home/models/Qwen2.5-VL-3B-Instruct

vllm 服务启动

vllm serve <model_path>

1
2
3
4
5
6
7
8
9
10
vllm serve /home/models/Qwen3-30B-A3B-Thinking-2507-FP8 \
--port 8003 \
--host 0.0.0.0 \
--gpu-memory-utilization 0.85 \
--max-model-len 12288 \
--max-num-seqs 256 \
--max-num-batched-tokens 4096 \
--tensor-parallel-size 1 \
--reasoning-parser deepseek_r1 \
--served-model-name Qwen3-30B-A3B-Thinking-2507-FP8
  • --tensor-parallel-size: 将您的模型分布到主机 GPU 中,GPU数量。
  • --GPU-memory-utilization:调整模型权重、激活和 KV 缓存的加速器内存使用率。作为 0.0 到 1.0 的比例测量,默认为 0.9.例如,您可以将此值设置为 0.8,将 AI Inference Server 的 GPU 内存消耗限制为 80%。使用部署稳定的最大值来最大化吞吐量。
  • --max-model-len: 限制模型的最大上下文长度,以令牌表示。如果模型的默认上下文长度太长,则将其设置为防止内存出现问题。
  • --max-num-batched-tokens: 每迭代的最大批处理令牌数量,将令牌的最大批处理大小限制为每个步骤处理(以令牌表示)。增加这可以提高吞吐量,但可能会影响输出令牌延迟。
  • --max-num-seqs: # 每迭代的最大序列数量,吞吐量关键!

参数相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
usage: vllm serve [-h] [--model MODEL]
[--task {auto,generate,embedding,embed,classify,score,reward,transcription}]
[--tokenizer TOKENIZER] [--hf-config-path HF_CONFIG_PATH]
[--skip-tokenizer-init] [--revision REVISION]
[--code-revision CODE_REVISION]
[--tokenizer-revision TOKENIZER_REVISION]
[--tokenizer-mode {auto,slow,mistral,custom}]
[--trust-remote-code]
[--allowed-local-media-path ALLOWED_LOCAL_MEDIA_PATH]
[--download-dir DOWNLOAD_DIR]
[--load-format {auto,pt,safetensors,npcache,dummy,tensorizer,sharded_state,gguf,bitsandbytes,mistral,runai_streamer}]
[--config-format {auto,hf,mistral}]
[--dtype {auto,half,float16,bfloat16,float,float32}]
[--kv-cache-dtype {auto,fp8,fp8_e5m2,fp8_e4m3}]
[--max-model-len MAX_MODEL_LEN]
[--guided-decoding-backend GUIDED_DECODING_BACKEND]
[--logits-processor-pattern LOGITS_PROCESSOR_PATTERN]
[--model-impl {auto,vllm,transformers}]
[--distributed-executor-backend {ray,mp,uni,external_launcher}]
[--pipeline-parallel-size PIPELINE_PARALLEL_SIZE]
[--tensor-parallel-size TENSOR_PARALLEL_SIZE]
[--enable-expert-parallel]
[--max-parallel-loading-workers MAX_PARALLEL_LOADING_WORKERS]
[--ray-workers-use-nsight] [--block-size {8,16,32,64,128}]
[--enable-prefix-caching | --no-enable-prefix-caching]
[--disable-sliding-window] [--use-v2-block-manager]
[--num-lookahead-slots NUM_LOOKAHEAD_SLOTS] [--seed SEED]
[--swap-space SWAP_SPACE] [--cpu-offload-gb CPU_OFFLOAD_GB]
[--gpu-memory-utilization GPU_MEMORY_UTILIZATION]
[--num-gpu-blocks-override NUM_GPU_BLOCKS_OVERRIDE]
[--max-num-batched-tokens MAX_NUM_BATCHED_TOKENS]
[--max-num-partial-prefills MAX_NUM_PARTIAL_PREFILLS]
[--max-long-partial-prefills MAX_LONG_PARTIAL_PREFILLS]
[--long-prefill-token-threshold LONG_PREFILL_TOKEN_THRESHOLD]
[--max-num-seqs MAX_NUM_SEQS] [--max-logprobs MAX_LOGPROBS]
[--disable-log-stats]
[--quantization {aqlm,awq,deepspeedfp,tpu_int8,fp8,ptpc_fp8,fbgemm_fp8,modelopt,nvfp4,marlin,gguf,gptq_marlin_24,gptq_marlin,awq_marlin,gptq,compressed-tensors,bitsandbytes,qqq,hqq,experts_int8,neuron_quant,ipex,quark,moe_wna16,None}]
[--rope-scaling ROPE_SCALING] [--rope-theta ROPE_THETA]
[--hf-overrides HF_OVERRIDES] [--enforce-eager]
[--max-seq-len-to-capture MAX_SEQ_LEN_TO_CAPTURE]
[--disable-custom-all-reduce]
[--tokenizer-pool-size TOKENIZER_POOL_SIZE]
[--tokenizer-pool-type TOKENIZER_POOL_TYPE]
[--tokenizer-pool-extra-config TOKENIZER_POOL_EXTRA_CONFIG]
[--limit-mm-per-prompt LIMIT_MM_PER_PROMPT]
[--mm-processor-kwargs MM_PROCESSOR_KWARGS]
[--disable-mm-preprocessor-cache] [--enable-lora]
[--enable-lora-bias] [--max-loras MAX_LORAS]
[--max-lora-rank MAX_LORA_RANK]
[--lora-extra-vocab-size LORA_EXTRA_VOCAB_SIZE]
[--lora-dtype {auto,float16,bfloat16}]
[--long-lora-scaling-factors LONG_LORA_SCALING_FACTORS]
[--max-cpu-loras MAX_CPU_LORAS] [--fully-sharded-loras]
[--enable-prompt-adapter]
[--max-prompt-adapters MAX_PROMPT_ADAPTERS]
[--max-prompt-adapter-token MAX_PROMPT_ADAPTER_TOKEN]
[--device {auto,cuda,neuron,cpu,openvino,tpu,xpu,hpu}]
[--num-scheduler-steps NUM_SCHEDULER_STEPS]
[--use-tqdm-on-load | --no-use-tqdm-on-load]
[--multi-step-stream-outputs [MULTI_STEP_STREAM_OUTPUTS]]
[--scheduler-delay-factor SCHEDULER_DELAY_FACTOR]
[--enable-chunked-prefill [ENABLE_CHUNKED_PREFILL]]
[--speculative-model SPECULATIVE_MODEL]
[--speculative-model-quantization {aqlm,awq,deepspeedfp,tpu_int8,fp8,ptpc_fp8,fbgemm_fp8,modelopt,nvfp4,marlin,gguf,gptq_marlin_24,gptq_marlin,awq_marlin,gptq,compressed-tensors,bitsandbytes,qqq,hqq,experts_int8,neuron_quant,ipex,quark,moe_wna16,None}]
[--num-speculative-tokens NUM_SPECULATIVE_TOKENS]
[--speculative-disable-mqa-scorer]
[--speculative-draft-tensor-parallel-size SPECULATIVE_DRAFT_TENSOR_PARALLEL_SIZE]
[--speculative-max-model-len SPECULATIVE_MAX_MODEL_LEN]
[--speculative-disable-by-batch-size SPECULATIVE_DISABLE_BY_BATCH_SIZE]
[--ngram-prompt-lookup-max NGRAM_PROMPT_LOOKUP_MAX]
[--ngram-prompt-lookup-min NGRAM_PROMPT_LOOKUP_MIN]
[--spec-decoding-acceptance-method {rejection_sampler,typical_acceptance_sampler}]
[--typical-acceptance-sampler-posterior-threshold TYPICAL_ACCEPTANCE_SAMPLER_POSTERIOR_THRESHOLD]
[--typical-acceptance-sampler-posterior-alpha TYPICAL_ACCEPTANCE_SAMPLER_POSTERIOR_ALPHA]
[--disable-logprobs-during-spec-decoding [DISABLE_LOGPROBS_DURING_SPEC_DECODING]]
[--model-loader-extra-config MODEL_LOADER_EXTRA_CONFIG]
[--ignore-patterns IGNORE_PATTERNS]
[--preemption-mode PREEMPTION_MODE]
[--served-model-name SERVED_MODEL_NAME [SERVED_MODEL_NAME ...]]
[--qlora-adapter-name-or-path QLORA_ADAPTER_NAME_OR_PATH]
[--show-hidden-metrics-for-version SHOW_HIDDEN_METRICS_FOR_VERSION]
[--otlp-traces-endpoint OTLP_TRACES_ENDPOINT]
[--collect-detailed-traces COLLECT_DETAILED_TRACES]
[--disable-async-output-proc]
[--scheduling-policy {fcfs,priority}]
[--scheduler-cls SCHEDULER_CLS]
[--override-neuron-config OVERRIDE_NEURON_CONFIG]
[--override-pooler-config OVERRIDE_POOLER_CONFIG]
[--compilation-config COMPILATION_CONFIG]
[--kv-transfer-config KV_TRANSFER_CONFIG]
[--worker-cls WORKER_CLS]
[--worker-extension-cls WORKER_EXTENSION_CLS]
[--generation-config GENERATION_CONFIG]
[--override-generation-config OVERRIDE_GENERATION_CONFIG]
[--enable-sleep-mode] [--calculate-kv-scales]
[--additional-config ADDITIONAL_CONFIG] [--enable-reasoning]
[--reasoning-parser {deepseek_r1}]

Qwen3 底座模型部署 部署

  • docker-compose.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    services:
    Qwen3-30B-A3B-Instruct-2507-FP8:
    image: vllm/vllm-openai:v0.10.1.1
    container_name: Qwen3-30B-A3B-Instruct-2507-FP8
    restart: unless-stopped
    profiles: ["Instruct"]
    volumes:
    - /home/models/Qwen3-30B-A3B-Instruct-2507-FP8:/models/Qwen3-30B-A3B-Instruct-2507-FP8
    command: ["--model", "/models/Qwen3-30B-A3B-Instruct-2507-FP8","--served-model-name", "Qwen3-30B-A3B-Instruct-2507-FP8","--gpu-memory-utilization", "0.80","--max-model-len", "32768","--tensor-parallel-size", "1"]
    ports:
    - 8003:8000
    deploy:
    resources:
    reservations:
    devices:
    - driver: nvidia
    device_ids: ["0"]
    capabilities: [gpu]

    Qwen3-30B-A3B-Thinking-2507-FP8:
    image: vllm/vllm-openai:v0.10.1.1
    container_name: Qwen3-30B-A3B-Thinking-2507-FP8
    restart: unless-stopped
    profiles: ["Thinking"]
    volumes:
    - /home/models/Qwen3-30B-A3B-Thinking-2507-FP8:/models/Qwen3-30B-A3B-Thinking-2507-FP8
    command: ["--model", "/models/Qwen3-30B-A3B-Thinking-2507-FP8","--served-model-name", "Qwen3-30B-A3B-Thinking-2507-FP8","--gpu-memory-utilization", "0.80","--max-model-len", "32768","--tensor-parallel-size", "1","--reasoning-parser", "deepseek_r1"]
    ports:
    - 8003:8000
    deploy:
    resources:
    reservations:
    devices:
    - driver: nvidia
    device_ids: ["0"]
    capabilities: [gpu]

    Qwen3-30B-A3B-FP8:
    image: vllm/vllm-openai:v0.10.1.1
    container_name: Qwen3-30B-A3B-FP8
    restart: unless-stopped
    profiles: ["Instruct&Thinking"]
    volumes:
    - /home/models/Qwen3-30B-A3B-FP8:/models/Qwen3-30B-A3B-FP8
    command: ["--model", "/models/Qwen3-30B-A3B-FP8","--served-model-name", "Qwen3-30B-A3B-FP8","--gpu-memory-utilization", "0.40","--max-model-len", "32768","--tensor-parallel-size", "1","--reasoning-parser", "deepseek_r1"]
    ports:
    - 8003:8000
    deploy:
    resources:
    reservations:
    devices:
    - driver: nvidia
    device_ids: ["0"]
    capabilities: [gpu]

    Qwen3-Embedding-8B:
    container_name: Qwen3-Embedding-8B
    restart: no
    image: vllm/vllm-openai:v0.10.1.1
    profiles: ["Embedding"]
    volumes:
    - /home/models/Qwen3-Embedding-8B:/models/Qwen3-Embedding-8B
    command: ["--model", "/models/Qwen3-Embedding-8B", "--served-model-name", "Qwen3-Embedding-8B", "--gpu-memory-utilization", "0.85"]
    ports:
    - 8001:8000
    deploy:
    resources:
    reservations:
    devices:
    - driver: nvidia
    device_ids: ["1"]
    capabilities: [gpu]

    Qwen3-Reranker-8B:
    container_name: Qwen3-Reranker-8B
    restart: no
    image: vllm/vllm-openai:v0.10.1.1
    profiles: ["Reranker"]
    volumes:
    - /home/models/Qwen3-Reranker-8B:/models/Qwen3-Reranker-8B
    command: ['--model', '/models/Qwen3-Reranker-8B', '--served-model-name', 'Qwen3-Reranker-8B', '--gpu-memory-utilization', '0.45', '--hf_overrides','{"architectures": ["Qwen3ForSequenceClassification"],"classifier_from_token": ["no", "yes"],"is_original_qwen3_reranker": true}']
    ports:
    - 8002:8000
    deploy:
    resources:
    reservations:
    devices:
    - driver: nvidia
    device_ids: ["1"]
    capabilities: [gpu]


  • 启动

    1
    2
    3
    4
    5
    docker compose --profile Instruct&Thinking up -d
    docker compose --profile Instruct up -d
    docker compose --profile Thinking up -d
    docker compose --profile Embedding up -d
    docker compose --profile Reranker up -d

    curl验证一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    {
    "model": "Qwen3-30B-A3B-Instruct-2507-FP8",
    "messages": [
    {
    "role": "system",
    "content": "你是一名旅游顾问,我计划在2024年夏季进行一次为期10天的欧洲之旅,主要目的地包括巴黎、米兰和马德里。预算为每人10000元人民币,希望体验当地的文化和美食,同时偏好舒适的住宿条件。用户特别强调需要包含到访每个城市的必游景点,并希望有自由活动的时间。"
    },
    {
    "role": "user",
    "content": "我计划7月中旬出发,请给我一份旅游计划"
    }
    ],
    "temperature": 0.3,
    "stream": true
    }

new api统一网关管理大模型

使用 FastAPI 集成 Qwen 大模型服务

  • 依赖文件(requirements.txt):
    1
    2
    3
    4
    5
    fastapi==0.116.1
    uvicorn==0.35.0
    openai==1.100.1
    dashscope==1.24.1
    python-dotenv==1.1.1
    安装依赖的 Python 库:
    1
    pip install -r requirements.txt
    将 DeepSeek 和 Qwen 的相关信息配置到系统环境变量中。
    1
    2
    3
    4
    5
    6
    7
    # DeepSeek 配置
    DEEPSEEK_API_KEY = "sk-your-deepseek-key"
    DEEPSEEK_BASE_URL = "https://api.deepseek.com"

    # Qwen3 配置(对接底座大模型)
    QWEN3_API_KEY = "sk-your-qwen-key"
    QWEN3_API_BASE_URL = "http://192.168.103.224:8003/v1"
    接口代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    import os
    import uvicorn
    from fastapi import FastAPI, HTTPException
    from pydantic import BaseModel
    from dotenv import load_dotenv
    from openai import OpenAI as DeepSeekClient
    from openai import OpenAI as Qwen3Client

    # 配置模型参数
    MODEL_CONFIG = {
        "deepseek": {
            "client": DeepSeekClient(
                api_key=os.getenv("DEEPSEEK_API_KEY"),
                base_url=os.getenv("DEEPSEEK_BASE_URL")
            ),
            "model_name""deepseek-chat"
        },
        "qwen3": {
            "client": Qwen3Client(
                api_key=os.getenv("QWEN3_API_KEY"),
                base_url=os.getenv("QWEN3_API_BASE_URL")
            ),
            "model_name""Qwen3-30B-A3B-Instruct-2507-FP8"
        }
    }


    # 统一请求格式(兼容OpenAI)
    class ChatRequest(BaseModel):
        model: str  # deepseek 或 qwen
        messages: list
        temperature: float0.7
        max_tokens: int1024


    # 统一响应格式
    class ChatResponse(BaseModel):
        model: str
        content: str


    @app.post("/v1/chat")
    asyncdef chat_completion(request: ChatRequest):
        model_type = request.model.lower()
        if model_type notin MODEL_CONFIG:
            raise HTTPException(400f"Unsupported model: {model_type}")

        try:
            if model_type == "deepseek":
                response = MODEL_CONFIG["deepseek"]["client"].chat.completions.create(
                    model=MODEL_CONFIG["deepseek"]["model_name"],
                    messages=request.messages,
                    temperature=request.temperature,
                    max_tokens=request.max_tokens
                )
                content = response.choices[0].message.content

            elif model_type == "qwen3":
                response = MODEL_CONFIG["deepseek"]["client"].chat.completions.create(
                    model=MODEL_CONFIG["deepseek"]["model_name"],
                    messages=request.messages,
                    temperature=request.temperature,
                    max_tokens=request.max_tokens
                )
                content = response.choices[0].message.content

            return ChatResponse(model=model_type, content=content)

        except Exception as e:
            raise HTTPException(500f"API Error: {str(e)}")

背景

我们在过往尝试过使用paimon进行分层 Flink & Paimon & StarRocks & Dinky 流式湖仓分层实践验证,一共制作了如下4张表,其中流任务的merge-engine分别为:

  • order_dw.dwd_orders (partial-update)
  • order_dw.dwm_users_shops (aggregation )
  • order_dw.dws_users (aggregation )
  • order_dw.dws_shops (aggregation )

我们经常会有这样的疑问:

1、假设mysql源表中的order_dw.orders的buy_fee在下游paimon流任务表dwm_users_shopsdws_usersdws_shops中都已经完成聚合统计后,这时候,这时候突然对order_dw.orders表中的某一条数据就行修改纠正,正常的业务场景就是改价,那么下游的aggregation任务会是如何?数据是否会纠正?纠正的原理和逻辑是什么?
2、假设我的dwm_users_shops任务突然终止,并且需要从某一个checkpoint/savepoint恢复,那么这时候aggregation任务又会是如何处理聚合数据的?

带着这个疑惑,我们来实践验证一下

实践

我们以Flink & Paimon & StarRocks & Dinky 流式湖仓分层实践验证为基础

0%