Celery+fastapi+redis做多任务AI模拟角色进行文件对话

新星杯·14天创作挑战营·第17期 10w+人浏览 488人参与

目录

前言

环境安装

实现思路

代码实现

测试结果演示


前言

本篇章节适用于多任务的服务器模拟操作,通过使用fastapi和celery框架实现分布式请求,关于ai模拟操作则是通过设定AI角色样式来进行模拟真人进行内容的回答。

本篇章只会涉及到两个模型,都是国内模型。

全模态模型:qwen3-omin-flash

聊天模型:qwen3-max

可以的话先去申请相关的api-key来获取模型请求,防止项目无法操作。

环境安装

python 3.9+< ,>3.12 即需要python环境处于3.9+以上的运行环境,并处于3.12以下的运行环境

安装的模块包

pip install openai #openai模块

pip install uvicorn 
pip install fastapi #安装fastapi框架

pip install celery #安装celery分布式框架
pip install redis #安装redis服务支持

pip install mariktdown #安装mariktdown文件识别框架

大抵就是这些,那么接下来就是对文件的简单处理了,与内容生成了。

实现思路

文字解析一下吧,就是当文件上传请求创建的时候进行task创建,然后进行文档解析,解析出来的文本拼接promt统一请求ai,然后让ai模拟角色进行内容的输出。

代码实现

首先要理解,核心代码就是md的使用,就是图片文件的解析,和提示词的拼接,其实就没什么内容了。

也就是官方文档这几句话,使用大语言模型的视觉能力进行文档内容的解析。

from markitdown import MarkItDown
from openai import OpenAI

client = OpenAI()
md = MarkItDown(llm_client=client, llm_model="gpt-4o", llm_prompt="optional custom prompt")
result = md.convert("example.jpg")
print(result.text_content)

创建一个类进行fastapi,openai的设置还有对话请求的设置。

这里就不过多赘述了

import os

import celery
from dotenv import load_dotenv


class Setting_config:

    def openai_config(self):
        """
        该函数主要是用来创建openai对象
        :return:
        """
        from openai import OpenAI
        from dotenv import load_dotenv
        import os
        load_dotenv()
        return OpenAI(
            api_key= os.getenv("OPENAI_API_KEY"),
            base_url= os.getenv("OPENAI_API_BASE_URL"),
            max_retries= 3,
            timeout= 60,
        )

    def fastapi_config(self):
        """
        该函数是用来创建fastapi对象的
        并且设置了跨域请求的中间键
        :return:
        """
        from fastapi import FastAPI
        from fastapi.middleware.cors import CORSMiddleware

        # 创建FastAPI实例
        app = FastAPI()

        # 设置跨域cors
        app.add_middleware(
            CORSMiddleware,
            allow_origins=["*"],
            allow_credentials=True,
            allow_methods=["*"],
            allow_headers=["*"],
        )

        return app



    def file_extension_suffix(self,file_path):
        """
        该函数主要是用来获取文件的后缀名
        :param file_path:
        :return:
        """
        import os
        return os.path.split(file_path)[1].split( ".")[1]

    def file_read(self,file_path):
        """
        该函数主要是用来处理文件解析的
        :param file_path:
        :return:
        """
        from markitdown import MarkItDown
        md  = MarkItDown()
        text = ""
        try:
            suffix = self.file_extension_suffix(file_path)
            if suffix in ["md","doc","docx","pdf","xsxl","txt","text"]:
                result = md.convert(file_path)
                res_text = result.text_content
                for line in res_text.split("\n"):
                    text += line + "\n"

                return  text
            elif suffix in ["png","jpg","jpeg"]:

                import os
                from dotenv import load_dotenv
                load_dotenv()

                client = self.openai_config()
                md = MarkItDown(llm_client=client, llm_model=os.getenv("OPENAI_AI_MODEL"),
                                llm_prompt="请描述该图片的内容")
                result = md.convert(file_path)
                res_text = result.text_content
                for line in res_text.split("\n"):
                    text += line + "\n"
                return  text
            else:
                return {"code":400,"message":f"目前不支持{{suffix}}类型文件"}
        except Exception as e:
            return {"code":400,"message":"文件不存在或者文件读取失败!"}

这个类定义很简单的,主要是涉及fastapi的中间键,openai的设置,和文件的解析设置。

然后创建该有的task任务流程。


