一、Functions简介

        可以把Tools作为依赖于外部服务的插件,Functions就是内部插件,二者都是用来增强open webui的能力的。Functions是轻量的,高度可定制的,并且是用纯Python编写的,所以你可以自由地创建任何东西——从新的人工智能工作流到与你使用的任何东西的集成,比如谷歌搜索或家庭助理。

       在open webui中的Function包括三种类型:Pipe Function、Filter Function和Action Function。

        Pipe类型的Function用于自定义Agent或模型,用户在对话中可以像普通的模型那样选择使用。

        Filter类型的Function用于对往返大模型的数据进行处理,从而可以在不中断对话的前提下,拦截对话内容并进行修改或其他处理,比如日志。过滤器一般用于轻量级处理,包括:发送数据到监控平台、记录日志、修改用户输入、阻断有害消息、翻译和限流等。

        Action类型的Function用来对聊天界面的按钮进行定制。这些按钮出现在单个聊天消息下方,让您可以方便地一键访问您定义的操作。

        本文仅对Action类型的Function进行解析。        

        二、导入一个Function

        1)进入open webui社区的Functions页面,选择一个Function,这里以Save Outputs为例

        2)点击Save Outputs,进入如下页面

        3)点击Get,在对话框填写你的open webui的地址

        4)点击Import to WebUI

        进入open webui页面,显示函数代码,核心代码为把大模型的输出写入本地文件中,完整源码如下:

"""
title: save_outputs
author: stefanpietrusky
author_url: https://downchurch.studio/
inspiration: add_to_memories_action_button @pad4651
instruction: you need to mount the container folder /app/data with a local folder when creating the container! „--mount type=bind,source="FOLDER PATH\docker_data",target=/app/data“
icon_url:  data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjNWY2MzY4Ij48cGF0aCBkPSJNODQwLTY4MHY0ODBxMCAzMy0yMy41IDU2LjVUNzYwLTEyMEgyMDBxLTMzIDAtNTYuNS0yMy41VDEyMC0yMDB2LTU2MHEwLTMzIDIzLjUtNTYuNVQyMDAtODQwaDQ4MGwxNjAgMTYwWm0tODAgMzRMNjQ2LTc2MEgyMDB2NTYwaDU2MHYtNDQ2Wk00ODAtMjQwcTUwIDAgODUtMzV0MzUtODVxMC01MC0zNS04NXQtODUtMzVxLTUwIDAtODUgMzV0LTM1IDg1cTAgNTAgMzUgODV0ODUgMzVaTTI0MC01NjBoMzYwdi0xNjBIMjQwdjE2MFptLTQwLTg2djQ0Ni01NjAgMTE0WiIvPjwvc3ZnPg==
version: 0.1
"""

import os
from pydantic import BaseModel, Field
from typing import Optional


