feat: 添加数字人服务及相关部署文件

This commit is contained in:
2026-04-22 16:26:07 +08:00
parent 69d948de88
commit abd4c3ce77
5 changed files with 624 additions and 228 deletions

176
downloads/digital-human.py Normal file
View File

@@ -0,0 +1,176 @@
import json
import redis
import uuid
import requests
from fastapi.responses import StreamingResponse
from fastapi import FastAPI, Query, Request
from pydantic import BaseModel
from typing import Optional, List
app = FastAPI()
# 连接到Redis服务器(默认端口是6379)
redis_instance = redis.Redis(host='localhost', port=6379, db=0)
mock_time_created = 20230512084843
mock_id1 = "131424542124"
mock_id2 = "1231414141541252525"
mock_user = "abc-123"
dify_url = "http://<your dify ip>/v1/chat-messages"
dify_api_key = 'Bearer <your dify api key>'
class Message(BaseModel):
content: str
class Resquest_Body(BaseModel):
messages: List[Message] = []
app_id: str
user: str
session_id: str
is_stream: bool
# 对接dify,非流式传输
def request_dify_api(question, request, conversation_id):
# 目标URL
url = dify_url
headers_json = {
'Content-Type': 'application/json',
'Authorization': dify_api_key
}
# 请求的数据
data = {
"inputs": {},
"query": question,
"response_mode": "blocking",
"user": mock_user
}
# 发送POST请求
response = requests.post(url, data=json.dumps(data), headers=headers_json)
data = json.loads(response)
return data["answer"]
# 对接dify,流式传输
async def generate_streaming_messages(question, request, session_id, conversation_id):
url = dify_url
headers_json = {
'Content-Type': 'application/json',
'Authorization': dify_api_key
}
# 请求的数据
request_data = {
"inputs": {},
"query": question,
"conversation_id": conversation_id,
"response_mode": "streaming",
"user": mock_user
}
r = requests.post(url, json=request_data, headers=headers_json, verify=False, stream=True)
if r.status_code != 200:
raise Exception(r.text)
data = ""
# 在这里,我们使用了 iter_lines 方法来逐行读取响应体
for line in r.iter_lines():
if await request.is_disconnected():
# 客户端已主动断开,中止流式输出,避免不必要的问题,打印报错信息方便定位
print("# 客户端已主动断开,中止流式输出")
break
# 将每一行解码为字符串
line = line.decode('utf-8').strip()
# 接下来我们要处理三种不同的情况:
# 1. 如果当前行是空行,则表明前一个数据块已接收完毕(即前文提到的,通过两个换行符结束数据块传输),我们可以对该数据块进行反序列化,并打印出对应的 content 内容;
# 2. 如果当前行为非空行,且以 data: 开头,则表明这是一个数据块传输的开始,我们去除 data: 前缀后,首先判断是否是结束符 [DONE],如果不是,将数据内容保存到 data 变量;
# 3. 如果当前行为非空行,但不以 data: 开头,则表明当前行仍然归属上一个正在传输的数据块,我们将当前行的内容追加到 data 变量尾部;
if len(line) == 0:
if data:
try:
chunk = json.loads(data)
except:
continue
if chunk["event"] != "message":
continue
# 如果redis里未缓存该session_id,则使用reids关联session_id和conversation_id,保证后续实现多轮对话
if not redis_instance.exists(session_id):
conversiation_id = str(chunk["conversation_id"])
redis_instance.set(session_id, conversiation_id)
# 设置redis中key(session_id)的超时时间,一天换算86400s
redis_instance.expire(session_id, 86400)
content = chunk["answer"]
new_data = {
"id": mock_id2,
"created": mock_time_created,
"choices": [{
"message": {
"content": content
}
}]
}
yield f"data: {json.dumps(new_data, ensure_ascii=False)}\n\n"
data = "" # 重置 data
elif line.startswith("data: "):
data = line.lstrip("data: ")
# 当数据块内容为 [DONE] 时,则表明所有数据块已发送完毕,可断开网络连接
if data == "[DONE]":
break
else:
# 仍然在追加内容时,为其添加一个换行符
data = data + "\n" + line
# 发送结束标记
yield "data: [DONE]\n\n"
@app.post("/digital-human")
async def digital_human(body: Resquest_Body, request: Request):
question = body.messages[0].content
session_id = body.session_id
conversation_id = ""
# 如果redis_instance里有缓存session_id,则说明为多轮对话,获取对应的dify中的多轮对话关键参数conversation_id
if redis_instance.exists(session_id):
# decode()用于将bytes转换为str,因为redis_instance存储的是bytes类型的数据。
conversation_id = redis_instance.get(session_id).decode("utf-8")
# 选择流式传输
if body.is_stream:
return StreamingResponse(
generate_streaming_messages(question, request, session_id, conversation_id),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
}
)
# 选择非流式传输
answer = request_dify_api(question, request, conversation_id)
choices = [{
"index": 0,
"message": {
"content": answer
}
}]
return {
"id": mock_id1,
"created": mock_time_created,
"choices": choices
}
# 启动应用
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)

View File

@@ -0,0 +1,11 @@
[Unit]
Description=Digital Human Service
After=network.target
[Service]
Type=simple
ExecStart=nohup python3 /digital-human.py &
Restart=on-failure
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,33 @@
#!/bin/bash
# 回到根路径
cd /
rm -rf /digital-human.rar
rm -rf /digital-human.py
rm -rf /etc/systemd/system/digital-human.service
# apt安装rar和redis-server
sudo apt update
sudo apt install rar redis-server
#启动redis-server,并设置开机自启动 redis-server
sudo systemctl enable redis-server
sudo systemctl start redis-server
# pip安装所需的包
pip install redis fastapi uvicorn -i https://pypi.tuna.tsinghua.edu.cn/simple
# 从obs桶获取转发服务代码
wget https://documentation-samples.obs.cn-north-4.myhuaweicloud.com/solution-as-code-publicbucket/solution-as-code-moudle/building-a-dify-llm-application-development-platform/human/digital-human.rar
rar x digital-human.rar
# 替换dify对应的url和api key
sed -i "s/<your dify ip>/$1/g" digital-human.py
sed -i "s/<your dify api key>/$2/g" digital-human.py
#启动对接数字人的python服务,并设置开机自启动
mv digital-human.service /etc/systemd/system/digital-human.service
sudo systemctl daemon-reload
sudo systemctl enable digital-human.service
sudo systemctl start digital-human.service