news 2026/4/30 12:22:43

RTP-LLM:实时音视频流与大语言模型融合架构与工程实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RTP-LLM:实时音视频流与大语言模型融合架构与工程实践

1. 项目概述:当大语言模型遇上实时音视频

最近在折腾一个挺有意思的项目,名字叫“rtp-llm”。光看这个名字,你可能觉得有点摸不着头脑,RTP(Real-time Transport Protocol)是实时传输协议,是音视频通话、直播的基石;而LLM(Large Language Model)大语言模型,则是当下AI领域最火热的明星。把这两个看似风马牛不相及的技术栈放在一起,到底能玩出什么花样?

简单来说,rtp-llm这个项目,其核心目标就是打通实时音视频流与大语言模型之间的壁垒。它不是一个独立的AI应用,而更像是一个“翻译官”或“适配器”。想象一下,你正在开一个线上会议,或者在看一场直播,会议或直播中的语音内容,能够被实时地转成文字,然后立刻送入像GPT这样的LLM中进行处理。处理的结果,可能是实时的会议纪要、关键点提炼、多语言翻译字幕,甚至是基于对话内容的智能问答和提醒。这个项目要解决的,就是如何高效、低延迟、稳定地将RTP承载的音频流,转化为LLM能够理解和处理的文本数据流。

这个想法背后,是音视频通信与AI能力融合的大趋势。传统的音视频应用,核心是“传输”和“呈现”,保证画面清晰、声音流畅、延迟够低。但随着AI,特别是LLM理解与生成能力的爆发,我们开始不满足于仅仅“听见”和“看见”,更希望系统能“听懂”和“看懂”,并给出智能反馈。无论是提升在线协作效率的智能会议助手,还是增强直播互动性的实时内容分析,亦或是为听障人士提供实时字幕服务,都需要这样一个桥梁。

所以,如果你是一名音视频开发工程师,正在思考如何为自己的产品注入AI能力;或者你是一名AI应用开发者,想处理实时语音流数据;又或者你单纯对这两个领域的交叉点感兴趣,那么这个项目的设计思路和实现细节,都值得你花时间深入了解。它涉及音视频编解码、网络传输、语音识别(ASR)、大模型API调用、流式处理等多个技术环节,是一个典型的系统集成与工程优化课题。

2. 核心架构与设计思路拆解

要理解rtp-llm,我们不能把它看成一个黑盒,而是需要拆解其内部的数据流转和模块设计。一个健壮的、可用于生产环境的系统,其架构设计必然要权衡性能、延迟、可靠性和扩展性。

2.1 核心数据流:从音频包到AI响应

整个系统的核心数据流可以概括为一条单向(或双向)的管道:

RTP音频流 -> 音频帧重组与解码 -> 语音识别(ASR)-> 文本流 -> LLM处理 -> 生成响应

  1. 输入侧(RTP Ingestion):系统需要作为一个RTP端点,接收来自音视频服务器(如SFU、MCU)或客户端推送的RTP包。这些包通常包含经过编码(如OPUS、PCMA/PCMU)的音频数据。这里第一个关键点就是流的识别与关联。一个会议房间可能有多个用户,每个用户对应一个SSRC(同步源标识符),系统需要能正确区分并处理多个并发的音频流。

  2. 音频处理层(Audio Processing):接收到的RTP包是网络层面的数据单元,可能乱序、丢包。因此需要Jitter Buffer来重新排序、缓冲,以消除网络抖动,输出连续的音频帧。然后,根据编码格式调用相应的解码器(如libopus),将压缩音频还原为原始的PCM(脉冲编码调制)数据,这是ASR引擎需要的输入格式。

  3. 语音转文本层(ASR Transcription):这是连接音视频和LLM的关键桥梁。我们需要一个支持流式识别的ASR服务。与传统的“录音-上传-识别”模式不同,流式ASR能够一边接收音频数据,一边实时输出中间和最终的识别结果,这对于低延迟场景至关重要。选择可以是云服务(如阿里云、腾讯云的实时语音识别API),也可以是本地部署的开源模型(如OpenAI Whisper的流式版本)。

  4. 文本流与LLM接口层(LLM Integration):ASR输出的文本流(可能带有时间戳、说话人标识)被送入LLM处理环节。这里的设计模式有多种:

    • 逐句处理:每当ASR输出一个完整的句子(以句号、问号等为界),就将其作为一个请求发送给LLM。优点是逻辑简单,缺点是有累积延迟。
    • 滑动窗口:维护一个最近N秒或N个词的文本窗口,定期或基于事件(如静音检测)将窗口内容发送给LLM。平衡了实时性和上下文连贯性。
    • 双工流式:与支持流式响应的LLM API(如OpenAI的GPT-4 Turbo with Vision的流式输出)结合,实现ASR文本流和LLM响应流的“双流并行”,体验最流畅,但实现复杂度最高。
  5. 输出与反馈(Output & Feedback):LLM生成的结果(摘要、翻译、回答等)需要被分发出去。方式可以是通过WebSocket推送给前端展示,写入会议纪要文件,或者甚至通过TTS(文本转语音)合成音频,再以RTP流的形式发回音视频房间,实现真正的AI交互。