class Action:
    class Valves(BaseModel):
        pass

    class UserValves(BaseModel):
        show_status: bool = Field(
            default=True, description="Show status of the action."
        )
        pass

    def __init__(self):
        self.valves = self.Valves()
        pass

    async def action(
        self,
        body: dict,
        __user__=None,
        __event_emitter__=None,
        __event_call__=None,
    ) -> Optional[dict]:
        print(f"action:{__name__}")

        user_valves = __user__.get("valves")
        if not user_valves:
            user_valves = self.UserValves()

        if __event_emitter__:
            last_assistant_message = body["messages"][-1]

            if user_valves.show_status:
                await __event_emitter__(
                    {
                        "type": "status",
                        "data": {"description": "Saving to file", "done": False},
                    }
                )

            try:
                directory = "/app/data"
                if not os.path.exists(directory):
                    os.makedirs(directory)

                file_path = os.path.join(directory, "saved_outputs.txt")
                with open(file_path, "a") as file:
                    file.write(f"{last_assistant_message['content']}\n\n")
                print("Output saved to file in the container, accessible on the host.")

            except Exception as e:
                print(f"Error saving output to file: {str(e)}")
                if user_valves.show_status:
                    await __event_emitter__(
                        {
                            "type": "status",
                            "data": {
                                "description": "Error Saving to File",
                                "done": True,
                            },
                        }
                    )

            if user_valves.show_status:
                await __event_emitter__(
                    {
                        "type": "status",
                        "data": {"description": "Output Saved", "done": True},
                    }
                )
 

        因为可能是恶意代码,所以需要阅读检查代码。检查无误后,可以保存,该函数便作为插件进入open webui体系中。

          三、具体使用

        函数生效后,在大模型返回对一个问题的应答后,在工具栏显示该函数图标。

        用户点击该链接,则保存当前大模型输出写入到文件中。

        三、源码分析

        1)数据模型

       Function数据保存在Function表中,表定义如下:

        其中:

                id:函数唯一标识

                userid:用户唯一标识

                name:函数名

                type:函数类型 filter|pipe|action

                content:方法源代码

               meta:元数据

                valves:阈值

                is_active:是否被激活(激活后才可见)

                is_global:全局还是局部(仅某个用户使用)

        2)导入函数

        从open webui社区页面点击 Import to WebUI时,浏览器启动一个新页面,并提交代码格式化请求到/app/v1/utils/code/format,后端调用black模块进行严格格式化处理,并把格式化后的代码返回前端。

@router.post("/code/format")
async def format_code(form_data: CodeForm, user=Depends(get_admin_user)):
    try:
        formatted_code = black.format_str(form_data.code, mode=black.Mode())
        return {"code": formatted_code}
    except black.NothingChanged:
        return {"code": form_data.code}
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))

        完成格式化处理后,后端再提交创建Function请求到 /api/v1/functions/create。请求数据为:

