快速上手:利用音频大模型与Java提取视频文案

利用大模型提取视频营销文案的实现
本文章已经生成可运行项目,
查看更多项目 查看更多项目 查看更多项目

1、前言

    近期要做个智能文案工具,来帮助电商公司的运营同学提效。包括AI创作、风格改写、文案续写、文案提取、智能问答等功能的运营文案创作助手。
    其中AI创作、风格改写啊,实现过程比较简单,基本上就是把用户的想法,包括主题、观点传给文本大模型即可,通过限定大模型系统提示词,加上外挂电商营销文案知识库,一般大模型会给出符合主题和观点的文案。
    在文案提取功能上遇到了卡点,我们希望能提取视频中的营销文案内容。在实现前简单的理解为文本大模型可以直接识别在线的视频链接,然后给出视频文案。实际上不是的。问了下DeepSeek、元宝、Kimi等都是给出"无法直接访问XX或其他外部链接的内容"。其中百度网页版也能分析出点内容,但是和我们想要的视频文案差距太大。

  • DeepSeek(网页版)
    在这里插入图片描述

  • Baidu(DeepSeek-R1网页版)
    在这里插入图片描述

2、需求说明

2.1 需求说明

    我们希望能提取出视频链接的原始文案,而不是对视频的内容分析,视频的理解,视频的标题等无关内容。
    因为有了原始文案,我们可以对文案进行二创、润色、风格改写等动作。
    同样的我们也能够获取视频分析、视频标题、视频标签等信息。

2.2 数据准备

在这里插入图片描述

  • 三方平台视频链接
// 精选链接:
https://www.douyin.com/jingxuan?modal_id=7359544025726143771
// 详情页链接
https://www.douyin.com/video/7359544025726143771
  • 视频文案如下
贫穷真的跟懒惰有关系吗?江秦这些年一直在思考这个问题。他觉得自己已经足够勤奋了,完全对得起自己的名字。可钱呢?钱到底是被谁给赚走了?小时候,爸妈曾语重心长地告诉他,只要你肯吃苦,就一定会出人头地。但他长大后发现的事实却是,只要你肯吃苦,就一定有吃不完的苦。现在,他的相亲对象要彩礼三十万。江秦,你有没有听我说话?嗯,我一直听着呢。那你怎么一声也不吭?我都说了半天,嗓子都哑了,你也不管。江秦放下水杯,沉默半晌后开口:这婚要不还是别结了吧。女人愣了一下,随即勃然大怒:你这话是什么意思?没什么,就是觉得好累,想回家睡一觉。江秦,你个孬种,怪不得你都三十八了,也没有女人想跟你。江秦不顾女人的咆哮,迈步走出了西餐厅,沿着马路漫无目的地往前走去。走到一个建筑工地的时候,他看到围墙上挂着一条横幅,写着:打工人是人上人。于是,他点上根烟,吧嗒抽了两口后,在在上面烫了个洞。他对那个女人其实没有太多的怨言,甚至觉得她的要求很正常。人家都三十五了,现实一点有什么毛病?他只是在思考一个问题,这样的日子哪一天是个尽头。没打过工的人拼命鼓吹着打工人是人上人,一直在打工的人却什么都不敢说,只能点头承认:啊,对对对。可自己到底哪里像个人上人?这辈子就混了两双爱意还是莆田的,你管这叫人上人?至于爱情,江秦甚至都不知道这东西存不存在。他相过几次亲,见过几个朋友介绍的女孩,无论哪个都可以凑合过,但最悲哀的也是仅限于凑合过。回顾一生,这辈子的遗憾真的太多了。江秦叹了口气,从口袋里摸出电话,想找个朋友陪自己喝点酒,但点菜后却看到了四条短信:一条信用卡催款通知,一条话费欠费预警,一条哥哥我在附近,今天家里没有人,最后一条来自他的直属领导,用语重心长的文字跟他说:最近公司效益不好,希望员工可以自愿降薪,与公司一起共渡难关。江秦瞬间失去了喝酒的心情,继续在施工楼下抽着烟。在这个时代,你想要有钱就绝对不能打工,因为这个社会的资源分配本来就是不公平的。可是一想到自己的年纪,江秦忍不住笑了。三十八了,再去创业有点不现实吧?他这两年腰都累断了,颈椎也出问题了,交叉神经痛比尿频还勤快。拖着这残破的身躯去创业,就算成功也得五十岁了。这人生还有什么可享受的?要是能重来就好了,打什么都别打工,能傍富婆就傍富婆,实在不行就创业,坚信钱没了可以再赚,可良心没了赚的更多。

3、功能实现

