理论学习地址:
https://zh.d2l.ai/chapter_linear-networks/index.html
AutoDL学术加速:
source /etc/network_turbo
AutoDL下载(scp):
sudo scp -rP 25741 root@connect.nmb1.seetacloud.com:/root/autodl-tmp/mymodel /llm
conda常见操作:
删除:
conda remove --name myenv --all -y
导出:
conda env export > environment.yml
导入:
conda env create -f environment.yml
修改安装虚拟环境目录和包缓存目录
修改配置:
conda config --add envs_dirs /root/autodl-tmp/conda/envs
conda config --add pkgs_dirs /root/autodl-tmp/conda/pkgs
验证配置是否生效:
conda config --show | grep -A 2 "envs_dirs"
conda config --show | grep -A 2 "pkgs_dirs"
修改.bashrc
root@autodl-container-271149a41f-a69b11b9:~# which conda
/root/miniconda3/bin/conda
vi ~/.bashrc
添加:
# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$('/root/miniconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
eval "$__conda_setup"
else
if [ -f "/path/to/conda/etc/profile.d/conda.sh" ]; then
. "/path/to/conda/etc/profile.d/conda.sh"
else
export PATH="/path/to/conda/bin:$PATH"
fi
fi
unset __conda_setup
# <<< conda initialize <<<
出来激活
conda activate opencompass
source ~/.bashrc
Docker常见操作:
Docker 常用命令大全!!_docker常用命令-CSDN博客
进入正在运行的
docker exec -it 容器ID/容器名 /bin/bash
将容器内的文件复制到主机
docker cp 容器ID:/路径/文件 主机路径
展示所有
docker ps -a
交互启动
docker run -it 镜像名 /bin/bash
端口映射
docker run -p 主机端口:容器端口 镜像名
查看端口映射
docker port 容器ID/容器名
交互加端口映射启动
docker run -it -p 主机端口:容器端口 --name 自己名字 镜像名 /bin/bash
docker run -it -p 8080:80 --name myubuntu ubuntu /bin/bash
docker命令顺序
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
curl
换源:
-i https://pypi.mirrors.ustc.edu.cn/simple/
测试专用代码:
pip install openai
#多轮对话
from openai import OpenAI
#定义多轮对话方法
def run_chat_session():
#初始化客户端
client = OpenAI(base_url="http://localhost:23333/v1/",api_key="suibianxie")
#初始化对话历史
chat_history = []
#启动对话循环
while True:
#获取用户输入
user_input = input("用户:")
if user_input.lower() == "exit":
print("退出对话。")
break
#更新对话历史(添加用户输入)
chat_history.append({"role":"user","content":user_input})
#调用模型回答
try:
chat_complition = client.chat.completions.create(messages=chat_history,model="/root/autodl-tmp/model/Qwen/Qwen2.5-1.5B-Instruct")
#获取最新回答
model_response = chat_complition.choices[0]
print("AI:",model_response.message.content)
#更新对话历史(添加AI模型的回复)
chat_history.append({"role":"assistant","content":model_response.message.content})
except Exception as e:
print("发生错误:",e)
break
if __name__ == '__main__':
run_chat_session()
ptorch:
https://pytorch.org/
cuda12.4:
ubuntu:
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124 -i https://pypi.mirrors.ustc.edu.cn/simple/
cudacu121:
win:
pip install torch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1 -f https://mirrors.aliyun.com/pytorch-wheels/cu121
cuda11.8:
win:
pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu118 -i https://pypi.mirrors.ustc.edu.cn/simple/
WSL:
【超详细的WSL教程:Windows上的Linux子系统】 https://www.bilibili.com/video/BV1tW42197za/?share_source=copy_web&vd_source=5260dbbb879acb9193fb2e7261e27631
常见对话生成数据集:
【对话生成】常见对话生成数据集整理,含下载链接(更新至2022.06.04)_日常对话得训练数据集-CSDN博客
大模型平台:
huggingface:
官网:
https://huggingface.co/
dataset(nlp):
https://huggingface.co/docs/datasets/quickstart#nlp
魔塔:
pip install modelscope
下载模型:
modelscope download --model Qwen/Qwen2.5-1.5B-Instruct
下载单个文件:
modelscope download --model Qwen/Qwen2.5-1.5B-Instruct README.md --local_dir ./dir
sdk下载:
#模型下载
from modelscope import snapshot_download
cache_dir="/root/autodl-tmp/model"
model_dir = snapshot_download('Qwen/Qwen2.5-1.5B-Instruct',cache_dir=cache_dir)
langchain:
中文文档:
pipo算力云(API调用)
https://ppinfra.com/invitation
推理部署框架:
ollama
配置环境
conda create -n ollama python=3.10 -y
conda activate ollama
pip install ollama -i https://pypi.mirrors.ustc.edu.cn/simple/
下载:
curl -fsSL https://ollama.com/install.sh | sh
启动:
ollama serve
运行:
ollama list
ollama run ollama run qwen2.5:0.5b
运行自定义gguf:
创建ModelFile:
ModelFile内容如下:
#GGUF文件路径
FROM /root/autodl-tmp/Llama3-8B/LLM-Research/Meta-Llama-3-8B-Instruct-gguf8.gguf
创建自定义模型:
ollama create zyhhsss --file ./ModeFile
运行:
ollama run zyhhsss
删除:
ollama list
ollama rm zyhhsss
安装命令解释:
1. 命令的作用
(a) curl 部分
curl 是一个命令行工具,用于从指定的 URL 下载内容。
参数解释:
-f: 如果请求失败(例如 HTTP 状态码为 404 或 500),则不输出错误信息到终端。
-s: 静默模式,不显示进度条或错误信息。
-S: 在静默模式下,如果发生错误,仍然显示错误信息。
-L: 如果遇到重定向(如 301 或 302),自动跟随新的地址。
组合起来,-fsSL 表示“安静地下载文件,并处理重定向”。
https://ollama.com/install.sh:这是脚本的下载地址。curl 将从这个 URL 下载脚本的内容。
(b) | sh 部分
| 是管道符号,表示将 curl 的输出直接传递给下一个命令。
sh 是一个 Shell 解释器,用于执行从 curl 下载的脚本内容。
整体来说,这条命令的含义是:
使用 curl 从 https://ollama.com/install.sh 下载脚本。
将脚本内容通过管道传递给 sh,实时执行脚本中的命令。
2. 脚本会做什么?
运行此命令后,脚本的内容会直接影响您的系统。通常情况下,这种安装脚本可能会执行以下操作:
检查系统的环境(如操作系统、架构等)。
下载必要的二进制文件或依赖项。
安装软件到特定位置(如 /usr/local/bin 或 /opt)。
创建快捷方式或配置文件。
添加服务或设置环境变量。
具体行为取决于 install.sh 脚本的内容。
3. 风险与注意事项
虽然这种“一键安装”方式非常方便,但也存在一定的风险。以下是一些需要注意的事项:
(a) 脚本来源是否可信
您正在从 https://ollama.com/install.sh 下载脚本并直接执行,这意味着脚本的内容完全决定了它对您的系统的影响。
如果脚本被恶意篡改,可能会导致安全问题(如安装恶意软件、窃取数据等)。
建议:
在运行之前,先检查脚本的内容。例如:
bash
浅色版本
curl -fsSL https://ollama.com/install.sh | less
这会将脚本内容输出到终端供您查看。确认无误后再运行。
(b) 是否需要管理员权限
如果脚本需要写入系统目录(如 /usr/local/bin),可能需要使用 sudo 提升权限:
bash
浅色版本
curl -fsSL https://ollama.com/install.sh | sudo sh
注意:使用 sudo 运行脚本时要格外小心,因为它会以超级用户权限执行所有命令。
(c) 日志记录
为了便于排查问题,可以将脚本的输出保存到日志文件中:
bash
浅色版本
curl -fsSL https://ollama.com/install.sh | sh > install.log 2>&1
这样,所有输出(包括标准输出和错误信息)都会被记录到 install.log 文件中。
4. 替代方法:手动下载并检查脚本
如果您希望更安全地执行此操作,可以采用以下步骤:
(a) 手动下载脚本
使用 curl 或 wget 下载脚本到本地:
bash
浅色版本
curl -fsSL https://ollama.com/install.sh -o install.sh
(b) 检查脚本内容
使用文本编辑器或查看工具检查脚本内容:
bash
浅色版本
less install.sh
(c) 执行脚本
确认脚本无误后,再运行它:
bash
浅色版本
sh install.sh
或者使用 chmod 赋予执行权限后运行:
bash
浅色版本
chmod +x install.sh
./install.sh
5. 总结
curl -fsSL https://ollama.com/install.sh | sh 是一种快速下载并执行脚本的方式。
它的本质是从远程服务器获取脚本内容,并通过 sh 实时执行。
优点:简单快捷。
缺点:存在潜在的安全风险,尤其是当脚本来源不可信时。
建议:在运行之前检查脚本内容,确保其来源可信,并根据需要记录日志以便排查问题。
vllm
对话模板
指定jinja2文件启动:
vllm serve <model> --chat-template ./path-to-chat-template.jinja
指定GPU启动
CUDA_VISIBLE_DEVICES=1 vllm serve {model_dir} --dtype=half --enforce-eager
1. CUDA_VISIBLE_DEVICES=1
作用:指定使用第 1 个 GPU(编号从 0 开始)。
说明:在多 GPU 系统中,这个环境变量决定了当前进程可以“看到”的 GPU。只设置为 1 表示只使用第 1 个 GPU。
2. vllm serve
作用:这是 vllm 的命令行工具,serve 表示启动一个 HTTP 服务,用于在线部署模型。
说明:你也可以用 vllm 的其他子命令,比如 benchmark、convert 等。
3. {model_dir}
作用:替换为你要加载的模型路径。
例子:比如 "meta-llama/Llama-2-7b-hf" 或本地路径如 ./checkpoints/llama2.
4. --dtype=half
作用:以半精度 (float16) 加载模型,减少显存占用,提高推理速度。
可选值:常见还有 float32、bfloat16,取决于你的硬件支持。
5. --enforce-eager
作用:强制使用 Eager 模式(即非 TorchScript / 非 Lazy 模式)。
说明:这可能有助于调试或确保某些模型行为一致,但可能牺牲一些性能。
lmdeploy
欢迎来到 LMDeploy 的中文教程! — lmdeploy
安装:
conda create -n lmdeploy python=3.10 -y
conda activate lmdeploy
pip install lmdeploy
pip install partial-json-parser
打开cli:
lmdeploy serve api_server /root/autodl-tmp/model/Qwen/Qwen2.5-1.5B-Instruct --server-port 23333 --model-name zyhhsss
对话模板
lmdeploy官方标准json
{
"model_name": "your awesome chat template name",
"system": "<|im_start|>system\n",
"meta_instruction": "You are a robot developed by LMDeploy.",
"eosys": "<|im_end|>\n",
"user": "<|im_start|>user\n",
"eoh": "<|im_end|>\n",
"assistant": "<|im_start|>assistant\n",
"eoa": "<|im_end|>",
"separator": "\n",
"capability": "chat",
"stop_words": ["<|im_end|>"]
}
json格式:
lmdeploy serve api_server model --chat-template ${JSON_FILE}
并行推理:
推荐使用 Key-Value(KV) Cache 量化
lmdeploy serve api_server internlm/internlm2_5-7b-chat --quant-policy 8
turbomind加速:
启动模型:
lmdeploy serve api_server /root/autodl-tmp/model/Qwen/Qwen2.5-1.5B-Instruct --server-port 23333
turbomind加速:
lmdeploy chat modelname
lmdeploy chat turbomind aaa --model-name bbb
模型转换:
lmdeploy convert 模型coinfig中的name huggingface的模型路径
生成的ws在命令执行的位置
environment.yml
env+cuda12.4
Ubuntu:
name: lmdeploy
channels:
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
- defaults
dependencies:
- _libgcc_mutex=0.1=main
- _openmp_mutex=5.1=1_gnu
- bzip2=1.0.8=h5eee18b_6
- ca-certificates=2025.2.25=h06a4308_0
- ld_impl_linux-64=2.40=h12ee557_0
- libffi=3.4.4=h6a678d5_1
- libgcc-ng=11.2.0=h1234567_1
- libgomp=11.2.0=h1234567_1
- libstdcxx-ng=11.2.0=h1234567_1
- libuuid=1.41.5=h5eee18b_0
- ncurses=6.4=h6a678d5_0
- openssl=3.0.16=h5eee18b_0
- pip=25.0=py310h06a4308_0
- python=3.10.16=he870216_1
- readline=8.2=h5eee18b_0
- setuptools=75.8.0=py310h06a4308_0
- sqlite=3.45.3=h5eee18b_0
- tk=8.6.14=h39e8969_0
- tzdata=2025a=h04d1e81_0
- wheel=0.45.1=py310h06a4308_0
- xz=5.6.4=h5eee18b_1
- zlib=1.2.13=h5eee18b_1
- pip:
- accelerate==1.5.2
- addict==2.4.0
- aiosignal==1.3.2
- airportsdata==20250224
- annotated-types==0.7.0
- anyio==4.9.0
- attrs==25.3.0
- certifi==2025.1.31
- cfgv==3.4.0
- charset-normalizer==3.4.1
- click==8.1.8
- cloudpickle==3.1.1
- diskcache==5.6.3
- distlib==0.3.9
- distro==1.9.0
- einops==0.8.1
- exceptiongroup==1.2.2
- fastapi==0.115.12
- filelock==3.18.0
- fire==0.7.0
- frozenlist==1.5.0
- fsspec==2025.3.0
- genson==1.3.0
- h11==0.14.0
- httpcore==1.0.7
- httpx==0.28.1
- huggingface-hub==0.29.3
- identify==2.6.9
- idna==3.10
- interegular==0.3.3
- iso3166==2.1.1
- jinja2==3.1.6
- jiter==0.9.0
- jsonschema==4.23.0
- jsonschema-specifications==2024.10.1
- lark==1.2.2
- lmdeploy==0.7.2.post1
- markdown-it-py==3.0.0
- markupsafe==3.0.2
- mdurl==0.1.2
- mmengine-lite==0.10.7
- mpmath==1.3.0
- msgpack==1.1.0
- nest-asyncio==1.6.0
- networkx==3.4.2
- nodeenv==1.9.1
- numpy==1.26.4
- nvidia-cublas-cu12==12.4.5.8
- nvidia-cuda-cupti-cu12==12.4.127
- nvidia-cuda-nvrtc-cu12==12.4.127
- nvidia-cuda-runtime-cu12==12.4.127
- nvidia-cudnn-cu12==9.1.0.70
- nvidia-cufft-cu12==11.2.1.3
- nvidia-curand-cu12==10.3.5.147
- nvidia-cusolver-cu12==11.6.1.9
- nvidia-cusparse-cu12==12.3.1.170
- nvidia-ml-py==12.570.86
- nvidia-nccl-cu12==2.21.5
- nvidia-nvjitlink-cu12==12.4.127
- nvidia-nvtx-cu12==12.4.127
- openai==1.69.0
- outlines==0.2.1
- outlines-core==0.1.26
- packaging==24.2
- partial-json-parser==0.2.1.1.post5
- peft==0.14.0
- pillow==11.1.0
- platformdirs==4.3.7
- pre-commit==4.2.0
- protobuf==6.30.2
- psutil==7.0.0
- pydantic==2.11.1
- pydantic-core==2.33.0
- pygments==2.19.1
- pynvml==12.0.0
- pyyaml==6.0.2
- ray==2.44.1
- referencing==0.36.2
- regex==2024.11.6
- requests==2.32.3
- rich==13.9.4
- rpds-py==0.24.0
- safetensors==0.5.3
- sentencepiece==0.2.0
- shortuuid==1.0.13
- sniffio==1.3.1
- starlette==0.46.1
- sympy==1.13.1
- termcolor==2.5.0
- tiktoken==0.9.0
- tokenizers==0.21.1
- tomli==2.2.1
- torch==2.5.1
- torchvision==0.20.1
- tqdm==4.67.1
- transformers==4.50.3
- triton==3.1.0
- typing-extensions==4.13.0
- typing-inspection==0.4.0
- urllib3==2.3.0
- uvicorn==0.34.0
- virtualenv==20.29.3
- yapf==0.43.0
prefix: /root/miniconda3/envs/lmdeploy
win11:
微调框架:
Llamafactory:
端口:7860
LLaMA-Factory/README_zh.md at main · hiyouga/LLaMA-Factory
conda:
conda create -n llamafactory python=3.10 -y
conda activate llamafactory
conda remove --name myenv --all
版本冲突:
解决一:
pip install -e .
pip install gradio==5.23.1
pip install bitsandbytes==0.45.3
pip install peft ==0.12.0
DISABLE_VERSION_CHECK=1 llamafactory-cli webui
解决二:
bitsandbytes=0.44.0
accelerate=1.1.1
peft= 0.12.0
transformers=4.49.0
torch=2.5.1
解决三:
docker
解决四:用requirement.txt/environment.yml
git:
git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
pip install -e ".[torch,metrics]"
推荐使用:
pip install -e .
bug!!!!
这个包(gradio)解析json有问题:
5.23.1这个版本可以但是原文中里面提示:5.21.0却不行
pip install --force-reinstall gradio==5.21.0
pip install --upgrade gradio
使用flashattn2加速:
pip install bitsandbytes==0.43.3
启动:
(llmdeploy) root@autodl-container-2fb0448cad-36aa5df2:~/autodl-tmp/LLaMA-Factory/LLaMA-Factory# llamafactory-cli
----------------------------------------------------------------------
| Usage: |
| llamafactory-cli api -h: launch an OpenAI-style API server |
| llamafactory-cli chat -h: launch a chat interface in CLI |
| llamafactory-cli eval -h: evaluate models |
| llamafactory-cli export -h: merge LoRA adapters and export model |
| llamafactory-cli train -h: train models |
| llamafactory-cli webchat -h: launch a chat interface in Web UI |
| llamafactory-cli webui: launch LlamaBoard |
| llamafactory-cli version: show version info |
----------------------------------------------------------------------
llamafactory-cli webui
注意:
在 Python 项目中,setup.py 文件通常会包含两个主要部分来定义依赖项:
install_requires:
这是项目运行所必需的基础依赖。
这些依赖项会在你运行 pip install . 或 pip install -e . 时被安装。
extras_require:
这是项目的可选依赖组(如 torch、metrics 等)。
这些依赖项只有在明确指定时才会被安装,例如通过 pip install -e ".[torch,metrics]"。
environment.yml
ubuntu+cuda12.4
name: llamafactory
channels:
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
- defaults
dependencies:
- _libgcc_mutex=0.1=main
- _openmp_mutex=5.1=1_gnu
- bzip2=1.0.8=h5eee18b_6
- ca-certificates=2025.2.25=h06a4308_0
- ld_impl_linux-64=2.40=h12ee557_0
- libffi=3.4.4=h6a678d5_1
- libgcc-ng=11.2.0=h1234567_1
- libgomp=11.2.0=h1234567_1
- libstdcxx-ng=11.2.0=h1234567_1
- libuuid=1.41.5=h5eee18b_0
- ncurses=6.4=h6a678d5_0
- openssl=3.0.16=h5eee18b_0
- pip=25.0=py310h06a4308_0
- python=3.10.16=he870216_1
- readline=8.2=h5eee18b_0
- setuptools=75.8.0=py310h06a4308_0
- sqlite=3.45.3=h5eee18b_0
- tk=8.6.14=h39e8969_0
- wheel=0.45.1=py310h06a4308_0
- xz=5.6.4=h5eee18b_1
- zlib=1.2.13=h5eee18b_1
- pip:
- accelerate==1.4.0
- aiofiles==23.2.1
- aiohappyeyeballs==2.6.1
- aiohttp==3.11.14
- aiosignal==1.3.2
- annotated-types==0.7.0
- anyio==4.9.0
- async-timeout==5.0.1
- attrs==25.3.0
- audioread==3.0.1
- av==14.2.0
- bitsandbytes==0.45.3
- certifi==2025.1.31
- cffi==1.17.1
- charset-normalizer==3.4.1
- click==8.1.8
- contourpy==1.3.1
- cycler==0.12.1
- datasets==3.3.2
- decorator==5.2.1
- dill==0.3.8
- docstring-parser==0.16
- einops==0.8.1
- exceptiongroup==1.2.2
- fastapi==0.115.12
- ffmpy==0.5.0
- filelock==3.18.0
- fire==0.7.0
- fonttools==4.56.0
- frozenlist==1.5.0
- fsspec==2024.12.0
- gradio==5.23.1
- gradio-client==1.8.0
- groovy==0.1.2
- h11==0.14.0
- httpcore==1.0.7
- httpx==0.28.1
- huggingface-hub==0.29.3
- idna==3.10
- jinja2==3.1.6
- joblib==1.4.2
- kiwisolver==1.4.8
- lazy-loader==0.4
- librosa==0.11.0
- llamafactory==0.9.3.dev0
- llvmlite==0.44.0
- markdown-it-py==3.0.0
- markupsafe==2.1.5
- matplotlib==3.10.1
- mdurl==0.1.2
- mpmath==1.3.0
- msgpack==1.1.0
- multidict==6.2.0
- multiprocess==0.70.16
- networkx==3.4.2
- numba==0.61.0
- numpy==1.26.4
- nvidia-cublas-cu12==12.4.5.8
- nvidia-cuda-cupti-cu12==12.4.127
- nvidia-cuda-nvrtc-cu12==12.4.127
- nvidia-cuda-runtime-cu12==12.4.127
- nvidia-cudnn-cu12==9.1.0.70
- nvidia-cufft-cu12==11.2.1.3
- nvidia-curand-cu12==10.3.5.147
- nvidia-cusolver-cu12==11.6.1.9
- nvidia-cusparse-cu12==12.3.1.170
- nvidia-cusparselt-cu12==0.6.2
- nvidia-nccl-cu12==2.21.5
- nvidia-nvjitlink-cu12==12.4.127
- nvidia-nvtx-cu12==12.4.127
- orjson==3.10.16
- packaging==24.2
- pandas==2.2.3
- peft==0.15.1
- pillow==11.1.0
- platformdirs==4.3.7
- pooch==1.8.2
- propcache==0.3.1
- protobuf==6.30.2
- psutil==7.0.0
- pyarrow==19.0.1
- pycparser==2.22
- pydantic==2.11.1
- pydantic-core==2.33.0
- pydub==0.25.1
- pygments==2.19.1
- pyparsing==3.2.3
- python-dateutil==2.9.0.post0
- python-multipart==0.0.20
- pytz==2025.2
- pyyaml==6.0.2
- regex==2024.11.6
- requests==2.32.3
- rich==13.9.4
- ruff==0.11.2
- safehttpx==0.1.6
- safetensors==0.5.3
- scikit-learn==1.6.1
- scipy==1.15.2
- semantic-version==2.10.0
- sentencepiece==0.2.0
- shellingham==1.5.4
- shtab==1.7.1
- six==1.17.0
- sniffio==1.3.1
- soundfile==0.13.1
- soxr==0.5.0.post1
- sse-starlette==2.2.1
- starlette==0.46.1
- sympy==1.13.1
- termcolor==2.5.0
- threadpoolctl==3.6.0
- tiktoken==0.9.0
- tokenizers==0.21.0
- tomlkit==0.13.2
- torch==2.6.0
- tqdm==4.67.1
- transformers==4.49.0
- triton==3.2.0
- trl==0.9.6
- typer==0.15.2
- typing-extensions==4.13.0
- typing-inspection==0.4.0
- tyro==0.8.14
- tzdata==2025.2
- urllib3==2.3.0
- uvicorn==0.34.0
- websockets==15.0.1
- xxhash==3.5.0
- yarl==1.18.3
prefix: /root/miniconda3/envs/llamafactory
对话模板转jinjia2:
放在src/llamafactory/data目录下
import sys
import os
# 将项目根目录添加到 Python 路径
root_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
sys.path.append(root_dir)
from llamafactory.data.template import TEMPLATES
from transformers import AutoTokenizer
# 1. 初始化分词器(任意支持的分词器均可)
tokenizer = AutoTokenizer.from_pretrained("/root/autodl-tmp/llm/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B")
# 2. 获取模板对象
template_name = "qwen" # 替换为你需要查看的模板名称
template = TEMPLATES[template_name]
# 3. 修复分词器的 Jinja 模板
template.fix_jinja_template(tokenizer)
# 4. 直接输出模板的 Jinja 格式
print("=" * 40)
print(f"Template [{template_name}] 的 Jinja 格式:")
print("=" * 40)
print(tokenizer.chat_template)
Xtuner:
官网:
欢迎来到 XTuner 的中文文档 — XTuner 0.2.0rc0 文档
配置环境:
安装环境:
conda create --name xtuner-env python=3.10 -y
conda activate xtuner-env
//conda env create -f environment.yml
git clone https://github.com/InternLM/xtuner.git
cd xtuner
pip install -e '.[deepspeed]' -i https://pypi.mirrors.ustc.edu.cn/simple/
版本冲突:
runtime.txt中
torch==2.5.1
torchvision==0.20.1
-
验证:
xtuner list-cfg
训练:
仅支持微调configs下的模型
见下方训练脚本
启动微调脚本
xtuner train internlm2_chat_1_8b_qlora_alpaca_e3.py --work-dir
模型转换为huggingface模型:
xtuner convert pth_to_hf ${FINETUNE_CFG} ${PTH_PATH} ${SAVE_PATH}
# 例如:xtuner convert pth_to_hf /root/autodl-tmp/xtuner-main/xtuner-main/jiaoben/qwen1_5_1_8b_chat_qlora_alpaca_e3.py /root/autodl-tmp/xtuner-main/xtuner-main/work_dirs/qwen1_5_1_8b_chat_qlora_alpaca_e3/iter_2500.pth /root/autodl-tmp/xtuner-main/xtu
ner-main/huggingface
lora/qlora进行模型合并:
xtuner convert merge ${基座模型} ${Huggingface模型} ${合并模型路径}
例如:
xtuner convert merge /root/autodl-tmp/model/Qwen/Qwen2.5-1.5B-Instruct /root/autodl-tmp/xtuner-main/xtuner-main/huggingface /root/autodl-tmp/xtuner-main/xtuner-main/merge
多卡并行:
# 以下命令根据需要任选其一
xtuner train xxx --deepspeed deepspeed_zero1
xtuner train xxx --deepspeed deepspeed_zero2
xtuner train xxx --deepspeed deepspeed_zero2_offload
xtuner train xxx --deepspeed deepspeed_zero3
xtuner train xxx --deepspeed deepspeed_zero3_offload
用下面这个多卡并行
NPROC_PER_NODE=${GPU_NUM} xtuner train ./config.py --deepspeed deepspeed_zero2
python脚本模板:
一共修改14个(最下面有个load权重)
# Copyright (c) OpenMMLab. All rights reserved.
import torch
from datasets import load_dataset
from mmengine.dataset import DefaultSampler
from mmengine.hooks import (
CheckpointHook,
DistSamplerSeedHook,
IterTimerHook,
LoggerHook,
ParamSchedulerHook,
)
from mmengine.optim import AmpOptimWrapper, CosineAnnealingLR, LinearLR
from peft import LoraConfig
from torch.optim import AdamW
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from xtuner.dataset import process_hf_dataset
from xtuner.dataset.collate_fns import default_collate_fn
from xtuner.dataset.map_fns import alpaca_map_fn, template_map_fn_factory
from xtuner.engine.hooks import (
DatasetInfoHook,
EvaluateChatHook,
VarlenAttnArgsToMessageHubHook,
)
from xtuner.engine.runner import TrainLoop
from xtuner.model import SupervisedFinetune
from xtuner.parallel.sequence import SequenceParallelSampler
from xtuner.utils import PROMPT_TEMPLATE, SYSTEM_TEMPLATE
#######################################################################
# PART 1 Settings #
#######################################################################
# Model
# pretrained_model_name_or_path = "Qwen/Qwen1.5-1.8B-Chat"
#基座模型 1
pretrained_model_name_or_path = "/root/autodl-tmp/model/Qwen/Qwen2.5-1.5B-Instruct"
use_varlen_attn = False
# Data 2
#
# data_files = [
# '/root/public/data/target_data_1.json',
# '/root/public/data/target_data_2.json',
# '/root/public/data/target_data_3.json'
# ]
data_files = '/root/autodl-tmp/xtuner-main/xtuner-main/data/output.json'#数据集
# 提示词模板 3
prompt_template = PROMPT_TEMPLATE.qwen_chat
# 长度 4
max_length = 512
pack_to_max_length = True
# parallel
sequence_parallel_size = 1
# Scheduler & Optimizer
# 优化器
# 批次 5
batch_size = 10 # per_device
accumulative_counts = 16
accumulative_counts *= sequence_parallel_size
dataloader_num_workers = 0
# 最大轮次 6
max_epochs = 3000
optim_type = AdamW
lr = 2e-4
betas = (0.9, 0.999)
weight_decay = 0
max_norm = 1 # grad clip
warmup_ratio = 0.03
# 多少轮保存 7
save_steps = 500
# 最大保存数量 8
save_total_limit = 2 # Maximum checkpoints to keep (-1 means unlimited)
# Evaluate the generation performance during the training
evaluation_freq = 500
SYSTEM = SYSTEM_TEMPLATE.alpaca
# 主观验证 9
evaluation_inputs = ["这只烤乳猪火出圈啦", "朕决定于今日称帝","珍爱生命,远离死亡"
,"吃书有助于消化知识"]
#######################################################################
# PART 2 Model & Tokenizer #
#######################################################################
tokenizer = dict(
type=AutoTokenizer.from_pretrained,
pretrained_model_name_or_path=pretrained_model_name_or_path,
trust_remote_code=True,
padding_side="right",
)
model = dict(
type=SupervisedFinetune,
use_varlen_attn=use_varlen_attn,
llm=dict(
type=AutoModelForCausalLM.from_pretrained,
pretrained_model_name_or_path=pretrained_model_name_or_path,
trust_remote_code=True,
torch_dtype=torch.float16,
# 微调方法 下面是qlora,用lora给注释掉 10
quantization_config=dict(
type=BitsAndBytesConfig,
# 四位
load_in_4bit=False,
# 八位
load_in_8bit=True,
llm_int8_threshold=6.0,
llm_int8_has_fp16_weight=False,
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
),
),
# lora配置 11
lora=dict(
type=LoraConfig,
r=64,
lora_alpha=128,
lora_dropout=0.1,
bias="none",
task_type="CAUSAL_LM",
),
)
#######################################################################
# PART 3 Dataset & Dataloader #
#######################################################################
alpaca_en = dict(
type=process_hf_dataset,
# dataset=dict(type=load_dataset, path=alpaca_en_path),
# 加载数据集 12
dataset=dict(type=load_dataset, path="json",data_files=data_files),
tokenizer=tokenizer,
max_length=max_length,
# 加载数据集匹配格式 13
dataset_map_fn=None,
template_map_fn=dict(type=template_map_fn_factory, template=prompt_template),
remove_unused_columns=True,
shuffle_before_pack=True,
pack_to_max_length=pack_to_max_length,
use_varlen_attn=use_varlen_attn,
)
sampler = SequenceParallelSampler if sequence_parallel_size > 1 else DefaultSampler
train_dataloader = dict(
batch_size=batch_size,
num_workers=dataloader_num_workers,
dataset=alpaca_en,
sampler=dict(type=sampler, shuffle=True),
collate_fn=dict(type=default_collate_fn, use_varlen_attn=use_varlen_attn),
)
#######################################################################
# PART 4 Scheduler & Optimizer #
#######################################################################
# optimizer
# 优化器相关 14
optim_wrapper = dict(
type=AmpOptimWrapper,
optimizer=dict(type=optim_type, lr=lr, betas=betas, weight_decay=weight_decay),
clip_grad=dict(max_norm=max_norm, error_if_nonfinite=False),
accumulative_counts=accumulative_counts,
loss_scale="dynamic",
dtype="float16",
)
# learning policy
# More information: https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/param_scheduler.md # noqa: E501
param_scheduler = [
dict(
type=LinearLR,
start_factor=1e-5,
by_epoch=True,
begin=0,
end=warmup_ratio * max_epochs,
convert_to_iter_based=True,
),
dict(
type=CosineAnnealingLR,
eta_min=0.0,
by_epoch=True,
begin=warmup_ratio * max_epochs,
end=max_epochs,
convert_to_iter_based=True,
),
]
# train, val, test setting
train_cfg = dict(type=TrainLoop, max_epochs=max_epochs)
#######################################################################
# PART 5 Runtime #
#######################################################################
# Log the dialogue periodically during the training process, optional
custom_hooks = [
dict(type=DatasetInfoHook, tokenizer=tokenizer),
dict(
type=EvaluateChatHook,
tokenizer=tokenizer,
every_n_iters=evaluation_freq,
evaluation_inputs=evaluation_inputs,
system=SYSTEM,
prompt_template=prompt_template,
),
]
if use_varlen_attn:
custom_hooks += [dict(type=VarlenAttnArgsToMessageHubHook)]
# configure default hooks
default_hooks = dict(
# record the time of every iteration.
timer=dict(type=IterTimerHook),
# print log every 10 iterations.
logger=dict(type=LoggerHook, log_metric_by_epoch=False, interval=10),
# enable the parameter scheduler.
param_scheduler=dict(type=ParamSchedulerHook),
# save checkpoint per `save_steps`.
checkpoint=dict(
type=CheckpointHook,
by_epoch=False,
interval=save_steps,
max_keep_ckpts=save_total_limit,
),
# set sampler seed in distributed evrionment.
sampler_seed=dict(type=DistSamplerSeedHook),
)
# configure environment
env_cfg = dict(
# whether to enable cudnn benchmark
cudnn_benchmark=False,
# set multi process parameters
mp_cfg=dict(mp_start_method="fork", opencv_num_threads=0),
# set distributed parameters
dist_cfg=dict(backend="nccl"),
)
# set visualizer
visualizer = None
# set log level
log_level = "INFO"
# load from which checkpoint
#15加载权重 load .pth文件夹
#ep:load_from = "path_to_pth"
load_from = None
# whether to resume training from the loaded checkpoint
resume = False
# Defaults to use random seed and disable `deterministic`
randomness = dict(seed=None, deterministic=False)
# set log processor
log_processor = dict(by_epoch=False)
environment.yml
name: xtuner-env
channels:
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
- defaults
dependencies:
- _libgcc_mutex=0.1=main
- _openmp_mutex=5.1=1_gnu
- bzip2=1.0.8=h5eee18b_6
- ca-certificates=2025.2.25=h06a4308_0
- ld_impl_linux-64=2.40=h12ee557_0
- libffi=3.4.4=h6a678d5_1
- libgcc-ng=11.2.0=h1234567_1
- libgomp=11.2.0=h1234567_1
- libstdcxx-ng=11.2.0=h1234567_1
- libuuid=1.41.5=h5eee18b_0
- ncurses=6.4=h6a678d5_0
- openssl=3.0.16=h5eee18b_0
- pip=25.0=py310h06a4308_0
- python=3.10.16=he870216_1
- readline=8.2=h5eee18b_0
- setuptools=75.8.0=py310h06a4308_0
- sqlite=3.45.3=h5eee18b_0
- tk=8.6.14=h39e8969_0
- wheel=0.45.1=py310h06a4308_0
- xz=5.6.4=h5eee18b_1
- zlib=1.2.13=h5eee18b_1
- pip:
- accelerate==1.6.0
- addict==2.4.0
- aiohappyeyeballs==2.6.1
- aiohttp==3.11.16
- aiosignal==1.3.2
- annotated-types==0.7.0
- async-timeout==5.0.1
- attrs==25.3.0
- bitsandbytes==0.45.0
- certifi==2025.1.31
- charset-normalizer==3.4.1
- contourpy==1.3.1
- cycler==0.12.1
- datasets==3.5.0
- deepspeed==0.16.2
- dill==0.3.8
- einops==0.8.1
- et-xmlfile==2.0.0
- filelock==3.18.0
- fonttools==4.57.0
- frozenlist==1.5.0
- fsspec==2024.12.0
- hjson==3.1.0
- huggingface-hub==0.30.1
- idna==3.10
- imageio==2.37.0
- jinja2==3.1.6
- kiwisolver==1.4.8
- lazy-loader==0.4
- loguru==0.7.3
- markdown-it-py==3.0.0
- markupsafe==3.0.2
- matplotlib==3.10.1
- mdurl==0.1.2
- mmengine==0.10.6
- modelscope==1.25.0
- mpi4py-mpich==3.1.5
- mpmath==1.3.0
- msgpack==1.1.0
- multidict==6.3.2
- multiprocess==0.70.16
- networkx==3.4.2
- ninja==1.11.1.4
- numpy==2.2.4
- nvidia-cublas-cu12==12.4.5.8
- nvidia-cuda-cupti-cu12==12.4.127
- nvidia-cuda-nvrtc-cu12==12.4.127
- nvidia-cuda-runtime-cu12==12.4.127
- nvidia-cudnn-cu12==9.1.0.70
- nvidia-cufft-cu12==11.2.1.3
- nvidia-curand-cu12==10.3.5.147
- nvidia-cusolver-cu12==11.6.1.9
- nvidia-cusparse-cu12==12.3.1.170
- nvidia-nccl-cu12==2.21.5
- nvidia-nvjitlink-cu12==12.4.127
- nvidia-nvtx-cu12==12.4.127
- opencv-python==4.11.0.86
- openpyxl==3.1.5
- packaging==24.2
- pandas==2.2.3
- peft==0.15.1
- pillow==11.1.0
- platformdirs==4.3.7
- propcache==0.3.1
- psutil==7.0.0
- py-cpuinfo==9.0.0
- pyarrow==19.0.1
- pydantic==2.11.2
- pydantic-core==2.33.1
- pygments==2.19.1
- pyparsing==3.2.3
- python-dateutil==2.9.0.post0
- pytz==2025.2
- pyyaml==6.0.2
- regex==2024.11.6
- requests==2.32.3
- rich==14.0.0
- safetensors==0.5.3
- scikit-image==0.25.2
- scipy==1.15.2
- sentencepiece==0.2.0
- six==1.17.0
- sympy==1.13.1
- termcolor==3.0.1
- tifffile==2025.3.30
- tiktoken==0.9.0
- tokenizers==0.21.1
- tomli==2.2.1
- torch==2.5.1
- torchvision==0.20.1
- tqdm==4.67.1
- transformers==4.48.0
- transformers-stream-generator==0.0.5
- triton==3.1.0
- typing-extensions==4.13.1
- typing-inspection==0.4.0
- tzdata==2025.2
- urllib3==2.3.0
- xxhash==3.5.0
- yapf==0.43.0
- yarl==1.18.3
prefix: /root/miniconda3/envs/xtuner-env
评测模型的工具:
OpenCompass:
官方:https://doc.opencompass.org.cn/get_started/installation.html
中文:https://opencompass.readthedocs.io/zh-cn/latest/get_started/installation.html
评估一个模型一般要评估两个数据集:
一、开源的数据集评估(评估通用能力)
二、自定义数据集评估(评估定制化能力)
配置环境:
本文用的 0.4.2
conda create --name opencompass python=3.10 -y
# conda create --name opencompass_lmdeploy python=3.10 -y
conda activate opencompass
git clone https://github.com/open-compass/opencompass opencompass
cd opencompass
pip install -e .
下载数据集:
wget https://github.com/open-compass/opencompass/releases/download/0.2.2.rc1/OpenCompassData-core-20240207.zip
unzip OpenCompassData-core-20240207.zip
把数据集放在代码的data目录下
(数据解压就是data文件夹)
environment.yml
name: opencompass
channels:
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
- defaults
dependencies:
- _libgcc_mutex=0.1=main
- _openmp_mutex=5.1=1_gnu
- bzip2=1.0.8=h5eee18b_6
- ca-certificates=2025.2.25=h06a4308_0
- ld_impl_linux-64=2.40=h12ee557_0
- libffi=3.4.4=h6a678d5_1
- libgcc-ng=11.2.0=h1234567_1
- libgomp=11.2.0=h1234567_1
- libstdcxx-ng=11.2.0=h1234567_1
- libuuid=1.41.5=h5eee18b_0
- ncurses=6.4=h6a678d5_0
- openssl=3.0.16=h5eee18b_0
- pip=25.0=py310h06a4308_0
- python=3.10.16=he870216_1
- readline=8.2=h5eee18b_0
- setuptools=75.8.0=py310h06a4308_0
- sqlite=3.45.3=h5eee18b_0
- tk=8.6.14=h39e8969_0
- tzdata=2025a=h04d1e81_0
- wheel=0.45.1=py310h06a4308_0
- xz=5.6.4=h5eee18b_1
- zlib=1.2.13=h5eee18b_1
- pip:
- absl-py==2.2.2
- accelerate==1.6.0
- addict==2.4.0
- aiohappyeyeballs==2.6.1
- aiohttp==3.11.16
- aiosignal==1.3.2
- annotated-types==0.7.0
- anyio==4.9.0
- async-timeout==5.0.1
- attrs==25.3.0
- certifi==2025.1.31
- charset-normalizer==3.4.1
- click==8.1.8
- colorama==0.4.6
- contourpy==1.3.2
- cpm-kernels==1.0.11
- cycler==0.12.1
- datasets==3.5.0
- dill==0.3.8
- distro==1.9.0
- einops==0.8.1
- evaluate==0.4.3
- exceptiongroup==1.2.2
- filelock==3.18.0
- fonttools==4.57.0
- frozenlist==1.6.0
- fsspec==2024.12.0
- func-timeout==4.3.5
- fuzzywuzzy==0.18.0
- gradio-client==1.8.0
- h11==0.14.0
- h5py==3.13.0
- httpcore==1.0.8
- httpx==0.27.2
- huggingface-hub==0.30.2
- idna==3.10
- immutabledict==4.2.1
- importlib-metadata==8.6.1
- jieba==0.42.1
- jinja2==3.1.6
- jiter==0.9.0
- joblib==1.4.2
- json5==0.12.0
- jsonlines==4.0.0
- kiwisolver==1.4.8
- levenshtein==0.27.1
- lxml==5.3.2
- markdown-it-py==3.0.0
- markupsafe==3.0.2
- matplotlib==3.10.1
- mdurl==0.1.2
- mmengine-lite==0.10.7
- mpmath==1.3.0
- multidict==6.4.3
- multiprocess==0.70.16
- networkx==3.4.2
- nltk==3.9.1
- numpy==1.26.4
- nvidia-cublas-cu12==12.4.5.8
- nvidia-cuda-cupti-cu12==12.4.127
- nvidia-cuda-nvrtc-cu12==12.4.127
- nvidia-cuda-runtime-cu12==12.4.127
- nvidia-cudnn-cu12==9.1.0.70
- nvidia-cufft-cu12==11.2.1.3
- nvidia-curand-cu12==10.3.5.147
- nvidia-cusolver-cu12==11.6.1.9
- nvidia-cusparse-cu12==12.3.1.170
- nvidia-cusparselt-cu12==0.6.2
- nvidia-ml-py==12.570.86
- nvidia-nccl-cu12==2.21.5
- nvidia-nvjitlink-cu12==12.4.127
- nvidia-nvtx-cu12==12.4.127
- nvitop==1.4.2
- openai==1.75.0
- opencc==1.1.9
- opencv-python-headless==4.11.0.86
- packaging==24.2
- pandas==1.5.3
- pillow==11.2.1
- platformdirs==4.3.7
- portalocker==3.1.1
- prettytable==3.16.0
- propcache==0.3.1
- protobuf==6.30.2
- psutil==7.0.0
- pyarrow==19.0.1
- pydantic==2.11.3
- pydantic-core==2.33.1
- pyext==0.7
- pygments==2.19.1
- pyparsing==3.2.3
- python-dateutil==2.9.0.post0
- python-levenshtein==0.27.1
- pytz==2025.2
- pyyaml==6.0.2
- rank-bm25==0.2.2
- rapidfuzz==3.13.0
- regex==2024.11.6
- requests==2.32.3
- retrying==1.3.4
- rich==14.0.0
- rouge==1.0.1
- rouge-chinese==1.0.3
- rouge-score==0.1.2
- sacrebleu==2.5.1
- safetensors==0.5.3
- scikit-learn==1.5.0
- scipy==1.15.2
- seaborn==0.13.2
- sentence-transformers==4.1.0
- shellingham==1.5.4
- six==1.17.0
- sniffio==1.3.1
- sympy==1.13.1
- tabulate==0.9.0
- termcolor==3.0.1
- threadpoolctl==3.6.0
- tiktoken==0.9.0
- timeout-decorator==0.5.0
- tokenizers==0.21.1
- tomli==2.2.1
- torch==2.6.0
- tqdm==4.67.1
- transformers==4.51.3
- tree-sitter==0.21.3
- tree-sitter-languages==1.10.2
- triton==3.2.0
- typer==0.15.2
- typing-extensions==4.13.2
- typing-inspection==0.4.0
- urllib3==2.4.0
- wcwidth==0.2.13
- websockets==15.0.1
- xxhash==3.5.0
- yapf==0.43.0
- yarl==1.20.0
- zipp==3.21.0
prefix: /root/autodl-tmp/conda/envs/opencompass
数据集评估:
数据集分类
_gen后缀数据集:生成式评估,需后处理提取答案(如ceval_gen)
_ppl后缀数据集:困惑度评估,直接比对选项概率(如ceval_ppl)
C-Eval:侧重中文STEM和社会科学知识,包含1.3万道选择题
LawBench:法律领域专项评估,需额外克隆仓库并配置路径
评估一个模型一般要评估两个数据集:
一、开源的数据集评估(评估通用能力)
二、自定义数据集评估(评估定制化能力)
1. 主流开源数据集
OpenCompass内置超过70个数据集,覆盖五大能力维度:
知识类:C-Eval(中文考试题)、CMMLU(多语言知识问答)、MMLU(英文多选题)。
推理类:GSM8K(数学推理)、BBH(复杂推理链)。
语言类:CLUE(中文理解)、AFQMC(语义相似度)。
代码类:HumanEval(代码生成)、MBPP(编程问题)。
多模态类:MMBench(图像理解)、SEED-Bench(多模态问答)
2. 自定义数据集
我们支持 .jsonl 和 .csv 两种格式的数据集。
2.1 选择题 (mcq)
对于选择 (mcq) 类型的数据,默认的字段如下:
question: 表示选择题的题干
A, B, C, …: 使用单个大写字母表示选项,个数不限定。默认只会从 A 开始,解析连续的字母作为选项。
answer: 表示选择题的正确答案,其值必须是上述所选用的选项之一,如 A, B, C 等。
对于非默认字段,我们都会进行读入,但默认不会使用。如需使用,则需要在 .meta.json 文件中进行指定。
.jsonl 格式样例如下:
{"question": "165+833+650+615=", "A": "2258", "B": "2263", "C": "2281", "answer": "B"}
{"question": "368+959+918+653+978=", "A": "3876", "B": "3878", "C": "3880", "answer": "A"}
{"question": "776+208+589+882+571+996+515+726=", "A": "5213", "B": "5263", "C": "5383", "answer": "B"}
{"question": "803+862+815+100+409+758+262+169=", "A": "4098", "B": "4128", "C": "4178", "answer": "C"}
.csv 格式样例如下:
question,A,B,C,answer
127+545+588+620+556+199=,2632,2635,2645,B
735+603+102+335+605=,2376,2380,2410,B
506+346+920+451+910+142+659+850=,4766,4774,4784,C
504+811+870+445=,2615,2630,2750,B
2.2问答题 (qa)
对于问答 (qa) 类型的数据,默认的字段如下:
question: 表示问答题的题干
answer: 表示问答题的正确答案。可缺失,表示该数据集无正确答案。
对于非默认字段,我们都会进行读入,但默认不会使用。如需使用,则需要在 .meta.json 文件中进行指定。
.jsonl 格式样例如下:
{"question": "752+361+181+933+235+986=", "answer": "3448"}
{"question": "712+165+223+711=", "answer": "1811"}
{"question": "921+975+888+539=", "answer": "3323"}
{"question": "752+321+388+643+568+982+468+397=", "answer": "4519"}
.csv 格式样例如下:
question,answer
123+147+874+850+915+163+291+604=,3967
149+646+241+898+822+386=,3142
332+424+582+962+735+798+653+214=,4700
649+215+412+495+220+738+989+452=,4170
评估命令:
评估本地的hf格式大模型:
参数解释:
--datasets:
评估所用数据集(数据集配置在框架系统中,可以使用
# 列出与llama和mmlu相关的所有配置
python tools/list_configs.py llama mmlu
来查看)
--hf-type:模型属于什么类型 一般模型名字后面由chat就填chat,没有写base或者不传这个参数
--hf-path:模型路径
--debug:捕获异常并提供详细信息
方法一:命令行(只能评估一个模型!!!!!!!!!!!!!!!!!!!!!!)
python run.py \
--datasets demo_gsm8k_chat_gen demo_math_chat_gen \
--hf-type chat \
--hf-path internlm/internlm2-chat-1_8b \
--debug
--work-dir /root/autodl-tmp/opencompass-main/opencompass-main/two_model
方法二:命令行+配置文件(多模型!!!!!!!!!!!!!!!!!!!!!!!)
--models:后面跟的模型名称,对应的配置文件目录在:opencompass/openconpass/configs/models/qwen2.5 去找模型。
模型名称解析:
hf前缀代表是huggingface评估方法
找到你要的py文件:
例如:hf_qwen1_5_0_5b_chat.py,然后修改 path换成绝对路径
run_cfg=dict(num_gpus=1)评估用的哪一块gpu,电脑上只有一块的写成0
from opencompass.models import HuggingFacewithChatTemplate
models = [
dict(
type=HuggingFacewithChatTemplate,
abbr='qwen1.5-0.5b-chat-hf',
path='Qwen/Qwen1.5-0.5B-Chat',
max_out_len=1024,
batch_size=8,
run_cfg=dict(num_gpus=0),
stop_words=['<|im_end|>', '<|im_start|>'],
)
]
注意:可以使用 python tools/list_configs.py hf_qwen 来查看模型名称,即跟在--models后面的参数
python run.py \
--models hf_internlm2_chat_1_8b hf_qwen2_1_5b_instruct \
--datasets demo_gsm8k_chat_gen demo_math_chat_gen \
--debug
--work-dir /root/autodl-tmp/opencompass-main/opencompass-main/two_model
评估加速:
1. pip install lmdeploy
2. 在config/models/下寻找 lmdeploy开头的py文件
2.1 修改path:
2.2 参数解释
engine_config=dict(session_len=16384, max_batch_size=16, tp=1),
tp:产生的结果用对应序号的gpu来评估。
from opencompass.models import TurboMindModelwithChatTemplate
models = [
dict(
type=TurboMindModelwithChatTemplate,
abbr='qwen1.5-1.8b-chat-turbomind',
path='Qwen/Qwen1.5-1.8B-Chat',
engine_config=dict(session_len=16384, max_batch_size=16, tp=1),
gen_config=dict(top_k=1, temperature=1e-6, top_p=0.9, max_new_tokens=4096),
max_seq_len=16384,
max_out_len=4096,
batch_size=16,
run_cfg=dict(num_gpus=1),
stop_words=['<|im_end|>', '<|im_start|>'],
)
]
3. 和上面一样 model填写你修改的配置文件地址
python run.py \
--models lmdeploy_xxxxx \
--datasets demo_gsm8k_chat_gen demo_math_chat_gen \
--debug
--work-dir /root/autodl-tmp/opencompass-main/opencompass-main/two_model
自定义数据及评估:
自定义数据集
我们支持 .jsonl 和 .csv 两种格式的数据集。
2.1 选择题 (mcq)
对于选择 (mcq) 类型的数据,默认的字段如下:
question: 表示选择题的题干
A, B, C, …: 使用单个大写字母表示选项,个数不限定。默认只会从 A 开始,解析连续的字母作为选项。
answer: 表示选择题的正确答案,其值必须是上述所选用的选项之一,如 A, B, C 等。
对于非默认字段,我们都会进行读入,但默认不会使用。如需使用,则需要在 .meta.json 文件中进行指定。
.jsonl 格式样例如下:
{"question": "165+833+650+615=", "A": "2258", "B": "2263", "C": "2281", "answer": "B"}
{"question": "368+959+918+653+978=", "A": "3876", "B": "3878", "C": "3880", "answer": "A"}
{"question": "776+208+589+882+571+996+515+726=", "A": "5213", "B": "5263", "C": "5383", "answer": "B"}
{"question": "803+862+815+100+409+758+262+169=", "A": "4098", "B": "4128", "C": "4178", "answer": "C"}
.csv 格式样例如下:
question,A,B,C,answer
127+545+588+620+556+199=,2632,2635,2645,B
735+603+102+335+605=,2376,2380,2410,B
506+346+920+451+910+142+659+850=,4766,4774,4784,C
504+811+870+445=,2615,2630,2750,B
2.2问答题 (qa)
对于问答 (qa) 类型的数据,默认的字段如下:
question: 表示问答题的题干
answer: 表示问答题的正确答案。可缺失,表示该数据集无正确答案。
对于非默认字段,我们都会进行读入,但默认不会使用。如需使用,则需要在 .meta.json 文件中进行指定。
.jsonl 格式样例如下:
{"question": "752+361+181+933+235+986=", "answer": "3448"}
{"question": "712+165+223+711=", "answer": "1811"}
{"question": "921+975+888+539=", "answer": "3323"}
{"question": "752+321+388+643+568+982+468+397=", "answer": "4519"}
.csv 格式样例如下:
question,answer
123+147+874+850+915+163+291+604=,3967
149+646+241+898+822+386=,3142
332+424+582+962+735+798+653+214=,4700
649+215+412+495+220+738+989+452=,4170
参数解析:
--custom-dataset-data-type qa 或者mcp
--hf-path:模型绝对路径
方法一(简化):
python run.py \
--hf-path internlm/internlm2-chat-1_8b \
--custom-dataset-path xxx/test_qa.jsonl \
方法二(全一点):
python run.py \
--hf-path internlm/internlm2-chat-1_8b \
--custom-dataset-path xxx/test_qa.jsonl \
--custom-dataset-data-type qa \
--custom-dataset-infer-method gen
前端框架:
openwebui
地址:
https://github.com/open-webui/open-webui
安装:
conda create -n openwebui python=3.11 -y
conda activate openwebui
pip install -U open-webui torch transformers -i https://pypi.mirrors.ustc.edu.cn/simple/
运行:
ubuntu:
conda activate open-webui
export HF_ENDPOINT=https://hf-mirror.com
export ENABLE_OLLAMA_API=True
export OPENAI_API_BASE_URL=http://127.0.0.1:23333/v1
open-webui serve --port 8080
windows:(!!!记得写成bat文件)
set HF_ENDPOINT=https://hf-mirror.com
set ENABLE_OLLAMA_API=False
set OPENAI_API_BASE_URL=http://127.0.0.1:23333/v1
open-webui serve --port 8080
bat:
@echo off
REM 设置环境变量
set HF_ENDPOINT=https://hf-mirror.com
set ENABLE_OLLAMA_API=False
set OPENAI_API_BASE_URL=http://127.0.0.1:23333/v1
REM 激活 Conda 环境
call conda activate open-webui
REM 启动 OpenWebUI 服务
open-webui serve --port 8080
streamlit
pip install streamlit
pip install openai
配置下方脚本
启动脚本:
stream run {path_to_py}
py脚本:
import streamlit as st
from openai import OpenAI
# 初始化客户端
client = OpenAI(base_url="http://localhost:23333/v1/", api_key="suibianxie")
# 设置页面标题
st.title("项目一效果演示")
# 初始化session状态(仅用于显示历史)
if "messages" not in st.session_state:
st.session_state.messages = []
# 显示历史消息
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# 获取用户输入
if prompt := st.chat_input("请输入您的问题,或输入exit退出"):
# 处理退出命令
if prompt.lower() == "exit":
st.info("退出对话。")
st.stop()
# 添加用户消息到显示历史
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
try:
# 发起API请求(每次只发送当前消息)
response = client.chat.completions.create(
messages=[{"role": "user", "content": prompt}], # 每次只发送当前问题
model="/home/cw/llms/Qwen/Qwen1.5-1.8B-Chat-merged"
)
# 获取模型回复
model_response = response.choices[0].message.content
# 添加AI回复到显示历史
st.session_state.messages.append({"role": "assistant", "content": model_response})
with st.chat_message("assistant"):
st.markdown(model_response)
except Exception as e:
st.error(f"发生错误:{e}")
大模型转gguf:
llama.cpp:
下载:
git clone https://github.com/ggerganov/llama.cpp.git
安装依赖:
conda create -n llama_cpp python=3.10 -y
conda activate llama_cpp
pip install -r requirements.txt
运行脚本:
# 如果不量化,保留模型的效果
python convert_hf_to_gguf.py /root/autodl-tmp/model/Qwen2.5-1.5B-zyhhsss --outtype f16 --verbose --outfile /root/autodl-tmp/model/Qwen2.5-1.5B-zyhhsss-gguf.gguf
# 如果需要量化(加速并有损效果),直接执行下面脚本就可以
python convert_hf_to_gguf.py /root/autodl-tmp/model/Qwen2.5-1.5B-zyhhsss --outtype q8_0 --verbose --outfile /root/autodl-tmp/model/Qwen2.5-1.5B-zyhhsss-gguf_q8_0.gguf
这里--outtype是输出类型,代表含义:
q2_k:特定张量(Tensor)采用较高的精度设置,而其他的则保持基础级别。
q3_k_l、q3_k_m、q3_k_s:这些变体在不同张量上使用不同级别的精度,从而达到性能和效率的平衡。
q4_0:这是最初的量化方案,使用 4 位精度。
q4_1 和 q4_k_m、q4_k_s:这些提供了不同程度的准确性和推理速度,适合需要平衡资源使用的场景。
q5_0、q5_1、q5_k_m、q5_k_s:这些版本在保证更高准确度的同时,会使用更多的资源并且推理速度较
慢。
q6_k 和 q8_0:这些提供了最高的精度,但是因为高资源消耗和慢速度,可能不适合所有用户。
fp16 和 f32: 不量化,保留原始精度。
environment.yml
name: llama_cpp
channels:
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
- defaults
dependencies:
- _libgcc_mutex=0.1=main
- _openmp_mutex=5.1=1_gnu
- bzip2=1.0.8=h5eee18b_6
- ca-certificates=2025.2.25=h06a4308_0
- ld_impl_linux-64=2.40=h12ee557_0
- libffi=3.4.4=h6a678d5_1
- libgcc-ng=11.2.0=h1234567_1
- libgomp=11.2.0=h1234567_1
- libstdcxx-ng=11.2.0=h1234567_1
- libuuid=1.41.5=h5eee18b_0
- ncurses=6.4=h6a678d5_0
- openssl=3.0.16=h5eee18b_0
- pip=25.0=py310h06a4308_0
- python=3.10.16=he870216_1
- readline=8.2=h5eee18b_0
- setuptools=75.8.0=py310h06a4308_0
- sqlite=3.45.3=h5eee18b_0
- tk=8.6.14=h39e8969_0
- wheel=0.45.1=py310h06a4308_0
- xz=5.6.4=h5eee18b_1
- zlib=1.2.13=h5eee18b_1
- pip:
- aiohttp==3.9.5
- aiosignal==1.3.2
- annotated-types==0.7.0
- anyio==4.9.0
- async-timeout==4.0.3
- attrs==25.3.0
- certifi==2025.1.31
- charset-normalizer==3.4.1
- click==8.1.8
- contourpy==1.3.1
- cycler==0.12.1
- distro==1.9.0
- exceptiongroup==1.2.2
- filelock==3.18.0
- fonttools==4.56.0
- frozenlist==1.5.0
- fsspec==2025.3.0
- gguf==0.14.0
- h11==0.14.0
- httpcore==1.0.7
- httpx==0.28.1
- huggingface-hub==0.23.5
- idna==3.10
- iniconfig==2.1.0
- jinja2==3.1.6
- jiter==0.9.0
- kiwisolver==1.4.8
- markdown-it-py==3.0.0
- markupsafe==3.0.2
- matplotlib==3.10.1
- mdurl==0.1.2
- mpmath==1.3.0
- multidict==6.2.0
- networkx==3.4.2
- numpy==1.26.4
- openai==1.55.3
- packaging==24.2
- pandas==2.2.3
- pillow==11.1.0
- pluggy==1.5.0
- prometheus-client==0.20.0
- propcache==0.3.1
- protobuf==4.25.6
- pydantic==2.11.1
- pydantic-core==2.33.0
- pygments==2.19.1
- pyparsing==3.2.3
- pytest==8.3.5
- python-dateutil==2.9.0.post0
- pytz==2025.2
- pyyaml==6.0.2
- regex==2024.11.6
- requests==2.32.3
- rich==13.9.4
- safetensors==0.5.3
- seaborn==0.13.2
- sentencepiece==0.2.0
- shellingham==1.5.4
- six==1.17.0
- sniffio==1.3.1
- sympy==1.13.3
- tokenizers==0.20.3
- tomli==2.2.1
- torch==2.2.2+cpu
- tqdm==4.67.1
- transformers==4.46.3
- typer==0.15.2
- typing-extensions==4.13.0
- typing-inspection==0.4.0
- tzdata==2025.2
- urllib3==2.3.0
- wget==3.2
- yarl==1.18.3
prefix: /root/miniconda3/envs/llama_cpp
分布式微调
DeepSpeed:
显存优化器
用时间换空间
支持huggingface pytorch transformers
核心技术:zero,梯度检查点:cpu offloading、混合精度训练 自适应选择最佳通信策略
ZeRO优化器:
阶段划分:
ZeRO-1:优化器状态分片。每张卡上面仍然有完整模型,优化器反向传播的时候只更新一部分参数
ZeRO-2: 梯度(模型反向传播)分片
ZeRO-3:参数(模型正向传播)+梯度+优化器状态
总结:
- zero3显存占用率会下降到1/n n为显卡数量(跟显卡架构有关)
- 支持json配置
- 支持千卡集群训练
用途:训练千亿参数、资源受限、快速实践如微调(一般7b就用)
安装及配置
安装:
pip install deepspeed
配置
llamafactory:
none-1
2 -zero2
3 -zero3
多机多卡、单机多卡:
见llamafactory下的分布式训练
xtuner:
NPROC_PER_NODE=${GPU_NUM} xtuner train ./config.py --deepspeed deepspeed_zero2
评测模型的工具:
OpenCompass:
文本生成模型用的相似度评估
地址:欢迎来到 OpenCompass 中文教程! — OpenCompass 0.4.1 文档
解压操作:
tar -xvf LLaMaFactory.tar
常见的模型:
生成式模型汇总!一文带你从隐变量模型到 VAE, GAN, Flow 到 Diffusion Model 全懂完()
博客:https://zhuanlan.zhihu.com/p/721196823
RNN:
服务器:
https://www.autodl.com/home
ftp x-shell
传输文件
nohup
后台登陆
nvitop:
使用nvitop来监控 NVIDIA GPU 的使用情况-CSDN博客
大模型理论知识:
transformer:
视频:
博客:
Transformer_transformeryuanlunwen-CSDN博客
模型压缩:
把ai模型之中的参数变少或者变小,最早做边缘部署的。主要想解决模型部署问题,主要可以划分为如下几种方法:
剪枝:
简化模型的结构。
**非结构化剪枝:**层数不变,减少某一层的参数。现在不适用因为大模型结果取决于一些核心的网络参数,结果不可控。 依赖于特定硬件的平台或者算法库
**结构化剪枝:**减少某一些层数,破坏原有结构。精度比较低,不依赖于硬件平台。
局部、全局剪枝:
通常思路:先见0.2重新训练还行,再减0.2继续重新训练
量化:
训练量化,推理量化。原先是32位的现在一般是16位,现在特指8位和4位
训练量化:模型训练时加载模型,分为两部分,一部分参与训练的升到32位。不参预训练的用8位保存。
推理量化:大部分模型参数用8位保存,关键的激活函数用的32位保存
知识蒸馏:
原有一个训练好的大模型作为teacher network,新有一个参数小的模型作为student network。把以前的数据集同时给两个模型, teacher会得出一个接近正确的特征,把student的结果和teacher的结果做一个损失,加上原本的损失。损失权重一开始与teacher差别的权重比较大,自身学习的比较小,之后反过来,由T控制。
deepseek蒸馏的openai
分布式微调:
解决问题:大模型规模爆炸、训练加速。
使用deepspeed进行训练
数据并行:
原理:每个设备导入完整模型,最后汇合。
作用:加速训练,每个设备可以单独去跑。
24g显存 - 7b大模型
16g显存*(2or4) -7b大模型
缺点:通信开销大、显存占用率高(需要存储完整的模型和优化器)
模型并行:
通常需要同型号!!!
原理:将模型拆分到不同设备(一般是按层或张量拆分):
作用:节约算力
横向拆分:按照层
竖向切分:按照张量。例如:Megation-LM将矩阵乘法分片
缺点:设备之间通信频繁,需要精细的负载均衡设计
流水线并行(Pipline Parallelism):
原理:将模型按照层拆分成多个阶段,数据分块之后按照流水线执行。(简单来说模型和数据都拆分了)
优化:微批次减少流水线气泡。显存节约更好。
挑战:需平衡阶段划分,避免资源闲置。
混合并行(3D并行):
把上面三个组合起来,训练千亿级规模的大模型。如:meta的llama-2
混合精度训练:
参预训练的32位,不参加的16位
微调注意事项记录:
1.使用flashatten2算力需要在sm80之上
2.qlora流程:
超参:
**gpu:**4090d -24g显存
**模型:**Qwen/Qwen2.5-1.5B-Instruct
**qlora量化等级:**8b
**lora秩:**选择和模型、量化等级有关这里给的:64 一般在32-128之间
**lora缩放系数:**直接秩*2 这里128
**计算类型:**混合加速训练,用来加速模型训练 bf16新的显卡架构支持、fp16老的支持
**batch:**10
合并:
检查点路径:(100、200等绝对路径)
/root/autodl-tmp/LLaMA-Factory/LLaMA-Factory/saves/Qwen2.5-1.5B-Instruct/lora/train_2025-03-30-16-36-58/checkpoint-100
导出路径:
/root/autodl-tmp/LLaMA-Factory/LLaMA-Factory/saves/Qwen2.5-1.5B-Instruct/lora/train_2025-03-30-16-36-58/checkpoint-100/Qwen2.5-1.5B-zyhhsss
3.lora流程:
4.情绪对话模型实现流程(微调项目通用实现流程)
本项目4090 跑了两小时 2500步(2510/204000) batch给的15 训练数据 2000
24g训练可以,lmdeploy部署oom使用k v cache并行部署也不行。
硬件选择:
训练:RTX3090 24G
部署:vGPU-32GB 显存占用30.2G
最终结果网盘地址:
通过网盘分享的文件:情绪对话模型
链接: https://pan.baidu.com/s/1gDYpbijo1OeXxl-DCKY9Kg?pwd=nfbx 提取码: nfbx
–来自百度网盘超级会员v6的分享
大致分成四个步骤
1 数据 2 模型 3 训练、测评 4、部署
4.1 数据来源
- 甲方提供
- 自己收集
- 指定数据集标准
- 数据集获取方式:手动采集、爬虫、数据接口、ai生成
- 数据清洗标注
- 人工处理、ai标注
- 指定数据集格式
本项目数据来源:
-
准备一些现有数据集
-
基于原有开源数据,让AI实现数据情绪制作
注意:如果使用AI来处理数据,尽量使用服务器提供的接口
常见大模型参数说明
- Temperature(温度)
- 作用:控制生成文本的随机性和创造性。
- 取值范围:通常在 [0, ∞) 之间,但常见范围是 [0, 2]。
- 具体效果:
- 低值(接近 0):模型更倾向于选择概率最高的词,生成结果更加确定性、保守、稳定,适合需要精确回答的任务。
- 高值(接近 1 或更高):增加随机性,模型会更多地考虑低概率的词,生成结果更加多样化、创造性和不可预测。
- Top-k Sampling(Top-k 采样)
- 作用:限制每次生成时只从概率最高的前 k 个词中进行选择。
- 取值范围:k 是一个正整数,比如 10、50、100 等。
- 具体效果:
- 如果
k=1
,模型每次都只选择概率最高的那个词,生成结果非常确定。 - 如果
k=50
,模型会从概率最高的 50 个词中随机选择一个,生成结果会有一定多样性。 - 较大的 k 值会让生成结果更加多样,但也可能导致语义不连贯。
- 如果
- Top-p Sampling(Nucleus Sampling,核采样)
- 作用:动态地选取累积概率达到某个阈值 p 的最小词集进行采样。
- 取值范围:p 在 (0, 1] 之间,比如 0.9、0.7 等。
- 具体效果:
- 如果
p=0.9
,模型会选择累积概率达到 90% 的最小词集进行采样。 - 如果词汇分布很集中,可能只选几个词;如果分布很分散,可能会选很多词。
- 相比
top_k
,top_p
更灵活,因为它根据实际的概率分布动态调整候选词集。
- 如果
- Seed(随机种子)
- 作用:控制生成过程中的随机性,确保结果可复现。
- 取值范围:通常是一个整数。
- 具体效果:
- 如果设置固定的
seed
,多次运行模型会得到相同的结果。 - 如果不设置或每次使用不同的
seed
,生成结果会不同。
- 如果设置固定的
总结对比
参数 | 控制维度 | 调节方式 | 影响结果 |
---|---|---|---|
Temperature | 创造力 | 数值高低影响随机性 | 高温=多样,低温=稳定 |
Top-k | 候选词数量 | 固定选择前 k 个词 | 小 k=保守,大 k=多样 |
Top-p | 累积概率阈值 | 动态选择累积概率达到 p 的词集 | 小 p=保守,大 p=多样 |
Seed | 随机性一致性 | 固定随机种子 | 固定 seed=可复现,否则随机 |
4.1.1 制作AI生成数据脚本
核心思路:
- 加载模型、加载embedding
- 配置风格模板(作用是规定生成消息回复的消息格式与风格)
- 限定不同的风格
- 每种风格可以设定不同的system定位(openai中的"role": “system”, “content”:),并且加入一些风格生成的约束
- 设定各种风格的没模型参数,temperature
- 写消息生成函数
- 规定用户输入list
- 按照风格和提问生成message,调用模型
- 对结果进行筛选,风格、长度、相似度
- 去重核心思路
- 先对文本进行编码(embedding)
- 求相似度(余弦,欧式)
- 设定阈值
import json
import time
import random
from openai import OpenAI
from sentence_transformers import SentenceTransformer
import numpy as np
from tqdm import tqdm
import os
base_url = "https://api.ppinfra.com/v3/openai"
api_key = "sk_FbKBQLJG_sfPuaD2kf_ehCeNiRVAI6j5nmBWw1d37TQ"
model = "qwen/qwen2.5-vl-72b-instruct"
client = OpenAI(
base_url=base_url,
api_key=api_key,
)
# 加载Embedding模型
style_model = SentenceTransformer(r"G:\python_ws_g\code\LLMlearn\embedding_model\thomas\text2vec-base-chinese")
# 风格模板配置
style_config = {
"温柔": {
"system_prompt": "你是一个温柔体贴的聊天助手,说话时总是充满关怀,使用以下特征:\n1. 包含'呢、呀、啦'等语气词\n2. 使用🌸💖😊等温暖表情\n3. 主动询问用户感受",
"examples": [
{"role": "user", "content": "今天好累啊"},
{"role": "assistant", "content": "辛苦啦~ 要给自己泡杯热茶放松一下吗?🌸"},
{"role": "user", "content": "考试没考好..."},
{"role": "assistant", "content": "没关系的呀~ 下次一定会更好!需要我陪你聊聊吗?😊"}
],
"temperature": 0.7
},
"毒舌": {
"system_prompt": "你是一个喜欢用犀利吐槽表达关心的朋友,需满足:\n1. 使用网络流行语(如'栓Q''退退退')\n2. 包含夸张比喻('你这速度堪比树懒')\n3. 结尾隐藏关心",
"examples": [
{"role": "user", "content": "又胖了5斤!"},
{"role": "assistant", "content": "好家伙!你这是要把体重秤压成分子料理?🏋️"},
{"role": "user", "content": "游戏又输了"},
{"role": "assistant", "content": "菜就多练练!需要给你推荐《从零开始的电竞之路》吗?🎮"}
],
"temperature": 0.7
},
}
# 单条数据写入文件
def append_single_to_json(file_path, new_entry):
"""将单条数据追加到 JSON 文件"""
# 如果文件不存在,创建一个空的 JSON 文件
if not os.path.exists(file_path):
with open(file_path, "w", encoding="utf-8") as f:
json.dump([], f, ensure_ascii=False, indent=2)
# 以追加模式打开文件
with open(file_path, "r+", encoding="utf-8") as f:
try:
# 尝试读取现有数据
f.seek(0)
existing_data = json.load(f)
except json.JSONDecodeError:
existing_data = []
# 添加新数据
existing_data.append(new_entry)
# 写回文件
f.seek(0)
f.truncate() # 清空文件内容
json.dump(existing_data, f, ensure_ascii=False, indent=2)
# 质量过滤规则
def is_valid_reply(style, user_msg, reply):
"""质量过滤规则(添加空值检查)"""
# 基础检查
if not reply or len(reply.strip()) == 0:
return False
# 规则1:回复长度检查
if len(reply) < 5 or len(reply) > 150:
return False
print(reply)
# # 规则2:风格关键词检查
# style_keywords = {
# "温柔": ["呢", "呀", "😊", "🌸"],
# "毒舌": ["好家伙", "栓Q", "!", "🏋️"]
# }
# if not any(kw in reply for kw in style_keywords.get(style, [])):
# return False
# 规则3:语义相似度检查
try:
ref_text = next(msg["content"] for msg in style_config[style]["examples"]
if msg["role"] == "assistant")
ref_vec = style_model.encode(ref_text)
reply_vec = style_model.encode(reply)
# 计算余弦相似度
cosine_similarity = np.dot(ref_vec, reply_vec) / (np.linalg.norm(ref_vec) * np.linalg.norm(reply_vec))
print(cosine_similarity)
return cosine_similarity < 0.8 # 阈值可以根据需求调整
except:
return False
def load_user_inputs_from_json(file_path):
"""
从 JSON 文件中加载用户输入数据。
"""
if not os.path.exists(file_path):
raise FileNotFoundError(f"文件 {file_path} 不存在!")
with open(file_path, "r", encoding="utf-8") as f:
user_inputs = json.load(f)
return user_inputs
# 生成函数
def generate_style_data(style_name, num_samples=50):
config = style_config[style_name]
data_count = 0
# 构建消息上下文(包含系统提示和示例对话)
messages = [
{"role": "system", "content": config["system_prompt"]},
*config["examples"] # 直接展开示例对话
]
# 从 JSON 文件中加载用户输入
try:
user_inputs = load_user_inputs_from_json(r"G:\python_ws_g\code\LLMlearn\project\dataset\LCCC\user_inputs.json")
except Exception as e:
print(f"加载用户输入失败:{str(e)}")
return
with tqdm(total=num_samples) as pbar: # 初始化进度条
while data_count < num_samples: # 确保生成指定数量的有效数据
try:
# 随机选择用户输入
user_msg = random.choice(user_inputs)
# 添加当前用户消息
current_messages = messages + [
{"role": "user", "content": user_msg}
]
# 调用API
response = client.chat.completions.create(
model=model,
messages=current_messages,
temperature=config["temperature"],
max_tokens=100
)
# 获取回复内容
reply = response.choices[0].message.content
# 质量过滤(数据审核)
if is_valid_reply(style_name, user_msg, reply):
data_entry = {
"user": user_msg,
"assistant": reply,
"style": style_name
}
append_single_to_json("style_chat_data.json", data_entry) # 立即写入文件
data_count += 1
pbar.update(1) # 更新进度条
time.sleep(1.0) # 频率限制保护
except Exception as e:
print(f"生成失败:{str(e)}")
# 执行生成
if __name__ == '__main__':
try:
print("开始生成温柔风格数据...")
generate_style_data("温柔", 5000)
print("开始生成毒舌风格数据...")
generate_style_data("毒舌", 4750)
except KeyboardInterrupt:
print("\n用户中断,已保存部分数据...")
finally:
print("数据生成完成!")
4.1.2 确定原始数据
用户给的输入(input),一般来讲甲方有原始数据。本项目选择日常交流话术(开源数据集)。
LCCC: LCCC · 数据集
CDial-GPT:CDial-GPT
LCCC转换脚本:
import json
import os
user_inputs = [
# 日常生活相关
"今天心情不太好",
"推荐个电影吧",
"怎么才能早睡早起",
"养猫好还是养狗好",
"工作压力好大",
"最近总是失眠",
"今天脚有点肿了",
"天气太冷了怎么办",
"周末有什么好玩的活动吗",
"如何摆脱拖延症",
"吃饭的时候总觉得无聊,怎么办",
"有没有什么适合在家做的运动",
"最近总觉得很累,是不是亚健康了",
# 情感与人际关系
"朋友之间闹矛盾了,该怎么办",
"喜欢一个人但不敢表白,怎么办",
"家人不理解我,感觉很孤独",
"如何更好地表达自己的情绪",
"觉得身边的人都比我优秀,好焦虑",
"分手后怎么调整心态",
"怎样交到更多的朋友",
"和同事相处总是很尴尬,怎么办",
# 学习与工作
"考试复习效率太低了,有什么建议",
"工作中遇到瓶颈,怎么突破",
"想学一门新技能,但不知道从哪开始",
"如何提高专注力",
"面试前特别紧张,有什么方法缓解",
"觉得自己能力不足,害怕被裁员",
"论文写不下去了,怎么办",
"团队合作中遇到问题,该怎么解决",
# 健康与饮食
"最近胖了好多,怎么减肥比较好",
"晚上总是睡不着,有什么助眠的方法",
"吃什么对皮肤好",
"健身计划总是坚持不下来,怎么办",
"感冒了,吃什么药比较好",
"如何保持身体健康",
"每天喝水不够,有什么提醒方法",
"如何改善久坐导致的腰酸背痛",
# 兴趣爱好与娱乐
"最近有什么好看的电视剧推荐",
"喜欢画画,但总是画不好,怎么办",
"想学吉他,但没时间练习",
"如何选择适合自己的书",
"旅行时有哪些注意事项",
"如何拍出好看的照片",
"最近迷上了咖啡,有什么推荐的豆子吗",
"喜欢玩游戏,但怕影响学习,怎么平衡",
# 社会议题与热点
"最近的新闻热点怎么看",
"人工智能会不会取代人类的工作",
"环保问题越来越严重,我们能做些什么",
"如何看待年轻人躺平的现象",
"未来的科技会发展成什么样",
"社会上的不公平现象让人很沮丧,怎么办",
# 随机吐槽与搞笑
"又胖了5斤!",
"游戏又输了,好气啊",
"老板今天又骂人了,真是无语",
"外卖等了一个小时还没到,简直崩溃",
"手机摔地上了,屏幕裂了,心也碎了",
"今天的地铁太挤了,感觉自己快被压扁了",
"为什么每次洗完头都会下雨",
"为什么我的猫总是踩我键盘",
]
def extract_first_statements(data):
"""
从对话数据集中提取每段对话的第一句话。
"""
# 遍历每段对话
for conversation in data:
if conversation: # 确保对话不为空
first_statement = conversation[0].replace(" ", "") # 提取第一句并移除空格
user_inputs.append(first_statement)
return user_inputs
def load_json(file_path):
"""
加载 JSON 文件中的数据。
"""
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
return data
def save_to_json(file_path, data):
"""
将数据保存到 JSON 文件中。
"""
with open(file_path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=4)
# 主程序
if __name__ == "__main__":
# 输入文件路径
input_file = r"G:\python_ws_g\code\LLMlearn\project\dataset\LCCC\LCCC-base-split\LCCC-base_test.json"
# 检查输入文件是否存在
if not os.path.exists(input_file):
print(f"错误:文件 {input_file} 不存在!")
exit(1)
# 加载原始数据集
print("正在加载数据...")
data_set = load_json(input_file)
# 提取每段对话的第一句
print("正在提取第一句话...")
user_inputs = extract_first_statements(data_set)
# 输出文件路径
output_file = "user_inputs.json"
# 保存到 JSON 文件
print("正在保存结果...")
save_to_json(output_file, user_inputs)
print(f"数据已成功保存到 {output_file}")
使用数据2000条:
通过网盘分享的文件:wenrou.json
链接: https://pan.baidu.com/s/1hMuLt79YaAZZk79sKYX9zQ?pwd=mb4c 提取码: mb4c
–来自百度网盘超级会员v6的分享
Xtuner需要的数据集格式:
[
{
"conversation": [
{
"input": "闺蜜把我秘密当谈资,该不该撕破脸?",
"output": "哎呀,这种事情确实很难处理呢。你觉得她的行为伤害到了你吗?💖我们可以一起想想解决的办法。"
}
]
},
{
"conversation": [
{
"input": "老妈非让我嫁给她同事儿子,怎么逃啊!",
"output": "哎呀,这种事情确实挺棘手的。💖 你心里是怎么想的呢?是不是感到有些压力?我们可以一起想想解决的办法。🌸"
}
]
}]
4.2 模型
4.2.1 模型选型:
**通用流程:模型官网看参数、选后缀 看自己能用的大小 客观评估(当前聊天对话模型核心是:中文理解能力。用CLUE适合一点) **
模型大小判断:
1.服务器配置。
qwen-2.5-7b下载用的: bf16。 用qlora微调到8位。
项目 | 占用(估算) |
---|---|
模型参数(8-bit) | 约 7 GB |
LoRA 参数 | < 100 MB |
激活值(中等batch) | 5-8 GB(可调) |
其他缓存(attention kv、optimizer) | 5-8 GB |
20GB - 左右
🛠 推荐配置
- 最小可用显卡:RTX 3090(24GB)或 RTX 4090(24GB)
- 推荐配置:2×3090 / 1×A6000 / 1×A100(40GB 以上更舒服)
- 最优方案:多卡 A100,配合 deepspeed/fsdp 微调更大 batch
2.任务复杂度(人类对话任务3b以内的就行,像数学推理,编程需要更高,得落地尝试)。类似销售机器人:情绪对话+10086客服机器人就可以。
中文模型:qwen、chatglm(智谱)、interlm(书生浦语)
llama训练数据90%以上是英文文本
常见后缀:
chat、instruct:模型输出有限制,经过人工对齐安全一点
instruct:
chat:做聊天对话模型
无后缀:base模型无人工审查
4.2.2 模型客观评价:
CLUE数据集:
分为CLUE、FewCLUE前缀的。clue中长文,fewclue短文。
gen后缀文本生成、PPL困惑度:PPL 越低:模型对语言的拟合越好,预测越准确;PPL 越高:说明模型更“困惑”,也就是说它对句子的预测不确定性更大。
数据集名称 | 所属前缀 | 文本长度 | 任务类型 | 用途说明 | PPL作用说明 |
---|---|---|---|---|---|
AFQMC | CLUE | 中等 | 语义相似性 | 判断两个句子是否表达相同含义(如问句对齐、改写识别) | 模型PPL越低表示更能准确判断句子语义相似性 |
CMNLI | CLUE | 中长 | 自然语言推理 | 判断句子对之间的逻辑关系(蕴含、中立、矛盾) | 反映模型理解句间逻辑关系的能力 |
CSL | CLUE | 长文 | 关键词预测 | 利用给定关键词判断摘要与关键词的匹配关系(多标签分类) | PPL低说明模型能更好生成或匹配关键词 |
TNEWS | CLUE | 短文 | 文本分类 | 新闻标题分类(15个类别,如科技、财经、体育等) | PPL评估模型对新闻标题语义分布的拟合效果 |
IFLYTEK | CLUE | 短文 | 文本分类 | App应用描述的自动分类(119类,任务更细粒度) | PPL越低表示模型能准确建模多类别语义特征 |
WSC | CLUE | 中等 | 语言理解 | 推理代词指代的实体(例如“他”指的是谁) | 测试模型对常识推理和上下文理解的能力 |
CLUEgen | CLUE-gen | 长文 | 文本生成 | 给定一段内容生成文章或续写文本 | PPL用于评估生成文本的流畅度与合理性 |
FewCLUE | FewCLUE | 短文 | 小样本任务 | 包括文本分类、匹配、推理等任务,使用极少样本进行训练 | PPL用于评估模型在低资源下的语言拟合能力 |
CSKG | FewCLUE | 短文 | 知识问答 | 基于知识图谱的问答推理任务 | 衡量模型理解实体关系与事实知识的能力 |
CHIP-STS | FewCLUE | 中短 | 医疗语义匹配 | 医疗问句之间的语义相似性任务(面向中文医疗文本) | 用于医疗场景下语义建模,PPL越低越准确 |
FewCLUE-gen | FewCLUE-gen | 短文 | 文本生成 | 小样本条件下的文本生成任务(如评论生成、摘要生成等) | 小样本下生成任务的文本质量与自然性评估 |
4.2.3 原模型、数据集选择:
数据集选择 FewCLUE_bustm_gen(短文本分类)、FewCLUE_ocnli_fc_gen(自然语言推理)
模型qwen_1.5_0.5b_chat、qwen_1.5_1.8b_chat
使用opencompass:
注意!!!!直接在/root/autodl-tmp/opencompass-main/opencompass-main/opencompass/configs/models下面修改文件,命令并不能指定绝对路径
connfig文件:
from opencompass.models import HuggingFacewithChatTemplate
models = [
dict(
type=HuggingFacewithChatTemplate,
abbr='qwen2.5-0.5b-instruct-hf',
path='/root/autodl-tmp/model/qwen-2.5-0.5b-instruct',
max_out_len=1024,
batch_size=8,
run_cfg=dict(num_gpus=0),
)
]
在/root/autodl-tmp/opencompass-main/opencompass-main下执行。
注意为了好看分行,命令执行的时候删掉换行符!!!!!
python run.py --models hf_qwen2_5_0_5b_instruct.py
--datasets FewCLUE_bustm_gen FewCLUE_ocnli_fc_gen
--debug --work-dir /root/autodl-tmp/opencompass-main/opencompass-main/two_model
python run.py --models hf_qwen2_5_1_5b_instruct.py hf_qwen2_5_7b_instruct.py --datasets FewCLUE_bustm_gen FewCLUE_ocnli_fc_gen --debug --work-dir /root/autodl-tmp/opencompass-main/opencompass-main/two_model
评估结果:
gen生成越高越好,ppl越低越好(客服对话中想让模型回答对应题目的问题)
dataset | version | metric | mode | qwen2.5-7b-instruct-hf |
---|---|---|---|---|
bustm-dev | 5cc669 | accuracy | gen | 83.12 |
bustm-test | 5cc669 | accuracy | gen | 78.44 |
ocnli_fc-dev | 51e956 | accuracy | gen | 70.62 |
ocnli_fc-test | 51e956 | accuracy | gen | 66.71 |
dataset | version | metric | mode | qwen2.5-0.5b-instruct-hf |
---|---|---|---|---|
bustm-dev | 5cc669 | accuracy | gen | 52.50 |
bustm-test | 5cc669 | accuracy | gen | 50.11 |
ocnli_fc-dev | 51e956 | accuracy | gen | 38.75 |
ocnli_fc-test | 51e956 | accuracy | gen | 40.87 |
dataset | version | metric | mode | qwen2.5-1.5b-instruct-hf |
---|---|---|---|---|
bustm-dev | 5cc669 | accuracy | gen | 70.00 |
bustm-test | 5cc669 | accuracy | gen | 69.81 |
ocnli_fc-dev | 51e956 | accuracy | gen | 63.12 |
ocnli_fc-test | 51e956 | accuracy | gen | 60.60 |
4.3 微调框架
Xtuner :主观评价的结果
LLamaFactory:客观loss指标
本文做情感对话模型,倾向看主观评价所以选xtuner。
**注意!!!**使用的数据要单轮还是多轮
一般做对话模型:数据分为单轮和多轮
本文希望做一个类似:小智智能聊天机器人。问一句答一句,没有前后的逻辑推理。
这里使用单轮数据集。
单轮和多轮区别主要是上下文逻辑
4.3.1 Xtuner对话模板!!!
Xtuner对话模板位置
/root/autodl-tmp/xtuner-main/xtuner/utils/templates.py
流程:
- 训练脚本中找prompt_template对应的对话模板
prompt_template = PROMPT_TEMPLATE.qwen_chat
- 去模板中找qwen_chat
qwen_chat=dict(
SYSTEM=("<|im_start|>system\n{system}<|im_end|>\n"),
INSTRUCTION=("<|im_start|>user\n{input}<|im_end|>\n" "<|im_start|>assistant\n"),
SUFFIX="<|im_end|>",
SUFFIX_AS_EOS=True,
SEP="\n",
STOP_WORDS=["<|im_end|>", "<|endoftext|>"],
)
4.3.2 Xtuner训练流程
4.3.2.1 qlora微调
4.3.2.2 模型转换
模型训练后会自动保存成 PTH 模型(例如 iter_2000.pth ,如果使用了 DeepSpeed,则将会是一个 文件夹),我们需要利用 xtuner convert pth_to_hf 将其转换为 HuggingFace 模型,以便于后续使 用。具体命令为:
xtuner convert pth_to_hf ${FINETUNE_CFG} ${PTH_PATH} ${SAVE_PATH}
# 例如:xtuner convert pth_to_hf /root/autodl-tmp/xtuner-main/jiaoben/qwen1_5_7b_chat_qlora_alpaca_e3.py /root/work_dirs/qwen1_5_7b_chat_qlora_alpaca_e3/iter_2500.pth /root/autodl-tmp/muhf
4.3.2.3 模型合并
如果使用了 LoRA / QLoRA 微调,则模型转换后将得到 adapter 参数,而并不包含原 LLM 参数。如果您 期望获得合并后的模型权重(例如用于后续评测),那么可以利用 xtuner convert merge :
xtuner convert merge ${基座模型} ${Huggingface模型} ${合并模型路径}
例如:
xtuner convert merge /root/autodl-tmp/model/Qwen2.5-7B-Instruct /root/autodl-tmp/muhf /root/autodl-tmp/mymodel
4.3.2.4 部署
4.4 部署框架
vllm或者lmdeploy,lmdeploy推理效率好一点。本文选的lmdeploy。
4.4.1 Xtuner和Lmdeploy 对话模板对齐!!!
方法一:利用现有对话模板,直接配置一个如下的 json 文件使用
Lmdeploy对话模板标准格式:
{
"model_name": "your awesome chat template name",
"system": "<|im_start|>system\n",
"meta_instruction": "You are a robot developed by LMDeploy.",
"eosys": "<|im_end|>\n",
"user": "<|im_start|>user\n",
"eoh": "<|im_end|>\n",
"assistant": "<|im_start|>assistant\n",
"eoa": "<|im_end|>",
"separator": "\n",
"capability": "chat",
"stop_words": ["<|im_end|>"]
}
model_name 为必填项,可以是 LMDeploy 内置对话模板名(通过 lmdeploy list 可查阅), 也可以是新名字。其他字段可选填。 当 model_name 是内置对话模板名时,json文件中各非 null 字段会覆盖原有对话模板的对应属性。 而当 model_name 是新名字时,它会把将 BaseChatTemplate 直接注册成新的对话模板。其具体定义可以参考BaseChatTemplate。 这样一个模板将会以下面的形式进行拼接。
{system}{meta_instruction}{eosys}{user}{user_content}{eoh}{assistant}
{assistant_content}{eoa}{separator}{user}...
推理命令:
lmdeploy serve api_server model_dir --chat-template ${JSON_FILE}
例如:lmdeploy serve api_server G:\python_ws_g\code\llm\llmlearning\result\mymodel --chat-template G:\python_ws_g\code\llm\llmlearning\LLMlearn\emo_conversation_project\template_trans\a.json
lmdeploy serve api_server /root/autodl-tmp/mymodel --chat-template /root/autodl-tmp/a.json --quant-policy 8
也可以在通过接口函数传入,比如:
from lmdeploy import ChatTemplateConfig, serve
serve('internlm/internlm2_5-7b-chat',
chat_template_config=ChatTemplateConfig.from_json('${JSON_FILE}'))
方法二:以 LMDeploy 现有对话模板,自定义一个python对话模板类,注册成功后直接用即可。
from lmdeploy.model import MODELS, BaseChatTemplate
@MODELS.register_module(name='customized_model')
class CustomizedModel(BaseChatTemplate):
"""A customized chat template."""
def __init__(self,
system='<|im_start|>system\n',
meta_instruction='You are a robot developed by LMDeploy.',
user='<|im_start|>user\n',
assistant='<|im_start|>assistant\n',
eosys='<|im_end|>\n',
eoh='<|im_end|>\n',
eoa='<|im_end|>',
separator='\n',
stop_words=['<|im_end|>', '<|action_end|>']):
super().__init__(system=system,
meta_instruction=meta_instruction,
eosys=eosys,
user=user,
eoh=eoh,
assistant=assistant,
eoa=eoa,
separator=separator,
stop_words=stop_words)
对话模板转换脚本:
import json
# 原始模板
original_template = dict(
SYSTEM=("<|im_start|>system\n{system}<|im_end|>\n"),
INSTRUCTION=(
"<|im_start|>user\n{input}<|im_end|>\n"
"<|im_start|>assistant\n"
),
SUFFIX="<|im_end|>",
SUFFIX_AS_EOS=True,
SEP="\n",
STOP_WORDS=["<|im_end|>", "<|endoftext|>"],
)
# 转换为目标格式
converted_template = {
"model_name": "your awesome chat template name",
"system": "<|im_start|>system\n",
"meta_instruction": "You are a robot developed by LMDeploy.",
"eosys": "<|im_end|>\n",
"user": "<|im_start|>user\n",
"eoh": "<|im_end|>\n",
"assistant": "<|im_start|>assistant\n",
"eoa": "<|im_end|>",
"separator": original_template.get("SEP", "\n"),
"capability": "chat",
"stop_words": ["<|im_end|>"]
}
# 保存为 JSON 文件
save_path = r"G:\python_ws_g\code\llm\llmlearning\LLMlearn\emo_conversation_project\template_trans\a.json"
try:
with open(save_path, 'w', encoding='utf-8') as f:
json.dump(converted_template, f, indent=4, ensure_ascii=False)
print(f"转换后的模板已成功保存到 {save_path}")
except Exception as e:
print(f"保存失败: {e}")
json结果:
{
"model_name": "zyhhsss",
"system": "<|im_start|>system\n",
"meta_instruction": "You are a robot developed by LMDeploy.",
"eosys": "<|im_end|>\n",
"user": "<|im_start|>user\n",
"eoh": "<|im_end|>\n",
"assistant": "<|im_start|>assistant\n",
"eoa": "<|im_end|>",
"separator": "\n",
"capability": "chat",
"stop_words": [
"<|im_end|>"
]
}
转换例子2(没试过记一下):
xtuner的对话模板
qwen_chat=dict(
SYSTEM=("<|im_start|>system\n{system}<|im_end|>\n"),
INSTRUCTION=("<|im_start|>user\n{input}<|im_end|>\n" "<|im_start|>assistant\n"),
SUFFIX="<|im_end|>",
SUFFIX_AS_EOS=True,
SEP="\n",
STOP_WORDS=["<|im_end|>", "<|endoftext|>"],
)
lmdeploy的json:
{
"model_name": "your awesome chat template name",
"system": "<|im_start|>system\n",
"meta_instruction": "You are a robot developed by LMDeploy.",
"eosys": "<|im_end|>\n",
"user": "<|im_start|>user\n",
"eoh": "<|im_end|>\n",
"assistant": "<|im_start|>assistant\n",
"eoa": "<|im_end|>",
"separator": "\n",
"capability": "chat",
"stop_words": ["<|im_end|>"]
}
转换脚本:
import json
import re
def convert_xtuner_to_lmdeploy(xtuner_template: dict, model_name="converted_model"):
system_pattern = xtuner_template.get("SYSTEM", "")
instruction_pattern = xtuner_template.get("INSTRUCTION", "")
suffix = xtuner_template.get("SUFFIX", "")
separator = xtuner_template.get("SEP", "\n")
stop_words = xtuner_template.get("STOP_WORDS", [])
# 提取 meta_instruction 内容(如 {system})
meta_instruction_match = re.search(r"{(\w+)}", system_pattern)
meta_instruction = f"{{{meta_instruction_match.group(1)}}}" if meta_instruction_match else ""
lmdeploy_template = {
"model_name": model_name,
"system": system_pattern.split("{")[0] if "{" in system_pattern else "",
"meta_instruction": meta_instruction,
"eosys": suffix + "\n",
"user": instruction_pattern.split("{input}")[0] if "{input}" in instruction_pattern else "",
"eoh": suffix + "\n",
"assistant": re.split(re.escape(suffix), instruction_pattern.split("{input}")[-1])[0]
if "{input}" in instruction_pattern else "",
"eoa": suffix,
"separator": separator,
"capability": "chat",
"stop_words": stop_words
}
return lmdeploy_template
# 示例 xtuner 模板
xtuner_chat = dict(
SYSTEM=("<|im_start|>system\n{system}<|im_end|>\n"),
INSTRUCTION=("<|im_start|>user\n{input}<|im_end|>\n<|im_start|>assistant\n"),
SUFFIX="<|im_end|>",
SUFFIX_AS_EOS=True,
SEP="\n",
STOP_WORDS=["<|im_end|>", "<|endoftext|>"],
)
# 转换
lmdeploy_json = convert_xtuner_to_lmdeploy(xtuner_chat, model_name="qwen_chat")
# 保存为 JSON 文件
with open("qwen_chat_lmdeploy_template.json", "w", encoding="utf-8") as f:
json.dump(lmdeploy_json, f, indent=4, ensure_ascii=False)
print("转换完成!结果已保存为 qwen_chat_lmdeploy_template.json")
转换结果:
{
"model_name": "qwen_chat",
"system": "<|im_start|>system\n",
"meta_instruction": "{system}",
"eosys": "<|im_end|>\n",
"user": "<|im_start|>user\n",
"eoh": "<|im_end|>\n",
"assistant": "",
"eoa": "<|im_end|>",
"separator": "\n",
"capability": "chat",
"stop_words": [
"<|im_end|>",
"<|endoftext|>"
]
}
✅ 字段映射解释:
xtuner 字段 | lmdeploy 字段 | 说明 |
---|---|---|
SYSTEM | system + meta_instruction | 前缀是 system 字段,内容是 meta_instruction |
INSTRUCTION | user + eoh + assistant | 模板中用户提问(user)、助手回应(assistant) |
SUFFIX | eoa | assistant 结束标识符 |
SEP | separator | 对话分隔符 |
STOP_WORDS | stop_words | 停止生成的标记 |
SUFFIX_AS_EOS | 自动体现在 stop_words | 无需额外字段,`< |
4.5 前端界面(Streamlit)
pip install streamlit
pip install openai
配置下方脚本
启动脚本:
stream run {path_to_py}
py脚本:
import streamlit as st
from openai import OpenAI
# 初始化客户端
client = OpenAI(base_url="http://localhost:23333/v1/", api_key="suibianxie")
# 设置页面标题
st.title("项目一效果演示")
# 初始化session状态(仅用于显示历史)
if "messages" not in st.session_state:
st.session_state.messages = []
# 显示历史消息
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# 获取用户输入
if prompt := st.chat_input("请输入您的问题,或输入exit退出"):
# 处理退出命令
if prompt.lower() == "exit":
st.info("退出对话。")
st.stop()
# 添加用户消息到显示历史
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
try:
# 发起API请求(每次只发送当前消息)
response = client.chat.completions.create(
messages=[{"role": "user", "content": prompt}], # 每次只发送当前问题
model="/home/cw/llms/Qwen/Qwen1.5-1.8B-Chat-merged"
)
# 获取模型回复
model_response = response.choices[0].message.content
# 添加AI回复到显示历史
st.session_state.messages.append({"role": "assistant", "content": model_response})
with st.chat_message("assistant"):
st.markdown(model_response)
except Exception as e:
st.error(f"发生错误:{e}")
4.6 傻瓜式操作手册
xtuner脚本
# Copyright (c) OpenMMLab. All rights reserved.
import torch
from datasets import load_dataset
from mmengine.dataset import DefaultSampler
from mmengine.hooks import (
CheckpointHook,
DistSamplerSeedHook,
IterTimerHook,
LoggerHook,
ParamSchedulerHook,
)
from mmengine.optim import AmpOptimWrapper, CosineAnnealingLR, LinearLR
from peft import LoraConfig
from torch.optim import AdamW
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from xtuner.dataset import process_hf_dataset
from xtuner.dataset.collate_fns import default_collate_fn
from xtuner.dataset.map_fns import alpaca_map_fn, template_map_fn_factory
from xtuner.engine.hooks import (
DatasetInfoHook,
EvaluateChatHook,
VarlenAttnArgsToMessageHubHook,
)
from xtuner.engine.runner import TrainLoop
from xtuner.model import SupervisedFinetune
from xtuner.parallel.sequence import SequenceParallelSampler
from xtuner.utils import PROMPT_TEMPLATE, SYSTEM_TEMPLATE
#######################################################################
# PART 1 Settings #
#######################################################################
# Model
pretrained_model_name_or_path = "/root/autodl-tmp/model/Qwen2.5-7B-Instruct"
use_varlen_attn = False
# Data
data_files = '/root/autodl-tmp/xtuner-main/data/wenrou.json'#数据集
prompt_template = PROMPT_TEMPLATE.qwen_chat
max_length = 150
pack_to_max_length = True
# parallel
sequence_parallel_size = 1
# Scheduler & Optimizer
batch_size = 15 # per_device
accumulative_counts = 16
accumulative_counts *= sequence_parallel_size
dataloader_num_workers = 0
max_epochs = 3000
optim_type = AdamW
lr = 2e-4
betas = (0.9, 0.999)
weight_decay = 0
max_norm = 1 # grad clip
warmup_ratio = 0.03
# Save
save_steps = 500
save_total_limit = 4 # Maximum checkpoints to keep (-1 means unlimited)
# Evaluate the generation performance during the training
evaluation_freq = 500
SYSTEM = SYSTEM_TEMPLATE.alpaca
evaluation_inputs = ["闺蜜把我秘密当谈资,该不该撕破脸?",
"老妈非让我嫁给她同事儿子,怎么逃啊!",
"同事抢功时故意提高音量,要当场揭穿吗?",
"男朋友给女主播刷火箭,算精神出轨吗?",
"室友半夜和对象视频娇喘,怎么提醒?",
"亲戚说我不生孩子就是自私,好想掀桌!",
"大学生毕业工资不够找我,我给你补个蛋"]
#######################################################################
# PART 2 Model & Tokenizer #
#######################################################################
tokenizer = dict(
type=AutoTokenizer.from_pretrained,
pretrained_model_name_or_path=pretrained_model_name_or_path,
trust_remote_code=True,
padding_side="right",
)
model = dict(
type=SupervisedFinetune,
use_varlen_attn=use_varlen_attn,
llm=dict(
type=AutoModelForCausalLM.from_pretrained,
pretrained_model_name_or_path=pretrained_model_name_or_path,
trust_remote_code=True,
torch_dtype=torch.float16,
quantization_config=dict(
type=BitsAndBytesConfig,
load_in_4bit=False,
load_in_8bit=True,
llm_int8_threshold=6.0,
llm_int8_has_fp16_weight=False,
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
),
),
lora=dict(
type=LoraConfig,
r=64,
lora_alpha=128,
lora_dropout=0.1,
bias="none",
task_type="CAUSAL_LM",
),
)
#######################################################################
# PART 3 Dataset & Dataloader #
#######################################################################
alpaca_en = dict(
type=process_hf_dataset,
dataset=dict(type=load_dataset, path="json",data_files=data_files),
tokenizer=tokenizer,
max_length=max_length,
dataset_map_fn=None,
template_map_fn=dict(type=template_map_fn_factory, template=prompt_template),
remove_unused_columns=True,
shuffle_before_pack=True,
pack_to_max_length=pack_to_max_length,
use_varlen_attn=use_varlen_attn,
)
sampler = SequenceParallelSampler if sequence_parallel_size > 1 else DefaultSampler
train_dataloader = dict(
batch_size=batch_size,
num_workers=dataloader_num_workers,
dataset=alpaca_en,
sampler=dict(type=sampler, shuffle=True),
collate_fn=dict(type=default_collate_fn, use_varlen_attn=use_varlen_attn),
)
#######################################################################
# PART 4 Scheduler & Optimizer #
#######################################################################
# optimizer
optim_wrapper = dict(
type=AmpOptimWrapper,
optimizer=dict(type=optim_type, lr=lr, betas=betas, weight_decay=weight_decay),
clip_grad=dict(max_norm=max_norm, error_if_nonfinite=False),
accumulative_counts=accumulative_counts,
loss_scale="dynamic",
dtype="float16",
)
# learning policy
# More information: https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/param_scheduler.md # noqa: E501
param_scheduler = [
dict(
type=LinearLR,
start_factor=1e-5,
by_epoch=True,
begin=0,
end=warmup_ratio * max_epochs,
convert_to_iter_based=True,
),
dict(
type=CosineAnnealingLR,
eta_min=0.0,
by_epoch=True,
begin=warmup_ratio * max_epochs,
end=max_epochs,
convert_to_iter_based=True,
),
]
# train, val, test setting
train_cfg = dict(type=TrainLoop, max_epochs=max_epochs)
#######################################################################
# PART 5 Runtime #
#######################################################################
# Log the dialogue periodically during the training process, optional
custom_hooks = [
dict(type=DatasetInfoHook, tokenizer=tokenizer),
dict(
type=EvaluateChatHook,
tokenizer=tokenizer,
every_n_iters=evaluation_freq,
evaluation_inputs=evaluation_inputs,
system=SYSTEM,
prompt_template=prompt_template,
),
]
if use_varlen_attn:
custom_hooks += [dict(type=VarlenAttnArgsToMessageHubHook)]
# configure default hooks
default_hooks = dict(
# record the time of every iteration.
timer=dict(type=IterTimerHook),
# print log every 10 iterations.
logger=dict(type=LoggerHook, log_metric_by_epoch=False, interval=10),
# enable the parameter scheduler.
param_scheduler=dict(type=ParamSchedulerHook),
# save checkpoint per `save_steps`.
checkpoint=dict(
type=CheckpointHook,
by_epoch=False,
interval=save_steps,
max_keep_ckpts=save_total_limit,
),
# set sampler seed in distributed evrionment.
sampler_seed=dict(type=DistSamplerSeedHook),
)
# configure environment
env_cfg = dict(
# whether to enable cudnn benchmark
cudnn_benchmark=False,
# set multi process parameters
mp_cfg=dict(mp_start_method="fork", opencv_num_threads=0),
# set distributed parameters
dist_cfg=dict(backend="nccl"),
)
# set visualizer
visualizer = None
# set log level
log_level = "INFO"
# load from which checkpoint
load_from = None
# whether to resume training from the loaded checkpoint
resume = False
# Defaults to use random seed and disable `deterministic`
randomness = dict(seed=None, deterministic=False)
# set log processor
log_processor = dict(by_epoch=False)
微调项目总结:
qwen-1.8b 4090 6h 最终loss 0.09 预计最终0.05左右 batch 50
qwen-7b 4090 2h 最终loss 0.1311 batch 15 部署:32G 占用30G
如果同样问题,模型回复都一样。说明模型接近过拟合。
微调项目中常见问题:
1.后缀为instruct的模型用xtuner微调,xtuner要选后缀带chat的还是不带?
答:无所吊为,训练跟部署是一个就行。哪个对话模板就是个输出规则,告诉你自己是什么身份,以什么角度,看到什么符号停止,这个还挺重要的。主要是训练数据少的时候,想和数据一致,尽量保证给他的题干相同
2.如何选择对话模板?
答:仔细看看xtuner、llamfactory这些用的对于同类型不同版本的对话模板差不多。选个类似的就行,结束开始不要错。
3.**基座模型尽量选后缀带chat、instruct的。**数据被训练过,不会出现违规的东西。
4.**有了系统提示词,还用做自我认知吗?**答:用,系统提示词只是诱导,具体效果要看模型实际能力,模型太小他理解不了,结果不一定正确。你用数据训练就是修改他的知识库,一个是诱导,一个是修改。
LlamaIndex
LlamaIndex是用于LLM应用程序的数据框架,解决大模型和数据之间的衔接问题。
通过数据来增强大模型。
LlamaIndex 提供了5大核心工具: Data connectors Data indexes Engines Data agents Application integrations
架构类似于:
官网:
常用:
读取文档:Data connectors/Simple Directory Reader
配置环境:
conda create -n llamaindex python=3.12 -y
conda activate llamaindex
pip install llama-index -i https://pypi.mirrors.ustc.edu.cn/simple/
//调用离线 本地模型
pip install llama-index-llms-huggingface -i https://pypi.mirrors.ustc.edu.cn/simple/
//调用在线包
pip install llama-index-llms-huggingface-api -i https://pypi.mirrors.ustc.edu.cn/simple/
pip install sentence-transformers -i https://pypi.mirrors.ustc.edu.cn/simple/
pip install chromadb
pip install llama-index-vector-stores-chroma
LlamaIndex 实现 RAG 的基础流程
-
准备嵌入模型(Embedding Model)
- 使用
HuggingFaceEmbedding
加载一个本地或在线的向量化模型,比如sentence-transformers
。 - 目的是把文本转成向量,方便后续做相似度检索。
embed_model = HuggingFaceEmbedding(model_name="本地或在线模型路径") Settings.embed_model = embed_model
- 使用
-
准备大语言模型(LLM)
- 使用
HuggingFaceLLM
加载本地的 LLM,比如 Qwen-1.5。 - 这个模型在检索到相关文档后负责做总结、回答等生成任务。
llm = HuggingFaceLLM(model_name="本地模型路径", tokenizer_name="本地tokenizer路径") Settings.llm = llm
- 使用
-
加载文档数据
- 使用
SimpleDirectoryReader
,从一个文件夹(txt、md、pdf等)读取文档,形成统一的数据结构。
documents = SimpleDirectoryReader("文件夹路径").load_data()
- 使用
-
构建索引(向量数据库)
- 通过
VectorStoreIndex.from_documents(documents)
,将文档向量化并存储在内存中的一个简单向量库里(当然也可以换成别的向量库,比如FAISS、Milvus等)。
index = VectorStoreIndex.from_documents(documents)
- 通过
-
构建查询引擎(Query Engine)
index.as_query_engine()
创建一个检索+生成一体的接口。- 先检索相关文档(基于embedding相似度),然后结合LLM回答问题。
query_engine = index.as_query_engine() rsp = query_engine.query("你的问题") print(rsp)
总结:
LlamaIndex的RAG流程= 文档加载 + 文本向量化 + 建索引 + 语义检索 + 大模型生成回答
阶段 | 你说的 | 补充细节 |
---|---|---|
1. 文档加载 | 文档加载(生成Node) | 支持多种来源:本地文件、数据库、API接口,生成标准化的 Document 或 TextNode 。 |
2. 文本向量化 | 文本向量化(embedding Node) | 用 embed_model 把每个 Node 转成向量,同时可以保存元数据(metadata)方便过滤。 |
3. 建立索引 | 建索引 (index) | 构建 VectorStoreIndex (或别的 Index,如KeywordTableIndex 、ListIndex 等)。索引中保存节点和它们的向量。 |
4. 语义检索 | 语义检索 (engine) | 通过 as_retriever() 获取最相关节点,支持向量相似度/关键词/BM25/混合检索。 |
5. LLM生成回答 | 大模型生成回答 (llm) | 通过 as_query_engine() 让LLM根据检索到的内容生成连贯、自然的最终回答。 |
常用模块:
步骤 | 主要模块 |
---|---|
文档加载 | SimpleDirectoryReader , Document , TextNode |
向量化 | HuggingFaceEmbedding , Settings.embed_model |
建立索引 | VectorStoreIndex , KeywordTableIndex , etc. |
检索 | Retriever (由 .as_retriever() 生成) |
生成回答 | LLM (通过 .as_query_engine() 包装) |
补充:as_query_engine()
是包含了as_retriever()
的一站式服务!
方法 | 作用 | 备注 |
---|---|---|
as_retriever() | 只检索,不回答 | 找到相关文本段,通常是TextNode,不生成最终回答 |
as_query_engine() | 检索 + 回答一体化 | 自动检索相关文本,再调用LLM生成最终回答,适合直接做问答 |
llamaindex调用本地框架回答问题脚本
from llama_index.core.llms import ChatMessage
from llama_index.llms.huggingface import HuggingFaceLLM
#使用HuggingFaceLLM加载本地大模型
llm = HuggingFaceLLM(model_name="/home/cw/llms/Qwen/Qwen1.5-1.8B-Chat",
tokenizer_name="/home/cw/llms/Qwen/Qwen1.5-1.8B-Chat",
model_kwargs={"trust_remote_code":True},
tokenizer_kwargs={"trust_remote_code":True})
#调用模型chat引擎得到回复 chat对话 query问答
rsp = llm.chat(messages=[ChatMessage(content="xtuner是什么?")])
print(rsp)
llamaindex实现RAG基本流程脚本
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.huggingface import HuggingFaceLLM
from llama_index.core import Settings, SimpleDirectoryReader, VectorStoreIndex
import torch
# ========================
# 1. 初始化本地 Embedding 模型 & 大模型(LLM)
# ========================
def setup_local_models():
embed_model = HuggingFaceEmbedding(
model_name="/home/cw/llms/embedding_model/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
device="cuda" if torch.cuda.is_available() else "cpu"
)
llm = HuggingFaceLLM(
model_name="/home/cw/llms/Qwen/Qwen1.5-1.8B-Chat",
tokenizer_name="/home/cw/llms/Qwen/Qwen1.5-1.8B-Chat",
model_kwargs={"trust_remote_code": True},
tokenizer_kwargs={"trust_remote_code": True},
device_map="auto",
generate_kwargs={"temperature": 0.3, "do_sample": True} # 控制生成风格
)
# 设置为全局模型
Settings.embed_model = embed_model
Settings.llm = llm
Settings.chunk_size = 512 # 控制文本切块大小
# ========================
# 2. 加载原始文档数据并切分为 Document 对象
# ========================
def load_documents(data_dir: str):
reader = SimpleDirectoryReader(data_dir)
return reader.load_data()
# ========================
# 3. 构建向量索引
# ========================
def build_index(documents):
return VectorStoreIndex.from_documents(documents)
# ========================
# 4. 构建查询引擎(RAG核心:语义检索 + LLM问答)
# ========================
def create_query_engine(index):
return index.as_query_engine(similarity_top_k=3) # 检索top-k相关片段
# ========================
# 5. 执行查询任务
# ========================
def run_query(query_engine, query_text: str):
response = query_engine.query(query_text)
print("🧠 回答:", response)
# ========================
# 主流程
# ========================
if __name__ == "__main__":
# 步骤1:设置模型
setup_local_models()
# 步骤2:加载数据
documents = load_documents("/home/cw/projects/demo_17/data")
# 步骤3:构建索引
index = build_index(documents)
# 步骤4:获取查询引擎
query_engine = create_query_engine(index)
# 步骤5:提问
run_query(query_engine, "xtuner是什么?")
切分为 Document 对象
集成Chromadb
安装
pip install chromadb
#lamaindex集成chromadb、huggingface
pip install llama-index-vector-stores-chroma
pip install llama-index-embeddings-huggingface
pip install llama-index
RAG数据收集!!!
数据结构
Document、Node关系
from llama_index.core.schema import Document, TextNode
# 创建 TextNode 和对应的 metadata
node1 = TextNode(
text="xtuner 是一个用于超参数优化的工具。",
metadata={"source": "用户手册", "section": "工具介绍"}
)
node2 = TextNode(
text="过拟合是指模型对训练数据的拟合过度,导致在测试集上表现不佳。",
metadata={"source": "机器学习基础", "section": "概念"}
)
# 创建 Document,包含多个 TextNode
doc1 = Document(
text="关于 xtuner 和机器学习的基础概念。",
nodes=[node1, node2],
metadata={"file": "machine_learning_guide.md", "author": "AI Research Team"}
)
# 创建另一个 Document
node3 = TextNode(
text="迁移学习可以将从一个任务学到的知识应用到另一个任务中。",
metadata={"source": "深度学习书籍", "section": "概念"}
)
doc2 = Document(
text="迁移学习和它的应用。",
nodes=[node3],
metadata={"file": "deep_learning_book.pdf", "author": "Deep Learning Experts"}
)
Document 1: "machine_learning_guide.md" (作者: AI Research Team)
├── TextNode 1: "xtuner 是一个用于超参数优化的工具。"
│ └── Metadata: { "source": "用户手册", "section": "工具介绍" }
└── TextNode 2: "过拟合是指模型对训练数据的拟合过度,导致在测试集上表现不佳。"
└── Metadata: { "source": "机器学习基础", "section": "概念" }
Document 2: "deep_learning_book.pdf" (作者: Deep Learning Experts)
└── TextNode 3: "迁移学习可以将从一个任务学到的知识应用到另一个任务中。"
└── Metadata: { "source": "深度学习书籍", "section": "概念" }
文档解析
文档解析就像把不同包装的食品拆开处理:
- PDF文件:类似于罐头食品,需要专用工具(如PDF阅读器)才能打开。
- Word文档:类似于盒装饼干,容易拆开但可能会有碎屑(即格式可能受损)。
- 扫描件/图片:类似于真空包装,需要剪刀(即图像识别工具)才能打开。
分步详解:
- 文件加载:找到文档存放位置,就像在电脑文件夹中定位文件。
- 常见问题:如果文件损坏,需要检查文件是否能正常打开。
- 格式转换:将文档统一转为纯文本,就像把不同货币兑换成美元。
- 示例:将PDF中的表格转换为Markdown格式。
- 元数据提取:获取文档的信息标签,就像查看食品包装上的生产日期。
- 包括:作者、创建时间、文档类型等。
- 结构化处理:整理内容层次,就像把食材分类放入保鲜盒。
- 建立标题层级:章节 > 段落 > 句子。
常用解析工具:
1. LlamaIndex自带工具
1.1 Simple Directory Reader(处理简单、结构化的数据)
主要处理一般的结构化的数据 txt md pdf json。缺点是:读取表格的信息、复杂信息的时候,会将原来表格中的信息读取重复、格式读取失败。
pip install llama-index
from llama_index.core import SimpleDirectoryReader
from llama_index.core.node_parser import SentenceSplitter
单个文件:
reader = SimpleDirectoryReader(
input_files=["/home/data/a.txt"]
)
docs = reader.load_data()
print(f"Loaded {len(docs)} docs")
返回的对象是Document
缺点是:表格数据会读取没了。
整个目录:
reader = SimpleDirectoryReader(
"/home/data"
)
docs = reader.load_data()
print(f"Loaded {len(docs)} docs")
结果:
1.2 HTML Tag Reader(读取Html)
pip install llama-index
pip install llama-index-readers-file
from llama_index.readers.file import HTMLTagReader
reader = HTMLTagReader(tag="section", ignore_no_id=True)
docs = reader.load_data(
"https://item.jd.com/100141961157.html"
)
for doc in docs:
print(doc.metadata)
现在只能本地的
结果:
2.第三方库
2.1 读取表格 pdfplumber
import pdfplumber
with pdfplumber.open("/home/cw/projects/demo_20/data/report_with_table.pdf") as pdf:
# 提取所有文本
text = ""
for page in pdf.pages:
text += page.extract_text()
print(text[:200]) # 打印前200字符
# 提取表格(自动检测)
for page in pdf.pages:
tables = page.extract_tables()
for table in tables:
print("\n表格内容:")
for row in table:
print(row)
结果:
2.2 读取图片内容 OCR
魔塔搜一下针对具体环境的有很多
2.3读取网页内容 BeautifulSoup
3.爬虫
文档切分:
为什么需要分块?
就像用收纳盒整理衣物:
- 太大 → 找衣服时要把整个箱子倒出来
- 太小 → 需要开太多盒子才能凑齐一套
分块三要素:
要素 | 说明 | 推荐值 |
---|---|---|
块大小 | 每段文字的长度 | 200-500字 |
块重叠 | 相邻块重复内容 | 10%-20% |
切分依据 | 按句子/段落/语义划分 | 语义分割最优 |
分块策略对比表:
策略类型 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
固定大小 | 实现简单 | 可能切断完整语义 | 技术文档 |
按段落分割 | 保持逻辑完整性 | 段落长度差异大 | 文学小说 |
语义分割 | 确保内容完整性 | 计算资源消耗较大 | 专业领域文档 |
常用切分工具:
1 Llamaindx自带
1.1 SentenceSplitter(简单句子切分)
固定长度切分 脚本
语句切分 脚本
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core import SimpleDirectoryReader
# 加载所有文档
documents = SimpleDirectoryReader(input_files=["/home/cw/projects/demo_20/data/ai.txt"]).load_data()
#1 使用固定节点切分
from llama_index.core.node_parser import TokenTextSplitter
#块大小chunk_size、重叠度chunk_overlap
fixed_splitter = TokenTextSplitter(chunk_size=256, chunk_overlap=20)
fixed_nodes = fixed_splitter.get_nodes_from_documents(documents)
print("固定分块示例:", [len(n.text) for n in fixed_nodes[:3]]) # 输出:[200, 200, 200]
print(print("首个节点内容:\n", fixed_nodes[0].text))
print(print("第二个节点内容:\n", fixed_nodes[1].text))
#2 使用句子分割器
splitter = SentenceSplitter(chunk_size=256)
nodes = splitter.get_nodes_from_documents(documents)
# 查看结果
print(f"生成节点数: {len(nodes)}")
print("首个节点内容:\n", nodes[0].text)
print("第二个节点内容:\n", nodes[1].text)
语义切分 脚本
from llama_index.core import SimpleDirectoryReader
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.core.node_parser import SemanticSplitterNodeParser
import os
# 2. 加载文档
documents = SimpleDirectoryReader(input_files=["/home/cw/projects/demo_20/data/test.txt"]).load_data()
# # 3. 筛选Markdown文档
# md_docs = [d for d in documents if d.metadata["file_path"].endswith(".md")]
# 4. 初始化模型和解析器
embed_model = HuggingFaceEmbedding(
#指定了一个预训练的sentence-transformer模型的路径
model_name="/home/cw/llms/embedding_model/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
)
# breakpoint_percentile_threshold
#数值越高(比如 95、99):更严格,只有语义变化非常明显的地方才会被分割,结果是 段落更长、更少;
#数值越低(比如 70、50):更宽松,会分割更多地方,结果是 段落更短、更多。
semantic_parser = SemanticSplitterNodeParser(
buffer_size=1,
breakpoint_percentile_threshold=90,
embed_model=embed_model
)
# 5. 执行语义分割
semantic_nodes = semantic_parser.get_nodes_from_documents(documents)
# 6. 打印结果
print(f"语义分割节点数: {len(semantic_nodes)}")
for i, node in enumerate(semantic_nodes[:2]): # 只打印前两个节点
print(f"\n节点{i+1}:\n{node.text}")
print("-"*50)
召回率
召回率(Recall) 是一个衡量模型能找出多少真实正例的指标。
公式是:
召回率=真正例(TP)真正例(TP)+假负例(FN)\text{召回率} = \frac{\text{真正例(TP)}}{\text{真正例(TP)} + \text{假负例(FN)}}召回率=真正例(TP)+假负例(FN)真正例(TP)
- 真正例(TP):模型正确预测为正类的样本数量。
- 假负例(FN):模型错把正类预测成负类的样本数量。
通俗点说,召回率高,说明模型把大多数该找出来的“目标”都找出来了。
比如做肿瘤筛查,宁可多查出来一点(有点误报),也不能漏掉真正有病的人,所以希望召回率高。
提升召回率的三个策略
1.查询扩展
给提问加上修饰词,原始问题:“如何做番茄炒蛋” 扩展后:“家常番茄炒蛋做法步骤 厨房新手教程 简单易学”。给用户一个例子,让他对着实例模仿提问。
2.混合检索
原本是语义检索,但是有些特殊情况,如专业单词太多,大模型理解不行。可选两个方案,微调或者加一个关键词检索。关键词检索就是把特殊名词做关键词搜索相关的。**问题:1.对性能有影响、响应速度慢。2. 用户提问不规范 **
3.微调
测试脚本:
语料库:
{
"knownledge": {
"query": "如何预防机器学习模型过拟合?",
"positive_passages": [
"正则化方法通过添加L1/L2惩罚项控制模型复杂度...",
"交叉验证将数据划分为训练集和验证集...",
"早停法(Early Stopping)监控验证集损失..."
],
"negative_passages": [
"GPU加速训练的技术方案...",
"数据标注的质量控制方法...",
"卷积神经网络结构解析..."
]
}
}
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.core import Settings, VectorStoreIndex
from llama_index.llms.huggingface import HuggingFaceLLM
from llama_index.core.schema import TextNode
import json
import torch
# 1. 初始化本地模型
def setup_local_models():
# 设置本地embedding模型
embed_model = HuggingFaceEmbedding(
model_name="/root/model/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
device="cuda" if torch.cuda.is_available() else "cpu"
)
# 设置本地LLM模型
llm = HuggingFaceLLM(
model_name="/root/model/Qwen/Qwen2.5-1.5B-Instruct",
tokenizer_name="/root/model/Qwen/Qwen2.5-1.5B-Instruct",
model_kwargs={"trust_remote_code": True},
tokenizer_kwargs={"trust_remote_code": True},
device_map="auto",
generate_kwargs={"temperature": 0.3, "do_sample": True} # 修改为do_sample=True避免警告
)
# 全局设置
Settings.embed_model = embed_model
Settings.llm = llm
Settings.chunk_size = 512
# 2. 加载数据并处理格式
def load_data(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
# print("data:", data)
nodes = []
for item in data.items():
# print("item:", (item), "type:", type(item))
if isinstance(item, tuple):
key, value = item
print("key:", key, "value:", value)
# 处理DPR格式数据,包括query和positive_passages
if 'query' in value and 'positive_passages' in value:
text = f"查询: {value['query']}\n相关文档: { value['positive_passages']}"
# 处理QA对格式,包括question和answer
# elif 'question' in item and 'answer' in item:
# text = f"问题: {item['question']}\n答案: {item['answer']}"
if 'query' in value and 'negative_passages' in value:
text += f"\n对问题: {value['query']}\n会造成负面影响的信息: {value['negative_passages']}"
# else:
# continue
elif isinstance(item, str):
# print("item:", item)
text = item
else:
continue
node = TextNode(text=text)
nodes.append(node)
return nodes
# 3. 初始化本地模型
setup_local_models()
# 4. 加载数据
data_path = "/root/code/data/qa_pairs.json"
nodes = load_data(data_path)
print("nodes:", nodes)
# 5. 示例查询
query = "如何预防机器学习模型过拟合?"
# 案例1:向量检索(使用本地embedding模型)
vector_index = VectorStoreIndex(nodes)
# 检索前3个最相似的节点,这里使用的是本地embedding模型,默认使用bm25模式
vector_retriever = vector_index.as_retriever(similarity_top_k=3)
print("向量检索结果:", [node.text[:300] + "..." for node in vector_retriever.retrieve(query)])
# 案例2:关键词检索(不使用bm25模式)
from llama_index.core import KeywordTableIndex
keyword_index = KeywordTableIndex(nodes)
keyword_retriever = keyword_index.as_retriever(similarity_top_k=3) # 使用默认模式
print("关键词检索结果:", [node.text[:300] + "..." for node in keyword_retriever.retrieve(query)])
# 案例3:查询引擎(使用本地LLM生成回答)
# query_engine = keyword_index.as_query_engine()
query_engine = vector_index.as_query_engine()
response = query_engine.query(query)
print("LLM生成回答:", response)
重排序
常见排序模型对比:
模型名称 | 速度 | 精度 | 硬件要求 | 适用场景 |
---|---|---|---|---|
BM25 | 快 | 中 | 低 | 关键词匹配 |
Cross-Encoder | 慢 | 高 | 高 | 小规模精准排序 |
ColBERT | 中 | 高 | 中 | 平衡速度与精度 |
例如:
# 初始检索结果(按相似度排序):
results = [
"模型正则化方法简述", # 相关度0.7
"硬件加速技术进展", # 相关度0.65
"过拟合解决方案详解", # 相关度0.92 ← 正确答案
"数据集清洗方法"
]
# 应用重排序
from llama_index.postprocessor.cohere_rerank import CohereRerank
reranker = CohereRerank(api_key="YOUR_KEY", top_n=2)
reranked_results = reranker.postprocess_nodes(results, query_str=query)
print("重排序后结果:", [res.text for res in reranked_results])
原始排序:
1. 模型正则化方法简述(相关度0.7)
2. 硬件加速技术进展(相关度0.65)
3. 过拟合解决方案详解(相关度0.92)← 正确答案
4. 数据集清洗方法
重排序后:
1. 过拟合解决方案详解(评分0.95)← 正确答案
2. 模型正则化方法简述(评分0.88)+
Embedding模型选型
简介
作用:文本转化为词向量。
关键点:文本的差异:汉语、英语、古诗词、文言文、对联。。。
embedding根据不同语料训练出来的,选型就是在选择合适的文本。
但是根据项目经验,常规的RAG项目(不是要求精确度天特别高的)随便选一个中文的,对结果影响不大。一般来说向量检索、rerank。。。全用完了发现还是不行,才考虑Embedding
Embedding选型只能从大方向(中、英文,英文包括代码)选,没啥特别好的指标。通常选择768维度的就行
嵌入原理
本质是一个bert模型,主要是对文本做encode编码,把他编码成向量特征。gpt是decode模型。
类型 | 模型 | 输入 | 输出 | 特点 |
---|---|---|---|---|
🔤 词向量模型 | Word2Vec、GloVe | 单词(词表中的词) | 一个静态向量 | 每个词只有一个表示,不能考虑上下文 |
🧠 上下文 embedding | BERT、Qwen、LLaMA | 整句或上下文 | 每个词都有上下文相关的向量 | 更丰富、更适合语义搜索、问答 |
🧭 多模态 embedding | CLIP、BLIP | 图像、文本等 | 图文都变成向量 | 支持跨模态检索(图搜文,文搜图) |
常见的维度(768-3072维),每个维度是一个特征。常用的728、1024、2048维。
通过这个方法来捕捉语义信息,相似的向量距离会更小。
核心作用:
1.语义编码:将文本、图像等转换为向量,保留上下文信息(如 BERT 的 CLS Token 或均值池化。
2.相似度计算:通过余弦相似度、欧氏距离、内积等度量向量关联性,支撑检索增强生成(RAG)、推荐系 统等应用。 余弦相似度比较常用,因为范围可控,欧氏距离无法控制。-1到1能看出来正相关、不相关、负相关,欧氏距离、内积用的画先做归一化。
3.信息降维:压缩复杂数据为低维稠密向量,提升存储与计算效率。
关键技术原理 上下文依赖:现代模型(如 BGE-M3)动态调整向量,捕捉多义词在不同语境中的含义。
训练方法:对比学习(如 Word2Vec 的 Skip-gram/CBOW)、预训练+微调(如 BERT)。
选型指南
因素 | 说明 |
---|---|
任务性质 | 匹配任务需求(问答、搜索、聚类等) |
领域特性 | 通用 vs 专业领域(医学、法律等) |
多语言支持 | 需处理多语言内容时考虑 |
维度 | 权衡信息丰富度与计算成本 |
许可条款 | 开源 vs 专有服务 |
最大tokens | 适合的上下文窗口大小 |
- 通用全能型
- BGE-M3:北京智源研究院开发,支持多语言、混合检索(稠密+稀疏向量),处理8K上下文,适合企业级知识库。
- NV-Embed-v2:基于Mistral-7B,检索精度高(MTEB得分62.65),但需较高计算资源。
- 垂直领域特化型
- 中文场景:BGE-large-zh-v1.5(合同/政策文件)、M3E-base(社交媒体分析)。
- 多模态场景:BGE-VL(图文跨模态检索),联合编码OCR文本与图像特征。
- 轻量化部署型
- nomic-embed-text:768维向量,推理速度比OpenAI快3倍,适合边缘设备。
- gte-qwen2-1.5b-instruct:1.5B参数,16GB显存即可运行,适合初创团队原型验。
选型决策树:
- 中文为主 → BGE系列 > M3E;
- 多语言需求 → BGE-M3 > multilingual-e5;
- 预算有限 → 开源模型(如Nomic Embed)。
对比选型
数据集
(以英文开源数据集squad_dev为例):
通过网盘分享的文件:embedding模型英文测试数据集
链接: https://pan.baidu.com/s/1eVfMk1kTbj7aNVKsdUf8_Q?pwd=jq4c 提取码: jq4c
–来自百度网盘超级会员v6的分享
Py代码
这里用的huggingface的sentence_transformers加载的。看大模型文件中有没有pooling层可以看出是不是sentence_transformers模型
#embedding_model效果对比
from sentence_transformers import SentenceTransformer, util
import json
import numpy as np
# 加载SQuAD数据(假设已处理成列表格式)
with open("squad_dev.json") as f:
squad_data = json.load(f)["data"]
# 提取问题和答案对
qa_pairs = []
for article in squad_data:
for para in article["paragraphs"]:
for qa in para["qas"]:
if not qa["is_impossible"]:
qa_pairs.append({
"question": qa["question"],
"answer": qa["answers"][0]["text"],
"context": para["context"]
})
# 初始化两个本地模型
model1 = SentenceTransformer('/home/cw/llms/embedding_model/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2') # 模型1
model2 = SentenceTransformer('/home/cw/llms/embedding_model/sungw111/text2vec-base-chinese-sentence') # 模型2
# 编码所有上下文(作为向量库)
contexts = [item["context"] for item in qa_pairs]
context_embeddings1 = model1.encode(contexts) # 模型1的向量库
context_embeddings2 = model2.encode(contexts) # 模型2的向量库
# 评估函数
def evaluate(model, query_embeddings, context_embeddings):
correct = 0
for idx, qa in enumerate(qa_pairs[:100]): # 测试前100条
# 查找最相似上下文
sim_scores = util.cos_sim(query_embeddings[idx], context_embeddings)
best_match_idx = np.argmax(sim_scores)
# 检查答案是否在匹配段落中
if qa["answer"] in contexts[best_match_idx]:
correct += 1
return correct / len(qa_pairs[:100])
# 编码所有问题
query_embeddings1 = model1.encode([qa["question"] for qa in qa_pairs[:100]])
query_embeddings2 = model2.encode([qa["question"] for qa in qa_pairs[:100]])
# 执行评估
acc1 = evaluate(model1, query_embeddings1, context_embeddings1)
acc2 = evaluate(model2, query_embeddings2, context_embeddings2)
print(f"模型1准确率: {acc1:.2%}")
print(f"模型2准确率: {acc2:.2%}")
Chroma向量数据库
安装
pip install chromadb
初始化客户端
- 内存模式 开发环境不会存到本地
import chromadb
client = chromadb.Client()
- 持久化模式 生产环境
import chromadb
client = chromadb.PersistentClient(path="path/to/save") #本地目录
基础操作
import chromadb
from sentence_transformers import SentenceTransformer
class SentenceTransformerEmbeddingFunction:
def __init__(self, model_path: str, device: str = "cuda"):
self.model = SentenceTransformer(model_path, device=device)
def __call__(self, input: list[str]) -> list[list[float]]:
if isinstance(input, str):
input = [input]
#需要控制转成list类型的
return self.model.encode(input, convert_to_numpy=True).tolist()
#1 创建/加载集合(含自定义嵌入函数)
embed_model = SentenceTransformerEmbeddingFunction(
model_path="/home/cw/llms/embedding_model/sungw111/text2vec-base-chinese-sentence",
device="cuda" # 无 GPU 改为 "cpu"
)
# 创建客户端和集合
client = chromadb.Client()
#2 表名:my_knowledge_base
#余弦相似度:metadata={"hnsw:space": "cosine"}
#embedding model:embedding_function=embed_model
#注意:新版chromadb不支持sentenceTransformer,需要提前转一下
collection = client.create_collection("my_knowledge_base",metadata={"hnsw:space": "cosine"},embedding_function=embed_model)
#3 添加文档
#documents文档
#metadatas 元数据—— 附加信息
#ids 索引名称
#注意数据库里面存的是转换之后的向量
#存的时候用metadata={"hnsw:space": "cosine"}进行衡量相似度,把相似的存在一起
collection.add(
documents=["RAG是一种检索增强生成技术", "向量数据库存储文档的嵌入表示","三英战吕布"],
metadatas=[{"source": "tech_doc"}, {"source": "tutorial"}, {"source": "tutorial1"}],
ids=["doc1", "doc2","doc3"]
)
#4 查询相似文档
#n_results 返回值个数
results = collection.query(
query_texts=["什么是RAG技术?"],
n_results=3
)
print(results)
#result输出的是distance
#5 根据ids更新数据,
collection.update(
ids=["doc1"], # 使用已存在的ID
documents=["更新后的RAG技术内容"]
)
# 查看更新后的内容 - 方法1:使用get()获取特定ID的内容
updated_docs = collection.get(ids=["doc1"])
print("更新后的文档内容:", updated_docs["documents"])
# 查看更新后的内容 - 方法2:查询所有文档
all_docs = collection.get()
print("集合中所有文档:", all_docs["documents"])
#删除内容
collection.delete(ids=["doc1"])
# 查看更新后的内容 - 方法2:查询所有文档
all_docs = collection.get()
print("集合中所有文档:", all_docs["documents"])
#统计条目
print(collection.count())
Dify本地部署实现RAG
Dify + Deepseek实现RAG
本地部署:
部署版本
Ubuntu22.04
Docker version 26.1.3, build 26.1.3-0ubuntu1~22.04.1
docker-compose version 1.29.2, build unknown
#dify源码获取
git clone --branch 0.15.3 --depth 1 https://github.com/langgenius/dify.git
cd dify/docker # docker目录
cp .env.example .env # 复制环境变量模板
添加国内镜像
sudo cat /etc/docker/daemon.json
echo '{
"registry-mirrors": [
"https://docker.1panel.live",
"https://docker.nju.edu.cn",
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com",
"https://registry.cn-hangzhou.aliyuncs.com"
]
}' | sudo tee /etc/docker/daemon.json
# 后台启动docker,会自动拉取镜像 dify默认80端口
sudo docker-compose -f docker-compose.yml -f docker-compose.override.yml up -d
或者直接在dify/docker文件夹下之执行
sudo docker-compose up -d
配置内网穿透、端口转发
docker-compose
:
这是 Docker 的一个工具,用于定义和管理多容器应用。它让你可以通过一个配置文件(通常是 docker-compose.yml
)来定义多个容器、网络、卷等,并且一次性启动或管理它们,而不需要手动写一堆 docker run
命令。
up
:
docker-compose up
是用来启动和运行容器的命令,它会根据 docker-compose.yml
文件中定义的服务启动所有需要的容器。
如果容器不存在,它会自动创建并启动这些容器。
-d
:
这是 --detach
的简写,意思是在后台运行。添加 -d
参数后,Docker Compose 会在后台启动容器,而不会阻塞终端。这意味着你可以继续在终端执行其他命令,而不用等待容器运行完毕。
win11
Docker version 27.4.0, build bde2b89
docker源
{
"builder": {
"gc": {
"defaultKeepStorage": "20GB",
"enabled": true
}
},
"experimental": false,
"registry-mirrors": [
"https://docker.1panel.live",
"https://docker.nju.edu.cn",
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com",
"https://registry.cn-hangzhou.aliyuncs.com"
]
}
#1 dify源码获取
git clone --branch 0.15.3 --depth 1 https://github.com/langgenius/dify.git
cd dify/docker # docker目录
cp .env.example .env # 复制环境变量模板
#2 在dify/docker目录下安装容器
docker compose up -d
#3 登录dify
http://localhost/signin
#4 点头像、设置、模型供应商
#5 启动dify添加ollama地址:
http://host.docker.internal:11434
#6 添加embedding和模型本身
#7 创建知识库本身
#8 创建模型 工作室/创建空白应用/聊天助手
(ubunt配置一下、windows不用配)
1.增加自定义模型:
编辑dify/docker/.env新增如下内容
# 启用自定义模型
CUSTOM_MODEL_ENABLED=true
# 将 OLLAMA_API_BASE_URL 改为宿主机的物理 IP
OLLAMA_API_BASE_URL=http://192.168.50.206:11434
# vLLM/Lmdeploy 的 OpenAI 兼容 API 地址
CUSTOM_OPENAI_API_BASE_URL=http://192.168.50.206:8000
**修改docker-compose.yaml(大概393行)**server顶格写
services:
# API service
api:
image: langgenius/dify-api:0.15.3
restart: always
environment:
# Use the shared environment variables.
<<: *shared-api-worker-env
# Startup mode, 'api' starts the API server.
MODE: api
SENTRY_DSN: ${API_SENTRY_DSN:-}
SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0}
SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0}
depends_on:
- db
- redis
volumes:
# Mount the storage directory to the container, for storing user files.
- ./volumes/app/storage:/app/api/storage
networks:
- ssrf_proxy_network
- default
- dify-net
extra_hosts:
- "host.docker.internal:192.168.50.206" # 直接映射宿主机 IP
# worker service
# The Celery worker for processing the queue.
worker:
image: langgenius/dify-api:0.15.3
restart: always
environment:
# Use the shared environment variables.
<<: *shared-api-worker-env
# Startup mode, 'worker' starts the Celery worker for processing the queue.
MODE: worker
SENTRY_DSN: ${API_SENTRY_DSN:-}
SENTRY_TRACES_SAMPLE_RATE: ${API_SENTRY_TRACES_SAMPLE_RATE:-1.0}
SENTRY_PROFILES_SAMPLE_RATE: ${API_SENTRY_PROFILES_SAMPLE_RATE:-1.0}
depends_on:
- db
- redis
volumes:
# Mount the storage directory to the container, for storing user files. RAG测试
- ./volumes/app/storage:/app/api/storage
networks:
- ssrf_proxy_network
- default
- dify-net
extra_hosts:
- "host.docker.internal:192.168.50.206" # 直接映射宿主机 IP
最后的network替换成:
networks:
# create a network between sandbox, api and ssrf_proxy, and can not access outside.
ssrf_proxy_network:
driver: bridge
internal: true
milvus:
driver: bridge
opensearch-net:
driver: bridge
internal: true
dify-net:
driver: bridge
attachable: true
完整配置:
链接: https://pan.baidu.com/s/1mr19RfEub6n0VMdQ7josrw?pwd=kbis 提取码: kbis
–来自百度网盘超级会员v6的分享
2.重启dify:
#重启dify
sudo docker-compose down
sudo docker-compose up -d
3.验证后端模型服务是否正常(可选):
curl http://192.168.50.206:11434/api/chat -H "Content-Type: application/json" -d '{
"model": "deepseek-r1:1.5b",
"messages": [
{"role": "user", "content": "你好!"}
]
}'
命令解释:
curl
curl
是一个命令行工具,用来发送 HTTP 请求 的。- 你可以用它模拟浏览器、调用 API,非常常用。
http://192.168.0.114:11434/api/chat
- 这是请求的地址(URL)。
- 意思是访问你局域网中 IP 为
192.168.0.114
,端口11434
的服务,接口路径是/api/chat
。 - 看起来应该是一个聊天接口,可能是本地部署的一个 LLM(大语言模型)服务,比如 Ollama 之类的。
-H "Content-Type: application/json"
-H
是指定一个请求头(Header)。Content-Type: application/json
告诉服务器:我发送的数据格式是 JSON 格式。- 很多 API 要求明确指定
Content-Type
,否则会拒绝请求。
-d '{"model": "deepseek-r1:1.5b"}'
-d
是指定请求体(body)。- 这里发送了一个 JSON 数据:
{"model": "deepseek-r1:1.5b"}
。 - 意思是告诉服务器:我想用模型
"deepseek-r1:1.5b"
来进行对话。
4.修改防火墙(可选)
#Ubuntu 默认使用 ufw,需确保 8000 端口开放:
sudo ufw allow 8000/tcp
sudo ufw reload
5.启动VLLM
注意启动端口:0.0.0.0监听所有端口
6.配置OpenAI兼容供应商
- 输入模型全称
- 填写url/v1,例如:http://192.168.0.114:8000/v1
- 上下文长度:4096 最大token:4096
- 其他不支持
- 验证是否支持跨网络访问
#验证跨网络访问
curl http://192.168.0.114:8000/v1/models
7.ollama配置embedding模型
新建知识库:
- 准备数据(txt、md、pdf、html、csv、docx…):
{
"中国移动4G套餐": [
{
"套餐名称": "4G飞享套餐",
"月费": "18元起",
"流量": "100MB起,最高可达20GB",
"通话": "包含一定分钟数,超出后按标准资费计费",
"适用人群": "适合流量需求较低的用户",
"优惠信息": "首月免费体验,赠送50分钟通话"
},
{
"套餐名称": "4G畅享套餐",
"月费": "58元起",
"流量": "1GB起,最高可达30GB",
"通话": "包含较多通话分钟,超出后按标准资费计费",
"适用人群": "适合流量和通话需求中等的用户",
"优惠信息": "赠送一年视频会员,每月额外赠送1GB流量"
}
],
"中国移动5G套餐": [
{
"套餐名称": "5G智享套餐",
"月费": "128元起",
"流量": "30GB起,最高可达300GB",
"通话": "无限通话",
"适用人群": "适合追求高速网络体验的用户",
"优惠信息": "赠送5G手机优惠购资格,每月赠送10GB夜间流量"
}
],
"中国联通4G套餐": [
{
"套餐名称": "4G沃享套餐",
"月费": "28元起",
"流量": "300MB起,最高可达10GB",
"通话": "包含一定分钟数,超出后按标准资费计费",
"适用人群": "适合流量需求一般的用户",
"优惠信息": "新用户首月免月租,赠送100MB国内流量"
}
],
"中国联通5G套餐": [
{
"套餐名称": "5G沃快套餐",
"月费": "138元起",
"流量": "40GB起,最高可达500GB",
"通话": "1500分钟通话",
"适用人群": "适合流量和通话需求较高的用户",
"优惠信息": "赠送半年音乐会员,每月赠送5GB流量"
}
],
"中国电信4G套餐": [
{
"套餐名称": "4G乐享套餐",
"月费": "38元起",
"流量": "500MB起,最高可达20GB",
"通话": "包含一定分钟数,超出后按标准资费计费",
"适用人群": "适合流量需求较低的用户",
"优惠信息": "赠送100分钟国内通话,每月赠送500MB流量"
}
],
"中国电信5G套餐": [
{
"套餐名称": "5G畅享套餐",
"月费": "158元起",
"流量": "60GB起,最高可达1TB",
"通话": "2000分钟通话",
"适用人群": "适合对流量和通话有极高需求的用户",
"优惠信息": "赠送一年视频会员,每月赠送10GB流量"
}
]
}
- 文档切分
- dify仅支持固定长度分块
- 测试发现,切块效果不好会导致数据造假。
- 大模型也能做embedding只不过比专有的会大,时间长。
结论:
通常vllm跑推理模型,embedding和rerank模型直接用huggingface。
数据质量影响模型质量,不同公司,模型大的不一定比小的好。测试使用qwen-1.8b-chat和deepseek-r1-1.5b模型。同样糟糕的数据,deepseek质量比qwen好很多,但都存在数据造假。因为固定长度分块数据质量很差。
RAG非常依赖模型能力,通常3b以上,数据一定要严谨。
RAG法律咨询助手
硬件:NVIDIA GeForce RTX 3090 24g显存
一、数据收集
爬虫代码保存为json
import json
import re
import requests
from bs4 import BeautifulSoup
import os
def fetch_and_parse(url):
# 请求网页
response = requests.get(url)
# 设置网页编码格式
response.encoding = 'utf-8'
# 解析网页内容
soup = BeautifulSoup(response.text, 'html.parser')
# 提取正文内容
content = soup.find_all('p')
# 初始化存储数据
data = []
# 提取文本并格式化
for para in content:
text = para.get_text(strip=True)
if text: # 只处理非空文本
# 根据需求格式化内容
data.append(text)
# 将data列表转换为字符串
data_str = '\n'.join(data)
return data_str
def extract_law_articles(data_str):
# 正则表达式,匹配每个条款号及其内容
pattern = re.compile(r'第([一二三四五六七八九十零百]+)条.*?(?=\n第|$)', re.DOTALL)
# 初始化字典来存储条款号和内容
lawarticles = {}
# 搜索所有匹配项
for match in pattern.finditer(data_str):
articlenumber = match.group(1)
articlecontent = match.group(0).replace('第' + articlenumber + '条', '').strip()
lawarticles[f"中华人民共和国劳动法 第{articlenumber}条"] = articlecontent
# 转换字典为JSON字符串
jsonstr = json.dumps(lawarticles, ensure_ascii=False, indent=4)
return jsonstr
if __name__ == '__main__':
url = "https://www.gov.cn/banshi/2005-05/25/content_905.htm"
data_str = fetch_and_parse(url)
# 这里返回的是 JSON 字符串
json_str = extract_law_articles(data_str)
# ✅ 转换为 Python dict
article_dict = json.loads(json_str)
# ✅ 包装成 list
article_list = [article_dict]
# ✅ 写入格式化的 JSON 文件
save_path = r"G:\python_ws_g\code\llm\llmlearning\RAG_ Legal_Assistant\data\中华人民共和国劳动法.json"
os.makedirs(os.path.dirname(save_path), exist_ok=True)
with open(save_path, "w", encoding="utf-8") as f:
json.dump(article_list, f, ensure_ascii=False, indent=2)
数据实例:
[
{
"中华人民共和国劳动法 第一条": "为了保护劳动者的合法权益,调整劳动关系,建立和维护适应社会主义市场经济的劳动制度,促进经济发展和社会进步,根据宪法,制定本法。",
"中华人民共和国劳动法 第二条": "在中华人民共和国境内的企业、个体经济组织(以下统称用人单位)和与之形成劳动关系的劳动者,适用本法。\n国家机关、事业组织、社会团体和与之建立劳动合同关系的劳动者,依照本法执行。",
"中华人民共和国劳动法 第三条": "劳动者享有平等就业和选择职业的权利、取得劳动报酬的权利、休息休假的权利、获得劳动安全卫生保护的权利、接受职业技能培训的权利、享受社会保险和福利的权利、提请劳动争议处理的权利以及法律规定的其他劳动权利。\n劳动者应当完成劳动任务,提高职业技能,执行劳动安全卫生规程,遵守劳动纪律和职业道德。",
"中华人民共和国劳动法 第四条": "用人单位应当依法建立和完善规章制度,保障劳动者享有劳动权利和履行劳动义务。"
}
]
二、数据处理
Node格式:
TextNode(
id_='d7b5c5f2-093e-4c55-9c48-d1d95b87c6e8',
text='LlamaIndex 是一个用于构建大语言模型应用的框架,提供索引、检索和问答能力。',
metadata={'file_name': 'llamaindex_intro.txt', 'page_number': '3', 'chapter': '概述', 'author': 'LlamaIndex Team'},
embedding=[0.021, -0.013, 0.045, ...],
relationships={
'PARENT': RelatedNodeInfo(node_id='parent_node_123'),
'NEXT': RelatedNodeInfo(node_id='node_002'),
'PREVIOUS': RelatedNodeInfo(node_id='node_000')
},
ref_doc_id='doc_001',
start_char_idx=0,
end_char_idx=48,
score=0.92
)
转化成Node格式:
# -*- coding: utf-8 -*-
import json
import time
from pathlib import Path
from typing import List, Dict
import chromadb
from llama_index.core import VectorStoreIndex, StorageContext, Settings
from llama_index.core.schema import TextNode
from llama_index.llms.huggingface import HuggingFaceLLM
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.vector_stores.chroma import ChromaVectorStore
# ================== 数据处理 ==================
def load_and_validate_json_files(data_dir: str) -> List[Dict]:
"""加载并验证JSON法律文件"""
json_files = list(Path(data_dir).glob("*.json"))
assert json_files, f"未找到JSON文件于 {data_dir}"
all_data = []
for json_file in json_files:
with open(json_file, 'r', encoding='utf-8') as f:
try:
data = json.load(f)
# 验证数据结构
if not isinstance(data, list):
raise ValueError(f"文件 {json_file.name} 根元素应为列表")
for item in data:
if not isinstance(item, dict):
raise ValueError(f"文件 {json_file.name} 包含非字典元素")
for k, v in item.items():
if not isinstance(v, str):
raise ValueError(f"文件 {json_file.name} 中键 '{k}' 的值不是字符串")
all_data.extend({
"content": item,
"metadata": {"source": json_file.name}
} for item in data)
except Exception as e:
raise RuntimeError(f"加载文件 {json_file} 失败: {str(e)}")
print(f"成功加载 {len(all_data)} 个法律文件条目")
return all_data
def create_nodes(raw_data: List[Dict]) -> List[TextNode]:
"""添加ID稳定性保障"""
nodes = []
for entry in raw_data:
law_dict = entry["content"]
source_file = entry["metadata"]["source"]
for full_title, content in law_dict.items():
# 生成稳定ID(避免重复)
node_id = f"{source_file}::{full_title}"
parts = full_title.split(" ", 1)
law_name = parts[0] if len(parts) > 0 else "未知法律"
article = parts[1] if len(parts) > 1 else "未知条款"
node = TextNode(
text=content,
id_=node_id, # 显式设置稳定ID
metadata={
"law_name": law_name,
"article": article,
"full_title": full_title,
"source_file": source_file,
"content_type": "legal_article"
}
)
nodes.append(node)
print(f"生成 {len(nodes)} 个文本节点(ID示例:{nodes[0].id_})")
return nodes
# ================== 主程序 ==================
def main():
print("\n初始化数据...")
raw_data = load_and_validate_json_files(r"/root/autodl-tmp/rag/data")
nodes = create_nodes(raw_data)
print(nodes)
# print("\n初始化向量存储...")
# start_time = time.time()
# index = init_vector_store(nodes)
# print(f"索引加载耗时:{time.time()-start_time:.2f}s")
if __name__ == "__main__":
main()
结果:
初始化数据...
成功加载 1 个法律文件条目
生成 107 个文本节点(ID示例:中华人民共和国劳动法.json::中华人民共和国劳动法 第一条)
[TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第一条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第一条', 'full_title': '中华人民共和国劳动法 第一条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='为了保护劳动者的合法权益,调整劳动关系,建立和维护适应社会主义市场经济的劳动制度,促进经济发展和社会进步,根据宪法,制定本法。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第二条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第二条', 'full_title': '中华人民共和国劳动法 第二条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='在中华人民共和国境内的企业、个体经济组织(以下统称用人单位)和与之形成劳动关系的劳动者,适用本法。\n国家机关、事业组织、社会团体和与之建立劳动合同关系的劳动者,依照本法执行。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第三条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第三条', 'full_title': '中华人民共和国劳动法 第三条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动者享有平等就业和选择职业的权利、取得劳动报酬的权利、休息休假的权利、获得劳动安全卫生保护的权利、接受职业技能培训的权利、享受社会保险和福利的权利、提请劳动争议处理的权利以及法律规定的其他劳动权利。\n劳动者应当完成劳动任务,提高职业技能,执行劳动安全卫生规程,遵守劳动纪律和职业道德。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第四条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第四条', 'full_title': '中华人民共和国劳动法 第四条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位应当依法建立和完善规章制度,保障劳动者享有劳动权利和履行劳动义务。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第五条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第五条', 'full_title': '中华人民共和国劳动法 第五条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国家采取各种措施,促进劳动就业,发展职业教育,制定劳动标准,调节社会收入,完善社会保险,协调劳动关系,逐步提高劳动者的生活水平。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第六条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第六条', 'full_title': '中华人民共和国劳动法 第六条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国家提倡劳动者参加社会义务劳动,开展劳动竞赛和合理化建议活动,鼓励和保护劳动者进行科学研究、技术革新和发明创造,表彰和奖励劳动模范和先进工作者。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第七条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第七条', 'full_title': '中华人民共和国劳动法 第七条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动者有权依法参加和组织工会。\n工会代表和维护劳动者的合法权益,依法独立自主地开展活动。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第八条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第八条', 'full_title': '中华人民共和国劳动法 第八条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动者依照法律规定,通过职工大会、职工代表大会或者其他形式,参与民主管理或者就保护劳动者合法权益与用人单位进行平等协商。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第九条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第九条', 'full_title': '中华人民共和国劳动法 第九条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国务院劳动行政部门主管全国劳动工作。\n县级以上地方人民政府劳动行政部门主管本行政区域内的劳动工作。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第十条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第十条', 'full_title': '中华人民共和国劳动法 第十条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国家通过促进经济和社会发展,创造就业条件,扩大就业机会。\n国家鼓励企业、事业组织、社会团体在法律、行政法规规定的范围内兴办产业或者拓展经营,增加就业。\n国家支持劳动者自愿组织起来就业和从事个体经营实现就业。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第十一条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第十一条', 'full_title': '中华人民共和国劳动法 第十一条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='地方各级人民政府应当采取措施,发展多种类型的职业介绍机构,提供就业服务。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第十二条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第十二条', 'full_title': '中华人民共和国劳动法 第十二条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动者就业,不因民族、种族、性别、宗教信仰不同而受歧视。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第十三条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第十三条', 'full_title': '中华人民共和国劳动法 第十三条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='妇女享有与男子平等的就业权利。在录用职工时,除国家规定的不适合妇女的工种或者岗位外,不得以性别为由拒绝录用妇女或者提高对妇女的录用标准。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第十四条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第十四条', 'full_title': '中华人民共和国劳动法 第十四条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='残疾人、少数民族人员、退出现役的军人的就业,法律、法规有特别规定的,从其规定。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第十五条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第十五条', 'full_title': '中华人民共和国劳动法 第十五条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='禁止用人单位招用未满十六周岁的未成年人。\n文艺、体育和特种工艺单位招用未满十六周岁的未成年人,必须依照国家有关规定,履行审批手续,并保障其接受义务教育的权利。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第十六条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第十六条', 'full_title': '中华人民共和国劳动法 第十六条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动合同是劳动者与用人单位确立劳动关系、明确双方权利和义务的协议。\n建立劳动关系应当订立劳动合同。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第十七条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第十七条', 'full_title': '中华人民共和国劳动法 第十七条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='订立和变更劳动合同,应当遵循平等自愿、协商一致的原则,不得违反法律、行政法规的规定。\n劳动合同依法订立即具有法律约束力,当事人必须履行劳动合同规定的义务。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第十八条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第十八条', 'full_title': '中华人民共和国劳动法 第十八条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='下列劳动合同无效:\n(一)违反法律、行政法规的劳动合同;\n(二)采取欺诈、威胁等手段订立的劳动合同。\n无效的劳动合同,从订立的时候起,就没有法律约束力。确认劳动合同部分无效的,如果不影响其余部分的效力,其余部分仍然有效。\n劳动合同的无效,由劳动争议仲裁委员会或者人民法院确认。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第十九条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第十九条', 'full_title': '中华人民共和国劳动法 第十九条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动合同应当以书面形式订立,并具备以下条款:\n(一)劳动合同期限;\n(二)工作内容;\n(三)劳动保护和劳动条件;\n(四)劳动报酬;\n(五)劳动纪律;\n(六)劳动合同终止的条件;\n(七)违反劳动合同的责任。\n劳动合同除前款规定的必备条款外,当事人可以协商约定其他内容。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第二十条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第二十条', 'full_title': '中华人民共和国劳动法 第二十条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动合同的期限分为有固定期限、无固定期限和以完成一定的工作为期限。\n劳动者在同一用人单位连续工作满十年以上,当事人双方同意续延劳动合同的,如果劳动者提出订立无固定期限的劳动合同,应当订立无固定期限的劳动合同。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第二十一条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第二十一条', 'full_title': '中华人民共和国劳动法 第二十一条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动合同可以约定试用期。试用期最长不得超过六个月。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第二十二条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第二十二条', 'full_title': '中华人民共和国劳动法 第二十二条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动合同当事人可以在劳动合同中约定保守用人单位商业秘密的有关事项。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第二十三条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第二十三条', 'full_title': '中华人民共和国劳动法 第二十三条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动合同期满或者当事人约定的劳动合同终止条件出现,劳动合同即行终止。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第二十四条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第二十四条', 'full_title': '中华人民共和国劳动法 第二十四条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='经劳动合同当事人协商一致,劳动合同可以解除。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第二十五条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第二十五条', 'full_title': '中华人民共和国劳动法 第二十五条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动者有下列情形之一的,用人单位可以解除劳动合同:\n(一)在试用期间被证明不符合录用条件的;\n(二)严重违反劳动纪律或者用人单位规章制度的;\n(三)严重失职,营私舞弊,对用人单位利益造成重大损害的;\n(四)被依法追究刑事责任的。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第二十六条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第二十六条', 'full_title': '中华人民共和国劳动法 第二十六条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='有下列情形之一的,用人单位可以解除劳动合同,但是应当提前三十日以书面形式通知劳动者本人:\n(一)劳动者患病或者非因工负伤,医疗期满后,不能从事原工作也不能从事由用人单位另行安排的工作的;\n(二)劳动者不能胜任工作,经过培训或者调整工作岗位,仍不能胜任工作的;\n(三)劳动合同订立时所依据的客观情况发生重大变化,致使原劳动合同无法履行,经当事人协商不能就变更劳动合同达成协议的。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第二十七条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第二十七条', 'full_title': '中华人民共和国劳动法 第二十七条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位濒临破产进行法定整顿期间或者生产经营状况发生严重困难,确需裁减人员的,应当提前三十日向工会或者全体职工说明情况,听取工会或者职工的意见,经向劳动行政部门报告后,可以裁减人员。\n用人单位依据本条规定裁减人员,在六个月内录用人员的,应当优先录用被裁减的人员。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第二十八条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第二十八条', 'full_title': '中华人民共和国劳动法 第二十八条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位依据本法第二十四条、第二十六条、第二十七条的规定解除劳动合同的,应当依照国家有关规定给予经济补偿。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第二十九条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第二十九条', 'full_title': '中华人民共和国劳动法 第二十九条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动者有下列情形之一的,用人单位不得依据本法第二十六条、第二十七条的规定解除劳动合同:\n(一)患职业病或者因工负伤并被确认丧失或者部分丧失劳动能力的;\n(二)患病或者负伤,在规定的医疗期内的;\n(三)女职工在孕期、产期、哺乳期内的;\n(四)法律、行政法规规定的其他情形。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第三十条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第三十条', 'full_title': '中华人民共和国劳动法 第三十条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位解除劳动合同,工会认为不适当的,有权提出意见。如果用人单位违反法律、法规或者劳动合同,工会有权要求重新处理;劳动者申请仲裁或者提起诉讼的,工会应当依法给予支持和帮助。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第三十一条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第三十一条', 'full_title': '中华人民共和国劳动法 第三十一条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动者解除劳动合同,应当提前三十日以书面形式通知用人单位。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第三十二条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第三十二条', 'full_title': '中华人民共和国劳动法 第三十二条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='有下列情形之一的,劳动者可以随时通知用人单位解除劳动合同:\n(一)在试用期内的;\n(二)用人单位以暴力、威胁或者非法限制人身自由的手段强迫劳动的;\n(三)用人单位未按照劳动合同约定支付劳动报酬或者提供劳动条件的。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第三十三条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第三十三条', 'full_title': '中华人民共和国劳动法 第三十三条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='企业职工一方与企业可以就劳动报酬、工作时间、休息休假、劳动安全卫生、保险福利等事项,签订集体合同。集体合同草案应当提交职工代表大会或者全体职工讨论通过。\n集体合同由工会代表职工与企业签订;没有建立工会的企业,由职工推举的代表与企业签订。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第三十四条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第三十四条', 'full_title': '中华人民共和国劳动法 第三十四条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='集体合同签订后应当报送劳动行政部门;劳动行政部门自收到集体合同文本之日起十五日内未提出异议的,集体合同即行生效。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第三十五条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第三十五条', 'full_title': '中华人民共和国劳动法 第三十五条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='依法签订的集体合同对企业和企业全体职工具有约束力。职工个人与企业订立的劳动合同中劳动条件和劳动报酬等标准不得低于集体合同的规定。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第三十六条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第三十六条', 'full_title': '中华人民共和国劳动法 第三十六条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国家实行劳动者每日工作时间不超过八小时、平均每周工作时间不超过四十四小时的工时制度。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第三十七条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第三十七条', 'full_title': '中华人民共和国劳动法 第三十七条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='对实行计件工作的劳动者,用人单位应当根据本法第三十六条规定的工时制度合理确定其劳动定额和计件报酬标准。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第三十八条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第三十八条', 'full_title': '中华人民共和国劳动法 第三十八条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位应当保证劳动者每周至少休息一日。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第三十九条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第三十九条', 'full_title': '中华人民共和国劳动法 第三十九条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='企业因生产特点不能实行本法第三十六条、第三十八条规定的,经劳动行政部门批准,可以实行其他工作和休息办法。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第四十条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第四十条', 'full_title': '中华人民共和国劳动法 第四十条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位在下列节日期间应当依法安排劳动者休假:\n(一)元旦;\n(二)春节;\n(三)国际劳动节;\n(四)国庆节;\n(五)法律、法规规定的其他休假节日。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第四十一条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第四十一条', 'full_title': '中华人民共和国劳动法 第四十一条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位由于生产经营需要,经与工会和劳动者协商后可以延长工作时间,一般每日不得超过一小时;因特殊原因需要延长工作时间的,在保障劳动者身体健康的条件下延长工作时间每日不得超过三小时,但是每月不得超过三十六小时。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第四十二条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第四十二条', 'full_title': '中华人民共和国劳动法 第四十二条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='有下列情形之一的,延长工作时间不受本法第四十一条的限制:\n(一)发生自然灾害、事故或者因其他原因,威胁劳动者生命健康和财产安全,需要紧急处理的;\n(二)生产设备、交通运输线路、公共设施发生故障,影响生产和公众利益,必须及时抢修的;\n(三)法律、行政法规规定的其他情形。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第四十三条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第四十三条', 'full_title': '中华人民共和国劳动法 第四十三条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位不得违反本法规定延长劳动者的工作时间。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第四十四条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第四十四条', 'full_title': '中华人民共和国劳动法 第四十四条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='有下列情形之一的,用人单位应当按照下列标准支付高于劳动者正常工作时间工资的工资报酬:\n(一)安排劳动者延长工作时间的,支付不低于工资的百分之一百五十的工资报酬;\n(二)休息日安排劳动者工作又不能安排补休的,支付不低于工资的百分之二百的工资报酬;\n(三)法定休假日安排劳动者工作的,支付不低于工资的百分之三百的工资报酬。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第四十五条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第四十五条', 'full_title': '中华人民共和国劳动法 第四十五条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国家实行带薪年休假制度。\n劳动者连续工作一年以上的,享受带薪年休假。具体办法由国务院规定。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第四十六条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第四十六条', 'full_title': '中华人民共和国劳动法 第四十六条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='工资分配应当遵循按劳分配原则,实行同工同酬。\n工资水平在经济发展的基础上逐步提高。国家对工资总量实行宏观调控。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第四十七条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第四十七条', 'full_title': '中华人民共和国劳动法 第四十七条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位根据本单位的生产经营特点和经济效益,依法自主确定本单位的工资分配方式和工资水平。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第四十八条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第四十八条', 'full_title': '中华人民共和国劳动法 第四十八条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国家实行最低工资保障制度。最低工资的具体标准由省、自治区、直辖市人民政府规定,报国务院备案。\n用人单位支付劳动者的工资不得低于当地最低工资标准。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第四十九条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第四十九条', 'full_title': '中华人民共和国劳动法 第四十九条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='确定和调整最低工资标准应当综合参考下列因素:\n(一)劳动者本人及平均赡养人口的最低生活费用;\n(二)社会平均工资水平;\n(三)劳动生产率;\n(四)就业状况;\n(五)地区之间经济发展水平的差异。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第五十条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第五十条', 'full_title': '中华人民共和国劳动法 第五十条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='工资应当以货币形式按月支付给劳动者本人。不得克扣或者无故拖欠劳动者的工资。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第五十一条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第五十一条', 'full_title': '中华人民共和国劳动法 第五十一条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动者在法定休假日和婚丧假期间以及依法参加社会活动期间,用人单位应当依法支付工资。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第五十二条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第五十二条', 'full_title': '中华人民共和国劳动法 第五十二条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位必须建立、健全劳动安全卫生制度,严格执行国家劳动安全卫生规程和标准,对劳动者进行劳动安全卫生教育,防止劳动过程中的事故,减少职业危害。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第五十三条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第五十三条', 'full_title': '中华人民共和国劳动法 第五十三条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动安全卫生设施必须符合国家规定的标准。\n新建、改建、扩建工程的劳动安全卫生设施必须与主体工程同时设计、同时施工、同时投入生产和使用。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第五十四条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第五十四条', 'full_title': '中华人民共和国劳动法 第五十四条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位必须为劳动者提供符合国家规定的劳动安全卫生条件和必要的劳动防护用品,对从事有职业危害作业的劳动者应当定期进行健康检查。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第五十五条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第五十五条', 'full_title': '中华人民共和国劳动法 第五十五条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='从事特种作业的劳动者必须经过专门培训并取得特种作业资格。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第五十六条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第五十六条', 'full_title': '中华人民共和国劳动法 第五十六条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动者在劳动过程中必须严格遵守安全操作规程。\n劳动者对用人单位管理人员违章指挥、强令冒险作业,有权拒绝执行;对危害生命安全和身体健康的行为,有权提出批评、检举和控告。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第五十七条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第五十七条', 'full_title': '中华人民共和国劳动法 第五十七条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国家建立伤亡事故和职业病统计报告和处理制度。县级以上各级人民政府劳动行政部门、有关部门和用人单位应当依法对劳动者在劳动过程中发生的伤亡事故和劳动者的职业病状况,进行统计、报告和处理。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第五十八条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第五十八条', 'full_title': '中华人民共和国劳动法 第五十八条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国家对女职工和未成年工实行特殊劳动保护。\n未成年工是指年满十六周岁未满十八周岁的劳动者。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第五十九条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第五十九条', 'full_title': '中华人民共和国劳动法 第五十九条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='禁止安排女职工从事矿山井下、国家规定的第四级体力劳动强度的劳动和其他禁忌从事的劳动。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第六十条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第六十条', 'full_title': '中华人民共和国劳动法 第六十条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='不得安排女职工在经期从事高处、低温、冷水作业和国家规定的第三级体力劳动强度的劳动。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第六十一条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第六十一条', 'full_title': '中华人民共和国劳动法 第六十一条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='不得安排女职工在怀孕期间从事国家规定的第三级体力劳动强度的劳动和孕期禁忌从事的劳动。对怀孕七个月以上的女职工,不得安排其延长工作时间和夜班劳动。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第六十二条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第六十二条', 'full_title': '中华人民共和国劳动法 第六十二条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='女职工生育享受不少于九十天的产假。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第六十三条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第六十三条', 'full_title': '中华人民共和国劳动法 第六十三条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='不得安排女职工在哺乳未满一周岁的婴儿期间从事国家规定的第三级体力劳动强度的劳动和哺乳期禁忌从事的其他劳动,不得安排其延长工作时间和夜班劳动。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第六十四条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第六十四条', 'full_title': '中华人民共和国劳动法 第六十四条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='不得安排未成年工从事矿山井下、有毒有害、国家规定的第四级体力劳动强度的劳动和其他禁忌从事的劳动。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第六十五条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第六十五条', 'full_title': '中华人民共和国劳动法 第六十五条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位应当对未成年工定期进行健康检查。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第六十六条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第六十六条', 'full_title': '中华人民共和国劳动法 第六十六条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国家通过各种途径,采取各种措施,发展职业培训事业,开发劳动者的职业技能,提高劳动者素质,增强劳动者的就业能力和工作能力。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第六十七条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第六十七条', 'full_title': '中华人民共和国劳动法 第六十七条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='各级人民政府应当把发展职业培训纳入社会经济发展的规划,鼓励和支持有条件的企业、事业组织、社会团体和个人进行各种形式的职业培训。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第六十八条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第六十八条', 'full_title': '中华人民共和国劳动法 第六十八条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位应当建立职业培训制度,按照国家规定提取和使用职业培训经费,根据本单位实际,有计划地对劳动者进行职业培训。\n从事技术工种的劳动者,上岗前必须经过培训。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第六十九条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第六十九条', 'full_title': '中华人民共和国劳动法 第六十九条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国家确定职业分类,对规定的职业制定职业技能标准,实行职业资格证书制度,由经过政府批准的考核鉴定机构负责对劳动者实施职业技能考核鉴定。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第七十条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第七十条', 'full_title': '中华人民共和国劳动法 第七十条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国家发展社会保险事业,建立社会保险制度,设立社会保险基金,使劳动者在年老、患病、工伤、失业、生育等情况下获得帮助和补偿。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第七十一条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第七十一条', 'full_title': '中华人民共和国劳动法 第七十一条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='社会保险水平应当与社会经济发展水平和社会承受能力相适应。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第七十二条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第七十二条', 'full_title': '中华人民共和国劳动法 第七十二条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='社会保险基金按照保险类型确定资金来源,逐步实行社会统筹。用人单位和劳动者必须依法参加社会保险,缴纳社会保险费。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第七十三条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第七十三条', 'full_title': '中华人民共和国劳动法 第七十三条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动者在下列情形下,依法享受社会保险待遇:\n(一)退休;\n(二)患病、负伤;\n(三)因工伤残或者患职业病;\n(四)失业;\n(五)生育。\n劳动者死亡后,其遗属依法享受遗属津贴。\n劳动者享受社会保险待遇的条件和标准由法律、法规规定。\n劳动者享受的社会保险金必须按时足额支付。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第七十四条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第七十四条', 'full_title': '中华人民共和国劳动法 第七十四条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='社会保险基金经办机构依照法律规定收支、管理和运营社会保险基金,并负有使社会保险基金保值增值的责任。\n社会保险基金监督机构依照法律规定,对社会保险基金的收支、管理和运营实施监督。\n社会保险基金经办机构和社会保险基金监督机构的设立和职能由法律规定。\n任何组织和个人不得挪用社会保险基金。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第七十五条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第七十五条', 'full_title': '中华人民共和国劳动法 第七十五条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国家鼓励用人单位根据本单位实际情况为劳动者建立补充保险。\n国家提倡劳动者个人进行储蓄性保险。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第七十六条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第七十六条', 'full_title': '中华人民共和国劳动法 第七十六条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国家发展社会福利事业,兴建公共福利设施,为劳动者休息、休养和疗养提供条件。\n用人单位应当创造条件,改善集体福利,提高劳动者的福利待遇。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第七十七条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第七十七条', 'full_title': '中华人民共和国劳动法 第七十七条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位与劳动者发生劳动争议,当事人可以依法申请调解、仲裁、提起诉讼,也可以协商解决。\n调解原则适用于仲裁和诉讼程序。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第七十八条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第七十八条', 'full_title': '中华人民共和国劳动法 第七十八条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='解决劳动争议,应当根据合法、公正、及时处理的原则,依法维护劳动争议当事人的合法权益。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第七十九条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第七十九条', 'full_title': '中华人民共和国劳动法 第七十九条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动争议发生后,当事人可以向本单位劳动争议调解委员会申请调解;调解不成,当事人一方要求仲裁的,可以向劳动争议仲裁委员会申请仲裁。当事人一方也可以直接向劳动争议仲裁委员会申请仲裁。对仲裁裁决不服的,可以向人民法院提起诉讼。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第八十条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第八十条', 'full_title': '中华人民共和国劳动法 第八十条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='在用人单位内,可以设立劳动争议调解委员会。劳动争议调解委员会由职工代表、用人单位代表和工会代表组成。劳动争议调解委员会主任由工会代表担任。\n劳动争议经调解达成协议的,当事人应当履行。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第八十一条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第八十一条', 'full_title': '中华人民共和国劳动法 第八十一条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动争议仲裁委员会由劳动行政部门代表、同级工会代表、用人单位方面的代表组成。劳动争议仲裁委员会主任由劳动行政部门代表担任。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第八十二条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第八十二条', 'full_title': '中华人民共和国劳动法 第八十二条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='提出仲裁要求的一方应当自劳动争议发生之日起六十日内向劳动争议仲裁委员会提出书面申请。仲裁裁决一般应在收到仲裁申请的六十日内作出。对仲裁裁决无异议的,当事人必须履行。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第八十三条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第八十三条', 'full_title': '中华人民共和国劳动法 第八十三条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动争议当事人对仲裁裁决不服的,可以自收到仲裁裁决书之日起十五日内向人民法院提起诉讼。一方当事人在法定期限内不起诉又不履行仲裁裁决的,另一方当事人可以申请人民法院强制执行。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第八十四条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第八十四条', 'full_title': '中华人民共和国劳动法 第八十四条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='因签订集体合同发生争议,当事人协商解决不成的,当地人民政府劳动行政部门可以组织有关各方协调处理。\n因履行集体合同发生争议,当事人协商解决不成的,可以向劳动争议仲裁委员会申请仲裁;对仲裁裁决不服的,可以自收到仲裁裁决书之日起十五日内向人民法院提起诉讼。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第八十五条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第八十五条', 'full_title': '中华人民共和国劳动法 第八十五条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='县级以上各级人民政府劳动行政部门依法对用人单位遵守劳动法律、法规的情况进行监督检查,对违反劳动法律、法规的行为有权制止,并责令改正。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第八十六条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第八十六条', 'full_title': '中华人民共和国劳动法 第八十六条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='县级以上各级人民政府劳动行政部门监督检查人员执行公务,有权进入用人单位了解执行劳动法律、法规的情况,查阅必要的资料,并对劳动场所进行检查。\n县级以上各级人民政府劳动行政部门监督检查人员执行公务,必须出示证件,秉公执法并遵守有关规定。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第八十七条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第八十七条', 'full_title': '中华人民共和国劳动法 第八十七条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='县级以上各级人民政府有关部门在各自职责范围内,对用人单位遵守劳动法律、法规的情况进行监督。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第八十八条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第八十八条', 'full_title': '中华人民共和国劳动法 第八十八条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='各级工会依法维护劳动者的合法权益,对用人单位遵守劳动法律、法规的情况进行监督。\n任何组织和个人对于违反劳动法律、法规的行为有权检举和控告。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第八十九条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第八十九条', 'full_title': '中华人民共和国劳动法 第八十九条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位制定的劳动规章制度违反法律、法规规定的,由劳动行政部门给予警告,责令改正;对劳动者造成损害的,应当承担赔偿责任。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第九十条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第九十条', 'full_title': '中华人民共和国劳动法 第九十条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位违反本法规定,延长劳动者工作时间的,由劳动行政部门给予警告,责令改正,并可以处以罚款。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第九十一条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第九十一条', 'full_title': '中华人民共和国劳动法 第九十一条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位有下列侵害劳动者合法权益情形之一的,由劳动行政部门责令支付劳动者的工资报酬、经济补偿,并可以责令支付赔偿金:\n(一)克扣或者无故拖欠劳动者工资的;\n(二)拒不支付劳动者延长工作时间工资报酬的;\n(三)低于当地最低工资标准支付劳动者工资的;\n(四)解除劳动合同后,未依照本法规定给予劳动者经济补偿的。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第九十二条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第九十二条', 'full_title': '中华人民共和国劳动法 第九十二条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位的劳动安全设施和劳动卫生条件不符合国家规定或者未向劳动者提供必要的劳动防护用品和劳动保护设施的,由劳动行政部门或者有关部门责令改正,可以处以罚款;情节严重的,提请县级以上人民政府决定责令停产整顿;对事故隐患不采取措施,致使发生重大事故,造成劳动者生命和财产损失的,对责任人员比照刑法第一百八十七条的规定追究刑事责任。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第九十三条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第九十三条', 'full_title': '中华人民共和国劳动法 第九十三条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位强令劳动者违章冒险作业,发生重大伤亡事故,造成严重后果的,对责任人员依法追究刑事责任。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第九十四条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第九十四条', 'full_title': '中华人民共和国劳动法 第九十四条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位非法招用未满十六周岁的未成年人的,由劳动行政部门责令改正,处以罚款;情节严重的,由工商行政管理部门吊销营业执照。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第九十五条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第九十五条', 'full_title': '中华人民共和国劳动法 第九十五条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位违反本法对女职工和未成年工的保护规定,侵害其合法权益的,由劳动行政部门责令改正,处以罚款;对女职工或者未成年工造成损害的,应当承担赔偿责任。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第九十六条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第九十六条', 'full_title': '中华人民共和国劳动法 第九十六条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位有下列行为之一,由公安机关对责任人员处以十五日以下拘留、罚款或者警告;构成犯罪的,对责任人员依法追究刑事责任:\n(一)以暴力、威胁或者非法限制人身自由的手段强迫劳动的;\n(二)侮辱、体罚、殴打、非法搜查和拘禁劳动者的。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第九十七条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第九十七条', 'full_title': '中华人民共和国劳动法 第九十七条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='由于用人单位的原因订立的无效合同,对劳动者造成损害的,应当承担赔偿责任。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第九十八条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第九十八条', 'full_title': '中华人民共和国劳动法 第九十八条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位违反本法规定的条件解除劳动合同或者故意拖延不订立劳动合同的,由劳动行政部门责令改正;对劳动者造成损害的,应当承担赔偿责任。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第九十九条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第九十九条', 'full_title': '中华人民共和国劳动法 第九十九条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位招用尚未解除劳动合同的劳动者,对原用人单位造成经济损失的,该用人单位应当依法承担连带赔偿责任。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第一百条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第一百条', 'full_title': '中华人民共和国劳动法 第一百条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位无故不缴纳社会保险费的,由劳动行政部门责令其限期缴纳,逾期不缴的,可以加收滞纳金。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第一百零一条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第一百零一条', 'full_title': '中华人民共和国劳动法 第一百零一条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='用人单位无理阻挠劳动行政部门、有关部门及其工作人员行使监督检查权,打击报复举报人员的,由劳动行政部门或者有关部门处以罚款;构成犯罪的,对责任人员依法追究刑事责任。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第一百零二条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第一百零二条', 'full_title': '中华人民共和国劳动法 第一百零二条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动者违反本法规定的条件解除劳动合同或者违反劳动合同中约定的保密事项,对用人单位造成经济损失的,应当依法承担赔偿责任。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第一百零三条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第一百零三条', 'full_title': '中华人民共和国劳动法 第一百零三条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='劳动行政部门或者有关部门的工作人员滥用职权、玩忽职守、徇私舞弊,构成犯罪的,依法追究刑事责任;不构成犯罪的,给予行政处分。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第一百零四条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第一百零四条', 'full_title': '中华人民共和国劳动法 第一百零四条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='国家工作人员和社会保险基金经办机构的工作人员挪用社会保险基金,构成犯罪的,依法追究刑事责任。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第一百零五条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第一百零五条', 'full_title': '中华人民共和国劳动法 第一百零五条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='违反本法规定侵害劳动者合法权益,其他法律、法规已规定处罚的,依照该法律、行政法规的规定处罚。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第一百零六条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第一百零六条', 'full_title': '中华人民共和国劳动法 第一百零六条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='省、自治区、直辖市人民政府根据本法和本地区的实际情况,规定劳动合同制度的实施步骤,报国务院备案。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}'), TextNode(id_='中华人民共和国劳动法.json::中华人民共和国劳动法 第一百零七条', embedding=None, metadata={'law_name': '中华人民共和国劳动法', 'article': '第一百零七条', 'full_title': '中华人民共和国劳动法 第一百零七条', 'source_file': '中华人民共和国劳动法.json', 'content_type': 'legal_article'}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, metadata_template='{key}: {value}', metadata_separator='\n', text='本法自1995年1月1日起施行。', mimetype='text/plain', start_char_idx=None, end_char_idx=None, metadata_seperator='\n', text_template='{metadata_str}\n\n{content}')]
三、向量存储
# ================== 向量存储 ==================
def init_vector_store(nodes: List[TextNode]) -> VectorStoreIndex:
chroma_client = chromadb.PersistentClient(path=Config.VECTOR_DB_DIR)
chroma_collection = chroma_client.get_or_create_collection(
name=Config.COLLECTION_NAME,
metadata={"hnsw:space": "cosine"}
)
# 确保存储上下文正确初始化
storage_context = StorageContext.from_defaults(
vector_store=ChromaVectorStore(chroma_collection=chroma_collection)
)
# 判断是否需要新建索引
if chroma_collection.count() == 0 and nodes is not None:
print(f"创建新索引({len(nodes)}个节点)...")
# 显式将节点添加到存储上下文
storage_context.docstore.add_documents(nodes)
index = VectorStoreIndex(
nodes,
storage_context=storage_context,
show_progress=True
)
# 双重持久化保障
storage_context.persist(persist_dir=Config.PERSIST_DIR)
index.storage_context.persist(persist_dir=Config.PERSIST_DIR) # <-- 新增
else:
print("加载已有索引...")
storage_context = StorageContext.from_defaults(
persist_dir=Config.PERSIST_DIR,
vector_store=ChromaVectorStore(chroma_collection=chroma_collection)
)
index = VectorStoreIndex.from_vector_store(
storage_context.vector_store,
storage_context=storage_context,
embed_model=Settings.embed_model
)
# 安全验证
print("\n存储验证结果:")
doc_count = len(storage_context.docstore.docs)
print(f"DocStore记录数:{doc_count}")
if doc_count > 0:
sample_key = next(iter(storage_context.docstore.docs.keys()))
print(f"示例节点ID:{sample_key}")
else:
print("警告:文档存储为空,请检查节点添加逻辑!")
return index
向量存储脚本解析:
1 chromadb创建配置
chroma_client = chromadb.PersistentClient(path=Config.VECTOR_DB_DIR)
chroma_collection = chroma_client.get_or_create_collection(
name=Config.COLLECTION_NAME,
metadata={"hnsw:space": "cosine"}
)
chromadb是import chromadb 中init来的
PersistentClient:创建一个持久化的 Chroma 客户端,指定数据存储路径。
get_or_create_collection:获取或创建一个名为 Config.COLLECTION_NAME 的集合,并设置元数据以使用余弦相似度度量。
2 llamaindex中的StorageContext
我自己的理解是:llamaindex自带的strageindex需要传入2个数据。一.文档数据Node包装成的Document,二、向量数据ChromaVector,它由chromadb中的chromadbclooection传入包含了数据库名?以及向量化方式余弦。索引来自于llamaindex的VrctorStoreIndex他需要传入Node。帮我把我的理解写成表格或者其他方式直观展示
StorageContext
组成部分 | 角色 | 构建方式 | 依赖关系/说明 |
---|---|---|---|
🧾 文档数据 (docstore) | 文本内容、文档节点 | TextNode.from_text() 或通过 SimpleNodeParser 解析 Document | 是原始文本的封装,后续要进行向量化和索引处理 |
📊 向量存储 (Vector Store) | 向量数据的存储与检索(如余弦检索) | ChromaVectorStore(chroma_collection=...) | 底层使用 Chroma,集合中指定 metadata={"hnsw:space": "cosine"} |
🧠 索引 (VectorStoreIndex) | 构建并使用嵌入索引 | VectorStoreIndex(nodes, storage_context=...) | 会将 Node 向量化,写入 vector_store,同时记录原始文档到 docstore |
# 确保存储上下文正确初始化
storage_context = StorageContext.from_defaults(
vector_store=ChromaVectorStore(chroma_collection=chroma_collection)
)
# 判断是否需要新建索引
if chroma_collection.count() == 0 and nodes is not None:
print(f"创建新索引({len(nodes)}个节点)...")
# 显式将节点添加到存储上下文
#storage_context:它定义了你存储向量和文档的具体地方,这里是 ChromaVectorStore。 #docstore.add_documents(nodes):它把文档添加到存储的同时,自动触发嵌入和向量化过程,把向量存入ChromaVectorStore
storage_context.docstore.add_documents(nodes)
#在这段代码中,VectorStoreIndex 会使用 StorageContext 中的 vector_store 和 docstore 来构建索引,并自动初始化与索引相关的存储组件。这时,index_store 会在 VectorStoreIndex 内部初始化,并保存索引数据。
index = VectorStoreIndex(
nodes,
storage_context=storage_context,
show_progress=True
)
# 双重持久化保障
storage_context.persist(persist_dir=Config.PERSIST_DIR)
index.storage_context.persist(persist_dir=Config.PERSIST_DIR) # <-- 新增
else:
print("加载已有索引...")
storage_context = StorageContext.from_defaults(
persist_dir=Config.PERSIST_DIR,
vector_store=ChromaVectorStore(chroma_collection=chroma_collection)
)
index = VectorStoreIndex.from_vector_store(
storage_context.vector_store,
storage_context=storage_context,
embed_model=Settings.embed_model
)
重点:
#storage_context:它定义了你存储向量和文档的具体地方,这里是 ChromaVectorStore。 #docstore.add_documents(nodes):它把文档添加到存储的同时,自动触发嵌入和向量化过程,把向量存入ChromaVectorStore
storage_context.docstore.add_documents(nodes)
VectorStoreIndex传入的storage_context至少有Vector
四、汇总脚本
# -*- coding: utf-8 -*-
import json
import time
from pathlib import Path
from typing import List, Dict
import chromadb
from llama_index.core import VectorStoreIndex, StorageContext, Settings
from llama_index.core.schema import TextNode
from llama_index.llms.huggingface import HuggingFaceLLM
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.vector_stores.chroma import ChromaVectorStore
# ================== 配置区 ==================
class Config:
EMBED_MODEL_PATH = r"/root/autodl-tmp/model/embedding"
LLM_MODEL_PATH = r"/root/autodl-tmp/model/qwen3-8b"
DATA_DIR = "/root/autodl-tmp/rag/data"
VECTOR_DB_DIR = "/root/autodl-tmp/rag/save/chroma_db"
PERSIST_DIR = "/root/autodl-tmp/rag/save/storage"
COLLECTION_NAME = "chinese_labor_laws"
TOP_K = 3
# ================== 初始化模型 ==================
def init_models():
"""初始化模型并验证"""
# Embedding模型
embed_model = HuggingFaceEmbedding(
model_name=Config.EMBED_MODEL_PATH,
# encode_kwargs = {
# 'normalize_embeddings': True,
# 'device': 'cuda' if hasattr(Settings, 'device') else 'cpu'
# }
)
Settings.embed_model = embed_model
# 验证模型
test_embedding = embed_model.get_text_embedding("测试文本")
print(f"Embedding维度验证:{len(test_embedding)}")
return embed_model
# ================== 数据处理 ==================
def load_and_validate_json_files(data_dir: str) -> List[Dict]:
"""加载并验证JSON法律文件"""
json_files = list(Path(data_dir).glob("*.json"))
assert json_files, f"未找到JSON文件于 {data_dir}"
all_data = []
for json_file in json_files:
with open(json_file, 'r', encoding='utf-8') as f:
try:
data = json.load(f)
# 验证数据结构
if not isinstance(data, list):
raise ValueError(f"文件 {json_file.name} 根元素应为列表")
for item in data:
if not isinstance(item, dict):
raise ValueError(f"文件 {json_file.name} 包含非字典元素")
for k, v in item.items():
if not isinstance(v, str):
raise ValueError(f"文件 {json_file.name} 中键 '{k}' 的值不是字符串")
all_data.extend({
"content": item,
"metadata": {"source": json_file.name}
} for item in data)
except Exception as e:
raise RuntimeError(f"加载文件 {json_file} 失败: {str(e)}")
print(f"成功加载 {len(all_data)} 个法律文件条目")
return all_data
def create_nodes(raw_data: List[Dict]) -> List[TextNode]:
"""添加ID稳定性保障"""
nodes = []
for entry in raw_data:
law_dict = entry["content"]
source_file = entry["metadata"]["source"]
for full_title, content in law_dict.items():
# 生成稳定ID(避免重复)
node_id = f"{source_file}::{full_title}"
parts = full_title.split(" ", 1)
law_name = parts[0] if len(parts) > 0 else "未知法律"
article = parts[1] if len(parts) > 1 else "未知条款"
node = TextNode(
text=content,
id_=node_id, # 显式设置稳定ID
metadata={
"law_name": law_name,
"article": article,
"full_title": full_title,
"source_file": source_file,
"content_type": "legal_article"
}
)
nodes.append(node)
print(f"生成 {len(nodes)} 个文本节点(ID示例:{nodes[0].id_})")
return nodes
# ================== 向量存储 ==================
def init_vector_store(nodes: List[TextNode]) -> VectorStoreIndex:
chroma_client = chromadb.PersistentClient(path=Config.VECTOR_DB_DIR)
chroma_collection = chroma_client.get_or_create_collection(
name=Config.COLLECTION_NAME,
metadata={"hnsw:space": "cosine"}
)
# 确保存储上下文正确初始化
storage_context = StorageContext.from_defaults(
vector_store=ChromaVectorStore(chroma_collection=chroma_collection)
)
# 判断是否需要新建索引
if chroma_collection.count() == 0 and nodes is not None:
print(f"创建新索引({len(nodes)}个节点)...")
# 显式将节点添加到存储上下文
storage_context.docstore.add_documents(nodes)
index = VectorStoreIndex(
nodes,
storage_context=storage_context,
show_progress=True
)
# 双重持久化保障
storage_context.persist(persist_dir=Config.PERSIST_DIR)
index.storage_context.persist(persist_dir=Config.PERSIST_DIR) # <-- 新增
else:
print("加载已有索引...")
storage_context = StorageContext.from_defaults(
persist_dir=Config.PERSIST_DIR,
vector_store=ChromaVectorStore(chroma_collection=chroma_collection)
)
index = VectorStoreIndex.from_vector_store(
storage_context.vector_store,
storage_context=storage_context,
embed_model=Settings.embed_model
)
# 安全验证
print("\n存储验证结果:")
doc_count = len(storage_context.docstore.docs)
print(f"DocStore记录数:{doc_count}")
if doc_count > 0:
sample_key = next(iter(storage_context.docstore.docs.keys()))
print(f"示例节点ID:{sample_key}")
else:
print("警告:文档存储为空,请检查节点添加逻辑!")
return index
# ================== 主程序 ==================
def main():
embed_model = init_models()
# 仅当需要更新数据时执行
if not Path(Config.VECTOR_DB_DIR).exists():
print("\n初始化数据...")
raw_data = load_and_validate_json_files(Config.DATA_DIR)
nodes = create_nodes(raw_data)
else:
nodes = None # 已有数据时不加载
print("\n初始化向量存储...")
start_time = time.time()
index = init_vector_store(nodes)
print(f"索引加载耗时:{time.time()-start_time:.2f}s")
if __name__ == "__main__":
main()
学习问题汇总:
1 special——token原理
[bert中的special token到底是怎么发挥作用的(1) - 知乎](https://zhuanlan.zhihu.com/p/361169990#:~:text=bert中的special token有 [cls],[sep],[unk],[pad],[mask];)
2 前几节课处理文本
3 数据的token化参数含义,embedding,transformer,rnn那些
4 bert sft这些,llamafactory上的微调方法
5 nvitop上的指标看看(完成)见服务器下的nvitop
6 常见的文本生成模型:llama qwen glm(谷歌)chatglm(质谱轻言)gemma(完成)
7 混和精度训练
8 知识蒸馏
9 为什么模型后面加个/v1? 版本控制api设计决定的,不用细看
10 curl用法 curl详解-CSDN博客
11 bf16 bf32 所谓单精度、双精度干嘛的
12 模型指标参数的含义:bleu-4、ROUGE-4、
13 量化
14 前半小时+21.45-结尾
15 截断长度的计算
16 sm80算力
17 openwebui
18 gguf转化(完成)见llama.cpp
19 qlora(完成)见下面
20 lora
21 xtuner实现单机多卡微调,实现训练对话模板转换与部署、导出上面所有框架的包
22 lmdeploy的推理引擎turbomind
21 对话模板(完成):
三套:微调、模型推理、前端界面。三套提示词模板
模型部署的时候可以使用微调框架使用的对话模板。
openwebui每次加载的时候会覆盖提示词模板
流程:llmfactory运行脚本转换成jinjia->模型推理平台启动->使用代码测试
注:openwebui暂不支持修改对话模板,以后用别的前端框架
22 Vllm的pageAttentation和张量并行技术
23 kv cache 分布式推理原理
24 看一下为什么相似度和例子差不多就行
25 什么叫数据同态同分布
26 llamaindex中数据读取成document,用document建立索引index。index建立引擎。我没理解索引和引擎的区别。(完成)
index类似给加载进去的数据一个索引,方便引擎去找到。引擎就是实际干活的,通过输入找到输出这个全流程就是引擎的工作。
27 看一下内积
28 修改大模型加一个归一化层
29 rerank及其优化 Rerank详解-CSDN博客
30 http请求具体流程 Http详解-CSDN博客
31 LlmaIndex中Node、Doucement混淆、LlamaIndex创建持久化数据库流程 LlamaIndex详解-CSDN博客
32 保存json将dict转化成list JSON 处理笔记-CSDN博客
33 Huggingface加载模型用的方法 HuggingFace常用加载模型方法-CSDN博客
模型加载类 | 适用任务 | 描述 | 适用类型 |
---|---|---|---|
AutoModel | Embedding、Transformer架构模型 | 用于加载基础的 transformer 模型(如 BERT、RoBERTa、DistilBERT 等),适合提取文本的 embedding 向量。 | 适用于 Embedding 任务,文本表示。 |
AutoModelForSequenceClassification | 文本分类 | 加载预训练的 transformer 模型,适用于文本分类任务。 | LLM(用于分类任务)。 |
AutoModelForTokenClassification | 实体识别(NER) | 用于命名实体识别等任务,加载适合 token 分类的模型。 | LLM(用于 token 级别的任务)。 |
AutoModelForQuestionAnswering | 问答任务 | 用于加载问答模型,基于 transformers 架构,适合回答给定文本中的问题。 | LLM(用于问答任务)。 |
AutoModelForCausalLM | 文本生成(Causal LM) | 用于加载自回归的语言模型(如 GPT-2),适合用于文本生成任务。 | LLM(用于生成任务)。 |
AutoModelForMaskedLM | 掩码语言模型(MLM) | 用于加载掩码语言模型(如 BERT、RoBERTa),适合用于填充缺失的文本部分。 | LLM(用于填充缺失词)。 |
AutoModelForSeq2SeqLM | 序列到序列任务(如翻译) | 用于加载序列到序列的模型(如 T5、BART),适合用于翻译、摘要等任务。 | LLM(用于序列到序列任务)。 |