Langchain-Chatchat实践详解

news2024/12/24 22:07:33

简介

本质上是在Langchain基础上封装的一层聊天服务,可以对接底层多种离线LLM和在线的LLM(也可以对接自定义的在线LLM)。提供基于知识库聊天功能相关的一系列API。

下载源码

源码地址:

https://github.com/chatchat-space/Langchain-Chatchat

实践版本:

注:

1. 因为requirements.txt里一些依赖没有标注版本号,所以,在安装使用中可能存在版本号不匹配问题,本文可用版本号参考

openai 0.28.1

langchain 0.0.330

2. 显卡卡住问题https://leiblog.wang/%E8%B8%A9%E5%9D%91nvidia-driver/

执行:nvidia-smi -pm 1

下载模型

修改配置

修改configs/model_config.py中MODEL_PATH,你要使用的模型为本地模型路径

启动服务

python startup.py --all-webui

webui界面:

API 体验界面

Docker运行服务

安装docker:

apt update

apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

apt update

apt install docker-ce docker-ce-cli containerd.io

systemctl status docker

Ubuntu 23.04 Support · Issue #72 · NVIDIA/nvidia-container-toolkit · GitHub

安装nvidia-container-toolkit

distribution=ubuntu18.04 \

      && curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \

      && curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \

            sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \

            sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

apt-get update && apt-get install -y nvidia-container-toolkit

service docker restart

设置docker镜像源:

sudo mkdir -p /etc/docker

sudo tee /etc/docker/daemon.json <<-'EOF'

{

    "registry-mirrors": [

        "http://hub-mirror.c.163.com",

        "https://z2ycya8q.mirror.aliyuncs.com"

    ]

}

EOF

sudo systemctl daemon-reload

sudo systemctl restart docker

生成镜像:

cd /home

git clone https://code.dobest.com/research-nlp/Langchain-Chatchat.git

初始化数据库:

cp -r /home/Langchain-Chatchat/knowledge_base/* /home/data/Langchain-Chatchat/knowledge_base/

cd Langchain-Chatchat

docker build -f ./Dockerfile -t langchain-chatchat .

docker run -it -d -p 8501:8501 -p 7861:7861 -p 20000:20000 -p 20001:20001 -p 20002:20002 -p 21007:21007 --gpus all -e ZMENV="online" --restart=always -v "/home/models:/home/models" -v "/home/data/Langchain-Chatchat/knowledge_base:/usr/src/Langchain-Chatchat/knowledge_base" -v "/home/data/Langchain-Chatchat/logs:/usr/src/Langchain-Chatchat/logs" -v /etc/localtime:/etc/localtime:ro langchain-chatchat

远程调试代码

pycharm远程调试

PyCharm远程调试代码 - 知乎

【已解决】Pycharm:Can't get remote credentials for deployment server-CSDN博客

设置个ssh interpreter就可以远程调试了。

代码解析

start_main_server

         -> run_controller

         -> run_model_worker

         -> run_openai_api

         -> run_api_server

         -> run_webui

主进程fork出7个子进程

每个模型起一个子进程

服务端运行FastChat

GitHub - lm-sys/FastChat: An open platform for training, serving, and evaluating large language models. Release repo for Vicuna and Chatbot Arena.

HTTP API Source | Segment Documentation

socket,长连接通信

run_api_server(startup.py)

run_api_server  -> create_app

start_main_server  -> run_model_worker

FastAPI拉起模型进程

document

    app.get("/",

            response_model=BaseResponse,

            summary="swagger 文档")(document)

openai_chat

    # Tag: Chat

    app.post("/chat/fastchat",

             tags=["Chat"],

             summary="与llm模型对话(直接与fastchat api对话)")(openai_chat)

openai_chat -> ChatCompletion : acreate -> EngineAPIResource : acreate -> api_requestor : request

不带历史条件的单问题对话

{

  "model": "chatglm2-6b",

  "messages": [

    {

      "role": "user",

      "content": "hello"

    }

  ],

  "temperature": 0.7,

  "n": 1,

  "max_tokens": 1024,

  "stop": [],

  "stream": false,

  "presence_penalty": 0,

  "frequency_penalty": 0

}

chat

    app.post("/chat/chat",

             tags=["Chat"],

             summary="与llm模型对话(通过LLMChain)")(chat)

chat每个入参对应post里面的一个json字段

def chat(query: str = Body(..., description="用户输入", examples=["恼羞成怒"]),
        
history: List[History] = Body([],
                                      
description="历史对话",
                                      
examples=[[
                                           {
"role": "user", "content": "我们来玩成语接龙,我先来,生龙活虎"},
                                          
{"role": "assistant", "content": "虎头虎脑"}]]
                                       )
,
        
stream: bool = Body(False, description="流式输出"),
        
model_name: str = Body(LLM_MODEL, description="LLM 模型名称。"),
        
):

chat ->chat_iterator

把所有历史记录作为prompt输入

[[ChatMessage(content='我们来玩成语接龙,我先来,生龙活虎', additional_kwargs={}, role='user'), ChatMessage(content='虎头虎脑', additional_kwargs={}, role='assistant'), ChatMessage(content='恼羞成怒', additional_kwargs={}, role='user')]]

chat ->chat_iterator ->acall ->llm : _acall -> agenerate -> agenerate_prompt ->agenerate ->_agenerate_with_cache ->openai:_agenerate ->openai: _astream -> openai: acompletion_with_retry -> chat_completion : acreate -> engine_api_resource : acreate -> api_requestor : arequest -> api_requestor : arequest_raw -> client : request

貌似大模型都按照openai_api的定义生成了一套标准http接口

langchain是封装了一层,对接所有大模型的openap_api

knowledge_base_chat

    app.post("/chat/knowledge_base_chat",

             tags=["Chat"],

             summary="与知识库对话")(knowledge_base_chat)

长连接通信:

pages = {

    "对话": {

        "icon": "chat",

        "func": dialogue_page,

    },

    "知识库管理": {

        "icon": "hdd-stack",

        "func": knowledge_base_page,

    },

  }

dialogue_page  -> utils: knowledge_base_chat -> chat. knowledge_base_chat

http 接口:

 create_app -> chat. knowledge_base_chat

所有接口都有两条路

knowledge_base_chat -> knowledge_base_chat_iterator -> kb_doc_api: search_docs -> base : search_docs -> faiss_kb_service : do_search -> langchain : similarity_search_with_score -> faiss : replacement_search

langchain 对接 faiss

docs返回相似度top5的答案

[ChatMessagePromptTemplate(prompt=PromptTemplate(input_variables=[], output_parser=None, partial_variables={}, template='{% raw %}我们来玩成语接龙,我先来,生龙活虎{% endraw %}', template_format='jinja2', validate_template=True), additional_kwargs={}, role='user'), ChatMessagePromptTemplate(prompt=PromptTemplate(input_variables=[], output_parser=None, partial_variables={}, template='{% raw %}虎头虎脑{% endraw %}', template_format='jinja2', validate_template=True), additional_kwargs={}, role='assistant'), ChatMessagePromptTemplate(prompt=PromptTemplate(input_variables=['context', 'question'], output_parser=None, partial_variables={}, template='<指令>根据已知信息,简洁和专业的来回答问题。如果无法从中得到答案,请说 “根据已知信息无法回答该问题”,不允许在答案中添加编造成分,答案请使用中文。 </指令>\n\n<已知信息>{{ context }}</已知信息>\n\n<问题>{{ question }}</问题>', template_format='jinja2', validate_template=True), additional_kwargs={}, role='user')]

chat_prompt = ChatPromptTemplate.from_messages(
    [i.to_msg_template()
for i in history] + [input_msg])

历史和prompt模版拼接成chat_prompt

knowledge_base_chat -> Chain : acall

会将文本作为已知信息,提出问题

所有的历史和搜索文本、问题拼装成提示语

Chain : agenerate -> Chain : aprep_prompts

将params一些数据转成bytes的data发给模型服务

knowledge_base_chat -> Chain : acall -> Chain : agenerate -> Chain : aprep_prompts -> BaseChatModel : agenerate -> BaseChatModel : _agenerate_with_cache -> ChatOpenAI : _agenerate -> ChatOpenAI : _astream -> chat_models : acompletion_with_retry -> _completion_with_retry -> ChatCompletion : acreate -> EngineAPIResource : acreate -> APIRequestor : arequest -> APIRequestor : arequest_raw

最后Chain : acall的response里获得模型答复

可以根据阈值返回TOPK作为先验知识

# 知识库匹配向量数量

  VECTOR_SEARCH_TOP_K = 5

  

  # 知识库匹配相关度阈值,取值范围在0-1之间,SCORE越小,相关度越高,取到1相当于不筛选,建议设置在0.5左右

  SCORE_THRESHOLD = 1

search_engine_chat

    app.post("/chat/search_engine_chat",

             tags=["Chat"],

             summary="与搜索引擎对话")(search_engine_chat)

search_engine_chat -> lookup_search_engine -> duckduckgo_search

调用第三方搜索API,我们没有对应的库,就会抛异常报错。

agent_chat

    app.post("/chat/agent_chat",

             tags=["Chat"],

             summary="与agent对话")(agent_chat)

LangChain Agent入门教程 - LangChain教程(Python版本) - 梯子教程网

是通过llm进行决策,然后,程序去调用对应的工具。目前不需要。对应工具可能没有。

都是调用langchain的agent接口。查天气不可用。翻译可用。

本质上还是语言模型输入输出,在大模型外层设计出对应字符串的action回调,回调中自定义功能。

天气这个是chatchat这边实现的,应该还没有调通,解决了一个参数问题,大模型返回格式还不太对。

大模型回复了,格式不对

修改了明确的提示语后,可以调通天气,只需要和风天气API的key就可以查询了。

明确的prompt很重要,问题后面就是接答案,不要乱搞。

_PROMPT_TEMPLATE = """用户将会向您咨询天气问题,您不需要自己回答天气问题,而是将用户提问的信息提取出来区,市和时间三个元素后使用我为你编写好的工具进行查询并返回结果,格式为 区++时间 每个元素用空格隔开。如果缺少信息,则用 None 代替。

  问题: ${{用户的问题}}

  答案:

```text

${{拆分的区,市和时间}}

```

... weather(提取后的关键字,用空格隔开)...

  

  

  这是两个例子:

  

  问题: 上海浦东未来1小时天气情况?

  答案:

```text

  浦东 上海 1

```

...weather(浦东 上海 1)...

  

  

  问题: 北京市朝阳区未来24小时天气如何?

  答案:

```text

  朝阳 北京 24

```

...weather(朝阳 北京 24)...

  

  

  现在,这是我的问题:

  问题: {question}

"""

利用ChatGPT的函数调用功能实现:实时查询天气 - FooFish

agent_chat -> agent_chat_iterator -> Chain : acall -> BaseSingleActionAgent : _acall

generations=[ChatGenerationChunk(text=' 我需要查询上海浦东的天气情况,可以使用天气查询工具帮助我。\nAction: 天气查询工具\nAction Input: 上海 浦东 未来1小时\nObservation', message=AIMessageChunk(content=' 我需要查询上海浦东的天气情况,可以使用天气查询工具帮助我。\nAction: 天气查询工具\nAction Input: 上海 浦东 未来1小时\nObservation'))] llm_output=None

通过tool定义,能找到对应的func

然后再根据对应天气的特定的prompt调用大模型,从问题中提取出对应的参数,然后,提取校验返回值,并最为入参给到获取天气的函数,拿到天气信息。

list_kbs

    # Tag: Knowledge Base Management

    app.get("/knowledge_base/list_knowledge_bases",

            tags=["Knowledge Base Management"],

            response_model=ListResponse,

            summary="获取知识库列表")(list_kbs)

SQlALchemy session详解 - 知乎

list_kbs -> list_kbs_from_db

使用SQLAlchemy来操作数据库

create_kb

    app.post("/knowledge_base/create_knowledge_base",

             tags=["Knowledge Base Management"],

             response_model=BaseResponse,

             summary="创建知识库"

             )(create_kb)

封装了向量库服务 kb_service

再去调用langchain.vectorstores中封装好的方法

create_kb -> get_service_by_name ->load_kb_from_db

             get_service_by_name -> KBServiceFactory : get_service

// 先创建向量库

create_kb -> KBService : create_kb -> do_create_kb -> load_vector_store -> load_faiss_vector_store

// 再创建对应数据库信息

          -> KBService : create_kb -> add_kb_to_db

delete_kb

    app.post("/knowledge_base/delete_knowledge_base",

             tags=["Knowledge Base Management"],

             response_model=BaseResponse,

             summary="删除知识库"

             )(delete_kb)

// 删除向量库内容

delete_kb ->clear_vs -> FaissKBService : do_clear_vs

// 删除对应数据库信息

delete_kb ->clear_vs -> delete_files_from_db

// 删除知识库

delete_kb -> drop_kb -> FaissKBService : do_drop_kb

delete_kb -> drop_kb -> delete_kb_from_db

list_files

    app.get("/knowledge_base/list_files",

            tags=["Knowledge Base Management"],

            response_model=ListResponse,

            summary="获取知识库内的文件列表"

            )(list_files)

// 查数据库

list_files -> KBService : list_files -> list_files_from_db

search_docs

    app.post("/knowledge_base/search_docs",

             tags=["Knowledge Base Management"],

             response_model=List[DocumentWithScore],

             summary="搜索知识库"

             )(search_docs)

search_docs -> KBService : search_docs -> FaissKBService : do_search -> FAISS : similarity_search_with_score

FaissKBService 属于Langchain基础上再封装

FAISS 是Langchain封装好的向量库接口

upload_doc

    app.post("/knowledge_base/upload_doc",

             tags=["Knowledge Base Management"],

             response_model=BaseResponse,

             summary="上传文件到知识库"

             )(upload_doc)

保存知识文件到对应知识库路径

会将文件内容以行为单位截断,那么相邻两部分的首尾有一部分内容重合。

// 清理向量库和数据库

upload_doc -> KBService : add_doc -> KBService : delete_doc

// 将文档内容加入向量库

upload_doc -> KBService : add_doc -> FaissKBService : do_add_doc -> VectorStore : add_documents

VectorStore:langchain封装的向量库接口

// 将生成的向量id和对应文档信息,存入数据库

upload_doc -> KBService : add_doc -> add_file_to_db

delete_doc

    app.post("/knowledge_base/delete_doc",

             tags=["Knowledge Base Management"],

             response_model=BaseResponse,

             summary="删除知识库内指定文件"

             )(delete_doc)

删除找对应id是遍历整个知识库比对文件路径

// 从向量库中删除数据

delete_doc -> KBService : delete_doc -> FaissKBService : do_delete_doc

// 从数据库中删除文件信息(可以选择是否删除内容)

delete_doc -> KBService : delete_doc -> delete_file_from_db

update_doc

    app.post("/knowledge_base/update_doc",

             tags=["Knowledge Base Management"],

             response_model=BaseResponse,

             summary="更新现有文件到知识库"

             )(update_doc)

update_doc -> KBService : update_doc

所谓的更新就是先删除再重新添加

download_doc

    app.get("/knowledge_base/download_doc",

            tags=["Knowledge Base Management"],

            summary="下载对应的知识文件")(download_doc)

http://10.225.20.233:7861/knowledge_base/download_doc?knowledge_base_name=samples&file_name=1.docx

postman选择 send and download可以下载文件

download_doc -> FileResponse

recreate_vector_store

    app.post("/knowledge_base/recreate_vector_store",

             tags=["Knowledge Base Management"],

             summary="根据content中文档重建向量库,流式输出处理进度。"

             )(recreate_vector_store)

// 最后刷新缓存

recreate_vector_store -> output -> KBService : add_doc -> FaissKBService : do_add_doc -> vector_store.save_local

list_models

# LLM模型相关接口

@app.post("/llm_model/list_models",

        tags=["LLM Model Management"],

        summary="列出当前已加载的模型")

list_running_models

应该是LLM Model + FSCHAT_MODEL_WORKERS

本地模型和在线接口

list_running_models -> Controller : list_models

list_config_models

app.post("/llm_model/list_config_models",

         tags=["LLM Model Management"],

         summary="列出configs已配置的模型",

         )(list_config_models)

list_config_models -> list_llm_models

stop_llm_model

@app.post("/llm_model/stop",

        tags=["LLM Model Management"],

        summary="停止指定的LLM模型(Model Worker)",

        )

stop_llm_model -> release_worker -> release_model

网络中转了几次,最后,模型被释放了,显存释放了

change_llm_model

@app.post("/llm_model/change",

        tags=["LLM Model Management"],

        summary="切换指定的LLM模型(Model Worker)",

        )

change_llm_model -> release_worker -> release_model

模型停掉,就不能切换了,看来不能动态的更换大模型

webui.py

UI框架Streamlit

SQLAlchemy

SQLAlchemy入门和进阶 - 知乎

https://pic1.zhimg.com/80/v2-984901c2903ec83589d641fb930ed738_720w.webp

# 数据库默认存储路径。

  # 如果使用sqlite,可以直接修改DB_ROOT_PATH;如果使用其它数据库,请直接修改SQLALCHEMY_DATABASE_URIDB_ROOT_PATH = os.path.join(KB_ROOT_PATH, "info.db")

SQLALCHEMY_DATABASE_URI = f"sqlite:///{DB_ROOT_PATH}"

默认使用了sqlite


  

  class KnowledgeFileModel(Base):

    """

    知识文件模型

    """

    __tablename__ = 'knowledge_file'

    id = Column(Integer, primary_key=True, autoincrement=True, comment='知识文件ID')

    file_name = Column(String(255), comment='文件名')

    file_ext = Column(String(10), comment='文件扩展名')

    kb_name = Column(String(50), comment='所属知识库名称')

    document_loader_name = Column(String(50), comment='文档加载器名称')

    text_splitter_name = Column(String(50), comment='文本分割器名称')

    file_version = Column(Integer, default=1, comment='文件版本')

    file_mtime = Column(Float, default=0.0, comment="文件修改时间")

    file_size = Column(Integer, default=0, comment="文件大小")

    custom_docs = Column(Boolean, default=False, comment="是否自定义docs")

    docs_count = Column(Integer, default=0, comment="切分文档数量")

    create_time = Column(DateTime, default=func.now(), comment='创建时间')

  

    def __repr__(self):

        return f"<KnowledgeFile(id='{self.id}', file_name='{self.file_name}', file_ext='{self.file_ext}', kb_name='{self.kb_name}', document_loader_name='{self.document_loader_name}', text_splitter_name='{self.text_splitter_name}', file_version='{self.file_version}', create_time='{self.create_time}')>"

  

  

  class FileDocModel(Base):

    """

    文件-向量库文档模型

    """

    __tablename__ = 'file_doc'

    id = Column(Integer, primary_key=True, autoincrement=True, comment='ID')

    kb_name = Column(String(50), comment='知识库名称')

    file_name = Column(String(255), comment='文件名称')

    doc_id = Column(String(50), comment="向量库文档ID")

    meta_data = Column(JSON, default={})

  

    def __repr__(self):

        return f"<FileDoc(id='{self.id}', kb_name='{self.kb_name}', file_name='{self.file_name}', doc_id='{self.doc_id}', metadata='{self.metadata}')>"

两张表KnowledgeFileModel 和 FileDocModel

用的是SessionLocal,本地数据库

自定义线上API

server/model_workers下面添加线上API的具体定义,可以参考目录下其它线上API定义。

generate_stream_gate里面定义具体的接口对接代码。

server_config.py中 FSCHAT_MODEL_WORKERS下面新增api绑定端口。

model_config.py 中ONLINE_LLM_MODEL 新增 api配置

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

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

相关文章

Apache Druid连接回收引发的血案

问题 线上执行大批量定时任务&#xff0c;发现SQL执行失败的报错&#xff1a; CommunicationsException, druid version 1.1.10, jdbcUrl : jdbc:mysql://xxx?useUnicodetrue&characterEncodingUTF-8&zeroDateTimeBehaviorconvertToNull,testWhileIdle true, idle …

《向经典致敬》第二届粤港澳大湾区著名歌唱家音乐会完美落幕

百年经典 歌坛盛会 “《向经典致敬》第二届粤港澳大湾区著名歌唱家音乐会暨2023福田人才之夜”完美落幕 2023年11月4日&#xff0c;阳光普照&#xff0c;秋意正浓&#xff0c;由中共深圳市福田区委宣传部、深圳市福田区文学艺术界联合会主办&#xff0c;深圳歌唱家协会承办&…

数据结构与算法C语言版学习笔记(3)-线性表的链式结构:链表

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言&#xff1a;回顾顺序表的优缺点&#xff1a;为什么要引入链式结构的线性表&#xff1f; 一、什么是链表&#xff1f;二、链表的分类①为什么要设置头节点&…

Oracle(15)Managing Users

目录 一、基础知识 1、Users and Security 用户和安全 2、Database Schema 3、Checklist for Creating Users创建用户步骤 二、基础操作 1、创建一个用户 2、OS Authentication 操作系统身份验证 3、Dropping a User 删除用户 4、Getting User Information 获取用户信…

Idea安装使用教程~

在本文中&#xff0c;我们将提供关于如何安装 IntelliJ IDEA 的详细步骤。如果您是初学者或只是想尝试一下 IDEA&#xff0c;我们建议您下载 Community 版。如果您需要更多高级功能&#xff0c;可以选择 Ultimate 版。 步骤一&#xff1a;下载 IntelliJ IDEA 首先&#xff0c;…

第三方商城对接项目(202311)

文章目录 1. 项目背景和目标2. 项目成果3. 项目经验总结4. 展望和建议 1. 项目背景和目标 竞标成功接口对接第三方商城&#xff0c;商品&#xff0c;订单&#xff0c;售后尽快完成对接 2. 项目成果 完成整个项目功能流程对接新业务功能移交项目等业务部门使用 3. 项目经验总…

app自动化测试——capability 配置参数解析

一、Capability 简介 功能&#xff1a;配置 Appium 会话&#xff0c;告诉 Appium 服务器需要自动化的平台的应用程序 形式&#xff1a;键值对的集合&#xff0c;键对应设置的名称&#xff0c;值对应设置的值 主要分为三部分 公共部分 ios 部分 android 部分 二、Session Appi…

【C++】特殊类实现——设计一个类、不能被拷贝、只能在堆上创建对象、只能在栈上创建对象、不能被继承、单例模式、饿汉模式、懒汉模式

文章目录 C特殊类实现1.设计一个类、不能被拷贝2.设计一个类、只能在堆上创建对象3.设计一个类、只能在栈上创建对象4.设计一个类、不能被继承5.设计一个类&#xff0c;只能创建一个对象(单例模式)5.1饿汉模式5.2懒汉模式 C 特殊类实现 1.设计一个类、不能被拷贝 在C中&#x…

11 # 手写 reduce 方法

reduce 使用 reduce() 方法对数组中的每个元素按序执行一个提供的 reducer 函数&#xff0c;每一次运行 reducer 会将先前元素的计算结果作为参数传入&#xff0c;最后将其结果汇总为单个返回值。 第一次执行回调函数时&#xff0c;不存在“上一次的计算结果”。如果需要回调…

短短45分钟,Open AI撼动了整个AI圈?

相信关注AI行业的人没有人不知道ChatGPT&#xff0c;作为人工智能新产品&#xff0c;ChatGPT一经发出就引爆全球&#xff0c;也让一众企业走上了探索AI大模型之路。而就在国内一众企业就AI大模型不断改进创新时&#xff0c;Open AI用一场仅45分钟的发布会&#xff0c;震惊了整个…

【JavaEESpring】Spring IoCDI

Spring IoC& DI 1. IoC2. IoC & DI 使⽤2.1 Bean的存储2.1 DI 注入 Autowired 3. 练习代码自取 1. IoC Spring 是包含了众多⼯具⽅法的 IoC 容器 IoC: Inversion of Control (控制反转), 也就是说 Spring 是⼀个"控制反转"的容器。 什么是控制反转呢? 也就…

uniapp使用vue

uniapp集成了Vuex&#xff0c;&#xff0c;并不需要安装vuex 定义自己的vuex vuex中独立命名空间&#xff1a; 可以在模块中使用 namespaced 属性&#xff0c;设置为 true&#xff0c;&#xff0c;这样做的好处是&#xff0c;&#xff0c;不同模块之间的state&#xff0c;mut…

电商库存随笔

好多年没有来写东西了&#xff0c;忙成狗&#xff0c;最近闲暇&#xff0c;有点时间&#xff0c;随手写一下之前的项目中的小点&#xff1b; 一方面是做个总结&#xff0c;一方面打发一下时间 出库 库存扣减时机 下单扣减 [生成订单]付款扣减预扣库存(实际使用) 预扣库存 并…

运营商大数据精准获客:我们提供精准客源渠道的最大资源体?

运营商大数据精准营销 谈起精准获客&#xff0c;竞争对手永远是为我们提供精准客源渠道的最大资源体&#xff01; 最新的获客方式&#xff0c;就是从竞争对手的手中把他们的精准客户资源变为自己的。 今年最火的运营商大数据精准营销是拒绝传统营销方式的烧钱推广&#xff0…

Nginx缓存基础

1 nginx缓存的流程 客户端需要访问服务器的数据时&#xff0c;如果都直接向服务器发送请求&#xff0c;服务器接收过多的请求&#xff0c;压力会比较大&#xff0c;也比较耗时&#xff1b;而如果在nginx缓存一定的数据&#xff0c;使客户端向基于nginx的代理服务器发送请求&…

ChatGPT - 在ChatGPT中设置通用提示模板

文章目录 Prompt设置验证 Prompt VERBOSITY: 我可能会使用 V[0-3] 来定义代码的详细程度&#xff1a;V0 简洁明了 V1 简练 V2 详细 V3 非常详细&#xff0c;附有例子助理回应 您是用户问题背景下的主题专家。我们一步一步来&#xff1a;除非您只是回答一个简短的问题&#xff…

Confluence 漏洞复现(CVE-2023-22515)

Confluence 漏洞复现&#xff08;CVE-2023-22515&#xff0c;CVE-2023-22518&#xff09; 1.CVE-2023-22515权限提升漏洞 1.1漏洞描述 Confluence近期推出的严重漏洞cve-2023-22515&#xff0c;由于未授权和xwork框架问题&#xff0c;导致攻击者可以未授权将系统设置为未安装…

分布式事务的华丽进化

说到分布式事务&#xff0c;大家并不陌生。之前我已做过相关的总结&#xff08;连接附本文后面&#xff09;&#xff0c;不过比较偏理论。在实际工作中&#xff0c;用得比较多的还是柔性分布式事务&#xff0c;今天主要把在工作中运用到的几种柔性分布式事务的场景及实现方式做…

使用nginx作为图片服务器

第一步&#xff1a; 下载nginx版本&#xff0c;去官网下载&#xff0c;这里不建议下载最新版本&#xff0c;因为有时候最新版本还不太稳定。 nginx下载地址官网&#xff1a;nginx: download&#xff0c;然后把下载好的安装包解压出来。 第二步&#xff1a; 在nginx目录下载创建…

在新的服务器上成功安装mysqlclient的方法【解决No matching distribution found for mysqlclient的问题】

前言&#xff1a;在某台Centos服务器上安装mysqlclient时一直报下面的错&#xff1a; WARNING: Discarding https://mirrors.aliyun.com/pypi/packages/6a/91/bdfe808fb5dc99a5f65833b370818161b77ef6d1e19b488e4c146ab615aa/mysqlclient-1.3.0.tar.gz#sha25606eb5664e3738b28…