2.2 技术选型背后的考量

为什么项目可能选择这样的技术栈?我们来分析一下:

  • 语言选择:项目仓库位于alibaba下,大概率使用GoJava。对于此类中间件系统,Go语言是绝佳选择。其高并发(Goroutine)、高性能网络处理、简洁的语法和优秀的部署特性,非常适合处理大量并发的RTP流和网络请求。如果追求极致的性能和控制力,C++也是一个选项,但开发效率会低一些。

  • ASR服务选择

    • 云服务:优点是开箱即用,准确率高,无需管理模型和算力。缺点是会产生持续费用,且依赖网络,可能涉及数据隐私考量。对于快速原型验证或对准确率要求高的商业应用,这是首选。
    • 本地模型:优点是数据不出私域,网络延迟为零(仅计算延迟),长期成本可能更低。缺点是需要强大的GPU算力,模型部署和优化有门槛,准确率可能略低于顶级云服务。Whisper模型是当前开源领域的佼佼者。
  • LLM集成方式

    • 直接API调用:对接国内外主流大模型平台(如OpenAI API、国内各大厂的模型平台)。最灵活,可以随时切换或使用最新模型,但同样有网络延迟、费用和合规性考虑。
    • 本地部署模型:使用量化后的开源LLM(如Qwen、Llama等系列)。数据完全私有,响应速度取决于本地GPU。适合对数据安全要求极高、且愿意投入硬件和优化成本的场景。

设计心得:在架构设计初期,一个重要的决策点是同步 vs 异步。音频接收、ASR、LLM调用、结果输出,这些环节应该设计成一条同步阻塞的流水线,还是通过消息队列(如Kafka、Pulsar)或Channel进行异步解耦?对于延迟极度敏感的场景(如实时字幕),同步流水线可能更简单直接;但对于需要复杂聚合、分析(如生成整场会议摘要)的场景,异步架构能提供更好的弹性和可扩展性。rtp-llm很可能采用了一种混合模式:核心的实时通路(音频->ASR->LLM->输出)保持低延迟的同步/准同步处理;而旁路的数据(如完整的录音、识别文稿)则异步存入数据库或对象存储,供后续深度分析使用。

3. 核心模块深度解析与实操要点

理解了宏观架构,我们深入到几个核心模块,看看在实现时会遇到哪些“坑”,以及如何规避。

3.1 RTP流的接收与处理:不仅仅是收包

很多人认为接收RTP就是开个UDP端口收包那么简单,实则不然。一个生产级的RTP处理器必须考虑以下问题:

1. 多流管理与SSRC冲突: 在一个会议中,每个参与者对应一个音频流,由唯一的SSRC标识。但SSRC并非绝对可靠,客户端可能重启、重连,导致SSRC改变或冲突。系统需要维护一个SSRC -> 用户/会话的映射表,并能处理SSRC变更通知(通过RTCP的SDES或BYE包)。更健壮的做法是结合业务层的用户ID,而不是完全依赖SSRC。

2. Jitter Buffer的动态调整: Jitter Buffer的大小直接影响到延迟和抗抖动能力。固定大小的缓冲区难以适应多变的网络环境。一个基本的优化是自适应抖动缓冲:根据连续包到达时间的方差(抖动)动态调整缓冲区深度。网络差时增大缓冲以防卡顿,网络好时减小缓冲以降低延迟。WebRTC中的NetEQ算法就是这方面的典范,可以参考其思想。

