# python&&ffmpeg&opencv

# python开发指南（索引合集）

#### 方法二（推荐）：安装python 多版本环境（conda）

由于python有各种不同版本，且各版本不能完全兼容，**anaconda** 工具包可以完美解决该问题。

下载地址： https://repo.anaconda.com/archive/

或者 **miniconda（推荐）** , 二者的区别在于内置的常用包的多少

下载地址：https://repo.anaconda.com/miniconda/

一路 **enter** 下来安装，可能出现找不到命令的提示，需要进行初始化配置

```
# windows 下是找到 `Anaconda Prompt`
conda init --all


```

```shell
# 进入安装目录执行 conda init bash
/root/anaconda3/bin/conda init bash

# bash参数为你的 shell 环境，可以使用 echo $SHELL 查看
# 也可以使用参数 --all 全部设置
/root/anaconda3/bin/conda init --all

# 此时，还可以出现找不到命令情况，需要重新加载shell
source /root/.bashrc 

```

```shell
# 查看所有已经安装的模块
pip list

# 将所有的模块输出到 文件
pip freeze > requirements.txt

# 卸载所有的模块
pip uninstall -r requirements.txt -y

# 从 requirements.txt 安装依赖
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple


```

```shell
# 查看当前python版本
python --version

# 查看安装了哪些包
conda list

# 查看环境列表
conda env list

# 查看环境列表
conda info -e

# 创建环境，后面为使用的python版本
conda create -n your_env_name python=x.x
conda create -n cvyolo python=3.12

# 切换环境
conda activate cvyolo

# 安装依赖，应该有限用conda安装，实在找不到采用pip安装
conda install numpy
conda install --file requirements.txt

# 删除环境
conda remove --name myenv --all


```

#### 使用清华源加速安装

```
pip install pyinstaller==6.5.0 -i https://pypi.tuna.tsinghua.edu.cn/simple

```

#### vscode相关插件

