【AI大模型】GPTS 与 Assistants API

前言

          2023 年 11 月 6 日,OpenAI DevDay 发表了一系列新能力,其中包括:GPT Store 和 Assistants API。

GPTs 和 Assistants API 本质是降低开发门槛

可操控性和易用性之间的权衡与折中:

  1. 更多技术路线选择:原生 API、GPTs 和 Assistants API
  2. GPTs 的示范,起到教育客户的作用,有助于打开市场
  3. 要更大自由度,需要用 Assistants API 开发
  4. 想极致调优,还得原生 API + RAG
  5. 国内大模型的 Assistants API,参考 Minimax

Assistants API 的主要能力

已有能力:

  1. 创建和管理 assistant,每个 assistant 有独立的配置
  2. 支持无限长的多轮对话,对话历史保存在 OpenAI 的服务器上
  3. 通过自有向量数据库支持基于文件的 RAG
  4. 支持 Code Interpreter
    1. 在沙箱里编写并运行 Python 代码
    2. 自我修正代码
    3. 可传文件给 Code Interpreter
  5. 支持 Function Calling
  6. 支持在线调试的 Playground

承诺未来会有的能力:

  1. 支持 DALL·E
  2. 支持图片消息
  3. 支持自定义调整 RAG 的配置项

收费:

  1. 按 token 收费。无论多轮对话,还是 RAG,所有都按实际消耗的 token 收费
  2. 如果对话历史过多超过大模型上下文窗口,会自动放弃最老的对话消息
  3. 文件按数据大小和存放时长收费。1 GB 向量存储 一天收费 0.10 美元
  4. Code interpreter 跑一次 $0.03

创建一个 Assistant

可以为每个应用,甚至应用中的每个有对话历史的使用场景,创建一个 assistant。

虽然可以用代码创建,也不复杂,例如:

from openai import OpenAI

# 初始化 OpenAI 服务
client = OpenAI()

# 创建助手
assistant = client.beta.assistants.create(
    name="小U",
    instructions="你叫小U,你是JAVA技术框架 UAP的助手,你负责回答用户关于UAP的问题。",
    model="gpt-4-turbo",
)

但是,更佳做法是,到 Playground 在线创建,因为:

  1. 更方便调整
  2. 更方便测试

样例 Assistant 的配置

Instructions:

你叫小U,你是JAVA技术框架 UAP的助手,你负责回答用户关于UAP的问题。

Functions:

{
  "name": "ask_database",
  "description": "Use this function to answer user questions about course schedule. Output should be a fully formed SQL query.",
  "parameters": {
    "type": "object",
    "properties": {
      "query": {
        "type": "string",
        "description": "SQL query extracting info to answer the user's question.\nSQL should be written using this database schema:\n\nCREATE TABLE Courses (\n\tid INT AUTO_INCREMENT PRIMARY KEY,\n\tcourse_date DATE NOT NULL,\n\tstart_time TIME NOT NULL,\n\tend_time TIME NOT NULL,\n\tcourse_name VARCHAR(255) NOT NULL,\n\tinstructor VARCHAR(255) NOT NULL\n);\n\nThe query should be returned in plain text, not in JSON.\nThe query should only contain grammars supported by SQLite."
      }
    },
    "required": [
      "query"
    ]
  }
}

代码访问 Assistant

管理 thread

Threads:

  1. Threads 里保存的是对话历史,即 messages
  2. 一个 assistant 可以有多个 thread
  3. 一个 thread 可以有无限条 message
  4. 一个用户与 assistant 的多轮对话历史可以维护在一个 thread 里
import json


def show_json(obj):
    """把任意对象用排版美观的 JSON 格式打印出来"""
    print(json.dumps(
        json.loads(obj.model_dump_json()),
        indent=4,
        ensure_ascii=False
    ))

from openai import OpenAI
import os

from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())

# 初始化 OpenAI 服务
client = OpenAI()   # openai >= 1.3.0 起,OPENAI_API_KEY 和 OPENAI_BASE_URL 会被默认使用