{
    "id": "save_outputs",
    "name": "Save Outputs",
    "meta": {
        "description": "Save outputs locally on your computer.",
        "manifest": {
            "title": "save_outputs",
            "author": "stefanpietrusky",
            "author_url": "https://downchurch.studio/",
            "inspiration": "add_to_memories_action_button @pad4651",
            "instruction": "you need to mount the container folder /app/data with a local folder when creating the container! „--mount type=bind,source=\"FOLDER PATH\\docker_data\",target=/app/data\"",
            "icon_url": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjNWY2MzY4Ij48cGF0aCBkPSJNODQwLTY4MHY0ODBxMCAzMy0yMy41IDU2LjVUNzYwLTEyMEgyMDBxLTMzIDAtNTYuNS0yNS41VDEyMC0yMDB2LTU2MHEwLTMzIDIzLjUtNTYuNVQyMDAtODQwaDQ4MGwxNjAgMTYwWm0tODAgMzRMNjQ2LTc2MEgyMDB2NTYwaDU2MHYtNDQ2Wk00ODAtMjQwcTUwIDAgODUtMzV0MzUtODVxMC01MC0zNS04NXQtODUtMzVxLTUwIDAtODUgMzV0LTM1IDg1cTAgNTAgMzUgODV0ODUgMzVaTTI0MC01NjBoMzYwdi0xNjBIMjQwdjE2MFptLTQwLTg2djQ0Ni01NjAgMTE0WiIvPjwvc3ZnPg==",
            "version": "0.1"
        },
        "type": "action",
        "user": {
            "id": "9e4f4854-71d9-429a-99b9-9338a393de9e",
            "username": "pietrusky",
            "name": "",
            "createdAt": 1724186428,
            "role": null,
            "verified": false
        },
        "id": "542145b0-59a0-44f2-86f1-dd2f1e64d705"
    },

    #content由注释和源代码组成
    "content":  "\"\"\"\ntitle: save_outputs\nauthor: stefanpietrusky\nauthor_url: https://downchurch.studio/\ninspiration: add_to_memories_action_button @pad4651\ninstruction: you need to mount the container folder /app/data with a local folder when creating the container! „--mount type=bind,source=\"FOLDER PATH\\docker_data\",target=/app/data\"\nicon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAtOTYwIDk2MCA5NjAiIHdpZHRoPSIyNHB4IiBmaWxsPSIjNWY2MzY4Ij48cGF0aCBkPSJNODQwLTY4MHY0ODBxMCAzMy0yMy41IDU2LjVUNzYwLTEyMEgyMDBxLTMzIDAtNTYuNS0yNS41VDEyMC0yMDBvLTU2MHEwLTMzIDIzLjUtNTYuNVQyMDAtODQwaDQ4MGwxNjAgMTYwWm0tODAgMzRMNjQ2LTc2MEgyMDB2NTYwaDU2MHYtNDQ2Wk00ODAtMjQwcTUwIDAgODUtMzV0MzUtODVxMC01MC0zNS04NXQtODUtMzVxLTUwIDAtODUgMzV0LTM1IDg1cTAgNTAgMzUgODV0ODUgMzVaTTI0MC01NjBoMzYwdi0xNjBIMjQwdjE2MFptLTQwLTg2djQ0Ni01NjAgMTE0WiIvPjwvc3ZnPg==\nversion: 0.1\n\"\"\"\n\nimport os\nfrom pydantic import BaseModel, Field\nfrom typing import Optional\n\n\nclass Action:\n    class Valves(BaseModel):\n        pass\n\n    class UserValves(BaseModel):\n        show_status: bool = Field(\n            default=True, description=\"Show status of the action.\"\n        )\n        pass\n\n    def __init__(self):\n        self.valves = self.Valves()\n        pass\n\n    async def action(\n        self,\n        body: dict,\n        __user__=None,\n        __event_emitter__=None,\n        __event_call__=None,\n    ) -> Optional[dict]:\n        print(f\"action:{__name__}\")\n\n        user_valves = __user__.get(\"valves\")\n        if not user_valves:\n            user_valves = self.UserValves()\n\n        if __event_emitter__:\n            last_assistant_message = body[\"messages\"][-1]\n\n            if user_valves.show_status:\n                await __event_emitter__(\n                    {\n                        \"type\": \"status\",\n                        \"data\": {\"description\": \"Saving to file\", \"done\": False},\n                    }\n                )\n\n            try:\n                directory = \"/app/data\"\n                if not os.path.exists(directory):\n                    os.makedirs(directory)\n\n                file_path = os.path.join(directory, \"saved_outputs.txt\")\n                with open(file_path, \"a\") as file:\n                    file.write(f\"{last_assistant_message['content']}\\n\\n\")\n                print(\"Output saved to file in the container, accessible on the host.\")\n\n            except Exception as e:\n                print(f\"Error saving output to file: {str(e)}\")\n                if user_valves.show_status:\n                    await __event_emitter__(\n                        {\n                            \"type\": \"status\",\n                            \"data\": {\n                                \"description\": \"Error Saving to File\",\n                                \"done\": True,\n                            },\n                        }\n                    )\n\n            if user_valves.show_status:\n                await __event_emitter__(\n                    {\n                        \"type\": \"status\",\n                        \"data\": {\"description\": \"Output Saved\", \"done\": True},\n                    }\n                )"
}

       对应函数源码如下:

处理流程如下:

1)防错处理,判断函数名是否符合python标识符的命名规则,不符合则报错

2)对源中import的模块名进行替换

3)加载源码成为可使用的模块

4)把该函数加载到全局FUNCTIONS中,供后继使用

5)为函数创建缓存目录