3.1 使用视频理解大模型能力

  • 思路
        把视频链接传给大模型,让大模型识别视频文案并输出。

  • 视频理解大模型原理
        对视频文件每隔0.5秒抽取一帧,采用图像理解技术识别每帧图像信息,通过图像分析间接实现视频内容分析的。
        允许设置fps参数控制抽帧频率,高速运动场景比如体育赛事、动作电影适合较高的fps;长视频或内容偏静态视频适合较低的fps。

  • 以通义千问VL模型为例

    • 文件形式
      • 在线视频链接要求:视频链接是公网访问且没有权限拦截的,文件是常见的视频文件格式。否则无法获取视频内容,就无法做视频理解。
      • 支持使用本地文件:通过SDK允许使用本地文件。需要将本地文件编码为Base64格式,或者直接传入本地路径。
    • 文件限制
      • 视频文件大小:Qwen2.5-VL系列模型支持传入的视频大小不超过1 GB,其他模型不超过150MB
      • 视频文件格式: MP4、AVI、MKV、MOV、FLV、WMV 等。
      • 视频时长:Qwen2.5-VL系列模型支持的视频时长为2秒至10分钟,其他模型为2秒至40秒。
  • 使用示例

curl -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
    "model": "qwen-vl-max-latest",
    "messages": [
    {"role": "system", "content": [{"type": "text","text": "You are a helpful assistant."}]},
    {"role": "user","content": [{"type": "video_url","video_url": {"url": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241115/cqqkru/1.mp4"}},
    {"type": "text","text": "这段视频的内容是什么?"}]}]
}'
3.1.1 三方平台视频在线链接解析

    直接拿着三方平台视频链接,因三方平台官方安全策略,大模型无法识别,提示传入的视频文件无效。

// 请求信息
{
    "enable_thinking": false,
    "max_tokens": 4096,
    "messages": [
        {
            "content": [
                {
                    "type": "video_url",            
                    "video_url": {"url": "https://www.douyin.com/video/7359544025726143771"}
                },
                {
                    "type": "text","text": "请提取视频文案"
                }
            ],
            "role": "user"
        }
    ],
    "model": "qwen-vl-max",
    "stream": false,
    "temperature": 0.7
}
// 响应结果
{
    "error": {
        "code": "invalid_parameter_error",
        "param": null,
        "message": "<400> InternalError.Algo.InvalidParameter: Invalid video file.",
        "type": "invalid_request_error"
    },
    "id": "chatcmpl-94d36a39-3322-9fbd-9b8f-2dedc1850d21",
    "request_id": "94d36a39-3322-9fbd-9b8f-2dedc1850d21"
}
3.1.2 三方平台视频内网链接解析

    三方平台视频内网链接是通过三方平台视频详情接口 /aweme/v1/web/aweme/detail 获取的,如下所示,调用用法在下面音频识别模块会介绍。
    获取到三方平台视频内网链接,大模型无法识别,提示链接资源无法下载或下载超时。

// 请求信息
{
    "enable_thinking": false,
    "max_tokens": 4096,
    "messages": [
        {
            "content": [
                {
                    "type": "video_url",            
                    "video_url": {"url": "https://v3-web.douyinvod.com/bf9cca29836d3612cc035943bc6e220c/685a7e6e/video/tos/cn/tos-cn-ve-15/ocQjQeefGBgQLKqB3QIodOIZGCEEbNMAPm7anA/?a=6383&ch=26&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C3&cv=1&br=5029&bt=5029&cs=0&ds=4&ft=AJkeU_TLRR0sTlC42Dv2Nc.xBiGNbLMY4jdU_45JCAxJNv7TGW&mime_type=video_mp4&qs=0&rc=NzM4OTVkaDc4N2gzZjtnOkBpanhuaW05cnhoMzMzNGkzM0BjL2IyLTViXi8xLV5hYWA0YSMzYHJvMmRzbTNhLS1kLS9zcw%3D%3D&btag=80000e00010000&cquery=100B_100x_100z_100o_100w&dy_q=1750750244&feature_id=59cb2766d89ae6284516c6a254e9fb61&l=20250624153044F89E8D93AC3D0D87B3C8"}
                },
                {
                    "type": "text","text": "请提取视频文案"
                }
            ],
            "role": "user"
        }
    ],
    "model": "qwen-vl-max",
    "stream": false,
    "temperature": 0.7
}
// 响应结果
{
    "error": {
        "code": "invalid_parameter_error",
        "param": null,
        "message": "<400> InternalError.Algo.InvalidParameter: Failed to download multimodal content",
        "type": "invalid_request_error"
    },
    "id": "chatcmpl-84b97fed-33ad-9d98-a40a-b893fb64c969",
    "request_id": "84b97fed-33ad-9d98-a40a-b893fb64c969"
}

具体错误信息如下:https://help.aliyun.com/zh/model-studio/error-code

  • 网络原因,请检查您的网络连接是否正常。
  • 该文件的URL为OSS的内网URL。由于OSS内网与阿里云百炼服务不互通,请勿使用OSS内网URL。
  • 提供的图片资源所在的IP地址不在中国内地。
  • 由于网络环境的差异,跨境资源访问可能会受到一定的限制或不稳定因素影响。建议您尽量使用中国内地的资源存储服务,以确保网络连接的稳定性和访问速度。
3.1.3 三方平台视频转存本地服务

    通过以上两种方式,看出三方平台视频链接因三方平台官方的安全策略,无法直接识别。
    可以下载视频到自己服务器上或者下载并转存到cos、oos云服务器上,获取到oss链接再交给大模型进行解析。
    此处就不再演示。

3.2 使用音频识别大模型能力

  • 思路
        获取视频的音频文件,把音频文件链接传给大模型,让大模型识别音频文案并输出。
        支持多种音频(包括说话人语音、自然声音、音乐、歌声)和文本作为输入,并输出文本。不仅能对输入的音频进行转录,还具备更深层次的语义理解、情感分析、音频事件检测、语音聊天等能力。

  • 以通义千问Audio模型为例

    • 文件形式
      • 在线音频链接要求:音频链接是公网访问且没有权限拦截的,文件是常见的音频文件格式。
      • 支持使用本地文件:通过SDK允许使用本地文件。需要传入本地音频的绝对路径。
    • 文件限制
      • 音频文件大小:建议不超过10 MB,超出也是可以解析的
      • 音频文件格式: AMR、WAV(CodecID: GSM_MS)、WAV(PCM)、3GP、3GPP、AAC、MP3等。
      • 音频时长:音频的时长建议不超过30秒,如果超过30秒,模型会自动截取前30秒的音频。实际上解析2min时长的音频也是可以的
      • 音频语言:中文、英语、粤语、法语、意大利语、西班牙语、德语和日语
  • 使用示例

curl -X POST https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
    "model": "qwen-audio-turbo-latest",
    "input":{
        "messages":[
            {
                "role": "system",
                "content": [
                    {"text": "You are a helpful assistant."}
                ]
            },
            {
                "role": "user",
                "content": [
                    {"audio": "https://dashscope.oss-cn-beijing.aliyuncs.com/audios/welcome.mp3"},
                    {"text": "这段音频在说什么?"}
                ]
            }
        ]
    }
}'
3.2.1 三方平台视频在线链接解析