# 创建 thread
thread = client.beta.threads.create()
show_json(thread)
{
    "id": "thread_zkT0xybD8VslhJzJJNY3IHuX",
    "created_at": 1716553797,
    "metadata": {},
    "object": "thread",
    "tool_resources": {
        "code_interpreter": null,
        "file_search": null
    }
}

可以根据需要,自定义 metadata,比如创建 thread 时,把 thread 归属的用户信息存入。

thread = client.beta.threads.create(
    metadata={"fullname": "王卓然", "username": "taliux"}
)
show_json(thread)
{
    "id": "thread_1LMmbLMx2ZrRQjymDOGQGYGX",
    "created_at": 1716553801,
    "metadata": {
        "fullname": "王卓然",
        "username": "taliux"
    },
    "object": "thread",
    "tool_resources": {
        "code_interpreter": null,
        "file_search": null
    }
}

Thread ID 如果保存下来,是可以在下次运行时继续对话的。

从 thread ID 获取 thread 对象的代码:

thread = client.beta.threads.retrieve(thread.id)
show_json(thread)
{
    "id": "thread_1LMmbLMx2ZrRQjymDOGQGYGX",
    "created_at": 1716553801,
    "metadata": {
        "fullname": "王卓然",
        "username": "taliux"
    },
    "object": "thread",
    "tool_resources": {
        "code_interpreter": {
            "file_ids": []
        },
        "file_search": null
    }
}

此外,还有:

  1. threads.modify() 修改 thread 的 metadata 和 tool_resources
  2. threads.retrieve() 获取 thread
  3. threads.delete() 删除 thread。

具体文档参考:https://platform.openai.com/docs/api-reference/threads

给 Threads 添加 Messages

这里的 messages 结构要复杂一些:

  1. 不仅有文本,还可以有图片和文件
  2. 也有 metadata
message = client.beta.threads.messages.create(
    thread_id=thread.id,  # message 必须归属于一个 thread
    role="user",          # 取值是 user 或者 assistant。但 assistant 消息会被自动加入,我们一般不需要自己构造
    content="你都能做什么?",
)
show_json(message)

{
    "id": "msg_wDZxbO2oi2rAvk9Yxgqw0hVP",
    "assistant_id": null,
    "attachments": [],
    "completed_at": null,
    "content": [
        {
            "text": {
                "annotations": [],
                "value": "你都能做什么?"
            },
            "type": "text"
        }
    ],
    "created_at": 1716553809,
    "incomplete_at": null,
    "incomplete_details": null,
    "metadata": {},
    "object": "thread.message",
    "role": "user",
    "run_id": null,
    "status": null,
    "thread_id": "thread_1LMmbLMx2ZrRQjymDOGQGYGX"
}

还有如下函数:

  1. threads.messages.retrieve() 获取 message
  2. threads.messages.update() 更新 message 的 metadata
  3. threads.messages.list() 列出给定 thread 下的所有 messages

具体文档参考:https://platform.openai.com/docs/api-reference/messages

也可以在创建 thread 同时初始化一个 message 列表:

thread = client.beta.threads.create(
    messages=[
        {
            "role": "user",
            "content": "你好",
        },
        {
            "role": "assistant",
            "content": "有什么可以帮您?",
        },
        {
            "role": "user",
            "content": "你是谁?",
        },
    ]
)