@router.post("/create", response_model=Optional[FunctionResponse])
async def create_new_function(
    request: Request, form_data: FunctionForm, user=Depends(get_admin_user)
):
    if not form_data.id.isidentifier(): #对id进行校验
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Only alphanumeric characters and underscores are allowed in the id",
        )

    form_data.id = form_data.id.lower()

    #从Function表查询该函数是否已经入库

    function = Functions.get_function_by_id(form_data.id)
    if function is None: 
        try:

            #用本地模块名,替换源码中的模块名,比如用from open_webui.utils替换from utils 
            form_data.content = replace_imports(form_data.content)

            #把函数加载为模块
            function_module, function_type, frontmatter = load_function_module_by_id(
                form_data.id,
                content=form_data.content,
            )
            form_data.meta.manifest = frontmatter

            #把Function实例增加到全局FUNCTIONS中

            FUNCTIONS = request.app.state.FUNCTIONS
            FUNCTIONS[form_data.id] = function_module

            #把函数数据插入到FUNCTION表中

            function = Functions.insert_new_function(user.id, function_type, form_data)

            #为该方法创建目录/app/backend/data/cache/functions/{函数名}

            function_cache_dir = CACHE_DIR / "functions" / form_data.id
            function_cache_dir.mkdir(parents=True, exist_ok=True)

            if function:
                return function
            else:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=ERROR_MESSAGES.DEFAULT("Error creating function"),
                )
        except Exception as e:
            log.exception(f"Failed to create a new function: {e}")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=ERROR_MESSAGES.DEFAULT(e),
            )
    else: #如果已经入库,则报错
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=ERROR_MESSAGES.ID_TAKEN,
        )
 

        在该方法中的核心代码是load_function_module_by_id,load_function_module_by_id实现代码动态加载,重点分析一下。

def load_function_module_by_id(function_id: str, content: str | None = None):

    #如果参数content为None,则从数据库查询
    if content is None:
        function = Functions.get_function_by_id(function_id)
        if not function:
            raise Exception(f"Function not found: {function_id}")
        content = function.content

        content = replace_imports(content)#替换源码中的导入的模块名
        Functions.update_function_by_id(function_id, {"content": content})#更新数据库content
    else:#从content提取元数据
        frontmatter = extract_frontmatter(content)

        #安装依赖模块
        install_frontmatter_requirements(frontmatter.get("requirements", ""))

   

    module_name = f"function_{function_id}"

    #创建function_{function_id}模块,比如function_save_outputs
    module = types.ModuleType(module_name)

    #加载模块到sys_modules
    sys.modules[module_name] = module

    # 创建临时文件,用于存储函数的源代码
    temp_file = tempfile.NamedTemporaryFile(delete=False)
    temp_file.close()
    try:

        #把源代码写入临时文件
        with open(temp_file.name, "w", encoding="utf-8") as f:
            f.write(content)
        module.__dict__["__file__"] = temp_file.name #设置模块的__file__为临时文件名

        # 在本模块的命名空间运行源代码,完成模块源码的载入
        exec(content, module.__dict__)
        frontmatter = extract_frontmatter(content)
        log.info(f"Loaded module: {module.__name__}")

        # 根据Function类型,返回对应类的实例
        if hasattr(module, "Pipe"):#返回管道实例
            return module.Pipe(), "pipe", frontmatter
        elif hasattr(module, "Filter"): #返回过滤器实例
            return module.Filter(), "filter", frontmatter
        elif hasattr(module, "Action"):
            return module.Action(), "action", frontmatter  #返回Action实例
        else:
            raise Exception("No Function class found in the module")
    except Exception as e:
        log.error(f"Error loading module: {function_id}: {e}")
        # Cleanup by removing the module in case of error
        del sys.modules[module_name]

        Functions.update_function_by_id(function_id, {"is_active": False})
        raise e
    finally:
        os.unlink(temp_file.name)

        3)执行函数

        用户在对话界面点击按钮执行函数时,后端入口为http://{ip:port}/api/chat/actions/{函数名},后端调用该函数执行对应的操作。对应入口函数为chat_action。

该方法和简洁,主要是调用chat_action_handle。