在这里插入图片描述
    通过浏览器访问三方平台视频详情页链接,打开浏览器控制台,可以看到有一个视频详情接口 /aweme/v1/web/aweme/detail 的调用,返回了视频的明细,包括视频链接、音频链接、视频标题、视频标签、ocr识别文案、分享信息等。

    下面我找了一写关键信息贴出来。
    其中我们发现有ocr识别结果 aweme_detail.seo_info.ocr_content,但是文案质量不佳,封面或者视频背景中的特殊字符文案也会识别出来。
    说明走ocr识别方案可能也不是我们想要的视频文案结果。

{
    "aweme_detail": {
        "caption": "#重生文 #AIGC #错哪儿了 都重生了谁谈恋爱啊",
        "desc": "#重生文 #AIGC #错哪儿了 都重生了谁谈恋爱啊",
        "music": {
            "play_url": {
                "height": 720,
                "uri": "https://sf3-cdn-tos.douyinstatic.com/obj/ies-music/7359544061390326578.mp3",
                "url_key": "7359544062350822195",
                "url_list": [
                    "https://sf3-cdn-tos.douyinstatic.com/obj/ies-music/7359544061390326578.mp3",
                    "https://sf6-cdn-tos.douyinstatic.com/obj/ies-music/7359544061390326578.mp3"
                ],
                "width": 720
            }
        },
        "preview_title": "#重生文 #AIGC #错哪儿了 都重生了谁谈恋爱啊",
        "seo_info": {
            "ocr_content": "起点读书 谁谈恋爱啊 吗 钱到底是被谁给赚走了 地告诉他 出人头地 却是 只要你肯吃苦就一定有 吃不完的苦 现在 十万 ”“嗯 我都说了半天 嗓子都哑了你也不管 沉默半晌后开口:“这 婚 aui 话是什么意思”“ E R4C 想回家睡一觉”“江勤 你个孬种 前走去 1001010 沿着马路漫无目的地往 候 横幅 吧嗒抽了两口后在上面 烫了个洞 太多的怨言 常 人家都三十五了 尽头 着 写着打工人是人上人 打工人是人上人 啊对对对 上人 还是莆田的 至于爱情 西存不存在 见过几个朋友介绍的女 孩 凑合过 回顾一生 这辈子的遗憾真的太多 奋了 酒 但点开后却看到了四条 短信 领导 说 有 心情 i 配 可是一想到自己的年纪 可是一想到自己的年纪 江勤瞬间失去了喝酒的 江勤忍不住笑了 不现实吧 乐 他只是在思考一个问题 交叉神经痛比尿频还勤 快 业 的 打工 打什么都不打工 都不敢说 该内容引用AI能力生产 可良心没了赚的更多"
        },
        "share_info": {
            "share_desc": "在XX,记录美好生活",
            "share_desc_info": "#在XX,记录美好生活##重生文 #AIGC #错哪儿了 都重生了谁谈恋爱啊",
            "share_link_desc": "3.02 Q@X.zT 03/05 mqE:/ # 重生文 # AIGC # 错哪儿了 都重生了谁谈恋爱啊  %s 复制此链接,打开Dou音搜索,直接观看视频!",
            "share_url": "https://www.iesdouyin.com/share/video/7359544025726143771/?region=CN\u0026mid=7359544062350822195\u0026u_code=353j2f7d77mc\u0026did=MS4wLjABAAAAqHqcov8rpj4LVZ8iF07s0MNfkPzs3ytKRY7R1ioqIPBXNmjExJVCCn98dkv4GFyX\u0026iid=MS4wLjABAAAANwkJuWIRFOzg5uCpDRpMj4OX-QryoDgn-yYlXQnRwQQ\u0026with_sec_did=1\u0026video_share_track_ver=\u0026titleType=title\u0026share_sign=IbKCVSSsLU6Xp_9LStddXTQcO8hqW30VeXjQ_PnB16A-\u0026share_version=190500\u0026ts=1750821377\u0026from_aid=6383\u0026from_ssr=1"
        },
        "text_extra": [
            {
                "caption_end": 4,
                "caption_start": 0,
                "end": 4,
                "hashtag_id": "1608221011432477",
                "hashtag_name": "重生文",
                "is_commerce": false,
                "start": 0,
                "type": 1
            },
            {
                "caption_end": 10,
                "caption_start": 5,
                "end": 10,
                "hashtag_id": "1722895911293971",
                "hashtag_name": "aigc",
                "is_commerce": false,
                "start": 5,
                "type": 1
            },
            {
                "caption_end": 16,
                "caption_start": 11,
                "end": 16,
                "hashtag_id": "1631690698606605",
                "hashtag_name": "错哪儿了",
                "is_commerce": false,
                "start": 11,
                "type": 1
            }
        ],
        "video": {
            "play_addr": {
                "data_size": 10329912,
                "file_cs": "c:0-172367-aa4c",
                "file_hash": "39d7feda1c7822ee2a3b4f3b3ce18240",
                "height": 960,
                "uri": "v0200fg10000coh5k7rc77u15ncs6cag",
                "url_key": "v0200fg10000coh5k7rc77u15ncs6cag_h264_540p_403786",
                "url_list": [
                    "https://v26-web.douyinvod.com/7bd3b24c1e6ca49a04eb7661c65ff0bd/685b94fd/video/tos/cn/tos-cn-ve-0015c800/oczi2CcPEBDEnIhCmteWA1BAb07VSgMdQLAJfz/?a=6383\u0026ch=26\u0026cr=3\u0026dr=0\u0026lr=all\u0026cd=0%7C0%7C0%7C3\u0026cv=1\u0026br=394\u0026bt=394\u0026cs=0\u0026ds=6\u0026ft=AJkeU_TLRR0sTlC42Dv2Nc.xBiGNbLjf~jdU_45JCAxJNv7TGW\u0026mime_type=video_mp4\u0026qs=0\u0026rc=ZDs2Mzk7Zmg1PGc0Njo1OUBpamZ2cTQ6Zjo4cjMzNGkzM0BhMDEzLS9eNTYxYmExXl8wYSNeM2AycjRvaGVgLS1kLS9zcw%3D%3D\u0026btag=80000e00028000\u0026dy_q=1750821377\u0026feature_id=f0150a16a324336cda5d6dd0b69ed299\u0026l=202506251116174010AE3B891530B28EB0",
                    "https://v3-web.douyinvod.com/ae0626098d54eda9e992e48653624ea3/685b94fd/video/tos/cn/tos-cn-ve-0015c800/oczi2CcPEBDEnIhCmteWA1BAb07VSgMdQLAJfz/?a=6383\u0026ch=26\u0026cr=3\u0026dr=0\u0026lr=all\u0026cd=0%7C0%7C0%7C3\u0026cv=1\u0026br=394\u0026bt=394\u0026cs=0\u0026ds=6\u0026ft=AJkeU_TLRR0sTlC42Dv2Nc.xBiGNbLjf~jdU_45JCAxJNv7TGW\u0026mime_type=video_mp4\u0026qs=0\u0026rc=ZDs2Mzk7Zmg1PGc0Njo1OUBpamZ2cTQ6Zjo4cjMzNGkzM0BhMDEzLS9eNTYxYmExXl8wYSNeM2AycjRvaGVgLS1kLS9zcw%3D%3D\u0026btag=80000e00028000\u0026dy_q=1750821377\u0026feature_id=f0150a16a324336cda5d6dd0b69ed299\u0026l=202506251116174010AE3B891530B28EB0",
                    "https://www.douyin.com/aweme/v1/play/?video_id=v0200fg10000coh5k7rc77u15ncs6cag\u0026line=0\u0026file_id=36b2a743d1ed49928eac2806604338da\u0026sign=39d7feda1c7822ee2a3b4f3b3ce18240\u0026is_play_url=1\u0026source=PackSourceEnum_AWEME_DETAIL"
                ],
                "width": 544
            },
            "ratio": "540p",
            "video_model": "",
            "width": 544
        },
        "video_tag": [
            {
                "level": 1,
                "tag_id": 2014,
                "tag_name": "二次元"
            },
            {
                "level": 2,
                "tag_id": 2014002,
                "tag_name": "二次元内容"
            },
            {
                "level": 3,
                "tag_id": 2014002001,
                "tag_name": "动漫IP"
            }
        ]
}
3.2.2 三方平台视频详情接口说明

    三方平台视频详情接口 /aweme/v1/web/aweme/detail,该接口目前可通过以下两个域名访问。

  • 第一个域名是我们访问三方平台视频详情页链接,在浏览器控制台获取到的
    https://www-hj.douyin.com/aweme/v1/web/aweme/detail/

在这里插入图片描述

  • 第二个域名是我们通过访问iframe嵌套的
    https://www.douyin.com/aweme/v1/web/aweme/detail/

在这里插入图片描述

  • 通过VideoID获取IFrame代码

https://developer.open-douyin.com/docs/resource/zh-CN/dop/develop/openapi/video-management/douyin/iframe-player/get-iframe-by-video
请求示例:

curl --location --request GET 'https://open.douyin.com/api/douyin/v1/video/get_iframe_by_video?video_id=7359544025726143771'

响应结果:

{
    "log_id" : "202506250953238DD93A552BB7E8653C75",
    "err_msg" : "",
    "err_no" : 0,
    "data" : {
        "iframe_code" : "<iframe width=\"544\" height=\"960\" frameborder=\"0\" src=\"https://open.douyin.com/player/video?vid=7359544025726143771&autoplay=0\" referrerpolicy=\"unsafe-url\" allowfullscreen></iframe>",
        "video_height" : 960,
        "video_title" : "#重生文 #AIGC #错哪儿了 都重生了谁谈恋爱啊",
        "video_width" : 544
    }
}
3.2.3 通过三方平台视频详情获取音频链接
  • 请求头配置

    此处使用https://www.douyin.com/aweme/v1/web/aweme/detail/域名接口,注意该接口的请求头需要配置

Origin: https://open.douyin.com
Referer: https://open.douyin.com

    如果使用https://www-hj.douyin.com/aweme/v1/web/aweme/detail/域名接口,注意该接口的请求头需要配置

Origin: https://www.douyin.com
Referer: https://www.douyin.com
  • 请求参数说明
        此处使用https://www.douyin.com/aweme/v1/web/aweme/detail/
        核心参数aweme_id传视频ID
        核心参数aid传固定值,用浏览器控制台抓取到的那个值就可以
        其他参数msToken、X-Bogus、_signature是三方平台官方安全策略参数,用官方的js文件可以找到加密方法,此处固定用浏览器控制台抓取到的值也可以。
  • DyDetailVO.java
        请求结果VO封装
package com.adtool.platform.controller.vo.wenan;

import lombok.Data;

import java.util.List;

/**
 * @className: DyDetailVO 
 * @description: 三方平台视频明细
 * @author: author
 * @date: 2025/6/24 18:24
 **/
@Data
public class DyDetailVO {
    private AwemeDetail aweme_detail;
    private LogPb log_pb;
    private Integer status_code;

    @Data
    public static class LogPb {
        private String impr_id;
    }
    @Data
    public static class AwemeDetail {
        /** 视频描述 */
        private String desc;
        private Double duration;
        /** 视频标题 */
        private String item_title;
        /** 音乐信息 */
        private Music music;
        /** 预览标题 */
        private String preview_title;
        /** seo信息 */
        private SeoInfo seo_info;
        /** 标题标签 */
        private List<TextExtra> text_extra;

        private Video video;

        /** 视频标签 */
        private List<VideoTag> video_tag;

        @Data
        public static class Music {
            /** 音乐名称 */
            private PlayUrl play_url;

            @Data
            public static class PlayUrl {
                /** 音频地址 */
                private String uri;
            }
        }


        @Data
        public static class SeoInfo {
            /** ocr_content */
            private String ocr_content;
        }
        @Data
        public static class TextExtra {
            /** 标签名称 */
            private String hashtag_name;
        }
        @Data
        public static class Video {
            /** 格式 */
            private String format;
            /** 分辨率 */
            private String ratio;

            private PlayAddr play_addr;

            @Data
            public static class PlayAddr {
                /**
                 * 播放地址,取list.get(0)
                 * 对帧率有要求可以取其他值
                 * play_addr
                 * play_addr_265
                 * play_addr_h264
                 */
                private List<String> url_list;
            }

        }

        @Data
        public static class VideoTag {
            /** 标签名称 */
            private String tag_name;
        }

    }

}
  • 获取三方平台视频明细
        请求过程是crul调用,这里就不写了。
@Value("${dy.video.detail:https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id={{videoId}}&aid=6383&msToken=KbscjTT6O_4LM5GZSm6ulplDk6kFy5lvsIznwgVdhWUng75b2NqTLC4lnKwENN1uiW52Ub2Q1P3yUS6GL9EUSNIDSqgiH7k5uGiDGYvrjt9YgpYRJKvqQw==&X-Bogus=DFSzswVOW7iANrWECCcE6QTQh4SC&_signature=_02B4Z6wo00001qQHOyAAAIDDe1zIbmL9oZKkBz-AAMFn8sgvQzXK4sUcPGou9UVpawJQumggtRNzYPluyC5lYZv1kLaElZP-aNIeKLcgp7x7moMOtAYAC6ggXbhscNqWEePdp0-JKSpODhxd6e}")
private String getDyAwemeDetail;

public void videoLinkAnalysis(String videoId) {
    String url = getDyAwemeDetail.replace(Constants.VIDEO_ID, videoId);
    Map<String, String> headers = new HashMap<>(2);
    headers.put("Origin".intern(), "https://open.douyin.com".intern());
    headers.put("Referer".intern(), "https://open.douyin.com".intern());
    Response response = httpClientService.buildResponseGet(url, videoId, headers);
    if (response.isSuccessful()) {
        try {
            dyDetailVO = JSON.parseObject(response.body().string(), DyDetailVO.class);
        } catch (IOException e) {
            log.error("获取视频明细失败,请稍后重试!", e);
            throw new RuntimeException("获取视频明细失败,请稍后重试!");
        }
    }
    String musicUrl = dyDetailVO.getAweme_detail().getMusic().getPlay_url().getUri();
    // TODO 获取到音频链接 ...
}
3.2.4 通过音频链接获取视频文案

    此处以Crul形式调用通义千问Audio模型。
    实现效果如下:
在这里插入图片描述

    下面是部分实现代码。

  • OpenAIApiService.java
    大模型请求实现类核心方法,流式解析大模型识别结果。
private static final String STREAM_MESSAGE_PREFIX_NO_EMPTY = "data:";
private static final String STOP = "stop";

@Resource
private ExecutorService chatRequestExecutor;
@Resource
private ChatEngineRetryService chatEngineRetryService;
/**
 * @param: chatInput
 * @param: emitter
 * @param: openaiUrl
 * @param: key
 * data:{"output":{"choices":[{"message":{"content":[{"text":"音频"}],"role":"assistant"},"finish_reason":"null"}]},"usage":{"audio_tokens":754,"input_tokens":785,"output_tokens":1},"request_id":"972092ff-3184-9aad-bfc0-8aa2a34a4f25"}
 * data:{"output":{"choices":[{"message":{"content":[],"role":"assistant"},"finish_reason":"stop"}]},"usage":{"audio_tokens":754,"input_tokens":785,"output_tokens":236},"request_id":"2537f174-7767-9680-91fd-73230ff14cd4"}
 * @return: void
 * @author: 音频大模型流式接口
 * @date: 2025/6/24
 */
@Override
public void streamIncrementalApi(ChatInput<String> chatInput, SseEmitter emitter, String openaiUrl, String key) {
    chatRequestExecutor.execute(() -> {
        long start = System.currentTimeMillis();
        try (Response response = buildResponse(chatInput, openaiUrl, key)) {
            log.info("API耗时响应: {}s", (System.currentTimeMillis()-start)/1000);
            try (InputStream is = response.body().byteStream();
                 InputStreamReader isr = new InputStreamReader(is);
                 BufferedReader bufferedReader = new BufferedReader(isr)) {
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    log.info("res stream:{}", line);
                    if (!line.contains(STREAM_MESSAGE_PREFIX_NO_EMPTY)){
                        continue;
                    }
                    String messageJsonStr = line.substring(line.indexOf(STREAM_MESSAGE_PREFIX_NO_EMPTY) + STREAM_MESSAGE_PREFIX_NO_EMPTY.length());
                    OpenAIRes res = JSONUtil.toBean(messageJsonStr, OpenAIRes.class);
                    Choice choice = res.getOutput().getChoices().get(0);
                    if (STOP.equals(choice.getFinish_reason())) {
                        emitter.send(new GptStreamDto(1, StringUtils.EMPTY, false, true));
                        continue;
                    }

                    List<MessageContent> content = JSONUtil.toList((JSONArray) choice.getMessage().getContent(), MessageContent.class);
                    if (!CollectionUtils.isEmpty(content)) {
                        emitter.send(new GptStreamDto(1, content.get(0).getText(), false, false));
                    }
                }
            }
        } catch (Exception e) {
            log.error("流处理异常", e);
        } finally {
            log.info("API耗时流式对话: {}s", (System.currentTimeMillis()-start)/1000);
            emitter.complete();
        }
    });
}
/**
 * @param: chatInput
 * @param: openaiUrl
 * @param: key
 * @description: 构建OkClient请求结果
 * @return: okhttp3.Response
 * @author: niaonao
 * @date: 2025/6/25
 */
