news 2026/10/24 4:17:34

大模型推理框架源码实战:KV Cache、连续批处理与压测调参

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
大模型推理框架源码实战:KV Cache、连续批处理与压测调参

简介:VLLM-0.7.3 源码包面向大语言模型推理引擎的学习者、研究者与工程实践者,聚焦超大规模模型在高速令牌生成、高效内存管理与企业级稳定推理上的性能优化问题,适合具备一定 Python 与深度学习基础、希望深入理解推理调度与算子实现的读者。压缩包共 1896 个文件、约 46.33MB,以 Python 代码为主体(1186 个),并包含 JSON 配置、Markdown 文档、YAML 与 Shell 部署脚本,以及 CUDA/C++ 扩展源码(55 个 cuh、47 个 cu 及配套 cpp/h 文件),覆盖从接口层到 GPU 内核的完整工程结构。已有 343 人学习,说明其在 LLM 推理方向具有一定关注度。读者可据此研读推理引擎的请求调度、KV Cache 管理、注意力与量化算子实现,并结合配置和脚本理解多硬件后端的构建方式,为二次开发、性能调优与源码级排错提供可复用的参考。

1. 先别急着跑 demo:这份 0.7.3 源码解决的是吞吐与显存账本

拿到一份 0.7.3 版大模型推理框架源码,很多人第一反应是找 demo 跑一句 hello,但真正决定它能不能落地的,是请求进入调度器之后 KV Cache 怎么分块、连续批处理怎么把吞吐拉起来。这个资源面向的是想把 LLM 推理从 notebook 推到服务端的人:你会看到源码目录里 engine、worker、model_executor 的分层,也会碰到显存碎片、参数不匹配、编译失败这些血泪经验。新手能按步骤跑通离线推理和兼容 Chat Completions 的服务,熟手能顺着调度链路改 block size、并发上限和并行度。它不是科普包,而是一份要动手拆的工程源码;下面按“先看主链路、再跑最小服务、最后压测调参”的顺序走。

2. 源码目录先看哪几层:从请求入口到 token 输出的主链路

2.1 目录分层:先锁定 entrypoints、engine、worker、model_executor

源码包解压后不要从最底层的 kernel 开始读,那样很容易陷进黑匣子:你知道这里在算 attention,却不知道一个请求为什么排队、为什么显存突然掉不下去。更稳的读法是从请求入口往回推,先建立“请求怎么进来、调度器怎么组 batch、worker 怎么执行、模型层怎么吐出 token”的主链路,再决定要不要改底层算子。0.7.3 这个版本的具体目录可能有些微调,但常见分层基本围绕下面几块展开,解压后以根目录实际结构为准。

目录/文件主要职责改动风险适合先看什么
entrypoints/HTTP 服务、命令行入口、请求解析低接口参数如何映射到引擎参数
engine/同步/异步引擎、请求生命周期中请求如何入队、如何返回
core/调度器、块管理、KV Cache 组织高连续批处理与 block table
worker/设备执行、模型加载后的运行态高单卡/多卡如何执行一步解码
model_executor/模型结构、层实现、权重加载高模型配置如何转成执行图
config.py一类配置启动参数、默认值、校验中哪些参数会改变显存账本

从落地角度看,entrypoints/和engine/是“服务能不能起来”的问题,core/和worker/是“起来后吞吐稳不稳”的问题,model_executor/是“模型能不能正确加载”的问题。很多翻车不是算法错,而是启动参数和模型配置没对齐:比如max_model_len按模型上限开满,KV Cache 预留把显存吃光;又比如张量并行度设成 2,但机器上只有一张卡可见。先读入口和配置,能最快排掉这类低级错误。

读源码时我一般会拿一个最小请求做标记:输入一句短 prompt,输出 16 个 token,然后在entrypoints/里找请求体字段,在engine/里找add_request一类入口,在core/里看调度输出,在worker/里看一次execute_model的输入输出。这样读的好处是每一步都有现实对象对照,不会把调度器当成黑匣子。源码包的价值也在这里:你可以改一个 block size、改一个并发上限,然后立刻用同一套请求复测,而不是只会在命令行里加参数。

2.2 最小离线推理:用 LLM.generate 验证源码是否装对

在改任何调度参数之前,先把源码包安装成可导入状态,再跑一个离线推理。离线推理不经过 HTTP 层,变量少,最适合确认 CUDA、torch、模型路径和源码安装是否一致。下面命令都在源码根目录执行,虚拟环境建议新建,避免和系统里已有的 torch 打架。