3. 丢包隐藏与网络适应性: RTP over UDP不保证可靠传输,丢包是常态。对于音频,我们需要PLC(Packet Loss Concealment)技术。对于OPUS这类编码,其本身具备一定的抗丢包能力,但严重的丢包仍需处理。简单的策略包括前向重复(重复最后一个包)、插值等。此外,系统应能生成和接收RTCP RR(接收者报告)和SR(发送者报告),以监控网络质量,为上层应用提供QoS数据。

实操代码片段示意(Go语言风格)

// 简化的RTP处理器结构 type RTPStreamProcessor struct { ssrc uint32 jitterBuffer *AdaptiveJitterBuffer decoder *opus.Decoder lastSeq uint16 // 用于检测丢包和乱序 // ... 其他状态 } func (p *RTPStreamProcessor) ProcessPacket(pkt *rtp.Packet) error { // 1. 检查序列号连续性,更新抖动计算 if !p.isSequenceContinuous(pkt.SequenceNumber) { p.handlePacketLoss(pkt.SequenceNumber) } p.updateJitter(pkt.Timestamp) // 2. 将包放入自适应抖动缓冲区 p.jitterBuffer.Push(pkt) // 3. 从缓冲区取出按序、去抖动的音频帧 for frame := p.jitterBuffer.Pop(); frame != nil; frame = p.jitterBuffer.Pop() { // 4. 解码(例如OPUS -> PCM) pcmData, err := p.decoder.Decode(frame.Payload, frame.PayloadSize) if err != nil { // 处理解码错误,可能是丢包导致的 pcmData = p.concealLoss(frame) } // 5. 将PCM数据送入下游ASR管道 p.asrPipe.Write(pcmData) } return nil }

3.2 流式ASR集成:低延迟的关键

将ASR无缝接入是项目的核心。无论是调用云端API还是本地模型,都要关注流式接口。

云端API集成要点

  1. 连接管理:保持长连接(如WebSocket)比每次发起HTTP请求更高效。需要处理连接重连、鉴权刷新。
  2. 数据发送策略:不要等到攒够一大段音频再发送。通常以100ms-500ms的音频帧为一个数据块(chunk)持续发送。太频繁会增加开销,太慢会增加延迟。
  3. 中间结果处理:流式ASR会返回is_final=false的中间结果。这些结果虽然可能不准确,但对于实时字幕展示至关重要,可以立即显示并随着识别修正而更新,给用户“实时”的感觉。
  4. 静音检测(VAD):在客户端或服务端集成VAD,可以在检测到静音时显式发送一个标记,促使ASR服务输出该段语音的最终识别结果,而不是一直等待超时。

本地Whisper模型流式处理: Whisper本身是端到端模型,其流式处理需要一些技巧。一种常见方法是使用一个滑动音频窗口,例如,每1秒音频,重叠0.5秒,连续进行识别。虽然会有重复识别,但能模拟流式效果。也有社区项目对Whisper进行改造,使其支持真正的流式编码和解码。

避坑指南:ASR的准确率受背景噪音、口音、领域专有名词影响极大。在集成时,务必提供自定义热词功能。将你业务场景中的高频词、专业术语、产品名、人名等以权重列表的形式提供给ASR引擎,能极大提升关键信息的识别准确率。这是提升实用性的一个小投入大回报的优化点。

3.3 与LLM的交互模式设计

如何把一段段实时产生的文本“喂”给LLM,并得到有意义的输出,这里模式的选择决定了系统的能力和体验。

模式一:实时问答(Q&A)

  • 场景:会议中,AI助手实时回答参与者提出的问题。
  • 设计:需要先进行问题检测。可以通过简单的规则(如文本包含“?”)或一个轻量级文本分类模型来判断当前句子是否是问题。如果是,则将问题文本(可附带最近几轮对话作为上下文)发送给LLM获取答案。答案可以通过TTS读出来,或显示在字幕区。
  • 挑战:如何区分向AI的提问和与会者之间的讨论?可能需要一个明确的触发词(如“嘿,小助手”)或按钮。