@Override
public Response buildResponse(Object chatInput, String openaiUrl, String key) {
    String body = com.alibaba.fastjson.JSON.toJSONString(chatInput);
    RequestBody requestBody = RequestBody.create(body, JSON);
    /**
     * 如果服务端接口本身不支持流式响应(SSE),即使客户端设置 X-DashScope-SSE: enable,服务端会忽略该头部并按默认非流式模式返回数据。
     * 主流API设计规范中,非流式接口会直接丢弃无关的流式控制头部,不会引发错误。
     */
    Request requestOpenai = new Request.Builder()
            .url(openaiUrl)
            .post(requestBody)
            .addHeader("Authorization", "Bearer " + key)
            .addHeader("api-key", key)
            .addHeader("X-DashScope-SSE", "enable")
            .build();

    log.info("url:{}", openaiUrl);
    log.info("requestBody:{}", body);
    Response response = chatEngineRetryService.execute(okClient, requestOpenai);
    return response;
}
  • ExecutorServiceConfig.java
    线程池配置类
import com.alibaba.ttl.threadpool.TtlExecutors;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 业务配置。
 */
@Configuration
@Data
public class ExecutorServiceConfig {

    /** 业务线程池核心线程数 */
    @Value("${chat.remote.request.pool.corePoolSize:25}")
    private int businessCorePoolSize;
    /** 业务线程池最大线程数 */
    @Value("${chat.remote.request.pool.maxPoolSize:50}")
    private int businessMaxPoolSize;
    /** 业务线程池最大空闲秒数 */
    @Value("${chat.remote.request.pool.keepAliveSeconds:60}")
    private int businessKeepAliveSeconds;
    /** 业务线程池任务队列长度 */
    @Value("${chat.remote.request.pool.taskQueueSize:1000}")
    private int businessTaskQueueSize;