@cl.task(name="file_read_task")
def file_read_task(file_path,request_dict):
    """
    该函数主要是用来处理文件解析任务
    :return:
    """
    start_time = time.time()
    print("开始执行文件解析任务")
    # 将字典转换回 Request_model 对象(使用相对导入以避免包路径问题)
    from ..Fastapi.setting.Class import Request_model
    request = Request_model(**request_dict)
    
    from ..setting_config import Setting_config
    text = Setting_config().file_read(file_path)
    print("文件解析任务执行完毕")

    from dotenv import load_dotenv
    import os
    load_dotenv()
    client = Setting_config().openai_config()

    # 安全获取person_info的属性
    person_info = request.person_info
    person_age = person_info.age_range if person_info else None
    person_gender = person_info.gender if person_info else None
    person_education = person_info.education if person_info else None
    person_income = person_info.income if person_info else None
    person_platform_usage = person_info.platform_useage if person_info else None
    person_content_preference = person_info.content_preference if person_info else None
    person_live_in = person_info.live_in if person_info else None
    person_custom_traits = person_info.custom_traits if person_info else None
    person_language = person_info.languange if person_info else None
    
    talk = client.chat.completions.create(
        model=os.getenv("OPENAI_AI_CHAT_MODEL"),
        messages=[
            {"role": "system", "content":
                f"用户上传的文件内容{text}\n"
                f"关于语言的设定'zh'为中文,'jp'为日语,'en'为英语,'kr'为韩语。\n"
                f"你是一个{request.platform or '未知'}平台的用户,界面应用的语言是{request.ui_language or '未知'},"
                f"你的画像是{person_age or '未知'}岁,性别是{person_gender or '未知'},受教育程度{person_education or '未知'},"
                f"收入水平{person_income or '未知'},平台的使用情况{person_platform_usage or '未知'},内容偏好{person_content_preference or '未知'},"
                f"居住地{person_live_in or '未知'},自定义个性化标签为{person_custom_traits or '未知'},使用的语言是{person_language or '未知'}的用户"
                f"请你根据用户画像来模拟用户,然后再随机设定你的当前情绪,依据用户的语言来使用该语言来进行评价,评价文件的内容,给出一个评价,并给出一个建议,以及为什么做这样子的评价。"
             },
        ],
    )
    end_time = time.time()
    process_time = end_time-start_time
    answer = talk.choices[0].message.content
    start = time.localtime(start_time)
    end = time.localtime(end_time)
    start_time = time.strftime("%Y-%m-%d %H:%M:%S", start)
    end_time = time.strftime("%Y-%m-%d %H:%M:%S", end)
    return text,answer,start_time,end_time,process_time

最后创建相应fastapi的接口

@app.post("/post_file_chat")
async def response_model(request: str = Form(...),
                         file_path: UploadFile = File(..., alias="file_path")):
    # 解析JSON字符串为Request_model对象
    try:
        request_data = json.loads(request)
        request_model = Request_model(**request_data)
    except json.JSONDecodeError as e:
        return {
            "error": "Invalid JSON format",
            "message": str(e)
        }
    except Exception as e:
        return {
            "error": "Invalid request data",
            "message": str(e)
        }
    
    file_location = f"{UPLOAD}/{uuid.uuid4().hex}_{file_path.filename}"
    with open(file_location, "wb") as f:
        while True:
            chunk = await file_path.read(1024 * 1024)
            if not chunk:
                break
            f.write(chunk)

    # 将 Pydantic 模型转换为字典以便 Celery 序列化
    request_dict = request_model.model_dump() if hasattr(request_model, 'model_dump') else request_model.dict()
    res = file_read_task.delay(file_location, request_dict)

    return {
        "task_id": res.id,
        "status": res.status,
        "message": "任务已提交,请轮询获/task/{task_id}取结果",
        "created_at_timestamp": time.time(),
    }

@app.get("/task/{task_id}")
async def response_model(task_id:str):
    res = AsyncResult(task_id, app=cl)
    if res.state == "SUCCESS":
        text, answer, start, end, duration = res.result

        return {
            "status": res.state,
            "text": text, #图片描述
            "answer": answer,
            "created_at_timestamp": start,
            "process_timestamp": f"{duration:.2f}秒",
            "completed_timestamp": end,
        }
    return {"status":f"任务的状态为{res.state}"}

那么到这里基本也就演示完毕,那么这里就跑一下看看

需要先启动celery任务然后再启动fastapi。

启动指令

python -m celery -A 根目录下包含该task的py名称:celery worker -l info -P solo

测试结果演示

传递的参数参考

{
  "platform": "小红书",
  "max_users": 20,
  "ui_language": "jp",
  "person_info": {
    "age_range": "18-25岁",
    "gender": "女",
    "education": "本科学历",
    "income": "5k-6k",
    "platform_usage": "一周5天,每天2小时",
    "content_preference": [
      "美妆,旅行,摄影"
    ],
    "live_in": [
      "中国湖南长沙市"
    ],
    "custom_traits": "不被定义,快乐小女孩",
    "language": "jp"
  },
  "versions": [
    {
      "version_text": "string",
      "images": [
        "string"
      ],
      "videos": [
        "string"
      ]
    }
  ]
}

上传的图片

博主自己画的图片,请勿商用,记得注明来源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值