# 在源码根目录执行;先确认驱动能看到卡 nvidia-smi # 创建隔离环境,避免多套 torch 混用 python -m venv .venv source .venv/bin/activate python -m pip install -U pip wheel setuptools # 按本机 CUDA 版本选择 torch wheel;装完立刻自检 pip install torch python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())" # 安装源码包自身;-e 便于改完调度参数后直接复跑 pip install -e .

这段命令的逻辑是“先看驱动,再隔离环境,再对齐 torch,最后可编辑安装源码”。nvidia-smi看的是驱动支持的最高 CUDA 运行时,torch.version.cuda看的是当前 wheel 编译时用的 CUDA 版本,两者大版本不要跨太多;torch.cuda.is_available()必须是True,否则后面加载模型一定失败。pip install -e .的好处是源码目录里的改动直接生效,调 block size、改日志、打临时 patch 都不用反复重装。注意源码包本身不带模型权重,模型路径必须指向本地已下载的模型目录。

from vllm import LLM, SamplingParams # 模型路径换成本地已下载的模型目录;源码包不包含权重 model_path = "/data/models/your-llm" llm = LLM( model=model_path, tensor_parallel_size=1, # 单卡先跑通,再改多卡 gpu_memory_utilization=0.85, # 给激活和临时张量留余量 max_model_len=4096, # 先小于模型上限,避免一启动吃满 KV Cache enforce_eager=True, # 排查阶段关掉图捕获,报错更直白 ) sampling = SamplingParams( temperature=0.0, # 验证阶段用贪心,结果可复现 top_p=1.0, max_tokens=128, ) prompts = ["用三句话解释连续批处理对 LLM 推理吞吐的影响。"] outputs = llm.generate(prompts, sampling) for out in outputs: print(out.outputs[0].text)

这段代码的关键参数不是越多越好。tensor_parallel_size=1是先把单卡链路跑通,多卡并行会引入通信和切分问题,不适合作为第一次验证。gpu_memory_utilization=0.85是给激活值、临时缓冲和框架自身留余量,直接拉到 0.95 很容易在长 prompt 或多请求时 OOM。max_model_len=4096是主动限制 KV Cache 上限,先别按模型最大长度开满。enforce_eager=True在排查阶段很有用,图捕获失败时的报错往往不够直白,关掉后更容易定位是模型加载、算子还是显存问题;稳定后再关掉它换性能。SamplingParams里温度设为 0,是为了让同一输入多次运行结果可比,压测时也一样。

跑通的标准不是“print 出字”就结束,而是同时满足三条:进程不报 CUDA error,输出不是空串或乱码,显存占用在请求结束后能回落到一个稳定水位。如果输出有字但显存一路涨,后面起服务一定会在并发上来时翻车。离线推理通过后,再进入 HTTP 服务层,变量会多出请求排队、并发上限、流式返回和模型名映射,这时才需要看下一章的启动参数。

3. 把服务跑起来:兼容 Chat Completions 的接口与三个关键启动参数

3.1 启动参数:显存、长度、并发三本账

离线推理能跑,不代表服务能扛。服务层多了一个常驻进程、一个请求队列和一套 HTTP 解析,启动参数会把显存账本放大。下面是一个偏保守的启动命令,适合第一次把服务拉起来;具体参数名在 0.7.3 源码里可能略有差异,启动前用--help对一遍,不要照抄旧版本参数。

# 在源码根目录、虚拟环境已激活的前提下启动 python -m vllm.entrypoints.openai.api_server \ --model /data/models/your-llm \ --served-model-name local-llm \ --host 0.0.0.0 \ --port 8000 \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.88 \ --max-model-len 8192 \ --max-num-seqs 64 \ --max-num-batched-tokens 8192 \ --dtype auto \ --disable-log-requests

这段命令里,--model是本地模型目录,--served-model-name是请求体里要填的模型名,两者不是一回事;--host 0.0.0.0让服务监听所有网卡,--port 8000是本地验证端口。--tensor-parallel-size控制张量并行卡数,单卡先写 1。--gpu-memory-utilization是显存占用目标,0.88 比离线阶段的 0.85 略高,是因为服务常驻需要更稳定的预留,但不要一上来写 0.95。--max-model-len是服务允许的最大上下文长度,它直接决定 KV Cache 的预留上限;模型支持 32K 不代表你要开 32K,先按业务真实长度开。--max-num-seqs是同时调度的序列数上限,--max-num-batched-tokens是一次批处理里 token 总量上限,这两个参数不是越大越好,后面压测会看到拐点。--dtype auto让框架按模型配置选精度,量化模型则需要额外的量化参数,不能只靠 auto 猜。--disable-log-requests在压测时减少日志噪音,排查阶段可以去掉。