    @Bean(name = "chatRequestExecutor", destroyMethod = "shutdown")
    public ExecutorService chatRequestExecutor() {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(businessCorePoolSize, businessMaxPoolSize,
                businessKeepAliveSeconds, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(businessTaskQueueSize), new ThreadPoolExecutor.CallerRunsPolicy());
        return TtlExecutors.getTtlExecutorService(threadPoolExecutor);
    }

}
  • ChatEngineRetryService.java、ChatEngineRetryServiceImpl.java
    OkClient调用封装接口
// 接口
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public interface ChatEngineRetryService {
    Response execute(OkHttpClient client, Request requestOpenai) throws RuntimeException;
}

OkClient调用封装实现类

import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.stereotype.Service;


/**
 * 可增加重试机制
 */
@Slf4j
@Service
public class ChatEngineRetryServiceImpl implements ChatEngineRetryService {
    /**
     * @param: client
     * @param: requestOpenai
     * @description: 封装okClient
     * @return: okhttp3.Response
     * @author: niaonao
     * @date: 2025/6/25
     */
    @Override
    public Response execute(OkHttpClient client, Request requestOpenai) throws RuntimeException{
        try {
            Response response = client.newCall(requestOpenai).execute();
            log.info("engine res:{}", response.toString());

            if (null != response && response.code() != 200) {
                throw new RuntimeException("大模型引擎失败,错误码不是200");
            }
            return response;
        }catch (Exception e){
            log.error("调用引擎异常", e);
            throw new RuntimeException("大模型引擎失败");
        }
    }
}
  • ChatInput.java
    大模型请求体封装类