@app.post("/api/chat/actions/{action_id}")
async def chat_action(
    request: Request, action_id: str, form_data: dict, user=Depends(get_verified_user)
):
    try:
        model_item = form_data.pop("model_item", {})

        if model_item.get("direct", False):
            request.state.direct = True
            request.state.model = model_item

        return await chat_action_handler(request, action_id, form_data, user)
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(e),
        )

        chat_action_handle实际对应 open_webui.utils.chat模块中的chat_action方法,具体源码如下:

async def chat_action(request: Request, action_id: str, form_data: dict, user: Any):
    if "." in action_id: #如果action_id是多层,则用'.'分割
        action_id, sub_action_id = action_id.split(".")
    else:
        sub_action_id = None

    action = Functions.get_function_by_id(action_id)#从数据库查找Function是否存在
    if not action:
        raise Exception(f"Action not found: {action_id}")

    #以下代码确定使用的模型

    if not request.app.state.MODELS: 
        await get_all_models(request, user=user)

    if getattr(request.state, "direct", False) and hasattr(request.state, "model"):
        models = {
            request.state.model["id"]: request.state.model,
        }
    else:
        models = request.app.state.MODELS

    data = form_data
    model_id = data["model"]

    if model_id not in models:
        raise Exception("Model not found")
    model = models[model_id]

    #通过websocket发送数据到前端

    __event_emitter__ = get_event_emitter(
        {
            "chat_id": data["chat_id"],
            "message_id": data["id"],
            "session_id": data["session_id"],
            "user_id": user.id,
        }
    )
    __event_call__ = get_event_call(
        {
            "chat_id": data["chat_id"],
            "message_id": data["id"],
            "session_id": data["session_id"],
            "user_id": user.id,
        }
    )

    #根据action_id获取模块

    function_module, _, _ = get_function_module_from_cache(request, action_id)

    #阀门处理

    if hasattr(function_module, "valves") and hasattr(function_module, "Valves"):
        valves = Functions.get_function_valves_by_id(action_id)
        function_module.valves = function_module.Valves(**(valves if valves else {}))

    if hasattr(function_module, "action"):
        try:
            action = function_module.action#从Action类中获取action方法

            # 得到函数签名
            sig = inspect.signature(action)
            params = {"body": data}

            # Extra parameters to be passed to the function
            extra_params = {
                "__model__": model,
                "__id__": sub_action_id if sub_action_id is not None else action_id,
                "__event_emitter__": __event_emitter__,
                "__event_call__": __event_call__,
                "__request__": request,
            }

            #把extra_params中的项中与函数签名中的参数匹配的项加入到params中
            for key, value in extra_params.items():
                if key in sig.parameters:
                    params[key] = value

            if "__user__" in sig.parameters:

                #如果函数签名中有__user__,则在调用参数中增加用户相关阀门设置
                __user__ = user.model_dump() if isinstance(user, UserModel) else {}

                try:
                    if hasattr(function_module, "UserValves"):
                        __user__["valves"] = function_module.UserValves(
                            **Functions.get_user_valves_by_id_and_user_id(
                                action_id, user.id
                            )
                        )
                except Exception as e:
                    log.exception(f"Failed to get user values: {e}")

                params = {**params, "__user__": __user__}

            if inspect.iscoroutinefunction(action): #如果action方法是协程,则await调用
                data = await action(**params)
            else: #非协程则直接调用
                data = action(**params)

        except Exception as e:
            return Exception(f"Error: {e}")

    return data
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/919728.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/919728.shtml
英文地址,请注明出处:http://en.pswp.cn/news/919728.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C2039 “unref“:不是“osgEarth::Symbology::Style”的成员 问题分析及解决方法

在osgEarth2.10中实现多线段连续测量功能时,遇到下图中的错误; 经过测试和验证,主要问题出现在下图圈出代码的定义上 图22-1 对于22-1中的两个变量这样定义是错误的。因为Style类没有继承自osg::Referenced,因此不能与osg::ref_ptr配合使用

