基于大模型 + 知识库的 Code Review 实践

news2024/9/20 18:35:04

背景

💡 想法源于在一次 Code Review 时,向 Claude 询问哪种写法代码更优雅得来。当时就想能不能让 AI 帮我们辅助做 Code Review?

图片

痛点

  • 信息安全合规问题:公司内代码直接调 ChatGPT / Claude 会有安全/合规问题,为了使用 ChatGPT / Claude 需要对代码脱敏,只提供抽象逻辑,这往往更花时间。

    • 三星引入 ChatGPT 不到 20 天,被曝发生 3 次芯片机密泄露[1]
  • 低质量代码耗费时间:达人业务每天至少 10~20 个 MR 需要 CR,虽然提交时 MR 经过 单测 + Lint 过滤了一些低级错误,但还有些问题(代码合理性、经验、MR 相关业务逻辑等)需要花费大量时间,最后可以先经过自动化 CR,再进行人工 CR,可大大提升 CR 效率!

  • 团队 Code Review 规范缺少执行:大部分团队的 Code Review 停留在文档纸面上,成员之间口口相传,并没有一个工具根据规范来严格执行。

介绍

一句话介绍就是:基于开源大模型 + 知识库的 Code Review 实践,类似一个代码评审助手(CR Copilot)。

图片

特性

符合公司安全规范,所有代码数据不出内网,所有推理过程均在内网完成。

  • 🌈 开箱即用:基于 Gitlab CI,仅 10 几行配置完成接入,即可对 MR 进行 CR。
  • 🔒 数据安全:基于开源大模型做私有化部署,隔离外网访问,确保代码 CR 过程仅在内网环境下完成。
  • 无调用次数限制:部署在内部平台,只有 GPU 租用成本。
  • 📚 自定义知识库:CR 助手基于提供的飞书文档进行学习,将匹配部分作为上下文,结合代码变更进行 CR,这将大大提升 CR 的准确度,也更符合团队自身的 CR 规范。
  • 🎯 评论到变更行:CR 助手将结果评论到变更代码行上,通过 Gitlab CI 通知,更及时获取 CR 助手给出的评论。

名词解释

名词释义
CR / Code Review越来越多的企业都要求研发团队在代码的开发过程中要进行 CodeReview(简称 CR),在保障代码质量的同时,促进团队成员之间的交流,提高代码水平。
llm / 大规模语言模型大规模语言模型(Large Language Models,LLMs)是自然语言处理中使用大量文本数据训练的神经网络模型,可以生成高质量的文本并理解语言。如 GPT、BERT 等。
AIGC利用 NLP、NLG、计算机视觉、语音技术等生成文字、图像、视频等内容。全称是人工智能生成/创作内容(Artificial Intelligence Generated Content);是继 UGC,PGC 后,利用人工智能技术,自动生成内容的生产方式;AIGC 底层技术的发展,驱动围绕不同内容类型(模态)和垂直领域的应用加速涌现。
LLaMAMeta(Facebook)的大型多模态语言模型。
ChatGLMChatGLM 是一个开源的、支持中英双语的对话语言模型,底座是 GLM 语言模型。
BaichuanBaichuan 2 是百川智能推出的新一代开源大语言模型,采用 2.6 万亿 Tokens 的高质量语料训练。
Prompt一段文本或语句,用于指导机器学习模型生成特定类型、主题或格式的输出。在自然语言处理领域中,Prompt 通常由一个问题或任务描述组成,例如“给我写一篇有关人工智能的文章”、“翻译这个英文句子到法语”等等。在图像识别领域中,Prompt 则可以是一个图片描述、标签或分类信息。
langchainLangChain 是一个开源 Python 库,由 Harrison Chase 开发,旨在支持使用大型语言模型(LLM)和外部资源(如数据源或语言处理系统)开发应用程序。它提供了标准的接口,与其他工具集成,并为常见应用程序提供端到端链 。
embedding将任意文本映射到固定维度的向量空间中,相似语义的文本,其向量在空间中的位置会比较接近。在 LLM 应用中常用于相似性的文本搜索。
向量数据库 (Vector stores)存储向量表示的数据库,用于相似性搜索。如 Milvus、Pinecone 等。
Similarity Search在向量数据库中搜索离查询向量最近的向量,用于检索相似项。
知识库存储结构化知识的数据库,LLM 可以利用这些知识增强自己的理解能力。
In-context LearningIn-Context Learning 是机器学习领域的一个概念,指不调整模型自身参数,而是在 Prompt 上下文中包含特定问题相关的信息,就可以赋予模型解决新问题能力的一种方式。
Finetune / 微调在预训练模型基础上使用特定数据集进行微调,提升模型在某任务上的性能。