import lombok.Data;

import java.io.Serializable;
import java.util.List;

@Data
public class ChatInput<T> implements Serializable {
    private List<ChatMessage<T>> messages;

    private Boolean stream;
    private String system;
    private Double temperature;
    private String model;
    private String stop;
    private Integer max_tokens;
    private ChatResponseFormat response_format;
    /** 联网搜索 */
    private Boolean enable_search;
    /** 深度思考-阿里云百炼 */
    private Boolean enable_thinking;

    /** 音频大模型-阿里云百炼 */
    private ChatInputMessage input;
    /** 音频大模型-流式输出-阿里云百炼 */
    private ChatParameters parameters;
}
  • ChatMessage.java
    大模型请求体message封装类
import lombok.Data;

import java.io.Serializable;

@Data
public class ChatMessage<T> implements Serializable {
    private String role;
    private T content;
}
  • ChatInputMessage.java
    音频大模型请求体messages封装
import lombok.Data;

import java.io.Serializable;
import java.util.List;

@Data
public class ChatInputMessage<T> implements Serializable {
    private List<ChatMessage<T>> messages;
}
  • ChatParameters.java
    音频大模型开启流式接口的属性
import lombok.Data;

import java.io.Serializable;

@Data
public class ChatParameters implements Serializable {
    private Boolean incremental_output;
}
  • 调用音频大模型方法