show_json(thread)  # 显示 thread
print("-----")
show_json(client.beta.threads.messages.list(
    thread.id))  # 显示指定 thread 中的 message 列表
{
    "id": "thread_Qf32rY62YZrpW8d3nEm51mLn",
    "created_at": 1716553813,
    "metadata": {},
    "object": "thread",
    "tool_resources": {
        "code_interpreter": null,
        "file_search": null
    }
}
-----
{
    "data": [
        {
            "id": "msg_MkAm2f87yE3ynnLOzXCLq5XR",
            "assistant_id": null,
            "attachments": [],
            "completed_at": null,
            "content": [
                {
                    "text": {
                        "annotations": [],
                        "value": "你是谁?"
                    },
                    "type": "text"
                }
            ],
            "created_at": 1716553813,
            "incomplete_at": null,
            "incomplete_details": null,
            "metadata": {},
            "object": "thread.message",
            "role": "user",
            "run_id": null,
            "status": null,
            "thread_id": "thread_Qf32rY62YZrpW8d3nEm51mLn"
        },
        {
            "id": "msg_tTgI3MS6t19pzQM7ItFJkZaA",
            "assistant_id": null,
            "attachments": [],
            "completed_at": null,
            "content": [
                {
                    "text": {
                        "annotations": [],
                        "value": "有什么可以帮您?"
                    },
                    "type": "text"
                }
            ],
            "created_at": 1716553813,
            "incomplete_at": null,
            "incomplete_details": null,
            "metadata": {},
            "object": "thread.message",
            "role": "assistant",
            "run_id": null,
            "status": null,
            "thread_id": "thread_Qf32rY62YZrpW8d3nEm51mLn"
        },
        {
            "id": "msg_imlUh5ckePY0SaBmE6dDB3AY",
            "assistant_id": null,
            "attachments": [],
            "completed_at": null,
            "content": [
                {
                    "text": {
                        "annotations": [],
                        "value": "你好"
                    },
                    "type": "text"
                }
            ],
            "created_at": 1716553813,
            "incomplete_at": null,
            "incomplete_details": null,
            "metadata": {},
            "object": "thread.message",
            "role": "user",
            "run_id": null,
            "status": null,
            "thread_id": "thread_Qf32rY62YZrpW8d3nEm51mLn"
        }
    ],
    "object": "list",
    "first_id": "msg_MkAm2f87yE3ynnLOzXCLq5XR",
    "last_id": "msg_imlUh5ckePY0SaBmE6dDB3AY",
    "has_more": false
}

开始 Run

  • 用 run 把 assistant 和 thread 关联,进行对话
  • 一个 prompt 就是一次 run

直接运行

assistant_id = "asst_pxKAiBW0VB62glWPjeRDA7mR"  # 从 Playground 中拷贝

run = client.beta.threads.runs.create_and_poll(
    thread_id=thread.id,
    assistant_id=assistant_id,
)
if run.status == 'completed':
    messages = client.beta.threads.messages.list(
        thread_id=thread.id
    )
    show_json(messages)
else:
    print(run.status)

Run 的状态

Run 的底层是个异步调用,意味着它不等大模型处理完,就返回。我们通过 run.status 了解大模型的工作进展情况,来判断下一步该干什么。

run.status 有的状态,和状态之间的转移关系如图。

流式运行

  1. 创建回调函数
from typing_extensions import override
from openai import AssistantEventHandler


class EventHandler(AssistantEventHandler):
    @override
    def on_text_created(self, text) -> None:
        """响应输出创建事件"""
        print(f"\nassistant > ", end="", flush=True)

    @override
    def on_text_delta(self, delta, snapshot):
        """响应输出生成的流片段"""
        print(delta.value, end="", flush=True)

     2. 运行 run

# 添加新一轮的 user message
message = client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="你说什么?",
)
# 使用 stream 接口并传入 EventHandler
with client.beta.threads.runs.stream(
    thread_id=thread.id,
    assistant_id=assistant_id,
    event_handler=EventHandler(),
) as stream:
    stream.until_done()

还有如下函数:

  1. threads.runs.list() 列出 thread 归属的 run
  2. threads.runs.retrieve() 获取 run
  3. threads.runs.update() 修改 run 的 metadata
  4. threads.runs.cancel() 取消 in_progress 状态的 run

具体文档参考:https://platform.openai.com/docs/api-reference/runs

使用 Tools

创建 Assistant 时声明 Code_Interpreter

如果用代码创建:

assistant = client.beta.assistants.create(
    name="Demo Assistant",
    instructions="你是人工智能助手。你可以通过代码回答很多数学问题。",
    tools=[{"type": "code_interpreter"}],
    model="gpt-4-turbo"
)

在回调中加入 code_interpreter 的事件响应

from typing_extensions import override
from openai import AssistantEventHandler