实现思路

流程图

图片

系统架构

完成一次 CR 流程,需要用到如下技术模块:

图片

LLMs / 开源大模型选型

CR Copilot 功能的核心在于大语言模型基座,基于不同大模型基座生成的 CR 质量也不尽相同。对于 CR 这个场景,我们需要选型的模型满足以下几个条件:

  • 理解代码
  • 对中文支持好
  • 较强的上下文学习能力

图片FlagEval 8 月大模型评测榜单(https://flageval.baai.ac.cn/#/trending

模型后面的 -{n}bn*10 亿参数量,比如 13b 就是 130 亿参数,个人试用下来参数量的多少并不能决定效果怎样,根据实际情况来判断。

起初在众多大模型中选择『Llama2-Chinese-13b-Chat[2]』和『chatglm2-6b[3]』、『Baichuan2-13B-Chat[4]』,通过一段时间模型赛马 🐎,主观上感觉 Llama2 会更适用于 CR 场景,而 ChatGLM2 更像是文科生,对代码评审没有太多建设性建议,但在中文 AIGC 上会比较有优势!

图片

两个模型执行过程中的记录

因大模型合规问题,CR Copilot 会默认使用 ChatGLM2-6B,如有使用 Llama2 模型需求需要向 Meta 申请[5],通过后可使用。

图片Llama 2 要求企业的月活用户数不超过 7 亿

目前支持的模型可选,仅供参考的评分[6]如下:

  • chatglm2-6b[7](默认)
  • Llama2-Chinese-13b-Chat[8](推荐)
  • Baichuan2-13B-Chat[9]

图片

知识库设计

为什么需要知识库?

大模型基座只包含互联网上的公开数据,对公司内部的框架知识和使用文档并不了解。

举个例子 🌰:公司内有个框架叫 Lynx,让大模型从内部文档中知道『什么是 Lynx?』、『怎么写 Lynx?

图片一图胜千言

这里的『强化模式』会使用向量数据库,并将匹配的知识库片段和问题『什么是 Lynx?』生成 Prompt,发送到 LLM 执行。

怎样找到相关度高的知识?

有了知识库后,怎样将我们『搜索的问题/代码』在『知识库』中找到『相关度最高的内容』?

答案是通过三个过程:

  1. Text Embeddings(文本向量化)
  2. Vector Stores(向量存储)
  3. Similarity Search(相似性搜索)

图片文本相似度匹配流程图,图源 Langchain-Chatchat

Text Embeddings(文本向量化)

不同于传统数据库的模糊搜索/匹配关键字,我们需要进行语义/特征匹配。

例如:你搜索『猫』,只能得到带 『猫』 关键字匹配的结果,没办法得到 『布偶』、『蓝白』 等结果,传统数据库认为『布偶』是『布偶』、『猫』是『猫』。要实现关联语义搜索,是通过人工打特征标签,这个过程也被称为特征工程(Feature Engineering)。

图片

如何才能将文本自动化的方式来提取这些特征?这就要通过 Vector Embedding 向量化实现,目前社区通过 OpenAI 提供的 text-embedding-ada-002 模型生成,这会引起两个问题:

  1. 数据安全问题:需要调用 OpenAI 的 API 才能做向量化
    图片
  2. 收费:大概 3000 页/美元图片

我们使用了国产文本相似度计算模型 bge-large-zh[10],并私有化部署公司内网,一次 embedding 向量化耗时基本在毫秒级。

图片

Vector Stores(向量存储)

提前将官方文档进行 Vector Embeddings,然后存储在向量数据库里,我们这里选择的向量数据库是 Qdrant,主要考虑到是用 Rust 写的,存储和查询也许会快一些!这里引用一个向量数据库选型的几个维度选择:

向量数据库URLGitHub StarLanguageCloud
chromahttps://github.com/chroma-core/chroma8.5KPython
milvushttps://github.com/milvus-io/milvus22.8KGo/Python/C++
pineconehttps://www.pinecone.io/
qdranthttps://github.com/qdrant/qdrant12.7KRust
typesensehttps://github.com/typesense/typesense14.4KC++
weaviatehttps://github.com/weaviate/weaviate7.4KGo

数据截止到 2023 年 9 月 10 号

Similarity Search(相似性搜索)

原理是通过比较向量之间的距离来判断它们的相似度

图片

那么有了『query 问题的向量』和『数据库里录入的知识库向量』后,这可以直接使用向量数据库提供的 Similarity Search 方式匹配相关内容。

图片

加载知识库

CR Copilot 知识库分为『内置官方文档知识库』、『自定义知识库』,query 输入是先用完整代码截取前半段 + LLM 生成 summary 总结,然后和知识库做相似上下文,匹配流程如下:

图片

截取完整代码前半段作为 query 输入,是因为大部分语言前半段都声明了 modules、packages,通过这种方式提高知识库相似匹配度。

官方文档-知识库(内置)

避免大家将官方文档重复录入、embedding,CR Copilot 内置了官方文档,目录包含:

内容数据源
React 官方文档https://react.dev/learn
TypeScript 官方文档https://www.typescriptlang.org/docs/
Rspack 官方文档https://www.rspack.dev/zh/guide/introduction.html
Garfishhttps://github.com/web-infra-dev/garfish
公司内 Go / Python / Rust 等编程规范

并通过一个简单的 CURD 来管理内置知识库

图片

自定义知识库-飞书文档(自定义)

飞书文档没有格式要求,能看懂正确代码是怎样就行

这里直接使用 LangChain 提供的 LarkSuite[11] 文档加载类,对有权限的飞书文档进行获取,使用 CharacterTextSplitter / RecursiveCharacterTextSplitter 将文本分割成固定长度的块(chunks),方法有两个主要参数:

  • chunk_size: 控制每个块的长度。例如设置为 1024,则每个块包含 1024 个字符。
  • chunk_overlap: 控制相邻两个块之间的重叠长度。例如设置为 128,则每个块会与相邻块重叠 128 个字符。

图片

Prompt 指令设计

因为大模型有足够多的数据,我们想让大模型按要求执行就需要用到『Prompt 提示词』。

图片(图源 Stephen Wolfram)

代码 summary 总结指令

让 LLM 通过文件代码分析当前代码涉及的知识点,用于后续知识库相似度匹配:

prefix = "user: " if model == "chatglm2" else "<s>Human: "
suffix = "assistant(用中文): let's think step by step." if model == "chatglm2" else "\n</s><s>Assistant(用中文): let's think step by step."

return f"""{prefix}根据这段 {language} 代码,列出关于这段 {language} 代码用到的工具库、模块包。
{language} 代码:
```{language}
{source_code}
、、、
请注意:
- 知识列表中的每一项都不要有类似或者重复的内容
- 列出的内容要和代码密切相关- 最少列出 3 个, 最多不要超过 6 个
- 知识列表中的每一项要具体
- 列出列表,不要对工具库、模块做解释
- 输出中文
{suffix}"""

其中:

  • language:当前文件的代码语言(TypeScript、Python、Rust、Golang 等)
  • source_code:是当前变更文件的完整代码

CR 指令

如果使用的模型(如 LLaMA 2)对中文 Prompt 支持较差,需要在设计 Prompt 时采用『输入英文』『输出中文』的方式,即:

# llama2f"""Human: please briefly review the {language}code changes by learning the provided context to do a brief code review feedback and suggestions. if any bug risk and improvement suggestion are welcome(no more than six)
<context>
{context}
</context>

<code_changes>
{diff_code}
</code_changes>\n</s><s>Assistant: """

# chatglm2
f"""user: 【指令】请根据所提供的上下文信息来简要审查{language} 变更代码,进行简短的代码审查和建议,变更代码有任何 bug 缺陷和改进建议请指出(不超过 6 条)。
【已知信息】:{context}

【变更代码】:{diff_code}

assistant: """

其中:

  • language:当前文件的代码语言(TypeScript、Python、Rust、Golang 等)
  • context:根据知识库返回的上下文信息
  • diff_code:是变更的代码(不使用完整代码主要是考虑 LLM max_tokens 最大限制)

评论到变更代码行

为了能计算出变更代码行,写了一个函数,通过解析 diff 来输出变更的行数:

import re

def parse_diff(input):
    if not input:    
        return []    
    if not isinstance(input, str) or re.match(r"^\s+$", input):     
        return []    
        
    lines = input.split("\n")    
    if not lines:    
        return []   
     
    result = []    
    current_file = None    
    current_chunk = None    
    deleted_line_counter = 0    
    added_line_counter = 0    
    current_file_changes = None    
    
    def normal(line):    
        nonlocal deleted_line_counter, added_line_counter        
        current_chunk["changes"].append({        
            "type": "normal",            
            "normal": True,            
            "ln1": deleted_line_counter,            
            "ln2": added_line_counter,            
            "content": line        
        })        
        deleted_line_counter += 1        
        added_line_counter += 1        
        current_file_changes["old_lines"] -= 1        
        current_file_changes["new_lines"] -= 1    
        
    def start(line):        
        nonlocal current_file, result        
        current_file = {        
            "chunks": [],            
            "deletions": 0,            
            "additions": 0        
        }        
        result.append(current_file)    
        
    def to_num_of_lines(number):    
        return int(number) if number else 1   
         
    def chunk(line, match):     
        nonlocal current_file, current_chunk, deleted_line_counter, added_line_counter, current_file_changes       
        if not current_file:       
            start(line)        
        old_start, old_num_lines, new_start, new_num_lines = match.group(1), match.group(2), match.group(         
            3), match.group(4)        
            
        deleted_line_counter = int(old_start)        
        added_line_counter = int(new_start)        
        current_chunk = {        
            "content": line,            
            "changes": [],           
            "old_start": int(old_start),            
            "old_lines": to_num_of_lines(old_num_lines),            
            "new_start": int(new_start),            
            "new_lines": to_num_of_lines(new_num_lines),        
        }        
        current_file_changes = {      
            "old_lines": to_num_of_lines(old_num_lines),            
            "new_lines": to_num_of_lines(new_num_lines),       
        }        
        current_file["chunks"].append(current_chunk)    
        
    def delete(line):     
        nonlocal deleted_line_counter        
        if not current_chunk:       
            return        
            
        current_chunk["changes"].append({      
            "type": "del",            
            "del": True,            
            "ln": deleted_line_counter,            
            "content": line       
        })        
        deleted_line_counter += 1        
        current_file["deletions"] += 1        
        current_file_changes["old_lines"] -= 1    
        
    def add(line):    
        nonlocal added_line_counter        
        if not current_chunk:       
            return        
        current_chunk["changes"].append({        
            "type": "add",            
            "add": True,            
            "ln": added_line_counter,            
            "content": line        
        })        
        added_line_counter += 1        
        current_file["additions"] += 1        
        current_file_changes["new_lines"] -= 1    
        
    def eof(line):     
        if not current_chunk:       
            return        
        most_recent_change = current_chunk["changes"][-1]        
        current_chunk["changes"].append({        
            "type": most_recent_change["type"],            
            most_recent_change["type"]: True,            
            "ln1": most_recent_change["ln1"],            
            "ln2": most_recent_change["ln2"],            
            "ln": most_recent_change["ln"],            
            "content": line        
        }) 
           
    header_patterns = [    
        (re.compile(r"^@@\s+-(\d+),?(\d+)?\s++(\d+),?(\d+)?\s@@"), chunk)    
    ]    
    
    content_patterns = [     
        (re.compile(r"^\ No newline at end of file$"), eof),        
        (re.compile(r"^-"), delete),        
        (re.compile(r"^+"), add),        
        (re.compile(r"^\s+"), normal)    
    ]    
    
    def parse_content_line(line):   
        nonlocal current_file_changes        
        for pattern, handler in content_patterns:          
            match = re.search(pattern, line)           
            if match:            
                handler(line)                
                break       
        if current_file_changes["old_lines"] == 0 and current_file_changes["new_lines"] == 0:        
            current_file_changes = None    
            
    def parse_header_line(line):     
        for pattern, handler in header_patterns:       
            match = re.search(pattern, line)           
            if match:           
                handler(line, match)                
                break    
                
    def parse_line(line):    
        if current_file_changes:        
            parse_content_line(line)        
        else:      
            parse_header_line(line)    
            
    for line in lines:    
        parse_line(line)   
        
    return result

图片

这里机器人账号调用 Gitlab API 进行的评论,会默认被 Resolved,这样可以避免 CR Copilot 评论过多造成每个评论要手动点下 Resolved

一点感想

  • 一切皆概率:基于 LLM 的应用最大特点在于『输出不确定性』,在候选词中选概率最高的进行输出,即使像 1+1=? 这样看起来有确定性输出的,LLM 也是基于概率给出的!

  • 开源 LLMs + 领域知识库 + 私有化部署是企业级应用的一种实践方式:

    • 这里 LLMs 指多个大模型组合使用;大模型再强大也必须结合内部的知识库才能发挥作用;
    • 私有化部署好处是打消各行各业对数据安全的担忧!
    • 大模型在 Chat 聊天的产品形态更多是秀肌肉 💪,让各行各业能被触达到;最终的产品形态需要具体场景具体分析!
  • AI+ 刚刚开始:CR Copilot 只是达人 LLMs + 研发工程化其中一个应用场景,还有一些应用/工具等达人团队打磨好后再和大家一起分享!

如何学习AI大模型?

作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

一、全套AGI大模型学习路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

img

二、640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

img

三、AI大模型经典PDF籍

随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。

img

四、AI大模型商业化落地方案

img

作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2075550.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HR招聘,如何解决面试流程繁琐的问题

要解决面试流程繁琐的问题&#xff0c;就必须要精简和优化招聘流程。比如精简面试环节&#xff0c;制定标准化流程&#xff0c;完善信息管理&#xff0c;对面试环节进行细致梳理之后&#xff0c;尽快识别并去除那些不必要的步骤&#xff0c;这样就能够减少求职者的等待时间&…

零成本搭建个人 APP 和小程序后台

前言 前面也说了&#xff0c;通过 GitHub PagesGitHub Actions 只是解决了动态数据展示&#xff0c;但是要零成本得完成将用户信息存储下来&#xff0c;并实现数据交互呢&#xff1f; 我开始是想用云文档&#xff0c;种种原因&#xff0c;我还是希望有个自己能二次修改的后台…

动态网格交易、科创板做市、股票 CTA……DolphinDB 中高频策略回测实例之股票篇

股票中高频交易是量化交易的重要分支&#xff0c;其核心在于在极短时间内处理大量市场数据&#xff0c;执行多笔交易&#xff0c;从而捕捉细微的价格波动并获取利润。中高频交易策略的成功依赖于精准的算法设计、低延迟的交易系统以及交易程序强大的数据分析能力。作为实盘操作…

【系统架构设计】设计模式

【系统架构设计】设计模式 设计模式概述GoF设计模式Factory MethodAbstract Factory &#xff08;*&#xff09;BuilderPrototype(原型)Singleton&#xff08;*&#xff09;AdapterBridgeComposite(组合)Decorator(装饰)&#xff08;*&#xff09;Facade(外观)&#xff08;*&am…

恢复指定人微信聊天记录,看这里,4种方法帮您找回记录

微信在今天成为了我们日常生活中不可或缺的一部分&#xff0c;它承载着我们的社交、工作乃至生活的点点滴滴。然而&#xff0c;有时出于各种原因&#xff0c;我们可能会选择注销微信账号&#xff0c;但随后又可能因种种原因希望恢复其微信进行使用。那么&#xff0c;微信注销了…

CSS溢出——WEB开发系列20

在网页设计中&#xff0c;“溢出”是一个常见且重要的概念。它涉及到如何处理那些超出预定范围的内容&#xff0c;以确保网页的布局和视觉效果达到预期。 一、什么是溢出&#xff1f; 在 CSS 中&#xff0c;“溢出”&#xff08;overflow&#xff09;指的是内容超出其包含块的…

构建高效NLP管道:PydanticOutputParser与Langchain的结合

PydanticOutputParser 是一个用于解析语言模型输出的实用工具&#xff0c;它允许用户指定一个 Pydantic 模型&#xff0c;并查询语言模型以生成符合该模型的 JSON 输出。这个工具特别有用&#xff0c;因为它可以帮助开发者确保从语言模型获得的结构化数据符合预期的格式&#x…

全面解析:四大LLM模型——预训练与后训练的新范式

导读 最初&#xff0c;大语言模型LLM的训练过程只关注于预训练&#xff0c;但如今已经扩展到包含预训练和后训练两个阶段。后训练通常包括监督指令微调和对齐&#xff0c;这一方法在ChatGPT发布后广泛流行&#xff01; 翻译自:https://magazine.sebastianraschka.com/p/new-l…

NAT网关产品手册

产品用途 SG-NAT-410 网关支持 IP 地址跨网段和通讯端口的转换&#xff0c;为不方便修改参数的以太网通讯设备的信息化联网提供便捷的解决方案。网关有 1 个两口以太网交换机接口(LAN 口 ) 和 1 个单口以太网通讯接口 (WAN 口 ) 。不对原系统做任何硬件和软件修改&…

WAF一般是怎么防御SQL注入的

目录 参数化查询在防止SQL注入中起到什么作用&#xff1f; 为什么要限制数据库用户权限来预防SQL注入&#xff1f; WAF是如何检测SQL注入特征的&#xff1f; WAF防范SQL注入的主要策略包括&#xff1a; 参数化查询和预编译语句&#xff1a;使用参数化查询或预编译语句可以确…

虚拟试穿技术分享

虚拟试穿技术&#xff0c;通过计算机视觉和人工智能&#xff0c;允许用户在数字界面上试穿服装&#xff0c;无需实际穿戴。这项技术广泛应用于电子商务和时尚工业&#xff0c;使得消费者可以在购买前预览衣物在自己身上的样子。 基于扩散的方法&#xff1a;这种方法使用基于图像…

微波网络分析仪VNA原理详解:电磁波测量基础-测试狗科研测试

微波网络分析仪VNA原理详解&#xff1a;电磁波测量基础 微波网络分析仪&#xff08;Vector Network Analyzer&#xff0c;简称VNA&#xff09;是一种用于测试和测量微波频段元件和网络特性的电子测试设备。 一、电磁波 电磁波是由电场和磁场相互垂直并相互依赖振荡传播的波动现…

餐饮商标怎么注册自己的品牌!

餐饮行业是创业者选的比较多的行业&#xff0c;也有许多人从小做起&#xff0c;成为当地知名品牌&#xff0c;那企业或个体户如何注册餐饮行业的商标。 在餐饮行业首先是申请注册类别43类餐饮住宿&#xff0c;这个是餐饮的核心类别&#xff0c;开店、饭店都在这个类别&#xf…

学习之adb命令

adb命令作用&#xff1a; 用来操作手机系统的 类似于window的cmd,linux的bash 如果添加环境变量的adb不能识别设备通常是因为存在两个不同的版本 如下&#xff1a; adb devices 如果没有发现&#xff0c;可以尝试手动连接设备 需要先执行 adb kill-server adb start-server …

前端模块自动导入的插件

前言 开发中通常会有很多导入语句&#xff0c;如何确保一些通用的api和hook无需每次手动导入即可使用。 <script setup lang"ts"> import { ref, reactive } from "vue" import { useRoute, useRouter } from "vue-router" import { log…

【Spring】Cookie和Session

系列文章目录 第一章 初识Spring MVC 文章目录 系列文章目录前言一、Cookie和Session二、Cookie操作获取Cookie指定Cookie 三、Session操作设置Session数据获取Session数据 四、获取Header总结 前言 HTTP是"无状态"的协议&#xff0c;我们可以理解为它没有记忆力。…

UnrealEngine学习(02):虚幻引擎编辑器界面详解

学习一款软件&#xff0c;我个人建议是先学习怎么用&#xff0c;然后是学习怎么用的好&#xff0c;再研究源码。 上一篇文章详细描述了我们该如何安装虚幻5引擎&#xff1a; UnrealEngine学习(01)&#xff1a;安装虚幻引擎https://blog.csdn.net/zuodingquan666/article/deta…

Kubernetes存储Volume

数据是一个企业的发展核心,他涉及到数据存储和数据交换的内容。在生产环境中尤为重要的一部分&#xff0c;在 Kubernetes 中另一个重要的概念就是数据持久化 Volume。 一、Volume的概念 对于大多数的项目而言&#xff0c;数据文件的存储是非常常见的需求&#xff0c;比如存储用…

SEO优化租用站群服务器需要考虑哪些?

在SEO(搜索引擎优化)的背景下&#xff0c;站群服务器是一种运行多个网站的器&#xff0c;每个网站共享同一个IP地址&#xff0c;但也可拥有独立IP。下面将详细探讨SEO使用站群服务器租用需要考虑的各种因素&#xff0c;rak小编为您整理发布。 1. 网站内容的原创性与相关性 内容…

淘客系统源码的架构分析

淘客系统的架构分析可以从多个层面进行探讨&#xff0c;包括业务流程、技术选型、系统模块、安全性、可扩展性等方面。 业务流程&#xff1a;淘客返利系统的主要业务流程包括用户注册、商品推广、订单跟踪、返利结算等。用户注册成功后&#xff0c;将获得推广链接&#xff0c;…