参数作用第一次启动建议常见误用
--gpu-memory-utilization控制框架可占用的显存比例0.85 到 0.90直接 0.95,长 prompt 时 OOM
--max-model-len限制上下文与 KV Cache 上限按业务真实长度按模型最大长度开满
--max-num-seqs同时调度的序列数32 到 64 起步只看并发数,不看显存
--max-num-batched-tokens单次批处理 token 上限4096 到 8192 起步拉太大,prefill 挤占 decode
--tensor-parallel-size张量并行卡数单卡写 1卡数不可见却写大于 1
--dtype模型计算精度auto量化权重不配量化参数

这张表的核心是“三本账”:显存账、长度账、并发账。显存账看gpu-memory-utilization和模型权重,长度账看max-model-len,并发账看max-num-seqs与max-num-batched-tokens。三本账互相牵制:长度开大,KV Cache 预留变多,能并发的序列就少;并发开大,decode 批次变厚,单请求延迟会抖;批处理 token 开大,prefill 吞吐上去,但短请求的首 token 可能被长请求拖住。第一次启动不要追求“全开”,先让服务稳定返回,再逐项压测。

注意:服务启动日志里要重点看模型加载耗时、KV Cache 块数、可用显存和最大并发估计。日志说启动成功但显存余量很小,后面一定会在真实流量下翻车。

3.2 接口验证:/v1/models 与 /v1/chat/completions 的最小闭环

服务起来后先别急着上业务代码,先用两个请求确认“模型名映射”和“推理链路”都是通的。第一个请求看模型列表,第二个请求走一次 chat completions。下面命令里的模型名必须和--served-model-name完全一致,大小写和短横线都算。

# 先确认服务看到的模型名 curl -s http://127.0.0.1:8000/v1/models | python -m json.tool # 再发一个贪心解码请求,便于复现 curl -s http://127.0.0.1:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "local-llm", "messages": [{"role": "user", "content": "用一句话说明 KV Cache 的作用"}], "temperature": 0, "max_tokens": 64 }' | python -m json.tool

这两个命令的逻辑是“先读模型注册表,再打一次最小推理”。/v1/models返回的是服务端注册的模型名,不是本地目录名;请求体里的model必须与它一致,否则会报模型不存在。temperature=0让输出可复现,max_tokens=64控制验证成本,避免第一次就生成几千 token 把显存和等待时间拉长。返回体里重点看choices[0].message.content是否非空,usage里的 prompt tokens 和 completion tokens 是否合理;如果 completion tokens 为 0,通常是生成参数或模板问题,不是服务没起来。

再补一个 Python 客户端验证,方便后面改造成压测脚本。这个脚本只发一次请求,重点是把超时、状态码和用量打印出来,不要把异常吞掉。

import requests url = "http://127.0.0.1:8000/v1/chat/completions" payload = { "model": "local-llm", "messages": [{"role": "user", "content": "解释连续批处理为什么能提升吞吐"}], "temperature": 0, "max_tokens": 128, } # 超时给足,首次请求可能包含编译和缓存初始化 resp = requests.post(url, json=payload, timeout=120) print(resp.status_code) print(resp.json()["usage"]) print(resp.json()["choices"][0]["message"]["content"][:200])

这段代码的timeout=120不是随便写的:首次请求可能触发图捕获、内存池初始化或前缀缓存构建,超时太短会把正常冷启动误判成服务故障。resp.status_code要先看,200 以外的响应体里通常有错误原因,比如上下文超限、模型名不匹配、请求体字段不合法。usage用来核对 token 计数,后面压测的吞吐计算就靠它。输出只截前 200 个字符,是为了避免终端被长文本刷屏,不影响判断。

接口通后,再验证流式返回。流式不是必需,但真实业务里首 token 时间很关键。请求体加"stream": true,客户端按行读data:前缀,最后以[DONE]结束。流式模式下不要用普通resp.json(),否则会解析失败。这个验证能提前暴露反向代理缓冲、超时设置和客户端读取方式的问题。服务层跑通后,接下来才是调度参数和 KV Cache 的调优,不要在这一步就急着把并发拉满。