class EventHandler(AssistantEventHandler):
    @override
    def on_text_created(self, text) -> None:
        """响应输出创建事件"""
        print(f"\nassistant > ", end="", flush=True)

    @override
    def on_text_delta(self, delta, snapshot):
        """响应输出生成的流片段"""
        print(delta.value, end="", flush=True)

    @override
    def on_tool_call_created(self, tool_call):
        """响应工具调用"""
        print(f"\nassistant > {tool_call.type}\n", flush=True)

    @override
    def on_tool_call_delta(self, delta, snapshot):
        """响应工具调用的流片段"""
        if delta.type == 'code_interpreter':
            if delta.code_interpreter.input:
                print(delta.code_interpreter.input, end="", flush=True)
        if delta.code_interpreter.outputs:
            print(f"\n\noutput >", flush=True)
            for output in delta.code_interpreter.outputs:
                if output.type == "logs":
                    print(f"\n{output.logs}", flush=True)

发个 Code Interpreter 请求:

# 创建 thread
thread = client.beta.threads.create()

# 添加新一轮的 user message
message = client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="用代码计算 1234567 的平方根",
)
# 使用 stream 接口并传入 EventHandler
with client.beta.threads.runs.stream(
    thread_id=thread.id,
    assistant_id=assistant_id,
    event_handler=EventHandler(),
) as stream:
    stream.until_done()
assistant > code_interpreter

import math

# 计算 1234567 的平方根
square_root = math.sqrt(1234567)
square_root

output >

1111.1107055554814

assistant > 1234567 的平方根是约 1111.11。

Code_Interpreter 操作文件

# 上传文件到 OpenAI
file = client.files.create(
    file=open("mydata.csv", "rb"),
    purpose='assistants'
)

# 创建 assistant
my_assistant = client.beta.assistants.create(
    name="CodeInterpreterWithFileDemo",
    instructions="你是数据分析师,按要求分析数据。",
    model="gpt-4-turbo",
    tools=[{"type": "code_interpreter"}],
    tool_resources={
        "code_interpreter": {
          "file_ids": [file.id]  # 为 code_interpreter 关联文件
        }
    }
)
# 创建 thread
thread = client.beta.threads.create()

# 添加新一轮的 user message
message = client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="统计总销售额",
)
# 使用 stream 接口并传入 EventHandler
with client.beta.threads.runs.stream(
    thread_id=thread.id,
    assistant_id=my_assistant.id,
    event_handler=EventHandler(),
) as stream:
    stream.until_done()

关于文件操作,还有如下函数:

  1. client.files.list() 列出所有文件
  2. client.files.retrieve() 获取文件对象
  3. client.files.delete() 删除文件
  4. client.files.content() 读取文件内容

具体文档参考:https://platform.openai.com/docs/api-reference/files

创建 Assistant 时声明 Function

如果用代码创建:

'''
python
assistant = client.beta.assistants.create(
  instructions="你叫瓜瓜。你是AGI课堂的助手。你只回答跟AI大模型有关的问题。不要跟学生闲聊。每次回答问题前,你要拆解问题并输出一步一步的思考过程。",
  model="gpt-4o",
  tools=[{
    "type": "function",
    "function": {
      "name": "ask_database",
      "description": "Use this function to answer user questions about course schedule. Output should be a fully formed SQL query.",
      "parameters": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "description": "SQL query extracting info to answer the user's question.\nSQL should be written using this database schema:\n\nCREATE TABLE Courses (\n\tid INT AUTO_INCREMENT PRIMARY KEY,\n\tcourse_date DATE NOT NULL,\n\tstart_time TIME NOT NULL,\n\tend_time TIME NOT NULL,\n\tcourse_name VARCHAR(255) NOT NULL,\n\tinstructor VARCHAR(255) NOT NULL\n);\n\nThe query should be returned in plain text, not in JSON.\nThe query should only contain grammars supported by SQLite."
          }
        },
        "required": [
          "query"
        ]
    }
  }]
)
'''

 创建一个 Function

# 定义本地函数和数据库

import sqlite3

