Langchain-Chatchat实践详解
简介
本质上是在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=["恼羞成怒"]), |
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入门和进阶 - 知乎

# 数据库默认存储路径。# 如果使用sqlite,可以直接修改DB_ROOT_PATH;如果使用其它数据库,请直接修改SQLALCHEMY_DATABASE_URI。DB_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配置

相关文章:
Langchain-Chatchat实践详解
简介 本质上是在Langchain基础上封装的一层聊天服务,可以对接底层多种离线LLM和在线的LLM(也可以对接自定义的在线LLM)。提供基于知识库聊天功能相关的一系列API。 下载源码 源码地址: https://github.com/chatchat-space/Lang…...
python求解优化问题的几个例子
目录 1、最优化问题 2、线性规划 3、无约束优化 3.1单变量 3.2多变量 1、最优化问题 使用scipy库中的minimize函数来求解最优化问题。在这个例子中,我们定义了一个目标函数 objective,其形式为x1^2 x2^2;以及一个约束条件 constraint&…...
HP惠普暗影精灵9P OMEN 17.3英寸游戏本17-cm2000(70W98AV)原装出厂Windows11-22H2系统镜像
链接:https://pan.baidu.com/s/1gJ4ZwWW2orlGYoPk37M-cg?pwd4mvv 提取码:4mvv 惠普暗影9Plus笔记本电脑原厂系统自带所有驱动、出厂主题壁纸、 Office办公软件、惠普电脑管家、OMEN Command Center游戏控制中心等预装程序 所需要工具:3…...
❤ Uniapp使用Ucharts(二)(组件类型)
❤ Uniapp使用Ucharts(二)(秋云组件类型) 一、折线图 1、结构 <template><view class"charts-box"><qiun-data-charts type"area":opts"opts":chartData"chartData"/&…...
Linux Vim批量注释和自定义注释
使用 Vim 编辑 Shell 脚本,在进行调试时,需要进行多行的注释,每次都要先切换到输入模式,在行首输入注释符"#"再退回命令模式,非常麻烦。连续行的注释其实可以用替换命令来完成。 换句话说,在指定…...
虚幻C++基础 day3
常见的游戏机制 Actor机关门 创建一个Actor类,添加两个静态网格与一个触发器 UBoxComponentUStaticMeshComponent 头文件: #include “Components/BoxComponent.h”#include “Components/StaticMeshComponent.h” TriggerDoor.h // Fill out your …...
第26期 | GPTSecurity周报
GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区,集成了生成预训练Transformer(GPT)、人工智能生成内容(AIGC)以及大型语言模型(LLM)等安全领域应用的知识。在这里,您可以…...
(动手学习深度学习)第7章 稠密连接网络---DenseNet
目录 DenseNetDenseNet的优点:DenseNet的改进思路总结 DenseNet代码实现 DenseNet DenseNet的优点: 省参数。在 ImageNet 分类数据集上达到同样的准确率,DenseNet 所需的参数量不到 ResNet 的一半。对于工业界而言,小模型可以显著…...
UART编程(寄存器)
1. 串口编程步骤 1.1 看原理图确定引脚 有很多串口,使用哪一个?看原理图确定 1.2 配置引脚为UART功能 至少用到发送、接收引脚:txd、rxd 需要把这些引脚配置为UART功能,并使能UART模块 1.3 设置串口参数 有哪些参数…...
事务码增删查改表数据
常用事务码 SE11 SE14 SE16 SE16N SM30 SE11:查看数据库表/修改表中字段数量_类型/查看表中数据/设置表为可维护或不可维护 SE14:查看数据库表的创建日期创建用户名/查看表中字段/删除表中全部数据(只能全部删) SE16:查看数据库表/对可维护数据库表进行数据维护/SE16通过调试…...
vue开发环境搭建部署(mac版)
前言 目前后端工作越来越少了,年底了,为了先过验收。项目负责人、产品、需求制定的方案就是先做假页面,所以前端的活多点。 其实现在不喜欢搞前端,原因很多,但是感觉现在似乎流行的码林绝学又是九九归一的瓶颈期…...
Java【算法 05】通过时间获取8位验证码(每两个小时生成一个)源码分享
通过时间获取验证码 1.需求2.代码实现2.1 依赖2.2 时间参数处理方法2.3 截取验证码方法2.4 验证方法 3.总结 1.需求 要求是很简单的,每个验证码的有效时间是2小时,这个并不是收到验证码开始计时的,而是每个两小时的时间段使用的是相同的验证…...
微服务 Spring Cloud 5,一图说透Spring Cloud微服务架构
目录 一、域名系统DNS二、LVS(Linux Virtual Server),Linux虚拟服务器三、CDN静态资源四、Nginx反向代理服务器1、Nginx的主要作用体现在以下几个方面:2、Nginx静态资源服务和CDN静态资源服务,如何选择? 五、Gateway网…...
conda清华源安装cuda12.1的pytorch
使用pytorch官方提供的conda command奇慢无比,根本装不下来(科学的情况下也这样) 配置一下清华源使用清华源装就好了 清华源:https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/ 配置方法:conda config --…...
安徽首届道医传承十八绝技发布会在合肥成功举办
近日,在安徽合肥举行了首届道医传承十八绝技发布会,本次会议由安徽渡罗门生物科技有限公司、北京道武易医文化传播有限公司、楼观台道医文化研究院联合举办。现场吸引了来自全国各地民族医学领域的专家学者参与讨论与交流。本次会议旨在促进道医的交流与…...
一款功能强大的web目录扫描器专业版
dirpro 简介 dirpro 是一款由 python 编写的目录扫描器,操作简单,功能强大,高度自动化。 自动根据返回状态码和返回长度,对扫描结果进行二次整理和判断,准确性非常高。 已实现功能 可自定义扫描线程 导入url文件进…...
【Linux网络】网卡配置与修改主机名,做好基础系统配置
目录 一、网络配置命令 1、查看网卡信息ifconfig Linux永久修改ip地址 2、主机名修改 ①hostname 临时修改主机名 ②永久修改主机名 第一种,使用命令修改 第二种:修改配置文件 3、路由信息 再来拓展一下,永久修改路由表信息 4、检查…...
三大基础排序 -选择排序、冒泡排序、插入排序
排序算法 文章目录 冒泡排序算法步骤动图代码优化总结 选择排序算法步骤动图代码总结 插入排序算法步骤动图代码总结 排序算法,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。一般默认排序是按照由小到大即…...
el-form添加自定义校验规则校验el-input只能输入数字
0 效果 1 代码 {1,5}是用来限制小数点后几位的 addFormRules: {investAmount: [{ validator: checkInvestAmount, trigger: blur }], }, const checkInvestAmount (rule, value, callback) > {if (value ! && value ! null && value ! undefined) {if (/…...
ios 开发问题小集 [持续更新]
文章目录 一、如何给列表上的UITableViewCell添加手势二、获取NSIndexPath的方式2.1 根据row, section 来创建2.2 根据point 的位置来找到 indexPath三、tableView在Grouped样式下,设置表头表尾空白一、如何给列表上的UITableViewCell添加手势 给cell添加手势,大家都会这么做…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