@Value("${aliyuncs.audioModel:qwen-audio-turbo-latest}")
private String audioModel;

@Value("${aliyuncs.audioUrl:https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation}")
private String apiAudioUrl;

@Value("${aliyuncs.key:sk-xxx}")
private String apiKey;

    /**
     * @param: videoContentDTO
     * @param: emitter
     * @description: 视频文案提取,传入的是三方平台视频链接,提取视频文案
     * @return: void 流式接口,通过SSE和前端交互
     * @author: niaonao
     * @date: 2025/6/24
     */
@Override
public void videoLinkAnalysis(VideoContentDTO videoContentDTO, SseEmitter emitter) {
    // ...
    // 此处获取到音频链接
    String musicUrl = dyDetailVO.getAweme_detail().getMusic().getPlay_url().getUri();
    String userPrompt = "请提取音频文案";
    
    Map<String, String> contentAudio = new HashMap<>(1);
    contentAudio.put("audio".intern(), musicUrl);
    Map<String, String> contentText = new HashMap<>(1);
    contentText.put("text".intern(), userPrompt);
    
    List<Map<String, String>> contentList = new ArrayList<>(2);
    contentList.add(contentAudio);
    contentList.add(contentText);
    
    ChatMessage<List<Map<String, String>>> chatMessage = new ChatMessage<List<Map<String, String>>>();
    chatMessage.setRole(PromptConstants.ROLE_USER);
    chatMessage.setContent(contentList);
    
    List<ChatMessage<List<Map<String, String>>>> messages = new ArrayList<>(1);
    messages.add(chatMessage);
    ChatInputMessage chatInputMessage = new ChatInputMessage();
    chatInputMessage.setMessages(messages);
    
    ChatInput<List<Map<String, String>>> chatInput = new ChatInput<List<Map<String, String>>>();
    chatInput.setInput(chatInputMessage);
    chatInput.setModel(audioModel);
    ChatParameters parameters = new ChatParameters();
    parameters.setIncremental_output(true);
    chatInput.setParameters(parameters);
    openAIApiService.streamIncrementalApi(chatInput, emitter, apiAudioUrl, apiKey);
}