# 创建数据库连接
conn = sqlite3.connect(':memory:')
cursor = conn.cursor()

# 创建orders表
cursor.execute("""
CREATE TABLE Courses (
    id INT AUTO_INCREMENT PRIMARY KEY,
    course_date DATE NOT NULL,
    start_time TIME NOT NULL,
    end_time TIME NOT NULL,
    course_name VARCHAR(255) NOT NULL,
    instructor VARCHAR(255) NOT NULL
);
""")


# 插入5条明确的模拟记录
timetable = [
    ('2024-01-23', '20:00', '22:00', 'aaaaa', '张三'),
    ('2024-01-25', '20:00', '22:00', 'bbbbbbbb', '张三'),
    ('2024-01-29', '20:00', '22:00', 'ccccc', '张三'),
    ('2024-02-20', '20:00', '22:00', 'dddddd', '张三'),
    ('2024-02-22', '20:00', '22:00', 'eeeeeee', '李四'),

]

for record in timetable:
    cursor.execute('''
    INSERT INTO Courses (course_date, start_time, end_time, course_name, instructor)
    VALUES (?, ?, ?, ?, ?)
    ''', record)

# 提交事务
conn.commit()


def ask_database(query):
    cursor.execute(query)
    records = cursor.fetchall()
    return str(records)


# 可以被回调的函数放入此字典
available_functions = {
    "ask_database": ask_database,
}

增加回调事件的响应

from typing_extensions import override
from openai import AssistantEventHandler


class EventHandler(AssistantEventHandler):
    @override
    def on_text_created(self, text) -> None:
        """响应回复创建事件"""
        print(f"\nassistant > ", end="", flush=True)

    @override
    def on_text_delta(self, delta, snapshot):
        """响应输出生成的流片段"""
        print(delta.value, end="", flush=True)

    @override
    def on_tool_call_created(self, tool_call):
        """响应工具调用"""
        print(f"\nassistant > {tool_call.type}\n", flush=True)

    @override
    def on_tool_call_delta(self, delta, snapshot):
        """响应工具调用的流片段"""
        if delta.type == 'code_interpreter':
            if delta.code_interpreter.input:
                print(delta.code_interpreter.input, end="", flush=True)
            if delta.code_interpreter.outputs:
                print(f"\n\noutput >", flush=True)
                for output in delta.code_interpreter.outputs:
                    if output.type == "logs":
                        print(f"\n{output.logs}", flush=True)

    @override
    def on_event(self, event):
        """
        响应 'requires_action' 事件
        """
        if event.event == 'thread.run.requires_action':
            run_id = event.data.id  # 获取 run ID
            self.handle_requires_action(event.data, run_id)

    def handle_requires_action(self, data, run_id):
        tool_outputs = []

        for tool in data.required_action.submit_tool_outputs.tool_calls:
            arguments = json.loads(tool.function.arguments)
            print(
                f"{tool.function.name}({arguments})",
                flush=True
            )
            # 运行 function
            tool_outputs.append({
                "tool_call_id": tool.id,
                "output": available_functions[tool.function.name](
                    **arguments
                )}
            )

        # 提交 function 的结果,并继续运行 run
        self.submit_tool_outputs(tool_outputs, run_id)

    def submit_tool_outputs(self, tool_outputs, run_id):
        """提交function结果,并继续流"""
        with client.beta.threads.runs.submit_tool_outputs_stream(
            thread_id=self.current_run.thread_id,
            run_id=self.current_run.id,
            tool_outputs=tool_outputs,
            event_handler=EventHandler(),
        ) as stream:
            stream.until_done()

# 创建 thread
thread = client.beta.threads.create()

# 添加 user message
message = client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="平均一堂课多长时间",
)
# 使用 stream 接口并传入 EventHandler
with client.beta.threads.runs.stream(
    thread_id=thread.id,
    assistant_id=assistant_id,
    event_handler=EventHandler(),
) as stream:
    stream.until_done()

两个无依赖的 function 会在一次请求中一起被调用

# 创建 thread
thread = client.beta.threads.create()