模式二:实时摘要与要点提取

  • 场景:自动生成会议讨论要点,实时刷新显示。
  • 设计:采用“滑动窗口”法。维护一个最近3-5分钟对话文本的窗口。每隔30秒或窗口内容变化较大时,将窗口内容发送给LLM,提示词为:“请用简短的三句话总结刚才讨论的核心内容。”然后将结果更新到摘要面板。
  • 挑战:LLM的调用有延迟和token限制。需要精心设计窗口大小和触发频率,避免频繁调用导致成本激增和响应堆积。

模式三:实时翻译字幕

  • 场景:将中文会议实时翻译成英文字幕。
  • 设计:这可以串联两个模型。ASR输出中文文本流,每个完整的句子(或段落)立即送入翻译专用LLM(或专门的翻译API),然后将翻译结果实时显示。更复杂的,可以尝试端到端的语音到外语文本模型,但定制性较差。

LLM提示词工程: 与LLM交互的质量,很大程度上取决于提示词。对于实时场景,提示词需要:

  • 明确角色和任务:“你是一个会议助理,负责实时总结技术讨论。”
  • 严格限制输出格式:“用不超过20个词的一句话输出。”“以要点列表形式输出,最多3条。”
  • 处理不完整信息:“以下是一段正在进行的对话片段,可能不完整,请基于此给出当前的最佳总结。”
  • 管理上下文:在每次请求中携带必要的历史信息,但要注意token数限制。

4. 性能优化与稳定性保障实战

当系统需要处理成百上千个并发音频流时,性能与稳定性就成为生命线。这部分分享一些从实践中得来的优化经验。

4.1 资源管理与并发控制

1. 连接与协程池: 为每个音频流都创建一个独立的goroutine(或线程)处理所有流程(收包、解码、ASR、LLM)是简单但危险的做法。流数量一多,goroutine爆炸,上下文切换开销巨大。更优的设计是流水线化池化

  • I/O密集型阶段池:RTP收包、网络请求(ASR/LLM API调用)使用单独的goroutine池。
  • CPU密集型阶段池:音频解码、可能的本地VAD/ASR推理,使用受控数量的worker goroutine。
  • 各阶段之间通过带缓冲的Channel通信,实现生产-消费者模型,避免阻塞。

2. 内存复用与对象池: 频繁创建和销毁RTP包、PCM音频缓冲区、JSON请求体等对象会带来严重的GC(垃圾回收)压力。对于高频创建的小对象,使用sync.Pool进行复用是Go中的标准优化手段。