参考文档
大模型服务平台百炼-视觉理解
大模型服务平台百炼-音频理解
三方平台开放平台-通过VideoID获取IFrame代码
Powered By niaonao

本文已生成可运行项目
标题SpringBoot智能在线预约挂号系统研究AI更换标题第1章引言介绍智能在线预约挂号系统的研究背景、意义、国内外研究现状及论文创新点。1.1研究背景意义阐述智能在线预约挂号系统对提升医疗服务效率的重要性。1.2国内外研究现状分析国内外智能在线预约挂号系统的研究应用情况。1.3研究方法及创新点概述本文采用的技术路线、研究方法及主要创新点。第2章相关理论总结智能在线预约挂号系统相关理论,包括系统架构、开发技术等。2.1系统架构设计理论介绍系统架构设计的基本原则和常用方法。2.2SpringBoot开发框架理论阐述SpringBoot框架的特点、优势及其在系统开发中的应用。2.3数据库设计管理理论介绍数据库设计原则、数据模型及数据库管理系统。2.4网络安全数据保护理论讨论网络安全威胁、数据保护技术及其在系统中的应用。第3章SpringBoot智能在线预约挂号系统设计详细介绍系统的设计方案,包括功能模块划分、数据库设计等。3.1系统功能模块设计划分系统功能模块,如用户管理、挂号管理、医生排班等。3.2数据库设计实现设计数据库表结构,确定字段类型、主键及外键关系。3.3用户界面设计设计用户友好的界面,提升用户体验。3.4系统安全设计阐述系统安全策略,包括用户认证、数据加密等。第4章系统实现测试介绍系统的实现过程,包括编码、测试及优化等。4.1系统编码实现采用SpringBoot框架进行系统编码实现。4.2系统测试方法介绍系统测试的方法、步骤及测试用例设计。4.3系统性能测试分析对系统进行性能测试,分析测试结果并提出优化建议。4.4系统优化改进根据测试结果对系统进行优化和改进,提升系统性能。第5章研究结果呈现系统实现后的效果,包括功能实现、性能提升等。5.1系统功能实现效果展示系统各功能模块的实现效果,如挂号成功界面等。5.2系统性能提升效果对比优化前后的系统性能
在金融行业中,对信用风险的判断是核心环节之一,其结果对机构的信贷政策和风险控制策略有直接影响。本文将围绕如何借助机器学习方法,尤其是Sklearn工具包,建立用于判断信用状况的预测系统。文中将涵盖逻辑回归、支持向量机等常见方法,并通过实际操作流程进行说明。 一、机器学习基本概念 机器学习属于人工智能的子领域,其基本理念是通过数据自动学习规律,而非依赖人工设定规则。在信贷分析中,该技术可用于挖掘历史数据中的潜在规律,进而对未来的信用表现进行预测。 二、Sklearn工具包概述 Sklearn(Scikit-learn)是Python语言中广泛使用的机器学习模块,提供多种数据处理和建模功能。它简化了数据清洗、特征提取、模型构建、验证优化等流程,是数据科学项目中的常用工具。 三、逻辑回归模型 逻辑回归是一种常用于分类任务的线性模型,特别适用于二类题。在信用评估中,该模型可用于判断借款人是否可能违约。其通过逻辑函数将输出映射为0到1之间的概率值,从而表示违约的可能性。 四、支持向量机模型 支持向量机是一种用于监督学习的算法,适用于数据维度高、样本量小的情况。在信用分析中,该方法能够通过寻找最佳分割面,区分违约非违约客户。通过选用不同核函数,可应对复杂的非线性关系,提升预测精度。 五、数据预处理步骤 在建模前,需对原始数据进行清理转换,包括处理缺失值、识别异常点、标准化数值、筛选有效特征等。对于信用评分,常见的输入变量包括收入水平、负债比例、信用历史记录、职业稳定性等。预处理有助于减少噪声干扰,增强模型的适应性。 六、模型构建验证 借助Sklearn,可以将数据集划分为训练集和测试集,并通过交叉验证调整参数以提升模型性能。常用评估指标包括准确率、召回率、F1值以及AUC-ROC曲线。在处理不平衡数据时,更应关注模型的召回率特异性。 七、集成学习方法 为提升模型预测能力,可采用集成策略,如结合多个模型的预测结果。这有助于降低单一模型的偏差方差,增强整体预测的稳定性准确性。 综上,基于机器学习的信用评估系统可通过Sklearn中的多种算法,结合合理的数据处理模型优化,实现对借款人信用状况的精准判断。在实际应用中,需持续调整模型以适应市场变化,保障预测结果的长期有效性。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

niaonao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值