# 添加 user message
message = client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="张三上几堂课,比李四多上几堂",
)
# 使用 stream 接口并传入 EventHandler
with client.beta.threads.runs.stream(
    thread_id=thread.id,
    assistant_id=assistant_id,
    event_handler=EventHandler(),
) as stream:
    stream.until_done()
assistant > function


assistant > function

ask_database({'query': "SELECT COUNT(*) AS count FROM Courses WHERE instructor = '张三';"})
ask_database({'query': "SELECT COUNT(*) AS count FROM Courses WHERE instructor = '李四';"})

assistant > 张三上了4堂课,而李四上了1堂课。因此,张三比李四多上了3堂课。

更多流中的 Event: https://platform.openai.com/docs/api-reference/assistants-streaming/events 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/739876.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

洗地机选购指南,什么品牌最值得购买?2024四大口碑品牌推荐

随着炎炎夏日的到来,家里的地板清洁会成为人们“沉重”的负担,而拥有一台能够高效又轻松完成地板深度清洁的洗地机是一件非常幸福的事儿。但是,面对市场上琳琅满目的洗地机品牌和型号,如何找到一款综合性能都不错的洗地机成为了许…

mac怎么压缩pdf文件,苹果电脑怎么压缩pdf文件大小

在当今数字化时代,PDF文件已成为广泛使用的文档格式之一。然而,PDF 文件可能会因其包含的图像、图形和其他元素而导致文件较大,这可能会影响文件的传输、存储和共享。因此,对 PDF 文件进行压缩以减小其文件大小是很有必要的。今天…

【pytorch04】创建Tensor