4. 调度与 KV Cache 参数:连续批处理不是把并发开大

4.1 PagedAttention 与块表:为什么 block size 会改变显存碎片

大模型推理的显存大头不只是权重,还有 KV Cache。自回归生成每多一个 token,就要为每一层缓存 key 和 value;请求长度不一,如果按最大长度给每个请求预留连续显存,碎片和浪费会非常严重。分页式 KV Cache 的思路是把缓存切成固定大小的 block,用逻辑块到物理块的映射表管理,请求按需拿块,不要求物理连续。这样显存利用率更稳,连续批处理才能在每一步解码时动态重组 batch。读源码时重点看块表怎么建、块怎么分配、请求结束后块怎么回收,这三个动作决定了长跑服务会不会显存泄漏。

block-size是这套机制里的关键参数。块太小,块表项变多,管理开销和寻址开销上来;块太大,最后一个块尾部浪费变多,短请求多的时候尤其明显。常见默认值是 16,实际选 8、16、32 要看请求长度分布:短请求占主导时可以试小一点,长上下文占主导时可以试大一点,但不要把它当成玄学开关,改完必须用同一组固定长度请求对比显存峰值和吞吐。块表本身不是免费午餐,它把“连续显存分配”换成了“间接寻址”,所以极端碎片场景下收益明显,整齐划一的短请求场景下收益没那么夸张。

连续批处理的核心也不是“同时收很多请求”,而是每一步解码都把还能继续跑的序列重新组批。一个请求生成结束后,它的槽位可以立刻让给新请求,不用等整批结束。这提高了 GPU 利用率,但也带来一个新问题:prefill 阶段和 decode 阶段抢资源。长 prompt 的 prefill 一次要算很多 token,如果批处理 token 上限开得太大,decode 的短请求就会被拖慢,表现为首 token 时间抖动。源码里调度器会在 prefill 和 decode 之间做取舍,参数就是用来约束这个取舍的,不是用来把并发数字做大的。

前缀缓存是另一个容易被误用的点。多个请求共享同一段系统提示或固定指令时,开启前缀缓存可以复用已经算过的 KV 块,降低重复 prefill 成本。但如果每个请求的提示词都不同,缓存命中率很低,开启后反而增加块管理开销。验证方法很简单:用同一段长系统提示加不同用户问题,对比开启前后的首 token 时间和显存;再用完全不同的提示词跑一遍,看收益是否消失。不要只看一个场景就下结论。

4.2 压测调参:用固定输入输出长度找拐点

调参最怕“感觉快了”。今天并发 16 觉得挺顺,明天换一批长短不一的请求就翻车。我的习惯是固定输入长度和输出长度,只改一个参数,记录吞吐、平均延迟和显存峰值。下面这个异步脚本用固定 prompt 和固定max_tokens打一轮并发,重点是看墙钟吞吐和平均延迟,不测首 token 时间;首 token 时间要用流式请求单独采。

import asyncio import time import aiohttp URL = "http://127.0.0.1:8000/v1/chat/completions" PROMPT = "请用 200 字解释大模型推理中的显存碎片。" * 4 async def one(session, i): payload = { "model": "local-llm", "messages": [{"role": "user", "content": PROMPT}], "temperature": 0, "max_tokens": 128, } t0 = time.perf_counter() async with session.post(URL, json=payload) as resp: data = await resp.json() return time.perf_counter() - t0, data["usage"]["completion_tokens"] async def main(concurrency=16): async with aiohttp.ClientSession() as session: tasks = [one(session, i) for i in range(concurrency)] results = await asyncio.gather(*tasks) total_tokens = sum(t for _, t in results) wall = max(lat for lat, _ in results) avg_lat = sum(lat for lat, _ in results) / len(results) print(f"并发={concurrency} 输出token={total_tokens} 墙钟={wall:.2f}s") print(f"吞吐={total_tokens / wall:.2f} tok/s 平均延迟={avg_lat:.2f}s") asyncio.run(main(16))