GitHub 热榜项目 - 日榜(2025-08-19)

GitHub 热榜项目 - 日榜(2025-08-19) 生成于:2025-08-19 统计摘要 共发现热门项目:12 个 榜单类型:日榜 本期热点趋势总结 本期GitHub热榜呈现三大技术热点:1)AI原生开发持续爆发,Archon OS、Parlant等…

ingress 配置ssl证书

模拟环境举例&#xff1a; # 生成带 OU 的证书配置文件 cat > csr.conf <<EOF [ req ] default_bits 2048 prompt no default_md sha256 distinguished_name dn[ dn ] C CN ST Beijing L Beijing O YourCompany, Inc. # 组织名称 (必填) OU DevOps De…

Pandas 合并数据集:concat 和 append

文章目录Pandas 合并数据集&#xff1a;concat 和 append回顾&#xff1a;NumPy 数组的拼接使用 pd.concat 进行简单拼接重复索引将重复索引视为错误忽略索引添加多级索引&#xff08;MultiIndex&#xff09;键使用连接&#xff08;Join&#xff09;方式拼接append 方法Pandas …

2025年5月架构设计师综合知识真题回顾,附参考答案、解析及所涉知识点(七)

本文主要回顾2025年上半年(2025-5-24)系统架构设计师考试上午综合知识科目的选择题,同时附带参考答案、解析和所涉知识点。 2025年5月架构设计师综合知识真题回顾,附参考答案、解析及所涉知识点(一) 2025年5月架构设计师综合知识真题回顾,附参考答案、解析及所涉知识点(…

面向RF设计人员的微带贴片天线计算器

微带贴片天线和阵列可能是仅次于单极天线和偶极天线的最简单的天线设计。这些天线也很容易集成到PCB中&#xff0c;因此通常用于5G天线阵列和雷达等高级系统。这些天线阵列在基谐模式和高阶模式下也遵循一组简单的设计方程&#xff0c;因此您甚至可以在不使用仿真工具的情况下设…

明基RD280U编程显示器深度测评:码农的「第二块键盘」竟然会发光?

文章目录前言一、开箱篇&#xff1a;当理工男遇到「俄罗斯套娃式包装」二、外观篇&#xff1a;深空灰的「代码容器」1. 桌面变形记2. 保护肩颈的人体工学设计三、显示篇&#xff1a;给代码做「光子嫩肤」1. 28寸超大大屏 3:2屏比 4K超清2.专业编程模式&#xff0c;让代码一目…

算法114. 二叉树展开为链表

题目&#xff1a;给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。 展开后的单链表应该与二叉树 先序遍历 顺序相同。…

智慧能源管理系统:点亮山东零碳园区的绿色引擎

一、概述在全球积极践行“双碳”目标的时代浪潮下&#xff0c;山东作为经济大省&#xff0c;正全力推动产业的绿色变革&#xff0c;零碳园区建设成为其中的关键一环。《山东省零碳园区建设方案》明确规划&#xff0c;到2027年建成15个左右省级零碳园区 &#xff0c;到2030年进一…

分布式日志分析平台(ELFK 与 EFK)理论

一、日志分析平台核心概念在分布式系统中&#xff0c;日志是系统运行状态监控、问题排查和业务分析的重要依据。随着系统规模扩大&#xff0c;单机日志管理方式已无法满足需求&#xff0c;分布式日志分析平台应运而生。其核心目标是实现日志的集中收集、统一处理、高效存储和可…

CoreShop微信小程序商城框架开启多租户-添加一个WPF客户端以便进行本地操作--读取店铺信息(6)

本节内容&#xff0c;使用登录的token进行店铺信息读取&#xff0c;顺利的话&#xff0c;进行EXCEL上传测试。 1。在后台编写 读取店铺信息代码 1.1 查看原来铺店信息在什么位置&#xff0c;店铺的表格为CoreCmsStore#region 获取列表// POST: Api/CoreCmsStore/GetPageList///…

UE5关卡蓝图能不能保存副本呀?

提问 关卡蓝图能不能保存副本呀&#xff1f; 回答 在 UE 里&#xff0c;“关卡蓝图&#xff08;Level Blueprint&#xff09;”本身其实是不能直接复制/保存成独立资源的&#xff0c;因为它和具体的 **Level&#xff08;.umap 文件&#xff09;**是绑定的——相当于一个“场景脚…

机器学习数据预处理学习报告

一、学习背景与目的在机器学习流程中&#xff0c;数据预处理是保障模型训练效果的关键环节。原始数据常存在缺失值、量纲不一致、特征格式不匹配等问题&#xff0c;直接影响模型对数据规律的学习。本次学习围绕 Pandas 与 Scikit-learn&#xff08;sklearn&#xff09;工具库&a…

git旧仓库迁移到新仓库

git旧仓库迁移到新仓库 A仓库(旧仓库)&#xff1a;git172.16.21.21:xxxx_software/Ni-Handler-Mgr.git B仓库(新仓库)&#xff1a;git172.16.11.11:yyyy/hostpc/ni-handler-mgr.git Step1 新建新仓库 创建新 GitHub 仓库‌ 在 GitHub 页面点击 “New repository”&#xff0c;命…

YOLO --- YOLOv5模型以及项目详解

YOLO — YOLOv5模型以及项目详解 文章目录YOLO --- YOLOv5模型以及项目详解一&#xff0c;开源地址二&#xff0c;改进点Focus 模块三&#xff0c;网络结构3.1 CSP1_X 与 CSP2_X3.2 自适应Anchor的计算3.3 激活函数3.3.1 SiLU3.3.2 Swish3.4 Bottleneck3.5 C33.5.1 BottleneckC…

Linux文本三剑客的使用及常见重点操作

文本三剑客指 Linux环境下的 grep&#xff08;搜索&#xff09;、sed&#xff08;编辑&#xff09;、awk&#xff08;分析&#xff09;三款用于文本处理的核心命令&#xff0c;三者分工明确、功能互补&#xff0c;是处理日志、配置文件、结构化数据等场景的 “刚需工具”。一、…

​​《开源字幕神器VideoCaptioner实战:基于Whisper+LLM的全链路方案,免费平替剪映会员》​​

&#x1f4cc; 大家好&#xff0c;我是智界工具库&#xff0c;每天分享好用实用且智能的开源项目&#xff0c;以及在JAVA语言开发中遇到的问题&#xff0c;如果本篇文章对您有所帮助&#xff0c;请帮我点个小赞小收藏小关注吧&#xff0c;谢谢喲&#xff01;&#x1f618; 博主…

redisIO模型

​​1. 总述核心​​“Redis采用了​​单线程的Reactor模型​​来处理网络IO和命令请求。其核心在于&#xff0c;​​它使用一个主线程通过IO多路复用机制来并发地处理大量的客户端连接&#xff0c;而实际的命令解析和执行则是单线程的​​。”这句话非常重要&#xff0c;它直接…

视觉采集模块的用法

一、图像源模块用法采集模块中最基础的单元就是图像源模块&#xff0c;其中图像的输入方式包括相机输入、本地图像、SDK三种。添加图像源后&#xff0c;需要对内部的参数进行对应的配置&#xff0c;正常我们连接相机后图像源选择我们对应的连接相机。配置所需要的相机参数&…

Linux下基于Electron的程序ibus输入法问题

Linux下基于Electron的程序ibus输入法问题 最近想体验一下KDE Plasma桌面&#xff0c;遇到一个问题&#xff0c;就是浏览器输入不了中文&#xff0c;Edge、Chrome都一样&#xff0c;当然它们都是基于Chromium的&#xff0c;出同样的问题很正常。后面发现Visual Code也有同样的问…