var rtpPacketPool = sync.Pool{ New: func() interface{} { return &rtp.Packet{} }, } func getRTPPacket() *rtp.Packet { return rtpPacketPool.Get().(*rtp.Packet) } func putRTPPacket(pkt *rtp.Packet) { // 重置包内部状态 pkt.Header = rtp.Header{} pkt.Payload = pkt.Payload[:0] rtpPacketPool.Put(pkt) }

3. 背压(Backpressure)传递: 如果下游的ASR或LLM服务处理速度慢,上游必须能感知并减速,否则数据会在内存中无限堆积,导致OOM(内存溢出)。在Channel通信模型中,可以使用带固定容量的缓冲Channel。当Channel满时,上游的发送操作会阻塞,自然形成背压。更复杂的系统可能需要更显式的流控信号。

4.2 延迟监控与调优

实时系统的延迟是核心指标。我们需要在全链路埋点监测。

延迟分解

  • T1 网络传输延迟:从说话者发出RTP包到我们服务器收到。这通常不可控。
  • T2 抖动缓冲延迟:音频包在Jitter Buffer中等待的时间。取决于网络抖动和缓冲区大小。
  • T3 处理延迟:解码、VAD等本地计算耗时。通常很短。
  • T4 ASR识别延迟:从发送音频到收到第一个中间结果和最终结果的时间。这是大头。
  • T5 LLM处理延迟:从发送文本到收到LLM回复的时间。另一个大头。
  • T6 输出延迟:结果渲染或推送的时间。

优化方向

  • 降低T2:优化自适应抖动缓冲算法,在可接受的丢包率下尽可能减小缓冲深度。
  • 降低T4
    • 选择低延迟的ASR引擎或模型。
    • 优化音频块(chunk)大小,太小增加开销,太大增加首字延迟。通常200-400ms是个平衡点。
    • 启用并积极使用VAD,在静音处立即获取最终结果,而不是等待超时。
  • 降低T5
    • 对于摘要类任务,不要过于频繁地调用LLM,合理设置滑动窗口和触发间隔。
    • 使用LLM的流式响应接口,让用户能边生成边看到部分结果,感知延迟降低。
    • 精简提示词,减少不必要的上下文,节约token和计算时间。
  • 全链路监控:在每个关键环节打上时间戳,通过分布式追踪(如OpenTelemetry)可视化整个链路,才能精准定位延迟瓶颈。

4.3 错误处理与降级策略

系统不可能永远完美运行,网络会波动,外部服务会超时。设计时必须考虑降级。

  • ASR服务降级:当云端ASR连续超时或失败,是否可以切换到本地的轻量级ASR模型(虽然准确率低)?或者直接放弃转写,只透传音频?
  • LLM服务降级:当LLM不可用时,实时摘要功能可以自动关闭,但基本的转写字幕是否还能保留?或者可以提供一个缓存的历史摘要?
  • 优雅降级提示:在UI上明确告知用户当前哪些AI功能因网络问题暂时不可用,而不是默默失败。
  • 重试与熔断:对于外部API调用,必须实现有退避策略的智能重试(如指数退避)和熔断器机制(如Hystrix模式)。当失败率达到阈值,熔断器打开,短时间内直接拒绝请求,给下游服务恢复时间,避免雪崩。

5. 部署与实践中的常见问题排查

即使设计和代码都看似完美,在真实部署中依然会碰到各种光怪陆离的问题。这里记录几个典型场景和排查思路。

5.1 音频质量问题导致ASR准确率低

现象:转写出来的文本错漏百出,与预期相差甚远。排查步骤

  1. 检查源头音频:首先确认发送端的音频质量。是否麦克风太差?环境噪音是否过大?可以在发送端录制一段原始音频进行回放评估。
  2. 检查编码与解码:确认RTP使用的音频编码格式(如OPUS)和参数(比特率、采样率)。在服务器端,将解码后的PCM数据保存为WAV文件,用音频播放软件收听。如果声音失真、卡顿或充满噪音,问题出在传输或解码环节。
  3. 检查网络丢包:通过RTCP RR报告或服务器统计,查看音频流的丢包率。超过5%的丢包率就会对OPUS解码质量产生明显影响,进而影响ASR。需要优化网络或调整抗丢包策略。
  4. ASR引擎诊断:如果音频本身听起来清晰,但ASR结果差,可能是ASR引擎不适应领域。尝试提供热词列表,或选择针对特定场景(如金融、医疗)优化过的ASR模型。

5.2 高并发下的内存泄漏与CPU飙升

现象:系统运行一段时间后,内存占用持续增长,或CPU使用率异常高,然后服务崩溃或响应变慢。排查工具:Go语言有强大的pprof工具。

  • go tool pprof http://localhost:6060/debug/pprof/heap分析内存分配。
  • go tool pprof http://localhost:6060/debug/pprof/profile分析CPU耗时。常见原因
  1. Channel阻塞导致goroutine泄漏:某个goroutine因为Channel阻塞而无法退出,并且持续持有对象引用。检查所有goroutine的退出条件,确保在流结束或出错时能正确清理。
  2. 缓存或池未正确释放:对象池中的对象被取出使用后,没有重置内部状态(如切片)就直接放回,导致下次取出时携带了旧数据,或切片底层数组不断变大,引起内存“假释放”。
  3. 外部调用阻塞:同步调用外部ASR/LLM API时,如果对方响应慢且没有设置合理的超时,会导致大量goroutine被挂起等待。务必为所有网络操作设置超时
  4. 循环引用:虽然Go有GC,但代码中的循环引用(如两个结构体互相持有指针)会阻止对象被回收。使用go vet或相关静态分析工具辅助检查。

5.3 流状态同步与残留问题

现象:用户已经离开会议,但系统仍在处理该用户的音频流,或者资源没有释放。解决方案

  1. 基于RTCP BYE包:RTP规范中,发送方离开时应发送RTCP BYE包。服务器端必须监听并处理此包,触发对应流的清理流程。
  2. 基于业务信令:更可靠的方式是依赖上层业务信令(如WebSocket通知“用户离开”)。当收到信令时,主动查找并清理该用户对应的所有RTP流处理器。
  3. 心跳与超时:为每个流维护一个最后活动时间戳。定期扫描所有流,如果某个流超过一定时间(如60秒)没有收到任何RTP或RTCP包,则判定为失效,进行强制清理。这是防止残留的最后一道防线。
  4. 资源清理清单:在清理一个流时,必须按顺序:停止收包goroutine -> 清空jitter buffer -> 关闭ASR连接 -> 取消可能的LLM请求 -> 释放解码器 -> 将对象放回池中。遗漏任何一步都可能导致泄漏。

5.4 时间戳与同步难题

现象:生成的字幕与语音不同步,或者时间戳错乱。根源:RTP时间戳的时钟频率(clock rate)与音频采样率相关。例如,OPUS通常以48000Hz采样,其RTP时间戳增量也是每采样一次加1(但实际打包间隔可能不是按采样)。而ASR结果和最终展示,可能需要的是基于真实时间的毫秒级时间戳。处理方案

  1. 记录基准时间:在收到流的第一个RTP包时,记录该包的RTP时间戳(ts)和服务器接收到它的系统时间(wall_clock)。
  2. 计算映射:对于后续任何一个RTP时间戳current_ts,其对应的预估真实时间可以通过线性映射计算:estimated_time = wall_clock + (current_ts - first_ts) / clock_rate。这里假设时钟是线性增长的,对于音频流基本成立。
  3. 传递时间戳:将计算出的预估时间戳随着PCM数据一起传递给ASR引擎。一些ASR API支持携带音频开始时间(start_time)参数,这样返回的识别结果片段就会自带准确的时间偏移量。
  4. 处理时钟漂移:如果发送端和接收端时钟有微小偏差,长时间运行后仍可能不同步。可以通过定期(如每收到一个RTCP SR发送者报告)对比发送端NTP时间和本地时间,计算时钟偏移并进行微调。对于要求极高的场景,这是一个深水区。

构建一个稳定、高效、低延迟的rtp-llm系统,就像在音视频和AI两座大山之间架设一座悬索桥。每一处细节——从网络包的处理、内存的管理,到外部服务的集成、异常情况的应对——都关乎整座桥梁的稳固。这个过程充满挑战,但当你看到实时语音经由你的系统,转化为精准的文字,再被大模型赋予理解和智能,最终提升线上协作与沟通的体验时,所有的调试和优化都是值得的。这不仅仅是技术的拼接,更是对实时系统设计哲学的深入实践。

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

Applera1n:iOS 15-16.6激活锁离线绕过技术深度解析

Applera1n:iOS 15-16.6激活锁离线绕过技术深度解析 【免费下载链接】applera1n icloud bypass for ios 15-16 项目地址: https://gitcode.com/gh_mirrors/ap/applera1n 当iPhone设备因遗忘Apple ID密码或二手设备交接问题而陷入激活锁限制时,传统…

作者头像 李华
网站建设 2026/4/30 12:17:30

如何彻底解决Zotero中文文献管理难题:茉莉花插件终极指南

如何彻底解决Zotero中文文献管理难题:茉莉花插件终极指南 【免费下载链接】jasminum A Zotero add-on to retrive CNKI meta data. 一个简单的Zotero 插件,用于识别中文元数据 项目地址: https://gitcode.com/gh_mirrors/ja/jasminum 如果你正在使…

作者头像 李华
网站建设 2026/4/30 12:11:25

标量量化(Scalar Quantization, SQ)压缩算法

标量量化(Scalar Quantization, SQ)压缩算法 概述 标量量化(Scalar Quantization, SQ)是一种基础的量化技术,它将连续的浮点数值映射到离散的有限集合中。与二进制量化不同,标量量化保留了更多的数值信息&a…

作者头像 李华
网站建设 2026/4/30 12:07:26

3分钟极速配置:Fast-GitHub浏览器扩展实战手册

3分钟极速配置:Fast-GitHub浏览器扩展实战手册 【免费下载链接】Fast-GitHub 国内Github下载很慢,用上了这个插件后,下载速度嗖嗖嗖的~! 项目地址: https://gitcode.com/gh_mirrors/fa/Fast-GitHub 还在为GitHub的蜗牛速度…

作者头像 李华