这段脚本的PROMPT故意重复四次,是为了让输入长度稳定在几百 token 量级,避免每次请求长度漂移。max_tokens=128固定输出上限,这样不同并发档位的 token 总量可比。wall取所有请求里最长的耗时,是近似墙钟时间;更严谨的做法是记录整轮开始和结束时间,但用来找拐点足够。total_tokens / wall是近似吞吐,不是严格的服务端吞吐,因为客户端和网络也占时间,所以只适合同一台机器、同一客户端条件下横向比较。并发档位从 1、4、8、16 逐档加,不要一次跳到 64。

参数调大后的直接变化调大后的风险适合观察的指标
max-num-seqs同时解码的序列变多KV Cache 增长,延迟抖动吞吐、显存峰值、TPOT
max-num-batched-tokens单批 token 变多prefill 挤占 decode首 token 时间、吞吐
block-size单块变大短请求尾部浪费显存峰值、块利用率
前缀缓存共享前缀复用低命中率时管理开销首 token 时间、命中率
swap-spaceCPU 交换空间变大掩盖显存不足,延迟变差交换次数、尾延迟

调参顺序建议是:先固定max-model-len,再调gpu-memory-utilization找到不 OOM 的水位,然后小步调max-num-seqs,最后动max-num-batched-tokens。每次只改一个,跑三轮取中位数。swap-space不要当成后悔药,它能把部分 KV Cache 换到 CPU,但 PCIe 往返会让尾延迟很难看,线上服务尤其要谨慎。压测时还要看显存峰值是不是在请求结束后回落,如果每轮都抬高一点,说明块回收或请求清理有问题,应该回源码看块表释放路径,而不是继续加参数。

5. 常见问题与排查:五条血泪踩坑记录

5.1 安装与启动阶段:CUDA、显存、模型路径

第一条踩坑是 CUDA 版本对不上。现象是源码安装时编译扩展报错,或者启动时直接抛undefined symbol、CUDA driver 相关错误;原因通常是驱动、CUDA 运行时和 torch wheel 三者版本跨太多,或者机器里同时存在 conda 和 pip 两套 torch,导入时命中了错误的那一套。解决顺序是先看驱动,再看 torch 自检,最后清环境重装,不要在没有卸载旧 torch 的情况下反复pip install。

# 对齐驱动、运行时和 torch 的 CUDA 版本 nvidia-smi python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())" # 看启动时显存是否被别的进程占用 nvidia-smi --query-gpu=memory.used,memory.total --format=csv

这段检查的逻辑是先确认“卡可见、torch 可见、显存没被占”。nvidia-smi右上角显示的是驱动支持的 CUDA 版本,torch.version.cuda是 wheel 编译版本,torch.cuda.is_available()为False时不要继续装源码包。memory.used如果接近总量,先杀掉残留推理进程或换卡,不要怀疑模型加载逻辑。清环境时把虚拟环境删掉重建,比在旧环境里修依赖更省时间。

第二条踩坑是“显存明明够,启动却 OOM”。现象是模型权重加载完成后,初始化 KV Cache 时失败;原因常见于max-model-len按模型上限开满,gpu-memory-utilization又设得过高,框架按比例预留显存后没有给激活值留余量。解决是把max-model-len降到业务真实长度,把gpu-memory-utilization从 0.95 降到 0.85 到 0.88,再把max-num-seqs降一档。不要只看nvidia-smi的瞬时占用,KV Cache 是启动时按上限预留的,后面请求越多越容易顶到边界。

第三条踩坑是模型路径和模型名混淆。现象是服务启动成功,但/v1/models返回的名字和请求体里的model对不上,chat 请求报模型不存在;原因是--model指向本地目录,--served-model-name才是接口层名字,两者被当成同一个东西。解决是启动时显式写--served-model-name,客户端统一用这个名字,先调/v1/models确认再发 chat。模型目录里缺config.json或 tokenizer 文件时,也会表现为加载失败或输出乱码,这类问题要看启动日志第一处报错,不要被后面的堆栈带偏。

5.2 运行与性能阶段:并发、长 prompt、量化

第四条踩坑是并发一高就排队,但 GPU 利用率并不高。现象是客户端并发 32,吞吐上不去,平均延迟却翻倍;原因往往是max-num-seqs和max-num-batched-tokens互相卡住,或者大量长 prompt 的 prefill 把 decode 批次切碎。解决是先固定输入输出长度,从并发 1 到 8 逐档压测,看吞吐拐点;如果首 token 时间先炸,优先降max-num-batched-tokens或限制单请求长度;如果 decode 阶段抖动,检查max-num-seqs是否超过显存能稳住的量。不要用“再加一张卡”掩盖调度参数问题,单卡拐点没找到,多卡只是把问题放大。