numpy中的数据创建tensor 数据已经在numpy中了,将numpy中的数据转到tensor中来,因为我们将使用tensor在GPU上进行加速运算 从NUMPY导入的FLOAT其实是DOUBLE类型 list中的数据创建tensor FloatTensor()和大写的Tensor()接收的是shape(即数据的…

解析JavaScript中逻辑运算符和||的返回值机制

本文主要内容:了解逻辑运算符 &&(逻辑与)和 ||(逻辑或)的返回值。 在JavaScript中,逻辑运算符 &&(逻辑与)和 ||(逻辑或)的返回值可能并不总…

区块链学习03-空投篇

Hybrid 是与 以太坊 兼容的第 2 层区块链,集成了混合专家 (MoE) 框架,支持以即插即用的方式轻松创建和货币化 AI 代理。该平台旨在提高区块链应用程序中数据的完整性和可用性,支持跨各个行业开发和部署 AI 驱动的解决方案。 Hybrid 正在为其…

php上传zip压缩包到服务器并解压,解析压缩包内excel表格数据导入到数据库

需求: 1.需要管理后台将excel表格中的每条单词数据导入到数据库中. 2.每条单词数据对应的图片和音频文件需要上传到服务器中. 为了让客户上传数据方便,考虑了一下决定通过后台上传压缩包的方式实现 测试压缩包: 压缩包的目录结构 管理后台导入教材 public function upload…

kylin v10 离线安装chrome centos离线安装chrome linux离线安装谷歌浏览器

1. 先用自己联网的计算机,下载离线安装包,浏览器输入链接下载安装包: https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm 1.2. 信创环境不用执行下面,因为没网 1.3. 若为阿里云服务器,或服…

【速速收藏】适用于Linux系统的五个优秀PDF编辑器

PDF (Portable Document Format) 是便携文档格式的缩写,这是一种用于电子共享文档的标准格式,广泛应用于各种文档类型的存储和分发。然而,有时我们可能需要对PDF文档进行更改和编辑。本文将介绍五款在Linux平台上广受欢迎的PDF编辑器。 ​​…

七种不同类型测宽仪技术参数 看看哪种能用于您的产线?

在线测宽仪种类众多,原理不同,产品不同,型号不同,其技术参数也各不相同。不同的测量范围与测量精度,适用于不同规格的板材,看看您的板材能适用于哪种范围。 1、单测头平行光测宽仪 点光源发射的光经过发射…

期末成绩怎么快速发给家长

Hey各位老师们,今天来聊一个超级实用的话题:如何快速高效的向家长们传达学生的期末成绩。你可能会想,这不是很简单吗?直接班级群发个消息不就得了?但别忘了,保护学生隐私和自尊心也是很重要的哦&#xff01…

【方案】基于5G智慧工业园区解决方案(PPT原件)

5G智慧工业园区整体解决方案旨在通过集成5G通信技术、物联网、大数据和云计算等先进技术,实现园区的智能化、高效化和绿色化。 该方案首先构建高速、稳定的5G网络,确保园区内设备、人员与物流的实时连接和高效沟通。其次,通过工业物联网技术&…

RFID技术在人工晶体清洗台上的应用案例分析

RFID技术在人工晶体清洗台上的应用案例分析 应用背景 在医疗领域中人工晶体清洗台发挥着极为重要的作用,随着市场需求的持续增长、技术的不断创新、定制化趋势的加强以及环保要求的提高,人工晶体清洗台不免暴露出一下应用痛点需要解决。 痛点&#xff…

「动态规划」如何求最长摆动子序列的长度?

376. 摆动序列https://leetcode.cn/problems/wiggle-subsequence/description/ 如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也…

720漫游工具又双叒叕上新了一批新功能

一、720漫游全景图片上传支持「自定义水印」 全景图片素材上传支持自定义水印设置,通过自定义水印,可以在全景图片上打上自定义的水印图片保护用户版权利益,同时强化自身品牌露出。具体操作如下: 打开「创建720漫游作品页」-选择…

MIL图像处理那些事:定义感兴趣区域ROI的两种方法(示例项目C#源码)

文章目录 效果展示第一种方法:通过鼠标框选GetROIForm构造函数如何缩放--MdispZoom的使用Ctr+滚轮缩放放大两倍:如何平移--MdispPan的使用双击返回ROI第二种方法:直接编辑ROI框显示ROI示例项目C#源码(百度网盘)本示例提供两种方法定义感兴趣区域ROI 效果展示 第一种方法:通过鼠…

从广州到上海|荣载光的智慧 与SSHT共同探索智能照明更多想象空间

随着生活水平的提高,大众对高品质生活的追求脚步逐步加快,人们对智能照明的需求日益多样化,不再仅仅满足于传统的照明功能,而是转向智能照明系统,提出更高的需求。 展望未来,中国智能照明市场预计将迎来全…

定时触发-uniapp + uniCloud 订阅消息实战教程(三)

上一节已经对云函数有了一定的了解,但是,为了发送订阅消息,只会云函数还是差了那么一点意思,所以接下来的这一节,将带领大家熟悉一下定时触发。 熟悉定时触发 如果云函数需要定时/定期执行,即定时触发&…

云计算【第一阶段(19)】磁盘管理与文件系统 LVM与磁盘配额(二)

目录 一、LVM概述 1.1、LVM机制的基本概念 ​编辑 1.2、LVM的管理命令 1.3、lvm存储 两种机制 1.4、lvm应用实例 二、磁盘配额概述 2.1、设置磁盘配额 2.2.1、实现磁盘限额的条件 2.2.2、linux磁盘限额的特点 2.2.3、磁盘配额管理 一、LVM概述 1.1、LVM机制的基本概…

一会一展一赛,共绘 AI 新篇章!和鲸出席 GAITC 2024 全球人工智能技术大会

“毕竟西湖六月中,风光不与四时同。” ——宋 杨万里 6 月 22 日,由中国人工智能学会主办的 2024 全球人工智能技术大会 (GAITC 2024) 于杭州市余杭区未来科技城璀璨启幕,上海和今信息科技有限公司(以下简称“和鲸科技”&#x…

《知人寻香—2024香水香氛趋势白皮书》

本报告由小红书联合凯度中国共同发布:《知人寻香—2024香水香氛趋势白皮书》。这份报告细致描绘了中国香水市场的五大消费人群特征和六大消费趋势,为香水品牌提供了丰富的创新灵感。它不仅分析了中国香水市场目前的发展趋势,还预测了未来市场的潜在增长,以及消费者对香水香氛产…