- 格式化：[https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter](https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter)
- vscode扩展：[https://marketplace.visualstudio.com/items?itemName=ms-python.python](https://marketplace.visualstudio.com/items?itemName=ms-python.python)
- 调试扩展：[https://marketplace.visualstudio.com/items?itemName=ms-python.debugpy](https://marketplace.visualstudio.com/items?itemName=ms-python.debugpy)
- IDEA快捷键：[https://marketplace.visualstudio.com/items?itemName=k--kato.intellij-idea-keybindings](https://marketplace.visualstudio.com/items?itemName=k--kato.intellij-idea-keybindings)

#### 将py打包为exe

```shell
# 安装打包工具，注意，
# 截止至2024年6月3日，其他版本打包出来的exe报毒
pip install pyinstaller==6.5.0 -i https://pypi.tuna.tsinghua.edu.cn/simple


# 高级设置
pyinstaller --onefile  \
            --windowed \
            --icon=./icon.ico \
            --name=贵阳燃气内网域名设置工具 \
            --uac-admin \
             main.py


```

#### 方法一：使用pyhton3的venv多环境

```

python -m venv venv

source venv/Scripts/activate

pip install -r requirements.txt


```

# liunx安装python3&&制作python镜像

#### 制作python3 docker镜像

```dockerfile
FROM alpine
RUN apk update &&  \ 
    apk add --no-cache  mysql-client  \
    bash  \
    curl  \
    build-base \
    libffi-dev \
    openssl-dev \
    zlib-dev  \
    bzip2-dev \
    readline-dev  \
    sqlite-dev  


RUN wget https://www.python.org/ftp/python/3.10.14/Python-3.10.14.tgz && \
    tar -xvzf Python-3.10.14.tgz && \
    cd Python-3.10.14 && \
    ./configure --prefix=/usr/local --enable-optimizations && \
    make -j ${nproc} && \
    make install && \
    rm -rf /Python-3.10.14 && \
    rm -rf /Python-3.10.14.tgz

```

#### linux安装

```shell
apk update

apk add --no-cache  mysql-client  bash  curl  build-base libffi-dev openssl-dev zlib-dev  bzip2-dev readline-dev  sqlite-dev  


wget https://www.python.org/ftp/python/3.10.14/Python-3.10.14.tgz && \
tar -xvzf Python-3.10.14.tgz
cd Python-3.10.14 
./configure --prefix=/usr/local --enable-optimizations
make -j ${nproc}
make install 
rm -rf /Python-3.10.14 
rm -rf /Python-3.10.14.tgz


```

# cuda安装

#### 安装cuda

CUDA Toolkit Archive：https://developer.nvidia.com/cuda-toolkit-archive

```shell
wget https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda-repo-rhel7-11-8-local-11.8.0_520.61.05-1.x86_64.rpm
sudo rpm -i cuda-repo-rhel7-11-8-local-11.8.0_520.61.05-1.x86_64.rpm
sudo yum -y install nvidia-driver-latest-dkms
sudo yum -y install cuda
sudo yum clean all

```

```shell
# 查看板卡信息
lspci | grep -i nvidia

# 查看驱动程序是否安装
nvidia-smi

# 查看nvcc版本
nvcc --version
# 此时可能nvcc已经安装，但是没有配置环境变量



```

#### 查看GPU加速CUDA是否生效

```shell
# 单次查看
nvidia-smi

# 监视查看
watch -n 10 nvidia-smi


```

#### docker支持nvidia gpu

```shell
# 主机安装docker
# 主机安装nvidia-container-toolkit
curl -s -L https://nvidia.github.io/libnvidia-container/centos7/libnvidia-container.repo | sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo

yum install -y nvidia-container-toolkit

# 配置docker运行时支持gpu
nvidia-ctk runtime configure --runtime=docker

# 命令行运行使容器支持gpu
docker run -itd --gpus all python:3.10.14

```

# 使用ffmpeg命令行工具

- 下载地址：[https://ffmpeg.org/download.html](https://ffmpeg.org/download.html)
- 官方文档：[https://ffmpeg.org/documentation.html](https://ffmpeg.org/documentation.html)
- 编译好的二进制：[https://github.com/GyanD/codexffmpeg/releases](https://github.com/GyanD/codexffmpeg/releases)

#### 使用命令行

```
# 命令行基本用法
# ffmpeg <全局参数> <输入参数> -i <输入文件> <输出参数> <输出文件>

```

```
# 自动推导视频播放URL
ffmpeg -v debug -re -i $(curl -L -s http://10.10.56.18:30088/video/common/videoApi?cid=557254322 | jq -r .data)  -vcodec libx264 -vf scale=1280:-1 -r 20 -an -f flv rtmp://ossrs:1935/csud/live/557254322

# 获取网络文件
ffmpeg -v debug -re -i https://qq829.cn/uploads/20221204/1.mp4 -vcodec libx264 -vf "scale=1280:-2" -r 20 -an -f flv rtmp:/ossrs:1935/csud/live/557254322

# 获取网络文件
ffmpeg -v debug -re -i https://qq829.cn/uploads/20221204/1.mp4 -vcodec libx264  -f flv rtmp://ossrs:1935/live/557254322

# 屏幕截图
ffmpeg -r 8 -f "gdigrab" -i "desktop" -vcodec libx264  -f flv rtmp://ossrs:1935/live/557254322

# -stream_loop -1 循环播放
# -v debug 打开调试信息
# -re 实时读取，既按原视频的帧率读取，否则会以最快的速度读取，通常在将视频文件读取为直播源的时候需要，摄像头（直播）源不需要
# -i 输入文件
# -vcodec libx264 视频编码器
# -vf "scale=1280:-2 "视频画面大小调整，-2指强制偶数对其，因为奇数会报错
# -r 20 输出帧率
# -an 静音
# -f flv  输出 格式


```

```
# 视频截图
ffmpeg  -i https://qq829.cn/uploads/20221204/1.mp4 -ss 20 -vframes 1 -vf scale=800:-2 -qscale:v 2 -y 2.jpg

# -ss 视频开始位置
# -vframes 输出视频的帧目数
# -vf scale 视频滤镜，-2 表示根据前一个参数自动计算，并强制对其偶数，因为奇数会报错
# -qscale:v 2 图片质量，取值为2-10之间，数值越小画质越好

```

#### 播放器

```
# 使用ffplay播放
ffplay 1.mp4

```

```
使用VLC播放:
https://srs.qq829.cn/csud/live/557254322.flv
https://srs.qq829.cn/csud/live/557254322.m3u8

```

```
# 使用bilibili开源的播放器
# https://github.com/xqq/mpegts.js

```

#### 视频信息查看

```shell

# 显示媒体格式信息
ffprobe -show_format d:/1.mp4

# 显示每天流信息
ffprobe -show_streams d:/1.mp4

```

# ffmpeg命令行硬件编解码

#### 查看硬件支持情况

```shell


# 查看支持的硬件解码方式
ffmpeg -hwaccels

# cuda nvidia显卡
# dxva2
# qsv  intel显卡
# d3d11va


# 查看解码器名称
ffmpeg -decoders | grep h264

# h264_qsv      intel 硬件解码
# h264_cuvid    nvidia 硬件解码


ffmpeg -decoders | grep hevc

# hevc_qsv       intel硬解
# hevc_cuvid     nvidia硬解

# 查看编码器名称

ffmpeg -encoders | grep h264

# h264_qsv       intel 硬件编码
# h264_nvenc     nvidia 硬件编码


ffmpeg -encoders | grep hevc

# hevc_qsv       intel 硬件编码
# hevc_nvenc     nvidia 硬件编码


```

#### 在命令行中使用

```shell

# 根据硬件自动选择
ffmpeg.exe -hwaccel auto -i ./static/1.mp4 -f flv rtmp://secm-video.vppark.cn:31935/live/1

# 指定硬件编解码器
ffmpeg.exe -hwaccel qsv -c:v h264_qsv  -i ./static/1.mp4 -c:v h264_qsv -an  -f flv rtmp://secm-video.vppark.cn:31935/live/1

# 视频音频硬件编码
ffmpeg.exe -hwaccel qsv -c:v h264_qsv  -i ./static/3.mp4 -c:v h264_qsv -c:a aac -ar 44100 -ab 48k  -f flv rtmp://secm-video.vppark.cn:31935/live/3


```

# ffmpeg SDK使用指南

#### 官方编译好的release下载

[https://github.com/GyanD/codexffmpeg/releases](https://github.com/GyanD/codexffmpeg/releases)

#### 修改过的支持dll调用的

[https://github.com/donglietao/ffmpeg-dll](https://github.com/donglietao/ffmpeg-dll)

```python

def ffmpeg():
    dll = ctypes.windll.LoadLibrary(
        "D:/cpp-sdk/FFmpeg-n7.0/ffmpeg-exe-dll/Release/release/ffmpeg-dll.dll"
    )

    ffmpeg_main = dll.ffmain

    ffmpeg_main.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)]
    ffmpeg_main.restype = ctypes.c_int
    argv = (ctypes.c_char_p * 10)()
    argv[0] = bytes("ffmpeg", "utf-8")
    argv[1] = bytes("-f", "utf-8")
    argv[2] = bytes("gdigrab", "utf-8")
    argv[3] = bytes("-i", "utf-8")
    argv[4] = bytes("desktop", "utf-8")
    argv[5] = bytes("-vcodec", "utf-8")
    argv[6] = bytes("libx264", "utf-8")
    argv[7] = bytes("-f", "utf-8")
    argv[8] = bytes("flv", "utf-8")
    argv[9] = bytes("rtmp://srs-push.qq829.cn:31935/live/557254322", "utf-8")
    ret = ffmpeg_main(10, argv)
    ctypes.windll.kernel32.FreeLibrary.argtypes = [ctypes.c_void_p]
    ctypes.windll.kernel32.FreeLibrary.restype = ctypes.c_int
    ctypes.windll.kernel32.FreeLibrary(dll._handle)
    return ret


ffmpeg()



```

#### ffmpeg编解码c++代码实现

```c
// 注册输入和输出设备
avdevice_register_all();
// 初始化网络库
avformat_network_init();
//----------------打开输入设备----------------------//
// 确定输入格式
av_find_input_format("gdigrab");
// 打开输入文件
avformat_open_input();
// 查找输入流
avformat_find_stream_info();
// 选择流
in_stream = input_fmt_ctx->streams[i];
// 输入流的编解码器
AVCodec *in_codec = avcodec_find_decoder(in_stream->codecpar->codec_id);
// 解密上下文
in_codec_ctx = avcodec_alloc_context3(in_codec);
// 解码参数复制
avcodec_parameters_to_context();
// 打开解码器
avcodec_open2();
//----------------打开输处设备----------------------//
// 确定输出格式
av_guess_format()
// 构建输出上下文
avformat_alloc_output_context2();
// 添加输出流
AVStream *out_stream = avformat_new_stream(output_fmt_ctx, NULL);
// 设置流的参数
out_stream->codecpar->codec_id = AV_CODEC_ID_H264;
// 输出流编解码
AVCodec *out_codec = avcodec_find_encoder(out_stream->codecpar->codec_id);
// 输出流编码器上下文
AVCodecContext *out_codec_ctx = avcodec_alloc_context3(out_codec);
// 编码器参数复制
avcodec_parameters_to_context(out_codec_ctx, out_stream->codecpar);
// 打开编码器
avcodec_open2();
// 写入SPS和PPS信息
out_stream->codecpar->extradata = out_codec_ctx->extradata;
out_stream->codecpar->extradata_size = out_codec_ctx->extradata_size;
// 打开输出IO
avio_open();
// 写入文件头信息
avformat_write_header();
//----------------解密和转换----------------------//
while (1){
        // 从输入设备读取数据包
        ret = av_read_frame(input_fmt_ctx, &packet);
        // 解码数据包为帧
        ret = avcodec_send_packet(in_codec_ctx, &packet);
        // 从解码器中获取帧
        ret = avcodec_receive_frame(in_codec_ctx, frame);
        // 格式转换
        ret = sws_scale(sws_ctx, (const uint8_t *const *)frame->data, frame->linesize,
                        0, frame->height,
                        enc_frame->data, enc_frame->linesize);
        // 设置帧的时间戳
        enc_frame->pts = av_rescale_q_rnd((frame->pts - start_pts), in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
        enc_frame->pkt_dts = enc_frame->pts;
        //  将帧发送到编码器
        ret = avcodec_send_frame(out_codec_ctx, enc_frame);
        // 从编码器中获取数据包
        while (avcodec_receive_packet(out_codec_ctx, &enc_packet) == 0)
        {
            // 写入数据包
            ret = av_interleaved_write_frame(output_fmt_ctx, &enc_packet);
            if (ret < 0)
            {
                printf("7:Error while writing output packet:%s\n", av_err2str(ret));
                break;
            }
        }
        // 释放编码数据包
        av_packet_unref(&enc_packet);
        // 释放编码数据包
        av_packet_unref(&packet);
}
//----------------后续工作----------------------//
    // 写入文件尾
    av_write_trailer(output_fmt_ctx);
    // 关闭输出文件
    avio_close(output_fmt_ctx->pb);
    // 释放数据包
    av_packet_free(&packet);
    av_packet_free(&enc_packet);
    // 释放帧
    av_frame_free(&frame);
    av_frame_free(&enc_frame);
    // 释放格式转换器
    sws_freeContext(sws_ctx);
    // 释放编码器上下文
    avcodec_free_context(&in_codec_ctx);
    avcodec_free_context(&out_codec_ctx);
    // 释放输入和输出设备
    avformat_close_input(&input_fmt_ctx);
    avformat_free_context(input_fmt_ctx);
    // 释放输出设备
    avformat_close_input(&output_fmt_ctx);
    avformat_free_context(output_fmt_ctx);
    // 释放网络库
    avformat_network_deinit();

```

# 将ffmpeg编译为dll供外部调用

#### 将ffmpeg 编译为dll供外部调用

#### 创建qt应用程序，设置项目为lib

```
// ffmpeg-dll.pro
#TEMPLATE = app
#CONFIG += console
TEMPLATE = lib

```

#### 增加dll函数导出

```c
// main.c
__declspec(dllexport) int __cdecl ffmain(int argc, char const *argv[]){
    return main(argc,argv);
}

```

#### 处理命令行参数

既ffmpeg的传入参数并不是以传入的为准，而是内部使用操作系统API又进行了替换，如果在某些系统出现兼容性问题，需要再次修改源代码

```c
// cmdutils.c
// split_commandline
// 注释掉 785 行
// prepare_app_arguments(&argc, &argv);

```

#### 使用python 调用

```python
import ctypes as ctypes
import time as time


def ffmpeg():
    dll = ctypes.windll.LoadLibrary(
        "D:/cpp-sdk/FFmpeg-n7.0/ffmpeg-exe-dll/Release/release/ffmpeg-dll.dll"
    )

    ffmpeg_main = dll.ffmain

    ffmpeg_main.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)]
    ffmpeg_main.restype = ctypes.c_int
    argv = (ctypes.c_char_p * 10)()
    argv[0] = bytes("ffmpeg", "utf-8")
    argv[1] = bytes("-f", "utf-8")
    argv[2] = bytes("gdigrab", "utf-8")
    argv[3] = bytes("-i", "utf-8")
    argv[4] = bytes("desktop", "utf-8")
    argv[5] = bytes("-vcodec", "utf-8")
    argv[6] = bytes("libx264", "utf-8")
    argv[7] = bytes("-f", "utf-8")
    argv[8] = bytes("flv", "utf-8")
    argv[9] = bytes("rtmp://srs-push.qq829.cn:31935/live/557254322", "utf-8")
    ret = ffmpeg_main(10, argv)
    ctypes.windll.kernel32.FreeLibrary.argtypes = [ctypes.c_void_p]
    ctypes.windll.kernel32.FreeLibrary.restype = ctypes.c_int
    ctypes.windll.kernel32.FreeLibrary(dll._handle)
    return ret


while ffmpeg() < 0:
    print("ffmpeg error")
    time.sleep(5)

```

# 开源直播服务器OSSRS

#### OSSRS

SRS(Simple Realtime Server)是一个简单高效的实时视频服务器，支持RTMP、WebRTC、HLS、HTTP-FLV、SRT等多种实时流媒体协议。

官方地址：ossrs.ne

#### docker-compose.yaml

```yaml

version: '3'
services:
  ossrs:
    image: registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5
    ports:
      - 1935:1935 # RTMP推流端口
      - 1985:1985 # HTTP API 端口
      - 8080:8080 # 内置NGINX端口，可以用于http观看flv  
    volumes:
      - ./conf/docker.conf:/usr/local/srs/conf/docker.conf:ro
    command: ./objs/srs -c conf/docker.conf


```

#### 服务器自动保留录像

```yaml


# conf/docker.conf

    dvr {
        enabled      on;
        dvr_path     ./objs/nginx/html/dvr/[app]/[stream].[timestamp].mp4;
        dvr_plan     segment;
        # 按时间段分割，单位为秒
        dvr_duration    300;
        # 等待关键帧，如果为off可能导致启动播放时花屏
        dvr_wait_keyframe       on;
    }

```

#### 使用FFMPEG推流

使用ffmpeg命令行: [https://iovhm.com/book/books/ffmpegopencv/page/ffmpeg](https://iovhm.com/book/books/pythonffmpegopencv/page/ffmpeg)

```
# 命令行基本用法
# ffmpeg <全局参数> <输入参数> -i <输入文件> <输出参数> <输出文件>

```

```
# 从一个源转换到另外一个源
ffmpeg -stream_loop -1  -re -i https://smart.saas.vppark.cn/oss/1.mp4 -vcodec libx264  -f flv rtmp://srs-push.qq829.cn:31935/live/557254322

# 桌面截图
ffmpeg -r 8 -f "gdigrab" -i "desktop" -vcodec libx264  -f flv rtmp://srs-push.qq829.cn:31935/live/557254322



```

#### 使用vlc播放

```yaml


# 播放地址
https://srs.qq829.cn/live/557254322.flv



```

# 项目集成paddleocr功能

[![](https://iovhm.com/book/uploads/images/gallery/2024-05/scaled-1680-/oygieJYG5jdOq22u-image-1715098062928.png)](https://iovhm.com/book/uploads/images/gallery/2024-05/oygieJYG5jdOq22u-image-1715098062928.png)

[![](https://iovhm.com/book/uploads/images/gallery/2024-05/scaled-1680-/ar7QAPA4eMyhu655-image-1715098088199.png)](https://iovhm.com/book/uploads/images/gallery/2024-05/ar7QAPA4eMyhu655-image-1715098088199.png)

[![](https://iovhm.com/book/uploads/images/gallery/2024-05/scaled-1680-/mQ17Rp5onz67ifT5-image-1715098102324.png)](https://iovhm.com/book/uploads/images/gallery/2024-05/mQ17Rp5onz67ifT5-image-1715098102324.png)

[![](https://iovhm.com/book/uploads/images/gallery/2024-05/scaled-1680-/hzn8SaxqVq09IJlk-image-1715098182395.png)](https://iovhm.com/book/uploads/images/gallery/2024-05/hzn8SaxqVq09IJlk-image-1715098182395.png)

#### 示例代码下载

- [paddle-ocr.zip](https://iovhm.com/book/attachments/6)

#### 参考网站

- https://www.paddlepaddle.org.cn/
- https://github.com/PaddlePaddle/PaddleOCR
- https://fastapi.tiangolo.com/

#### 安装paddle和paddle ocr环境

```shell
# 安装paddlepaddle
# https://www.paddlepaddle.org.cn/
pip install paddlepaddle==2.6.1 -i https://pypi.tuna.tsinghua.edu.cn/simple

# 安装paddleocr
# 提示，截止至2024年5月2日，需要的python版本为3.11.更高版本会出现安装不上的情况
# 提示，截止至2024年5月2日，paddleocr==2.7.5有bug
# 需要修改的地方
#  C:\Python311\Lib\site-packages\paddleocr\paddleocr.py
# 第54行之后
# from ppstructure import predict_system 


pip install paddleocr==2.7.5 -i https://mirror.baidu.com/pypi/simple


# 可能出现其他安装失败情况，请使用清华源安装,更新pip工具
pip install --upgrade pip wheel setuptools



```

#### 安装fast api服务框架

```shell
# 安装fastAPI
pip install fastapi -i https://pypi.tuna.tsinghua.edu.cn/simple

# 安装web容器
pip install "uvicorn[standard]" -i https://pypi.tuna.tsinghua.edu.cn/simple

# 安装from参数表单
pip install python-multipart -i https://pypi.tuna.tsinghua.edu.cn/simple

```

#### 编写后台代码

```python
import paddle as paddle
from paddleocr import PaddleOCR, draw_ocr
from PIL import Image
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
import time
import uvicorn

# 打印paddle版本
print(paddle.__version__)

app = FastAPI()


def get_image_ocr(img_path: str):
    # 创建一个OCR实例
    ocr = PaddleOCR(
        use_angle_cls=True, lang="ch"
    )  # need to run only once to download and load model into memory
    # img_path = "./images/test.png"
    # 使用PaddleOCR进行文字检测和识别
    ocr_result = ocr.ocr(img_path, cls=True)
    # 显示结果
    for line in ocr_result:
        print(line)

    # 对现实结果在原图进行标注
    result = ocr_result[0]
    image = Image.open(img_path).convert("RGB")
    boxes = [elements[0] for elements in result]
    # pairs = [elements[1] for elements in ocr_result]
    # txts = [pair[0] for pair in pairs]
    # scores = [pair[1] for pair in pairs]
    im_show = draw_ocr(image, boxes)
    im_show = Image.fromarray(im_show)
    im_show.save(f"{img_path}")
    # im_show.save("./images/result.jpg")
    return {"image": img_path, "result": result}


app.mount("/static", StaticFiles(directory="./static"), name="static")


@app.get("/")
def read_root():
    html_content = """
    <html>
        <head>
            <title>OCR</title>
        </head>
        <body>
            <h1>OCR</h1>
            <p>Upload an image to extract text from it.</p>
        </body>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)


sequence_counter = 1


@app.post("/ocr/")
async def ocr_image(file: UploadFile | None = File(None)):
    # 判断文件是否存在
    if file is None:
        return {"code": 400, "message": "No file found", data: None}

    # 生成时间戳保存上传图片以免被覆盖
    current_time = int(time.time() * 10000)
    unique_sequnce = current_time + sequence_counter
    new_fileName = f"./static/{unique_sequnce}--{file.filename}.jpg"
    # 将文件写入磁盘
    with open(f"{new_fileName}", "wb+") as fsio:
        fsio.write(await file.read())
    pass
    # 调用OCR方法
    ocr_result = get_image_ocr(new_fileName)

    return {"code": 200, "message": "ok", "data": ocr_result}


# 启动服务
uvicorn.run(app, host="0.0.0.0", port=8000)


```

</body></html>

# pandas && 强大的数据操作处理程序

#### 安装

```shell

pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple

# excel支持
pip install openpyxl -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install xlsxwriter  -i https://pypi.tuna.tsinghua.edu.cn/simple


```

#### 连接到数据

支持读取多张数据：**CSV,JSON,Excel,mysql**等。

具体可以参考：[https://pandas.pydata.org/pandas-docs/stable/user\_guide/io.html](https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html)

api设计的非常简洁：

- 读取：read\_csv , read\_json , read\_excel,read\_sql
- 写入：to\_csv , to\_json，to\_excel , to\_sql

##### 连接到mysql

```shell

pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install sqlalchemy -i https://pypi.tuna.tsinghua.edu.cn/simple 


```

```python


# 增加pandas库的导入
import pandas as pd

# 添加SQLAlchemy的导入
from sqlalchemy import create_engine

# 示例用法
if True:
    engine = create_engine(
        "mysql+pymysql://root:password@192.168.0.10:33318/renren_cloud_basic"
    )
    query = "SELECT * FROM park_user"
    # 使用chunksize参数来实现流式处理
    df = pd.read_sql(query, engine)
    # 显示数据的结构和数据类型
    print(df.info())
    # 显示列名
    print(df.columns)




```

##### 连接到mysql的额外操作

- read\_sql\_table(table\_name,connection)
- read\_sql\_query(sql,connection)
- read\_sql(sql,connection)

#### 操作数据

- 获取数据形状

```python

row_size, col_size = grouped.shape
log.info(f"row_size:{row_size},col_size:{col_size}")

# row_size:13,col_size:7

```

- 数据清洗：提取数据到新的列

```python

# 将月份列提取为年
dataFrame["年份"] = pd.to_datetime(dataFrame["月份"]).dt.year


```

- 数据清洗：清洗空值
- 数据清洗：清洗格式错误数据
- 数据清洗：清洗错误数据
- 数据清洗：清洗重复数据
- 分组聚合

```python

grouped = (
    dataFrame.groupby(["客户名称", "合同编号", "年份"])
    .agg(
        {
            "减免总金额": "sum",
            "政策减免金额": "sum",
            "运营减免金额": "sum",
            "其他减免金额": "sum",
        }
    )
    .reset_index()  # 使分组变成一个平面
)

# 设置索引列的名称为“序号”
grouped.index.name = "序号"


```

- 筛选数据

```python

# 提取整列数据
print(grouped["客户名称"])


# 筛选客户名称列数据
print(grouped[grouped["客户名称"] == "山东智拓大数据有限公司"])


```

#### 写入Eexcel时设置样式

pandas是做数据处理的，默认导出并不能设置样式、合并单元格等，需要记住其他工具。

```python

# 自定义写入程序，使用xlsxwriter写入
with pd.ExcelWriter("./files/output.xlsx", engine="xlsxwriter") as writer:

    # 直接写入数据，没有样式
    # grouped.to_excel(writer, sheet_name="Sheet1", startrow=1, startcol=1, index=True)

    # 获取工作表
    workbook = writer.book
    worksheet = None
    # 如果worksheet不存在，则创建一个工作表
    if "Sheet1" in writer.sheets:
        worksheet = writer.sheets["Sheet1"]
    else:
        worksheet = workbook.add_worksheet("Sheet1")

    # 创建样式
    default_format = {
        "bold": False,
        # 边框
        "border": 1,
        # 垂直居中
        "valign": "vcenter",
        # 自动换行
        "text_wrap": True,
        # 水平居中
        # "align": "center",
        # 字体大小
        "font_size": 9,
    }

    # 创建样式
    cell_format_default = workbook.add_format(default_format)
    cell_format_head = workbook.add_format(
        {
            **default_format,
            **{
                "align": "center",
                "bold": True,
            },
        }
    )

    # 设置列的宽度，单位是多少个字符，一个中文占两个字符
    worksheet.set_column("A:A", 10)
    worksheet.set_column("B:B", 30)
    worksheet.set_column("C:C", 20)
    worksheet.set_column("E:H", 12)

    # 插入logo图片
    worksheet.insert_image(
        "A1",
        "./files/logo.png",
        {
            "x_scale": first_row_height / logo_image_height,
            "y_scale": first_row_height / logo_image_height,
            "x_offset": 5,
            "y_offset": 5,
        },
    )
    # 合并单元格
    worksheet.merge_range("A1:B1", "", cell_format_head)
    worksheet.merge_range("C1:H1", "租金减免表", cell_format_head)
    # 设置行高，单位是像素
    worksheet.set_row(0, first_row_height)

    # 增加序号列
    worksheet.write("A2", "序号", cell_format_head)
    worksheet.write_column("A3", range(1, len(grouped) + 1), cell_format_head)

    # 计算行和列的偏移量
    row_offset = 2
    col_offset = 1

    # 写入数据并设置样式，使用xlsxwriter作为驱动引擎时，需要将数据与样式一起写入，并不能直接修改样式
    for row_num, row_data in grouped.iterrows():
        for col_num, col_data in enumerate(row_data):
            worksheet.write(
                row_num + row_offset,
                col_num + col_offset,
                col_data,
                cell_format_default,
            )

    # 合并合同编号列，如果连续的行数据相同，则合并
    start_row = 0
    for row_num, row_data in grouped.iterrows():
        current_value = row_data["合同编号"]
        next_row = row_num + 1
        next_value = (
            grouped.iloc[next_row]["合同编号"] if next_row < len(grouped) else None
        )
        if current_value != next_value:
            log.info(f"{start_row} - {row_num} - {current_value}")
            if row_num - start_row > 0:
                worksheet.merge_range(
                    start_row + row_offset,
                    grouped.columns.get_loc("合同编号") + col_offset,
                    row_num + row_offset,
                    grouped.columns.get_loc("合同编号") + col_offset,
                    current_value,
                    cell_format_default,
                )
            start_row = next_row


```

# python中的模块（module）包(package)和import

python因为其灵活性，也带来了很多特殊的用法，

#### 项目结构约定

```
project/
│
├── main.py
├── routers/
│   ├── __init__.py
│   ├── items.py
│   └── users.py
├── models/
│   ├── __init__.py
│   ├── item.py
│   └── user.py
├── schemas/
│   ├── __init__.py
│   ├── item.py
│   └── user.py
├── utils/
│   ├── __init__.py
│   ├── string_util.py
│   └── log_factory.py
└── templates/
    └── index.html


```

#### package和import

- 一个py源文件就是一个模块，可以直接使用**import py\_name** 进行导入

```
project/
│
├── main.py
├── vp_smtp.py
└── 

```

```python

# vp_smtp


def send_mail(msg: str):
    print(msg)
    pass


# main.py

# 模块全部导入
import vp_smtp
# 使用模块名称访问
vp_smtp.send_mail("使用模块名称访问")

# 导入模块并重别名
import vp_smtp as vpsmtp
# 使模块别名调用
vpsmtp.send_mail("使模块别名调用")

# 导入模块中的具体的方法
from vp_smtp import send_mail
# 调用方法
send_mail("直接调用方法")


```

- 将源文件放入到一个文件夹,并在文件夹下面防一个 **\_\_init\_\_.py** 文件 , 则成了一个包(一组模块)

```python

# utils\vp_smtp.ly

def send_mail(msg: str):
    print(msg)
    pass


# main.py



#  按源文件存储路径 package.moduel 方式导入,自动查找对应的py文件
import utils.vp_smtp
# 完全限定调用:package.moduel.fun
utils.vp_smtp.send_mail("完全限定调用:package.moduel.fun")

#  按源文件存储路径 package.moduel 方式导入,并增加别名,自动查找对应的py文件
import utils.vp_smtp as vpsmtp
# 别名调用
vpsmtp.send_mail("别名调用:alias.fun")

# 从包中导入 vp_smtp
from utils import vp_smtp
vp_smtp.send_mail("从包中导入 vp_smtp")




```

#### \_\_init\_\_.py

\_\_init\_\_.py 是一个特殊的文件

- 标记文件夹是一个package
- 可以加入一些初始化代码,例如大家都喜欢放入 **\_\_version\_\_ = 0.1.1**
- 定义包级别的变量 , 函数 , 等,用作访问控制

```python

# __init__.py


# 先在__init__.py 初始化包和组织包的结构
from .vp_smtp import send_mail


# main.py

# 推荐使用这种方式,便于组织和管理源文件的公开
from utils import send_mail
send_mail("从_init__导入")


```

# pyqt使用资源文件

#### 1. 创建资源文件

首先，创建一个资源文件（例如 resources.qrc），并定义所需的资源。例如：

```xml
<RCC>
    <qresource prefix="/icons">
        <file>icon.png</file>
    </qresource>
</RCC>


```

- prefix：资源的前缀路径，用于在代码中访问资源。
- &lt;file&gt;：资源文件的路径（相对于资源文件所在目录）

#### 2. 将资源文件转换为 Python 代码

使用 pyrcc5 工具将资源文件转换为 Python 代码。在终端中运行以下命令：

```shell

pyrcc5 -o resources_rc.py resources.qrc


```

#### 3. 在 PyQt5 应用程序中使用资源

在代码中导入生成的资源文件，并使用资源路径来加载资源。以下是完整的代码示例：

```python


import sys
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtGui import QIcon
import resources_rc  # 导入生成的资源文件

class ExampleApp(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 设置窗口标题
        self.setWindowTitle('PyQt5 窗口图标示例')
        # 设置窗口大小
        self.setGeometry(300, 300, 300, 200)
        # 使用资源文件中的图标
        self.setWindowIcon(QIcon(":/icons/icon.png"))  # 注意路径格式

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = ExampleApp()
    ex.show()
    sys.exit(app.exec_())


```

# fastapi



# python restful API服务器fastAPI

####  组件

- fastAPI : <https://fastapi.tiangolo.com/>
- uvicorn: <https://www.uvicorn.org/>

- 项目模板：<https://iovhm.com/book/attachments/16>

#### 快速开始

```shell


# 安装fastAPI
pip install fastapi -i https://pypi.tuna.tsinghua.edu.cn/simple

# 安装web容器
pip install "uvicorn[standard]" -i https://pypi.tuna.tsinghua.edu.cn/simple

# 安装from参数表单
pip install python-multipart -i https://pypi.tuna.tsinghua.edu.cn/simple


```



#### 编写代码

```python


# main.py

from fastapi import FastAPI, APIRouter
from fastapi.staticfiles import StaticFiles

app = FastAPI()
router = APIRouter()

# 静态目录
app.mount("/static", StaticFiles(directory="./static"), name="static")


@app.get("/")
def read_root():
    return {"Hello": "World"}

@app.get("/")
async def read_root():
    html_content = """
    <html>
        <head>
            <title>FastAPI</title>
        </head>
        <body>
            <h1>Hello, FastAPI!</h1>
            <p>
                <a href="https://fastapi.tiangolo.com">
                    https://fastapi.tiangolo.com
                </a>
            </p>
        </body>
    </html>
    """
    return HTMLResponse(content=html_content, media_type="text/html")


# 运行
uvicorn main:app --reload
    
# 也可以用代码实现
if __name__ == "__main__":
    uvicorn.run(app, host="localhost", port=8000)

# 生成环境运行参数
# uvicorn main:app --reload  --host=localhost --port=8000


```

#### 约定和规范

- 项目结构
```
├── main.py              # 主应用文件
├── config.py            # 配置文件
├── run.py               # 启动脚本
├── requirements.txt     # 项目依赖
├── .env                 # 环境变量
├── README.md            # 项目说明
├── routers/             # 路由
├──
├── models/              # 数据模型
├──
├── schemas/             # 数据架构  
├──
├── repositories/        # 数据库操作
├──
├── services/            # 业务逻辑
├──
└── utils/               # 工具类

```


##### 公共日志类

```python
# common_logger.py

import logging
import sys

__all__ = ["get_logger"]

# 1. 只初始化一次
_INITIALIZED = False

# 2. 颜色表 + Windows 兼容
COLORS = {
    "DEBUG": "\033[36m",
    "INFO": "\033[32m",
    "WARNING": "\033[33m",
    "ERROR": "\033[31m",
    "CRITICAL": "\033[35m",
    "RESET": "\033[0m",
}


def _init_logger(level: int = logging.INFO):
    global _INITIALIZED
    # force=True 防止重复
    if _INITIALIZED:
        return

    class ColorFormatter(logging.Formatter):
        def format(self, record):
            levelname = record.levelname
            record.levelname = (
                f"{COLORS.get(levelname, '')}{levelname}{COLORS['RESET']}"
            )
            return super().format(record)

    handler = logging.StreamHandler(sys.stdout)
    handler.setFormatter(
        ColorFormatter(
            fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
            datefmt="%Y-%m-%d %H:%M:%S",
        )
    )
    logging.basicConfig(level=level, handlers=[handler], force=True)
    # force=True 防止重复
    _INITIALIZED = True


def get_logger(name: str, level: int = logging.INFO) -> logging.Logger:
    _init_logger(level)
    return logging.getLogger(name)



```

##### uvicorn 日志格式统一

```python


#!/usr/bin/env python3
"""
FastAPI 应用启动脚本
"""

import uvicorn
from config import settings
from common_logger import get_logger

log = get_logger(__name__)

LOGGING_CONFIG = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "default": {
            "fmt": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
        },
        "access": {
            "fmt": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
        },
    },
}


if __name__ == "__main__":
    log.info(f"启动 {settings.app_name} v{settings.app_version}")
    log.info(f"服务器地址: http://{settings.host}:{settings.port}")
    # print(f"API 文档: http://{settings.host}:{settings.port}/docs")
    log.info("-" * 50)

    uvicorn.run(
        "main:app",
        host=settings.host,
        port=settings.port,
        reload=settings.reload,
        log_level=settings.log_level.lower(),
        log_config=LOGGING_CONFIG,
    )


```

# fastapi 路由管理

#### 定义包并编写路由

```python
# routers\zlmedia_hook.py

from fastapi import APIRouter


router = APIRouter(prefix="/zlmedia/hook", tags=["zlmedia_hook"])


@router.get("/")
async def hello():
    return {"code": "0", "message": "success", "data": "hello world"}


```

#### 自动加载路由

```python

# main.py

import routers.zlmedia_hook as zlmedia_hook


# 附件路由
app.include_router(zlmedia_hook.router)

```

# fastapi项目结构

```

.
├── main.py              # 主应用文件
├── config.py            # 配置文件
├── run.py               # 启动脚本
├── requirements.txt     # 项目依赖
├── .env                 # 环境变量
├── README.md            # 项目说明
├── routers/             # 路由
├── models/              # 数据模型
├── schemas/             # 数据架构  
├── repositories/        # 数据库操作
├── services/            # 业务逻辑
└── utils/               # 工具类

```

##### 公共日志类

```python
# common_logger.py

import logging
import sys

__all__ = ["get_logger"]

# 1. 只初始化一次
_INITIALIZED = False

# 2. 颜色表 + Windows 兼容
COLORS = {
    "DEBUG": "\033[36m",
    "INFO": "\033[32m",
    "WARNING": "\033[33m",
    "ERROR": "\033[31m",
    "CRITICAL": "\033[35m",
    "RESET": "\033[0m",
}


def _init_logger(level: int = logging.INFO):
    global _INITIALIZED
    # force=True 防止重复
    if _INITIALIZED:
        return

    class ColorFormatter(logging.Formatter):
        def format(self, record):
            levelname = record.levelname
            record.levelname = (
                f"{COLORS.get(levelname, '')}{levelname}{COLORS['RESET']}"
            )
            return super().format(record)

    handler = logging.StreamHandler(sys.stdout)
    handler.setFormatter(
        ColorFormatter(
            fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
            datefmt="%Y-%m-%d %H:%M:%S",
        )
    )
    logging.basicConfig(level=level, handlers=[handler], force=True)
    # force=True 防止重复
    _INITIALIZED = True


def get_logger(name: str, level: int = logging.INFO) -> logging.Logger:
    _init_logger(level)
    return logging.getLogger(name)



```

##### uvicorn 日志根式统一

```python


#!/usr/bin/env python3
"""
FastAPI 应用启动脚本
"""

import uvicorn
from config import settings
from common_logger import get_logger

log = get_logger(__name__)

LOGGING_CONFIG = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "default": {
            "fmt": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
        },
        "access": {
            "fmt": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
        },
    },
}


if __name__ == "__main__":
    log.info(f"启动 {settings.app_name} v{settings.app_version}")
    log.info(f"服务器地址: http://{settings.host}:{settings.port}")
    # print(f"API 文档: http://{settings.host}:{settings.port}/docs")
    log.info("-" * 50)

    uvicorn.run(
        "main:app",
        host=settings.host,
        port=settings.port,
        reload=settings.reload,
        log_level=settings.log_level.lower(),
        log_config=LOGGING_CONFIG,
    )


```

# 参数传递

**from参数和json body 参数是互斥的**，只能使用一种

Query， Form， Body， 可以省略 ,匹配优先级是：路径参数，查询参数，body参数

###### 查询参数和from参数
```python


@app.post("/query_1")
def query_1(q: Any = (None), f: Any = (None)):
    return {"code": 0, "message": "success", "data": {"q": q, "f": f}}


```



##### 查询参数和json body
```python


class B_Model(BaseModel):
    b: str | int | Any = None


@app.post("/query_2")
def query_2(
    q: str = Query(None),
    b: B_Model = Body(None),
    token: str = Header(None, alias="token"),
    user: str = Cookie(None, alias="user"),
):
    return {
        "code": 0,
        "message": "success",
        "data": {"q": q, "b": b, "token": token, "user": user},
    }



```

# 使用中间件（拦截器）

##### 标准做法

```python
# LoginMiddleware.py

from fastapi import  Request,status
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi.responses import JSONResponse

WHITE_LIST={
    "/",
    "/docs",
    "/health"
}

# 需要登陆中间件
class  LoginMiddleware(BaseHTTPMiddleware):
    async def dispatch(self,request: Request, call_next):
        token = request.headers.get("token", "")
        if request.url.path in (WHITE_LIST):
            return await call_next(request)
        if not token:
            return JSONResponse(
                status_code=status.HTTP_401_UNAUTHORIZED,
                content={"code": 401, "message": "Missing or invalid token"}
            )

        response = await call_next(request)
        return response

# 中间件示例
class  XPowerByMiddleware(BaseHTTPMiddleware):
    async def dispatch(self,request: Request, call_next):
        response = await call_next(request)
        response.headers["x-powered-by"]="iovhm-com © 2022"
        return response

```


```python
# main.py

from LoginMiddleware import LoginMiddleware,XPowerByMiddleware

app.add_middleware(LoginMiddleware)
app.add_middleware(XPowerByMiddleware)
`
```

# 链接数据库

```python

# 安装
pip install sqlalchemy aiomysql -i https://pypi.tuna.tsinghua.edu.cn/simple

```

# deepseek



# deepseek 环境搭建

这篇文章是入门文章，没有什么实用价值，仅是将运行deepseek的环境运行起来了。

#### 主要软件

- ollama：模型运行平台,<https://ollama.com/download>
- open-webui：简单的chat页面，<https://github.com/open-webui/open-webui>
- dify ：模型编排平台，<https://github.com/langgenius/dify>
- docker
- python
  - 3.11：open-webui需要的运行时版本
- 科学上网：<https://iovhm.com/book/books/cee63/page/9872e>

#### ollama可以直接在容器运行

不用担心在容器运行会有性能损失，经过多年对docker的实践，除了网络方面会有损失外，其他方面并没有损失，可以将网络设置为**host**模式来规避**bridge**网络的性能损失。

相反，如果在主机直接运行，各种版本的依赖，新版本的升级，环境更加容易出问题。

```yaml

version: "3"
services:
  ollama:
    image: harbor.iovhm.com/hub/ollama/ollama:0.5.12
    container_name: ollama
    restart: always
    privileged: true
    ports:
      - "11434:11434"
    volumes:
      - ./ollama:/root/.ollama
    networks:
      - vpclub-bridge


```

#### 安装模型运行平台ollama

模型运行平台下载：<https://ollama.com/download>

##### 模型备份

每次下载模型都需要很久，可以将模型备份出来

进入到用户文件夹下面，例如 **C:\Users\admin\.ollama\models** ， 将模型复制出来，复制到新的机器对应的目录（未验证）

#### 下载和运行模型

```shell

# 显示所有命令行参数
ollama

# 所有的命令行

serve       Start ollama
create      Create a model from a Modelfile
show        Show information for a model
run         Run a model
stop        Stop a running model
pull        Pull a model from a registry
push        Push a model to a registry
list        List models
ps          List running models
cp          Copy a model
rm          Remove a model
help        Help about any command

```

```shell

# 运行服务，如果服务没有运行，可以用这个启动服务，非必须，一般安装完成后都会自动运行
ollama serve

# 验证安装
ollama -v


# 下载模型
ollama pull deepseek-r1:1.5b

# 运行模型
# 如果本地没有这个模型，则会自动下载并运行
# 运行后，此时会出现一个对话窗口，可以进行输入文字进行对话
# 键入/bye 或者ctrl+d可以退出对话窗口，模型在后台运行
# 退出对话框后，如果需要再次进入对话框，可以在此run模型
ollama run deepseek-r1:1.5b

# 显示已经安装的模型列表
ollama list

# 显示所有在运行的模型列表
ollama ps

# 显示模型信息
ollama show deepseek-r1:1.5b


```

#### 使用API对话

默认安装没有修改配置的话，**ollama** 运行在**11343**端口，可以使用命令行或者postmain测试

**这种方式太简陋，要实现的内容太多，不推荐**


```shell
curl -X POST -H "Content-Type: application/json" \
    -d '{"model": "deepseek-r1:1.5b", "prompt": "你好，世界！"}' \
    http://localhost:11434/api/generate

```


#### 使用open-webui

这是一个简单的兼容多个模型的、兼容openai接口调用方式的可视化界面

下载地址：<https://github.com/open-webui/open-webui>

文档地址：<https://docs.openwebui.com/>

可以直接使用docker运行，docker的安装方式自行百度，优先推荐使用docker安装


```shell

# GPU版

docker run -d -p 3000:8080 --gpus all --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:cuda

# CPU版
docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main

# pip 安装
pip install open-webui -i https://pypi.tuna.tsinghua.edu.cn/simple

# python运行
# 此时系统运行在 http://localhost:8080
open-webui serve





```

详细设置请看：<https://iovhm.com/book/books/bbcbf/page/deepseek-docker>

# docker搭建deepseek运行环境和open-webui

这篇文章没有什么实用价值，仅作为快速体验ollama运行环境。或者当做一个附加工具，用来管理和查看ollama环境。

#### docker-compose.yaml

这个脚本将同时启动**ollama** 和 **open-webui**

```yaml

version: "3"
services:
  ollama:
    image: harbor.iovhm.com/hub/ollama/ollama:0.5.12
    container_name: ollama
    restart: always
    privileged: true
    ports:
      - "11434:11434"
    volumes:
      - ./ollama:/root/.ollama
    networks:
      - vpclub-bridge

# docker-compose --profile open-webui up -d
  open-webui:
    # CPU版
    image: harbor.iovhm.com/public/open-webui/open-webui:main
    # GPU版
    # image: harbor.iovhm.com/public/open-webui/open-webui:main-gpu
    container_name: open-webui
    restart: always
    privileged: true
    ports:
      - "3000:8080"
    volumes:
      - ./open-webui:/app/backend/data
    environment:
      # 如果你的 ollama 服务器不在本机，请修改此地址，如果OLLAMA_BASE_URLS被设置，则使用OLLAMA_BASE_URLS
      # - OLLAMA_BASE_URL=http://ollama:11434
      # 如果你的 ollama 服务器不在本机，请修改此地址，可以使用分号分割多个地址提供负载均衡能力
      - OLLAMA_BASE_URLS=http://ollama:11434
      # 关闭 openai api 否则会因为连不上openai而卡界面
      - ENABLE_OPENAI_API=false
      # 禁用自带的 Arena Model模型（竞技场模型）
      - ENABLE_EVALUATION_ARENA_MODELS=false
      # 关闭社区分享功能
      - ENABLE_COMMUNITY_SHARING=false
      # 离线模式，不自动从Internet下载模型(非必须，有魔法的话不需要设置)
      # - HF_HUB_OFFLINE=true
      # 默认的语义向量模型引擎(非必须)
      # - RAG_EMBEDDING_ENGINE=ollama
      # 默认的语义向量模型(非必须)
      # - RAG_EMBEDDING_MODEL=nomic-embed-text:latest
    networks:
      - vpclub-bridge


networks:
  vpclub-bridge:
    external:
      name: vpclub-bridge  


```

#### 测试一下服务


打开浏览器，输入ollama的服务地址， **http://ip:11434** ,界面提示:**Ollama is running** ,则表示**ollama**安装成功了

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/BiuUdBKkSZ84xqu1-image-1740998429287.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/BiuUdBKkSZ84xqu1-image-1740998429287.png)


输入open-webui的地址，**http://ip:3000** , 则可以看到open-webui的地址。

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/qVkNjWcJacl6qx45-image-1741150619335.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/qVkNjWcJacl6qx45-image-1741150619335.png)


但是这个时候可能是白屏，什么都看不到；或者好不容易刷出来界面，设置好用户名密码进入系统后，也是白屏，此时应该去查看open-webui容器的出错提示，多半都是被墙拉取不到镜像的原因。那你需要魔法。


##### 界面打不开，或者进去了也很卡问题解决

- 打开open-webui等很久，不出来界面，白屏

因为软件有很多外部依赖要下载，如果被墙，下载不到、就一直卡住，可以先不使用**openai**和不使用内置的**语义向量模型模型**

```yaml

# 修改open-webui的环境变量设置

      # 关闭 openai api 否则会因为连不上openai而卡界面
      - ENABLE_OPENAI_API=false
      # 默认的RAG引擎(非必须)
      - RAG_EMBEDDING_ENGINE=ollama
      # 默认的语义向量模型(非必须)
      - RAG_EMBEDDING_MODEL=nomic-embed-text:latest

```

##### 进入软件后，每次刷新都要等很久

**关闭使用openai外部链接，修改语义向量模型**


[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/WpbjKcI2bviH0DgW-image-1741153227244.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/WpbjKcI2bviH0DgW-image-1741153227244.png)

**修改语义向量模型**

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/hzIZlpfesgHvWmYD-image-1741142128397.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/hzIZlpfesgHvWmYD-image-1741142128397.png)


#### 进入到ollama容器下载模型

```shell

# 安装完成后进入ollma容器
docker exec -it ollama /bin/bash

# 查看ollama版本
ollama -v

# 下载deepseek模型
ollama run deepseek-r1:7b

# 语义向量模型
ollama pull nomic-embed-text

```


#### 查看系统当前都安装了什么模型



[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/rwA6k3Sa89tpKccp-image-1741152828292.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/rwA6k3Sa89tpKccp-image-1741152828292.png)


#### 配置知识库



[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/fkz6DVGPKeSyYHBx-image-1741150695356.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/fkz6DVGPKeSyYHBx-image-1741150695356.png)

点击上传文件，建议的文件类型为**markdown**,使用 **####** 进行段落区分，如果保存知识库出错，那是因为没有设置正确的语义向量模型

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/Myex8Ea0Ex5FeqIr-image-1741150764668.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/Myex8Ea0Ex5FeqIr-image-1741150764668.png)




##### 为模型关联知识库，或者在聊天界面使用 **#** 引用知识库

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/jOeOgUNEL9rJmQo7-image-1741142780405.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/jOeOgUNEL9rJmQo7-image-1741142780405.png)


##### 在聊天界面使用# 引用知识库

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/h9hU4ioINq49Vl0m-image-1741142824988.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/h9hU4ioINq49Vl0m-image-1741142824988.png)


那我们就可以开始体验deepseek能力啦，来看看deepseek静静的装B

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/7Zobkh3mmisSRGgi-image-1741017091300.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/7Zobkh3mmisSRGgi-image-1741017091300.png)

# deepseek和dify环境搭建

dify是一个用于构建AI应用的模型编排软件，开箱即用，可以通过拖拉拽的形式，快速组合出一个AI应用，支持接入各厂商的云上模型，也支持接入本地**ollama**引擎运行的模型。

#### 准备工作

- 需要魔法：<https://iovhm.com/book/books/cee63/page/9872e>
- 镜像代理：<https://iovhm.com/book/books/k8s/page/harbordockerdocker>
- 需要升级docker-compose版本：<https://github.com/docker/compose/releases>
- 安装ollama并下载模型：<https://iovhm.com/book/books/bbcbf/page/deepseek>
- 下载dify源代码：<https://github.com/langgenius/dify>


#### 安装ollama并下载模型

```yaml
version: "3"
services:
  ollama:
    image: harbor.iovhm.com/hub/ollama/ollama:0.5.12
    container_name: ollama
    restart: always
    privileged: true
    ports:
      - "11434:11434"
    volumes:
      - ./ollama:/root/.ollama
    # deploy:
    #   resources:
    #     reservations:
    #       devices:
    #         - driver: nvidia
    #           capabilities: [gpu]
    #           count: all
    networks:
      - vpclub-bridge

# 下载最少2个模型
# ollama pull deepseek-r1
# ollama pull bge-m3

```

#### 下载dify源代码，进入到docker目录，修改被墙的docker镜像地址使用魔法地址


下载到源代码后，进入到**docker**目录，打开**docker-compose.yaml**，里面总共有26个服务，将镜像地址修改为私有仓库。如果并不打算二次开发和在服务器运行，只需要将**docker**目录上传到服务器，不需要把dify的所有源代码全部上传。

##### 真正有用的服务只有10个，其他的是各种不同类型的向量数据库

将如下10个服务的镜像地址修改为镜像代理地址，使用**docker-compose up -d** 即可以将软件运行起来。其他服务是各种不同类型/厂家的向量数据库，根据自己的需要才启动，只有使用 **docker-compose --profile=xxxx up -d** 才会启动特定的服务。不用担心启动了太多的服务。

**为了保持和官方版本升级时候的兼容性**，不建议直接修改docker-compose.yaml，比喻把镜像下载回来了重命名一下。

- api
- worker
- web
- db
- redis
- sandbox ， 一些模型可以调用代码，用于运行代码的沙箱容器
- plugin_daemon ， 开发插件用的
- ssrf_proxy ， 一个用来防止SSRF_PROXY攻击的代理软件
- nginx ， 入口nginx
- weaviate，向量数据库

##### env配置文件

官方指导是将 **.env.example** 复制一个后改名为 **.env** , 但是需配置项太多，从头看到尾很需要时间，在此我摘抄了一个极简的 **.env** ，实际上不提供任何 **.env**文件，也可以运行。如果你的默认的**80和443端口被占用**，那就需要提供 **.env** 进行配置更改。

官方文档：<https://docs.dify.ai/zh-hans/getting-started/install-self-hosted/environments>

```conf

CONSOLE_API_URL=
CONSOLE_WEB_URL=
SERVICE_API_URL=
APP_API_URL=
APP_WEB_URL=
FILES_URL=

# 对外公布的服务端口
EXPOSE_NGINX_PORT=80
EXPOSE_NGINX_SSL_PORT=443

# 是否开启检查版本策略,若设置为 false,则不调用 https://updates.dify.ai 进行版本检查。
# 由于目前国内无法直接访问基于 CloudFlare Worker 的版本接口,
# 设置该变量为空,可以屏蔽该接口调用
CHECK_UPDATE_URL=

# 向量数据库配置
VECTOR_STORE=weaviate

# Weaviate 端点地址,如:http://weaviate:8080
WEAVIATE_ENDPOINT=http://weaviate:8080

# 连接 Weaviate 使用的 api-key 凭据
WEAVIATE_API_KEY=WVF5YThaHlkYwhGUSmCRgsX3tD5ngdN8pkih

```

#### 接入ollama并添加模型

进入dify后，点击右上角自己的用户名图标，点击设置，进行模型供应商接入

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/V3RBO9HDf6qpOYVb-image-1741230074543.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/V3RBO9HDf6qpOYVb-image-1741230074543.png)

查看已经增加的模型

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/885RQ4DGNZsHsRl3-image-1741230213534.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/885RQ4DGNZsHsRl3-image-1741230213534.png)

增加模型，需要增加2个模型，一个是LLM模型，一个是Text Embeding模型，模型需要先到ollama下载好。

- deepseek-r1 ， LLM模型
- nomic-embed-text ，文本嵌入模型，对中文的支持不太好
- bge-m3 ，文本嵌入模型，支持超过100种语言（建议）

后面的其他参数不知道怎么填，可以使用默认值。

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/Am6wAHtbnv2vr7sW-image-1741230261287.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/Am6wAHtbnv2vr7sW-image-1741230261287.png)

#### 创建知识库

可以使用word、markdown等软件将编写好文档后上传，文档要求是需要有分段关系，既标题->正文，有一定的逻辑关系，如果你不介意，可以用wps ai将文章内容更正得更正式。


创建一个新的知识库，并上传文档

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/Pa97AIvyk8PhZklL-image-1741234212647.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/Pa97AIvyk8PhZklL-image-1741234212647.png)


对知识库进行设置

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/TmNl2c4jtN38rwv0-image-1741234262684.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/TmNl2c4jtN38rwv0-image-1741234262684.png)

设置检索方式和嵌入式文本模型

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/tcCzfhcRaDjXQQLc-image-1741234298816.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/tcCzfhcRaDjXQQLc-image-1741234298816.png)

#### 创建一个新的应用

在工作室标签下，选择创建空白应用，选择你要创建的应用类型

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/IPwxl4kIGq0wHe7a-image-1741234387917.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/IPwxl4kIGq0wHe7a-image-1741234387917.png)


对应用进行设置


[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/bZE229Iz7gHm9RVg-image-1741236686712.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/bZE229Iz7gHm9RVg-image-1741236686712.png)


应用的更多设置，比喻开场白，连续提问等。

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/zPyfKcfCS8ipZJm6-image-1741236713669.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/zPyfKcfCS8ipZJm6-image-1741236713669.png)


#### 调试体验一下

此时AI的回复特别生硬，需要修改提示词。

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/hKSEWoHIAHJivZpf-image-1741236832365.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/hKSEWoHIAHJivZpf-image-1741236832365.png)


#### 提示词设置


一个好的提示词，对AI的影响非常大，deepseek帮助文档给出了一些参考建议


<https://api-docs.deepseek.com/zh-cn/prompt-library/>

还是不知道怎么写？那我们蒸馏一下，让deepseek帮忙生成一个.

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/QLEWM89A3nAEdaMN-image-1741234863492.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/QLEWM89A3nAEdaMN-image-1741234863492.png)


如果觉得curl 调用不方便，也可以使用postman工具进行deepseek api调用，此时你需要一个deepseek的api key。

```shell

curl --location 'https://api.deepseek.com/chat/completions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer sk-xxxxxxxxxxxxxxxx' \
--data '{
        "model": "deepseek-chat",
        "messages": [
          {"role": "system", "content": "你是一位大模型提示词生成专家，请根据用户的需求编写一个智能助手的提示词，来指导大模型进行内容生成，要求：1. 以 Markdown 格式输出\n 2. 贴合用户需求，描述智能助手的定位、能力、知识储备\n 3. 提示词应清晰、精确、易于理解，在保持质量的同时,尽可能简洁\n 4. 使用清晰、简洁的语言回复提问，确保用户容易理解\n 5.使用友好且专业的语气与用户交流，如“您好，关于您的这个问题，我可以为您详细解答。"},
          {"role": "user", "content": "请帮我生成一个'\''智慧园区智能问答助手'\''的提示词"}
        ],
        "stream": false
}'

```

#### 蒸馏一下deepseek,用魔法打败魔法

到deepseek注册一个账号：<https://www.deepseek.com/> 

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/5PmUAWZBdhcde6p3-image-1741235212948.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/5PmUAWZBdhcde6p3-image-1741235212948.png)

创建一个api key

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/YtSo9JQMkojGLyPa-image-1741235303952.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/YtSo9JQMkojGLyPa-image-1741235303952.png)

充值10块钱，就可以用啦。顺便吐槽一下，老说程序员工资太高，但是你们看到别人背后的持续学习成本了吗？5-10年就技术更新一次，知识全部作废，熬夜写文档，买key，买机器，这些成本你们算过吗？你们某些行业，一本小破书就可以用到退休。就别整天BBB了。

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/emEN9prClqLgvE0k-image-1741235341899.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/emEN9prClqLgvE0k-image-1741235341899.png)


看下deepseek蒸馏生成的提示词，我们此基础上稍微修改下。



```markdown

# 威海智慧谷智慧园区智能问答助手小智提示词

## 定位
我叫小智，我是威海智慧谷智慧园区智能问答助手，是一个专为园区管理、企业员工及访客设计的智能交互平台。旨在通过自然语言处理技术，提供即时、准确的园区相关信息和服务支持。

## 能力
1. **信息查询**：能够快速响应关于园区设施、服务、活动等信息的查询。
2. **导航指引**：提供园区内的导航服务，包括建筑物位置、会议室预订等。
3. **问题解答**：解答关于园区政策、安全规定、技术支持等常见问题。
4. **服务预约**：协助用户进行会议室预订、设备租赁等服务预约。
5. **反馈收集**：收集用户对园区服务的反馈和建议，帮助园区管理方优化服务。

## 知识储备
1. **园区信息**：包括园区地图、设施介绍、服务项目等。
2. **政策法规**：园区相关的政策、规定及安全指南。
3. **技术支持**：常见技术问题的解决方案和操作指南。
4. **服务流程**：各类服务的预约流程、使用指南等。

## 交互示例
- **用户**：最近的咖啡厅在哪里？
- **助手**：贵宾，您好，园区内最近的咖啡厅位于A栋一楼，营业时间为早上8点到晚上8点。您可以通过园区导航系统找到具体位置。希望我的服务能帮助您。如有任何问题，欢迎随时咨询。

- **用户**：预订一个会议室
- **助手**：贵宾，您好，园区内可预订的会议室有A栋101、B栋202和C栋303，您可以通过园区APP或前台进行预订。希望我的服务能帮助您。如有任何问题，欢迎随时咨询。

- **用户**：...漏水...灯不亮了...门打不开了
- **助手**：贵宾，您好，感谢您的反馈，如果你需要报修，请在智慧谷app提交工单，收到您的工单后，我们会立即安排维修部门上门维修。希望我的服务能帮助您。如有任何问题，欢迎随时咨询。


## 提示词
- **查询信息**：请告诉我关于...
- **导航指引**：我需要找到...
- **问题解答**：关于...的问题，我需要帮助。
- **服务预约**：我想预订...
- **反馈收集**：我有一些建议/反馈...



```

#### 再来体验一下

此时AI回复已经很客气啦！

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/vBpW8eT6Rzox8fTn-image-1741238620736.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/vBpW8eT6Rzox8fTn-image-1741238620736.png)

**记得要点右上角的发布按钮哦。找了半天都没找到保存按钮，直接刷新或者关闭页面，可能导致部分内容没有保存**

#### 发布应用

点击左上角的应用图标，会弹出来dify已经嵌入好的聊天页面，如果页面风格你不太满意，可以使用API对接自己的定制页面。

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/jhle3W6njDxY6co3-image-1741238769940.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/jhle3W6njDxY6co3-image-1741238769940.png)

# deepseek嵌入工作流实现直接驱动业务的探索

#### 背景交代

市面上有很多问答式的AI产品，回复的内容确实很有参考意义，但是开放式的AI的上下文没有关联业务，还是需要先复制出来再修改一遍。

不过研发人员的ide插件可靠度就很高了，基本上都能运行。


**那么问题来了，客户他只有一个一次性的需求，还需要研发吗，特别是领导们喜欢提稀奇古怪的问题，一通改下来，最后说还是第一个好，浪费时间还不给钱**


#### 生成式大屏逻辑分析

- 分析用户的输入，例如统计今天成交了多少订单（实际业务肯定比这个要求复杂），不过我们今天是入门尝试，暂不选择很复杂的例子
- 从知识库查找业务上下游的表、字段关系
- 将用户输入与表关系（业务上下文），全部传递给deepseek
- 使用deepseek生成sql语句，驱动http接口执行动态SQL（不安全，不可靠）
- 那分两步进行，先返回SQL语句，再到大屏设计器里面去绑定语句，这样的话客户自己也可以定制一些大屏了。

#### 开整

在dify里面创建一个工作流应用

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/Fl4QmNmTlU85Ez2H-image-1741255908663.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/Fl4QmNmTlU85Ez2H-image-1741255908663.png)

在工作流面板上右键，增加各种节点

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/Z5yBuw6PWrcuyuGo-image-1741255981032.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/Z5yBuw6PWrcuyuGo-image-1741255981032.png)



如我们前面提到的，我们总共需要2个节点来完成，在加上一个开始和一个结束，总共需要四个节点



[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/0GKYImRo2ucN5C7B-image-1741256232214.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/0GKYImRo2ucN5C7B-image-1741256232214.png)


选中每一种不同类型的节点，都有这个节点的单独属性设置。

开始节点，增加一个输入字段，接受外部的输入

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/ZIkapWDbXMTOZGGa-image-1741256279686.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/ZIkapWDbXMTOZGGa-image-1741256279686.png)

增加一个知识库检索节点，拖拽建立关系，并接受上一个节点的输出，作为本节点的输入

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/dx414mqEIBgXY36f-image-1741256323445.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/dx414mqEIBgXY36f-image-1741256323445.png)


创建一个LLM模型，将知识库的输入绑定的LLM的上下文

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/N5usNRq5O8Sj4ezm-image-1741256669517.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/N5usNRq5O8Sj4ezm-image-1741256669517.png)

#### 重点，提示词和知识库

- **提示词的重要性我们前面讲过了**，可能需要反复尝试

```markdown
# MYSQL语法生成助手

## 定位
分析用户的输入，生成MYSQL代码

## 能力
- 分析用户输入，提取有用的内容
- 请输出MYSQL代码，不输出其他内容
- 只从用户输入中查找数据表、数据字段的映射关系，如果没有找到对应的数据映射关系，则不进行输出
- 只输出最正确内容的唯一一条
- 只输出select语句，对于delete,update,drop等语句不输出
- 只有用户指定的字段才进行查询输出，不要随便轻易输出*，除非用户没有限定条件
- 限定mysql5.7兼容语法，不要输出其他版本的语法

## 示例
- **用户**：查询用户
- **助手**：select * from users

- **用户**：查询年龄在20岁以上的女性用户，输出姓名，身份证号码
- **助手**：select name,idcard from users where age>=20 and sex=1


```

- 引用的业务内容上下文，也就是知识库，这个时候就需要精心编辑了,如果你有更好的更简洁的办法，请告诉我，例如直接导入sql表结构

**为了验证确实采纳了业务规则，而不是通用回答，我特意把表名增加了前缀vp_xxx**

```markdown
# 表名：vp_xxx_user（用户表）
## 字段：
- id（用户ID，主键）
- name（用户名）
- email（用户邮箱）
- idcard（身份证）

# 表名：vp_xxx_order（订单表）
## 字段：
- order_id（订单ID，主键）
- user_id（用户ID，关联用户表）
- amount（订单金额）
- img_url（图片地址）

# 表名：vp_xxx_customer（客户表）
## 字段：
- id（用户ID，主键）
- name（用户名）
- email（用户邮箱）
- idcard（身份证）

```

最后一个节点是结束节点，既把一串流程下来的结果输出。

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/Pc5TSPivNCQ10shX-image-1741259648196.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/Pc5TSPivNCQ10shX-image-1741259648196.png)

还可以追踪一下每一步的执行情况

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/lNgwGO5r8kU7iCMk-image-1741259702139.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/lNgwGO5r8kU7iCMk-image-1741259702139.png)

#### 验证一下



输入：查询所有年龄在20岁以上的客户，返回姓名和身份证。**注意客户与用户的区别**

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/gSsYsmP89BMIvyE1-image-1741257633918.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/gSsYsmP89BMIvyE1-image-1741257633918.png)


输入：查询所有年龄在20岁以上的用户，返回姓名和身份证。**注意客户与用户的区别**

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/Ayia0EQPYMWHWk76-image-1741257589515.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/Ayia0EQPYMWHWk76-image-1741257589515.png)

输入：查询2024年9月的物业合同。这个表在知识库并不存在，输出了错误的语句，提示词还需要调优

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/sRCw66uObDXK2xM3-image-1741258514211.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/sRCw66uObDXK2xM3-image-1741258514211.png)

输入：查询2024年9月的订单，返回了正确的结果。

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/IqxNbLJ6nLznz2JK-image-1741258553105.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/IqxNbLJ6nLznz2JK-image-1741258553105.png)


#### 后记

这个例子非常简单，不足以说明能或者不能满足直接驱动业务，但是最少是一种尝试，清晰的知识库、良好的提示词与约束限定、反复的调优，应该是可以满足AI直接驱动业务的。

# 网站采集工具firecrawl

#### 参考

反正就是一个很牛逼的网站爬取工具，支持纯JS网站，也就是现在流行的VUE等没有html的网站，原理是集成了一个无头chrome浏览器，等页面渲染了才爬取。

##### 特性

- 整站爬取、单个页面爬取、纯JS网站爬取
- 提取为LLM支持的markdown格式，当然了，直接爬取HTML是基本操作
- 只抓取main页面，无意义的重复内容


#### 相关文档和参考地址

- 官方帮助：<https://docs.firecrawl.dev/introduction>
- 网上的资料很多是V0版本的，但是现在**firecrawl**已经升级到V版本啦，而且v0版本将在2025年4月1日下线：<https://docs.firecrawl.dev/v1-welcome>
- github源代码地址：https://github.com/mendableai/firecrawl

#### 安装

下载源代码后，**docker-compose build** 生成镜像，再使用**docker-compose up -d** 运行

#### playwright-service

这是处理纯JS网站的服务，例如现在几乎所有的网站都是动态生成的，所以这个服务是必须的

#### 极简.env文件，其实不要也可以跑

```
# 核心配置
NUM_WORKERS_PER_QUEUE=8
PORT=3002
HOST=0.0.0.0
REDIS_URL=redis://redis:6379
REDIS_RATE_LIMIT_URL=redis://redis:6379
PLAYWRIGHT_MICROSERVICE_URL=http://playwright-service:3000/html

# 数据库及其他可选配置
USE_DB_AUTHENTICATION=false

```

#### API调用，现在GPT很强大，不懂的文GPT吧

- 整站爬取

```shell

curl -X POST http://localhost:3002/v1/crawl \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -d '{
      "url": "https://docs.firecrawl.dev",
      "limit": 100,
      "scrapeOptions": {
        "formats": ["markdown", "html"]
      }
    }'


```

- 获取爬取状态、或者说是爬取的结果

```shell

curl -X GET http://localhost:3002/v1/crawl/<jobid>

```

- 抓取单个 URL

```shell

curl -X POST http://localhost:3002/v1/scrape \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -d '{
      "url": "https://docs.firecrawl.dev",
      "formats": ["markdown", "html"]
    }'

```

- 获取网站地图

```shell

curl -X POST http://localhost:3002/v1/map \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -d '{
      "url": "https://firecrawl.dev"
    }'

``

- 执行搜索

```shell

curl -X POST http://localhost:3002/v1/search \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -d '{
      "query": "AI tools",
      "limit": 5,
      "scrapeOptions": {
        "formats": ["markdown"]
      }
    }'

```


-  结构化数据

```shell

curl -X POST http://localhost:3002/v1/extract \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -d '{
      "url": "https://example.com",
      "extract": {
        "schema": {
          "type": "object",
          "properties": {
            "title": {"type": "string"},
            "price": {"type": "number"}
          }
        }
      }
    }'

```

- 批量抓取`

```shell

curl -X POST http://localhost:3002/v1/batch/scrape \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -d '{
      "urls": ["https://example1.com", "https://example2.com"],
      "options": {
        "formats": ["markdown"]
      }
    }'

```

# deepseek提示词小技巧

提示词对模型的影响非常大,如果个人的写作习惯，描述的并不规范，建议大致意思写好后，放到wps使用**更正式**风格更正一下，或者放到其他AI让AI修正一下。


提示词虽然没有固定要求，但是根据实践，还是有一些格式可以借鉴的

- Role: 角色定位，明确模型扮演的身份和职业。例：自动化测试脚本编写专家
- Goals: 目标和任务。  例：根据用户需求,使用python和curl两种代码生成测试脚本，放在<context></context>XML标签内的内容是你的知识储备
- Constrains: 约束条件，明确禁止、限制或应该避免的内容。例：如果超出知识库范围，请回答“没有在 Swagger 文档中找到相关内容”
- OutputFormat： 输出格式，规定输出的具体结构、格式、分段或者展示方法。例：正式回复时，增加固定内容：“经过查找 Swagger 文档，为你生成测试脚本如下：”
- Example: 示例
- Optional: 额外注意事项



。deepseek模型的提示词设计一般分为三个区块

```markdown


# 威海智慧谷智慧园区智能问答助手

## 定位
我叫小智，是威海智慧谷智慧园区智能问答助手，是一个专为园区管理、企业员工及访客设计的智能交互平台。旨在通过自然语言处理技术，提供即时、准确的园区相关信息和服务支持。


## 知识储备
- 放在<context></context>XML标签内的内容是你的知识储备。
- 你的回答应当精确、简洁，并易于理解。
- 若您无法提供准确的答案，请直接回复“对不起，这个问题我不会回答”，随后立即终止对话，切勿添加任何无关内容。

## 交互示例
- **用户**：最近的咖啡厅在哪里？
- **助手**：贵宾，您好，园区内最近的咖啡厅位于A栋一楼，营业时间为早上8点到晚上8点。您可以通过园区导航系统找到具体位置。希望我的服务能帮助您。如有任何问题，欢迎随时咨询。


```


- 回答的时候不要添加额外的内容，以便于后续程序化处理

```markdown


##  知识储备

若您无法提供准确的答案，请直接回复“对不起，这个问题我不会回答”，随后立即终止对话，切勿添加任何无关内容。

```

- 限定知识范围

```markdown


##  知识储备

放在<context></context>XML标签内的内容是你的知识储备。

```

- 友好的语气回答

```markdown

## 交互示例
- **用户**：最近的咖啡厅在哪里？
- **助手**：贵宾，您好，园区内最近的咖啡厅位于A栋一楼，营业时间为早上8点到晚上8点。您可以通过园区导航系统找到具体位置。希望我的服务能帮助您。如有任何问题，欢迎随时咨询。

```

# deepseek开发流程

#### deepseek的工作路径

根据公开资料显示，LLM的工作方式如下。


[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/veTBXlaE7EzT5Wsx-image-1741612673196.png)](https://iovhm.com/book/uploads/images/gallery/2025-03/veTBXlaE7EzT5Wsx-image-1741612673196.png)

- 人工部分：
  
采集和创建知识库，可以是文档，结构化数据等，需要进行段落分割，存放到向量数据库。

- 程序部分
  
可以是dify等一类的平台，也可以是使用OpenaAI、http接口调用。初创和小公司推荐使用dify。

- 模型运行部分

这个就不用说了，全国会搞模型的也没几个，可以调ollama运行的本地模型，也可以调用云上模型。

- 问题，向量化归哪个环节呢

向量化是归于人工部分还是程序部分呢，知识库被向量化的好坏，对模型有阵非常大的影响，我们可以选择手工向量化，也可以使用程序自动向量化


#### 学习路径

既然我们已经弄清楚了deepseek的工作路径，第一步要做的事情就是准备知识库。

##### 采集知识


```python

import requests
import json

bas_url = "http://localhost:3002/v1/scrape"


headers = {"Content-Type": "application/json"}


req_data = {
    "url": "http://www.eweihai.gov.cn/art/2025/3/10/art_159136_5310185.html",
    "formats": ["markdown", "links"],
    "includeTags": [".page-bd.article-bd"],
    "onlyMainContent": True,
}


response = requests.post(bas_url, headers=headers, data=json.dumps(req_data))
print(response.json())




```

##### 构建知识库






##### 编程方式连接到ollama运行模型

```python

import openai

base_url = "http://192.168.0.11:11434/v1"
api_key = "sk-"


## 阻塞式
response = client.chat.completions.create(
    model="deepseek-r1:1.5b",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Hello!"},
    ],
    stream=False,
)

print(response.choices[0].message.content)


## 流式
# response = client.chat.completions.create(
#     model="deepseek-r1:1.5b",
#     messages=[
#         {"role": "system", "content": "You are a helpful assistant."},
#         {"role": "user", "content": "Hello!"},
#     ],
#     stream=True,
# )

# for chunk in response:
#     # 检查块中是否有内容
#     if chunk.choices and chunk.choices[0].delta.content:
#         print(chunk.choices[0].delta.content)


```

# 通义千问&&deepseek模型对比

- 知识库：威海小智问答
- 问题：**如何办理停车月卡**

#### 耗时与效果

结论：客服助手选择 **deepseek-v3** 和 **qwen-plus** ， 另外，**deepseek-chat**应该就是**deepseek-v3**

[![](https://iovhm.com/book/uploads/images/gallery/2025-03/scaled-1680-/jQRCDxPRMeWsmP4b-image-1742968726611.jpg)](https://iovhm.com/book/uploads/images/gallery/2025-03/jQRCDxPRMeWsmP4b-image-1742968726611.jpg)

# 优秀提示词示例

提示词虽然没有固定要求，但是根据实践，还是有一些格式可以借鉴的

- Role: 角色定位，明确模型扮演的身份和职业。
- Goals: 目标和任务。
- Constrains: 约束条件，明确禁止、限制或应该避免的内容。
- OutputFormat： 输出格式，规定输出的具体结构、格式、分段或者展示方法。
- Example: 示例
- Optional: 额外注意事项

#### 自动化脚本生成专家

```markdown

- Role: 自动化测试脚本编写专家
- Goals: 根据用户需求,使用python和curl两种代码生成测试脚本，放在<context></context>XML标签内的内容是你的知识储备
- Constrains: 如果超出知识库范围，请回答“没有在 Swagger 文档中找到相关内容”
- OutputFormat：正式回复时，增加固定内容：“经过查找 Swagger 文档，为你生成测试脚本如下：”

```
#### mock数据生成专家

```markdown

- Role: 高级数据模拟工程师和API测试专家
- Goals: 根据用户提供的样本数据格式，生成符合要求的测试数据。
- Constraints: 生成的数据必须严格符合用户指定的格式和逻辑，确保数据的准确性和一致性。
- Output Format: 仅输出纯数据内容，不包含任何解释、说明、格式标记（如Markdown符号）、代码块或其他多余符号。请以纯文本形式回答，仅提供文字内容，确保输出中不含任何格式化元素。

```

# yolo



# yolov8图像识别

#### 官方网址

https://docs.ultralytics.com/



####  安装

```shell

# 使用清华大学源加速
# https://pypi.tuna.tsinghua.edu.cn/simple/ 

# 查看已经安装的模块
pip list

# 卸载模块
pip uninstall <package-Name>

# 安装opencv
pip install python-opencv -i https://pypi.tuna.tsinghua.edu.cn/simple/

# 安装opencv 扩展
pip install opencv-contrib-python  -i https://pypi.tuna.tsinghua.edu.cn/simple/

# 安装pytorch
# https://pytorch.org/get-started/locally/
pip install torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple/

# 安装yolov8
# https://docs.ultralytics.com/quickstart/
pip install ultralytics -i https://pypi.tuna.tsinghua.edu.cn/simple/

```



#### 使用命令行

```shell
# 命令行格式
yolo TASK MODE ARGS

# TASK：[detect : 侦测], [segment ：分割], [classify ：分类], [pose ：姿态]
# MODE ：[train:训练], [val：验证], [predict：预测/测试], [export:导出], [track:跟踪]
```



```shell
# 使用yolov8n.pt 预测图片
yolo detect predict model="./yolo/yolov8n.pt" source="./images/1.jpg"
```

[![](https://iovhm.com/book/uploads/images/gallery/2024-06/scaled-1680-/qtEF5TSiT5bRoOh3-image-1717405855413.png)](https://iovhm.com/book/uploads/images/gallery/2024-06/qtEF5TSiT5bRoOh3-image-1717405855413.png)



```shell
# 使用yolov8n-seg.pt 分割图片
yolo segment predict model="./yolo/yolov8n-seg.pt" source="./images/1.jpg"
```

[![](https://iovhm.com/book/uploads/images/gallery/2024-06/scaled-1680-/NMaNy5AFSncjM6b1-image-1717405866827.png)](https://iovhm.com/book/uploads/images/gallery/2024-06/NMaNy5AFSncjM6b1-image-1717405866827.png)



#### 训练模型



```shell
# 使用coco128数据集进行模型训练
yolo detect train data=./yolo/coco128.yaml model=./yolo/yolov8n.pt epochs=100 imgsz=640
```



```yaml
# 数据集实际上一个yaml配置文件
# 文件示例：https://github.com/ultralytics/ultralytics/tree/main/ultralytics/cfg/datasets
# coco128数据集

path: ../datasets/coco128 # dataset root dir
train: images/train2017 # train images (relative to 'path') 128 images
val: images/train2017 # val images (relative to 'path') 128 images
test: # test images (optional)

# Classes
names:
  0: person
  1: bicycle
 
download: https://ultralytics.com/assets/coco128.zip

```



#### python代码

```python
from ultralytics import YOLO

model = YOLO('./yolo/yolov8n.pt')  # load a pretrained model (recommended for training)
results = model.train(data='./images/coco128.yaml', epochs=100, imgsz=640)
```

# yolo读取视频并检测&cv

**这是一段示例代码，用于跑通基础验证，不可用于生产，除非你的视频路数少于4路**

```python


from time import sleep
import cv2 as cv, torch
from ultralytics import YOLO
import logging
import time

logging.basicConfig(
    level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
log = logging.getLogger(__name__)

# log.info("-- OpenCV Info --")
# log.info(cv.getBuildInformation())
# log.info("-" * 50)

# device = "cuda:0" if torch.cuda.is_available() else "cpu"
# log.info(f"Using device: {device}")

yolo_model = YOLO("./yolo_model/yolo11s.pt")
image_size = 640
yolo_model.overrides["imgsz"] = image_size

cls_map = {
    0: "person",
    1: "bicycle",
    2: "car",
    3: "motorcycle",
    5: "bus",
    7: "truck",
}

# 帧率
FPS_TARGET = 12
# 帧间隔
T_INTERVAL = 1.0 / FPS_TARGET
#  上一次时间
last_t = time.time()
# 检测间隔
detect_every = 3
# 检测计数
detect_count = 0

cap = cv.VideoCapture(
    "https://smart.saas.vppark.cn/oss/1.mp4",
)
 

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break
    # if time.time() - last_t < T_INTERVAL:
    #     continue
    # last_t = time.time()

        
    detect_count += 1
    if detect_count % detect_every == 0 or True:
        # 手动 resize
        h0, w0 = frame.shape[:2]
        scale = min(image_size / h0, image_size / w0)
        h1, w1 = int(h0 * scale), int(w0 * scale)
        frame_resize = cv.resize(frame, (w1, h1), interpolation=cv.INTER_LINEAR)

        # 执行检测
        results = yolo_model(
            frame_resize, imgsz=image_size, classes=list(cls_map.keys())
        )
        # 把框映射回原图
        for result in results:
            boxes_data = result.boxes.data.clone()
            boxes_data[..., :4] /= scale
            result.boxes.data = boxes_data
            react_frame = result.plot(img=frame, line_width=2)
            cv.imshow("frame", react_frame)
    else:
        cv.imshow("frame", frame)
    # if cv.waitKey(int(1000 / FPS_TARGET)) & 0xFF == ord("q"):
    if cv.waitKey(1) & 0xFF == ord("q"):
        break

cap.release()
cv.destroyAllWindows()




```

# pandas创建excel

```python

# 创建excel
data_report_task_item_detail_excel = pd.DataFrame(
    columns=[
        "item_id",
        "taskId",
        "taskName",
        "flowStateId",
        "targetTableId",
        "targetTableValue",
        "controlName",
        "value",
    ]
)

# 创建表头
data_report_task_item_detail_excel.loc[len(data_report_task_item_detail_excel)] = {
    "item_id": "子项ID",
    "taskId": "任务ID",
    "taskName": "任务名称",
    "flowStateId": "关联的明细ID",
    "targetTableId": "目标单位ID",
    "targetTableValue": "目标单位名称",
    "controlName": "项目",
    "value": "值",
}

# 插入数据

        data_report_task_item_detail_excel.loc[
            len(data_report_task_item_detail_excel)
        ] = {
            "item_id": item_id,
            "taskId": taskId,
            "taskName": taskName,
            "flowStateId": flowStateId,
            "targetTableId": targetTableId,
            "targetTableValue": targetTableValue,
            "controlName": controlName,
            "value": value,
        }


# 保存为excel
data_report_task_item_detail_excel.to_excel(
    "data_report_task_item_detail.xlsx", index=False
)


```

# 常用ai语音配音

#### 微软

- xiaoxiao（晓晓）

<audio controls="" id="bkmrk-"> <source src="https://iovhm.com/book/attachments/9" type="audio/mpeg"></source></audio>- yunjian（云建）

<audio controls="" id="bkmrk--1"> <source src="https://iovhm.com/book/attachments/12" type="audio/mpeg"></source></audio>- yunxi（云熙）

<audio controls="" id="bkmrk--2"> <source src="https://iovhm.com/book/attachments/10" type="audio/mpeg"></source></audio>- yunyang（云扬）

<audio controls="" id="bkmrk--3"> <source src="https://iovhm.com/book/attachments/11" type="audio/mpeg"></source></audio>- hsiaochen（小陈，台湾）

<audio controls="" id="bkmrk--4"> <source src="https://iovhm.com/book/attachments/13" type="audio/mpeg"></source></audio>- 魔音工坊，魔西毒

# 微软语言合成

#### 使用方法

```python

# 安装
pip install -U edge-tts -i https://pypi.tuna.tsinghua.edu.cn/simple


# 查看支持的语言
edge-tts --list-voices


# 使用
edge-tts --voice zh-CN-XiaoxiaoNeural --text "大家好，我是微软晓晓" --write-media demo.mp3

# 从文本文件读取
edge-tts --voice zh-CN-XiaoxiaoNeural  --file 1.txt  --write-media 1.mp3

# 写入字幕文件
edge-tts --voice zh-CN-XiaoxiaoNeural --file 1.txt  --write-media 1.mp3 --write-subtitles 1.srt


```

#### 小技巧

- 使用 -- 断句
- 使用双引号将专有名词连读连读

# 千问图片和视频相关提示词

#### 图像生成

[https://bailian.console.aliyun.com/cn-beijing/?tab=api#/api/?type=model&amp;url=3001143](https://bailian.console.aliyun.com/cn-beijing/?tab=api#/api/?type=model&url=3001143)

##### 人物绘制和补全

```

# 模型名称
wan2.6-image


将图片中的人物绘制成全身照，并漏出脚的部分(光脚)，人物的身高应该控制在160mm-170mm之间，人物的头部、肢体结构需要符合解剖学正确比例，四肢协调自然。严格保持原人物面部特征（五官位置、眼睛形状、鼻子轮廓、嘴唇厚度、脸型比例、发型发色、配饰耳环等）完全不变。图片背景颜色纯绿色背景(#00FF00)。禁止出现多只手、多只脚、肢体残缺等穿帮问题。


```

###### 人物美颜（保留最大细节）

```

# 模型名称
wan2.6-image

# 简洁

对图片中的人物轻微美白、轻微磨皮，保留皮肤纹理和五官特征。衣服褶皱自然抹平。**禁止改变人物面部特征（面部轮廓、五官位置、眼睛形状、鼻子轮廓、嘴唇形状、嘴唇厚度、脸型比例、发型发色、配饰耳环等）**

# 反向提示词
低分辨率，低画质，肢体畸形，手指畸形，画面过饱和，蜡像感，人脸无细节，过度光滑，画面具有AI感，惨白，塑料感，面部特征（面部轮廓、五官位置、眼睛形状、鼻子轮廓、嘴唇形状、嘴唇厚度、脸型比例、发型发色、配饰耳环）






```

##### 将人物绘制成Q版卡通形象

```

# 模型名称
wan2.6-image

将图片中的人物绘Q版卡通形象，人物的头部、肢体结构需要符合解剖学正确比例，四肢协调自然。尽可能的保留原人物面部特征（五官位置、眼睛形状、鼻子轮廓、嘴唇厚度、脸型比例、发型发色、配饰耳环等）基本不变。图片背景颜色纯绿色背景(#00FF00)。禁止出现多只手、多只脚、肢体残缺等穿帮问题。


```

#### 视频生成

##### 背景璀璨烟花(基于首帧生成)

[https://bailian.console.aliyun.com/cn-beijing/?tab=api#/api/?type=model&amp;url=2867393](https://bailian.console.aliyun.com/cn-beijing/?tab=api#/api/?type=model&url=2867393)

```
# 模型名称
wan2.6-i2v-flash

夜空中三束以上形态各异的烟花正处于不同绽放阶段，色彩绚丽柔和不刺眼，画面首尾无缝衔接，可无限循环播放。


```

##### 人物舞动(基于首帧生成)

```
# 模型名称
wan2.6-i2v-flash


一位可爱动漫风格少女正在跳舞。舞蹈动作包括:双手提着裙子扭胯、顺时针旋转360度、扭胯、抬手，抚摸脸庞、比爱心动作；需要注意肢体动作的衔接自然、流畅。


```

##### (首尾帧生成)

[https://bailian.console.aliyun.com/cn-beijing/?tab=api#/api/?type=model&amp;url=2880649](https://bailian.console.aliyun.com/cn-beijing/?tab=api#/api/?type=model&url=2880649)

```
# 模型名称
wan2.2-kf2v-flash


```

##### 图生动作(基于参考视频)

- 舞蹈克隆（将视频中动作复制到图片中让图片动起来）

[https://bailian.console.aliyun.com/cn-beijing/?tab=api#/api/?type=model&amp;url=2981852](https://bailian.console.aliyun.com/cn-beijing/?tab=api#/api/?type=model&url=2981852)

```
# 模型名称
wan2.2-animate-move


```

- 视频换脸（将人物复制到原视频中，保持原视频背景不变）

```
# 模型名称
wan2.2-animate-mix


```

##### 舞蹈克隆

[https://bailian.console.aliyun.com/cn-beijing/?tab=api#/api/?type=model&amp;url=2786459](https://bailian.console.aliyun.com/cn-beijing/?tab=api#/api/?type=model&url=2786459)

```
# step1 检查图片是否可以克隆
animate-anyone-detect-gen2

# 生成动作模板
animate-anyone-template-gen2

# 查看模板ID供生成视频时使用


# 生成新的视频
animate-anyone-gen2

```

# comfyui安装

#### 条件

- python 3.13 : [https://www.python.org/ftp/python/3.13.11/python-3.13.11-amd64.exe](https://www.python.org/ftp/python/3.13.11/python-3.13.11-amd64.exe)
- vc13 : [https://aka.ms/vc14/vc\_redist.x64.exe](https://aka.ms/vc14/vc_redist.x64.exe)
- git

可以先运行cpu版本跑一个实例确定基础环境是好的，再跑GPU版把问题限定在驱动和cuda兼容性方面

#### 扩展管理器

```shell

cd custom_nodes/

git clone https://github.com/ltdrdata/ComfyUI-Manager comfyui-manager


```

#### 坑

```



# 确定cuda版本
nvidia-smi

# 卸载pytorch
pip uninstall torch torchvision torchaudio -y

# 安装对应的版本
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124

# --index-url 后面的cu124表示版本号， 与cuda版本一致


```

# comfyui文生图

综合来看，好像wan-image效果更好，但是z-image速度更快。

**Qwen-image**全能型文生图基座，海报、PPT、信息图、插画

**Z-image**轻量高效文生图，人像写真、电商配图、社交媒体

**Wan-image**商用级图像生成与编辑，品牌广告、电商主图、角色营销

#### 使用z\_image

- UNET加载器
- CLIP加载器
- VAE加载器
- LORA加载器
- 模型采样算法
- 正向提示词：CLIP文本编码器
- 负向提示词：CLIP文本编码器
- K采样器
- VAE解码器
- 保存/显示图像

[![](https://iovhm.com/book/uploads/images/gallery/2026-02/scaled-1680-/Jz87j9QWsmdvq75h-image-1770470938556.png)](https://iovhm.com/book/uploads/images/gallery/2026-02/Jz87j9QWsmdvq75h-image-1770470938556.png)

- 工作流下载

[z\_image.json](https://iovhm.com/book/attachments/25)

#### 使用qwen-image

- UNET加载器
- CLIP加载器
- VAE加载器
- LORA加载器
- 模型采样算法
- 正向提示词：CLIP文本编码器
- 负向提示词：CLIP文本编码器
- K采样器
- VAE解码器
- 保存/显示图像

```

18岁的亚洲少女，黑色长直发及腰，空气刘海，杏仁眼，清透妆容，浅粉色唇彩，身穿白色蕾丝连衣裙，坐在开满樱花的公园长椅上，春天午后，阳光透过花瓣洒下斑驳光影，柔和的自然光，浅景深虚化背景，日系胶片色调，Canon EOS R5, 85mm f/1.2，超精细皮肤质感，8k分辨率


```

[![](https://iovhm.com/book/uploads/images/gallery/2026-02/scaled-1680-/PJjRC186Qyf7rHVC-image-1770635242826.png)](https://iovhm.com/book/uploads/images/gallery/2026-02/PJjRC186Qyf7rHVC-image-1770635242826.png)

- 工作流下载

[Qwen-image.json](https://iovhm.com/book/attachments/24)

#### 步骤描述

- 模型加载器：Checkpoint加载器(简易)
- 正向提示词：CLIP文本编码器
- 负向提示词：CLIP文本编码器
- 空Latent
- K采样器
- VAE解码
- 保存/显示图像

[![](https://iovhm.com/book/uploads/images/gallery/2026-01/scaled-1680-/CVUkyVq5VvbzPFhn-image-1769784213510.png)](https://iovhm.com/book/uploads/images/gallery/2026-01/CVUkyVq5VvbzPFhn-image-1769784213510.png)

#### 效果图

**Qwen-Image**

当你需要在图片里准确生成中文标题、海报文字或排版时用它，这是目前开源界唯一能可靠处理复杂汉字结构的模型

![](https://iovhm.com/book/uploads/images/gallery/2026-02/f6qZSgA4kJj0f68i-image-1770008154700.png)

**Z-Image**

当你显卡显存有限（6GB就能跑）但需要秒级出图且画质不打折时用它，性价比最高的本地部署选择

![](https://iovhm.com/book/uploads/images/gallery/2026-02/G5lhYpM6O43WU80Z-image-1770008145335.png)

**Wan-Image**

当你需要生成视频（文生视频/图生视频）或自定义任意分辨率的摄影级写实图像时用它，功能最全但硬件要求最高

# comfyui图生图

#### KSampler 简易采样器参数介绍

- seed:随机种，确保生成结果可复现，相同的种子可以保证生成一样结果
- control after generate：后复现，有些地方没有，直接使用seed=1进行控制
- steps：迭代步数，步数越多细节越丰富,图生图时可适当减少步数（12-18 步）
- cfg：提示词引导系数，控制 AI 对提示词的遵循程度，就是AI对提示词的遵循度,数值越高，越遵循提示词（也可能僵硬），数值越低，AI自由发挥越高（可能不是你想要的），
- sampler name : 采样器名称
- scheduler ： 噪声调度器
- denoise：降噪强度，0 = 原图不变，1 = 完全生成新内容,建议 (0.4-0.7)

#### qwen-image-edit

- KSampler
- UNET加载器:Qwen-Image-Edit-2509\_fp8\_e4m3fn.safetensors
- LoRA加载器(仅模型):Qwenimage/Qwen-Image-Edit-2509-Lightning-8steps-V1.0-bf16.safetensors
- 模型采样算法AuraFlow
- CLIP加载器:qwen\_2.5\_vL7b\_fp8\_scaled.safetensors
- VAE加载器:qwen\_image\_vae.safetensors
- TextEncodeQwenImageEditPlus
- 图片加载器：Load Image
- VAE Encode
- VAE Decode
- Save Image

```
📂 ComfyUI/
├── 📂 models/
│   ├── 📂 text_encoders/
│   │      └── qwen_2.5_vl_7b_fp8_scaled.safetensors
│   ├── 📂 loras/
│   │      └── Qwen-Image-Edit-Lightning-4steps-V1.0-bf16.safetensors
│   ├── 📂 diffusion_models/
│   │      └── qwen_image_edit_fp8_e4m3fn.safetensors
│   └── 📂 vae/
│          └── qwen_image_vae.safetensors

```

```

将图一中女生的衣服替换为图二中女生的衣服


```

[![](https://iovhm.com/book/uploads/images/gallery/2026-02/scaled-1680-/FeaOnfk4XRD3jibf-image-1770491905201.png)](https://iovhm.com/book/uploads/images/gallery/2026-02/FeaOnfk4XRD3jibf-image-1770491905201.png)

#### 默认工作流（不推荐，效果太差）

- KSampler
- 正向：CLIP Text Encode (Prompt)
- 负向：CLIP Text Encode (Prompt)
- 模型加载器：Load Checkpoint
- 图片加载器：Load Image
- VAE Encode
- VAE Decode
- Save Image

# comfy图生视频



# facefusion&&人脸替换

#### 源代码下载

https://github.com/facefusion/facefusion

#### 准备环境

```

# 创建环境
conda create -n facefusion python=3.13

# 切换环境
conda activate facefusion

# 安装环境
python install.py --onnxruntime cuda

# 安装依赖
pip install -r requirements.txt


# 运行
python facefusion.py run



```

#### 找不到 openvino.dll

```

set "OPENVINO_PATH=C:\Users\leo\.conda\envs\facefusion\Lib\site-packages\openvino\libs"
set "PATH=%OPENVINO_PATH%;%PATH%"


```

#### 常用配置

**首次运行最好什么配置也不加**

**3.5版 `output_audio_encoder` 默认设置没有声音**

```
[paths]
output_path = C:\Users\leo\Videos\

[output_creation]
output_audio_encoder = aac
output_video_encoder = libx264
output_video_preset = veryfast


```

#### 命令行参数说明

```
python facefusion.py [commands] [options]

options:
  -h, --help                                      show this help message and exit
  -v, --version                                   show program's version number and exit

commands:
    run                                           run the program
    headless-run                                  run the program in headless mode
    batch-run                                     run the program in batch mode
    force-download                                force automate downloads and exit
    benchmark                                     benchmark the program
    job-list                                      list jobs by status
    job-create                                    create a drafted job
    job-submit                                    submit a drafted job to become a queued job
    job-submit-all                                submit all drafted jobs to become a queued jobs
    job-delete                                    delete a drafted, queued, failed or completed job
    job-delete-all                                delete all drafted, queued, failed and completed jobs
    job-add-step                                  add a step to a drafted job
    job-remix-step                                remix a previous step from a drafted job
    job-insert-step                               insert a step to a drafted job
    job-remove-step                               remove a step from a drafted job
    job-run                                       run a queued job
    job-run-all                                   run all queued jobs
    job-retry                                     retry a failed job
    job-retry-all                                 retry all failed jobs



```

# 千问修图

#### 先用千问修人

```
{
    "model": "wan2.6-image",
    "input": {
        "messages": [
            {
                "role": "user",
                "content": [
                    {
                        "image": ""
                    },
                    {
                        "text": "对图片中的人物轻微美白、轻微磨皮，保留皮肤纹理和五官特征。衣服褶皱自然抹平。**禁止改变人物面部特征（面部轮廓、五官位置、眼睛形状、鼻子轮廓、嘴唇形状、嘴唇厚度、脸型比例、发型发色、配饰耳环等）**"
                    }
                ]
            }
        ]
    },
    "parameters": {
        "n": 1,
        "negative_prompt": "低分辨率，低画质，肢体畸形，手指畸形，画面过饱和，蜡像感，人脸无细节，过度光滑，画面具有AI感，惨白，塑料感，面部特征（面部轮廓、五官位置、眼睛形状、鼻子轮廓、嘴唇形状、嘴唇厚度、脸型比例、发型、发色、耳环、配饰）",
        "prompt_extend": true,
        "watermark": false,
        // "size": "720*1280",
        "seed": 2345
    }
}


```

#### 在回贴到PS