第五条踩坑是量化模型加载失败或输出质量异常。现象是启动时报量化格式不支持,或者能加载但输出重复、乱码;原因是权重目录里的量化配置与启动参数不一致,比如权重是某种量化格式,启动时却用了另一种--quantization,或者根本没加量化参数让框架按默认精度加载。解决是先用非量化权重跑通整条链路,再确认权重目录里的量化配置和文档要求,启动时显式指定量化参数,不要靠猜。量化后的输出质量要用固定问题集对比,不能只看一句“能出字”。如果显存省下来了但吞吐没升,检查反量化开销是否落在关键路径上,必要时换更匹配的量化格式或回到非量化基线。

注意:排查时一次只改一个变量。同时改模型长度、并发上限和量化参数,日志里出现的新错误很难归因,最后只能全部回滚重来。

6. 进阶验证:用固定长度压测把参数从“能跑”推到“能扛”

6.1 三组固定负载与一张指标表

我一般不会只看一条平均延迟。固定 prompt 长度 512、输出 128,把并发从 1 拉到 16,记录首 token 时间、每输出 token 时间、吞吐和显存峰值;再换一组 2048 长 prompt 和一组 32 短输出,观察长请求对短请求的挤压。首 token 时间突然翻倍,多半是 prefill 排队;每输出 token 时间抖动,先看 decode 批次是否被长请求切碎;吞吐上不去但显存没满,检查max-num-seqs和max-num-batched-tokens是否互相卡住。下面这张表是我压测时必填的,填不满就说明这轮数据不可信。

指标采集方式判断标准
首 token 时间流式请求记录首个data到达时间长 prompt 下是否线性恶化
每输出 token 时间总生成时间除以输出 token 数并发升高后是否剧烈抖动
吞吐总输出 token 除以墙钟时间是否出现明显拐点
显存峰值压测中轮询nvidia-smi是否逼近预留上限
请求失败率统计非 200 响应和超时任何非零都要先查

压测脚本不要只跑一次。同一档并发跑三轮,取中位数,第一轮通常包含缓存初始化和图捕获,直接拿第一轮数据会低估性能。改完block-size或前缀缓存后,用同一组 prompt 复跑,差异超过几个百分点再决定是否保留。线上流量和压测流量差别很大,压测只负责找边界,不负责模拟全部业务;真实上线前还要用脱

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/24 4:16:51

JavaEE网上书店课程设计:三层架构、购物车与下单事务实战

简介:面向课程设计与毕业设计场景的JavaEE网上书店项目,提供完整源代码与MySQL建库脚本,适合需要完成Web课程作业、理解企业级应用分层与全栈流程的学生参考。压缩包共88个文件,约519KB,以40个JSP页面、11个Java源文件…

作者头像 李华
网站建设 2026/10/24 4:15:08

v0 入门:用对话和 Design Mode 做出一个作品集网页

适合人群:想快速做一个好看的网页界面(个人作品集、活动介绍页、小工具),但前端基础薄弱的同学。需要说明:v0 由 Vercel 推出,发布功能依托 Vercel;套餐与额度以官方定价页为准;界面…

作者头像 李华
网站建设 2026/10/24 4:13:53

AI 换脸带货:一百元一条的视频,五十万元的单品月销

一、凌晨三点的那条微博上周日凌晨三点,演员吴越发了一条微博。起因是朋友转来一段视频,问她是不是接了这个广告。画面里的「她」拎着一瓶精华液,说这个产品是自己找上门的,收到之后效果真的很直观,孕妇也能用。口型对…

作者头像 李华
网站建设 2026/10/24 4:13:48

防火墙命令

Linux:iptables(老版本,考试最爱考)、firewalld(centos7)Windows:netsh advfirewall(高级防火墙,cmd),还有 powershell 的 Set-NetFirewallRule1.…

作者头像 李华
网站建设 2026/10/24 4:12:59

选门窗别只看品牌知名度,得先看你住哪儿

选门窗别把主流品牌当标准答案,得先看你住哪儿。大宅看重综合性能,就盯派雅和罗兰西尼;临街怕吵,皇派和致尚主攻隔音气密;高层或沿海怕台风,新标和亿合的抗风压防坠落更对症。北方重保温看轩尼斯和飞宇的K值…

作者头像 李华