2024年4月

免费、低成本、自托管、开源替代品...这些词就是本周的热门开源项目的关键字。常见的 AI 提升图片分辨率的工具,大多是在线服务或者调用接口的客户端,而「Upscaler」是一款下载即用的免费 AI 图片修复(超分)工具,无需联网可离线使用。机械臂这个词大家应该不会陌生,我查了一下这东西(不是屏幕支架)售价在几千至上万不等,这个低成本的开源机械臂项目「low_cost_robot」,仅需 250 美元确实很便宜。别再用广告满天飞的导航页了,这里推荐一款可以自托管的极简导航页「homepage」,都自托管了什么都是自己说了算!

最后,还有 3 个可平替付费服务的开源项目,它们是智能电子表格「nocodb」、开源的产品分析平台「PostHog」和 跨平台的密码管理器「keepassxc」,每个都在挑逗着我跃跃欲试的心。

  • 本文目录
    • 1. 开源新闻
      • 1.1 德国州政府计划将数万台 PC 迁移至 Linux
    • 2. 开源热搜项目
      • 2.1 免费的 AI 图像升级器:Upscaler
      • 2.2 调查问卷生成器:HeyForm
      • 2.3 低成本的机械臂:low_cost_robot
      • 2.4 自托管的极简导航页:homepage
      • 2.5 开源的智能电子表格:nocodb
    • 3. HelloGitHub 热评
      • 3.1 开源的产品分析平台:PostHog
      • 3.2 跨平台的密码管理器:keepassxc
    • 4. 结尾

1. 开源新闻

1.1 德国州政府计划将数万台 PC 迁移至 Linux

近日,德国的石勒苏益格-荷尔斯泰因州,计划将州政府的 3 万台电脑的操作系统从 Windows 迁移至 Linux,以及用开源的 LibreOffice 办公软件替代 Microsoft Office,该州政府解释是此计划是为了节省政府 IT 支出和保护数字主权。


但是,之前德国慕尼黑政府也曾实施过类似的计划,而且完成了 1.4 万台 PC 的迁移,还算了笔账得出“拥抱开源后省钱”了。但随后在长期的使用中,问题开始层出不穷、带来诸多不便,最终慕尼黑政府宣布放弃 Linux 重回 Windows 系统。这一计划历经 10 多年,一通折腾后又回到了起点,最终钱也没省下来

系列文章:

《还在担心报表不好做?不用怕,试试这个方法》(一)

《还在担心报表不好做?不用怕,试试这个方法》(二)

概要

在上一篇文章
《还在担心报表不好做?不用怕,试试这个方法》(二)
中,小编介绍了模板语言中的的一些基本概念和用法,今天小编将继续为大家介绍如何不同字段间的父子关系,是如何在模板语言中定义的。

首先重新回顾一下,上一次使用到的数据表。如下所示:

可以看到,在数据表中,销售公司和员工两列数据拥有一个所属关系。

因此在某些情况下,我们可以将员工按照所属的公司关系进行分类,如下图所示:

这样就实现了一个基于数据间的主从关系的显示布局。

数据中的主从关系

在数据表中,刚提到了销售公司与员工有一个所属关系的存在,即数据本身存在逻辑关系。员工列的数据隶属于某一个销售公司。

同时,商品和商品类型,也有一个逻辑关系,商品隶属于某一种商品类型。通常这种所属或隶属的关系,会被称为主从关系,或者父子关系。

为了方便理解,这里举一个例子,让您更好的理解这种包含,或者主从的关系。其本身更像是一种树形结构,也有另一种说法,来称呼这种关系,叫做“一对多”,1:N 的关系。

以国家为例,一个国家,有多个省,每个省有多个市,每个市又有多个区,每个区又有多个街道。显示出来就是:

国家 -> 省 -> 市 -> 区

这样一来,国家和省,一对多,省对市,一对多。对于 1:N 的关系,通常我们会将【1】这一方称之为父或者主,将【N】一方称之为子或者从。

至此,我们已经了解到概要中提到的数据表,其中有两个主从关系,即 公司:员工,类型:商品。

报表模板中的主从关系

介绍完数据中的主从关系之后,我们现在再看下面这个报表的图片,我们可以看到 A 列绑定的是 “公司” 字段, C 列绑定的是 “员工” 字段。同时,A 列会将相同的数据合并成为一个单元格。

基于上一篇文章
《还在担心报表不好做?不用怕,试试这个方法》(二)
讲到的数据绑定,我们做一个如下的模板。

生成的结果为:

但是会发现这与期望的结果不同,A 列的数据虽然仅出现了一次,但是 A 列与 C 列数据间的关系丢失了,从行的角度看,数据是错误的。

因此为了适应数据内部的主从关系,我们可以使用
Context 属性
来实现,下面来介绍一下:

Context 属性

Context的概念是可以为一个单元格指定一个主单元格。Context 的简写是【C】,将模板改写如下图所示:

这样生成的结果如下所示:

可以看到,Context 属性为 A 列和 C 列构建了一种主从关系。在生成报表时,其遵循如下原则:

  1. 先扩展主单元格,后扩展从单元格。
  2. 主单元格扩展的时候,需要复制从单元格,并调整主从关系。
  3. 从单元格获取数据的时候要受主单元格的约束,扩展的时候要考虑调整主单元格的位置和大小。

隐式主从关系

在报表中,数据的主从关系非常普遍。因此,在数据绑定后,需要为单元格指定主从关系的情况会很多。因此为了使得模板看起来更简洁,易读。我们需要实现隐式主从关系的设计,也就是说当两个单元格左右相邻时,这两个单元格会自动产生主从关系,且左边为主,右边为从。比如我们对模板进行如下修改时:

就会生成如下报表:

Range 属性

前面介绍了Context 属性和隐式主从关系,这两个都是在主从单元格的角度指定主单元格。但是在某些场合,还是需要我们手工设置 Context,例如:

  1. 空单元格或常量单元格,由于本身没有数据,不能扩展,不能隐式的成为其它单元格的主单元格。
  2. 函数都是汇总函数,不具有扩展能力,也不能隐式成为其它单元格的主单元格。

这一步可以使用 Range 属性来解决这个问题。观察下面这个模板,除了总销量单元格,其它单元格都得显示设置 Context,因为它们左边的单元格用的函数,不具有扩展性,所以没有默认的主单元格。

通过使用 Range 属性,则仅通过一个单元格来制定主从关系,模板修改后如下:

Range 属性设置单元格时有以下规则:

  1. Range 的优先级是最低的,当有主从关系冲突时,Context 或者隐式主从关系优先级更高。
  2. 扩展方向相同的 Range 属性扩展,可以嵌套,但不能出现交叉。当出现多个 Range 作用于同一个单元格时,即嵌套的情况,单元格将受到最内部的 Range 属性影响。
  3. 不同扩展方向的 Range 则允许交叉,且能解决交叉报表的问题。

这样根据上面的规则,我们就可以使用 Range 简化模板,如下例子:

通过为 A3 和 D1 设置 Range 属性,就可以轻松的给 B3,C3,D3,E3 指定了主单元格。

总结

本章主要对模板语言中数据和单元格之间主从关系进行讲解,本章的主要内容如下:

  • 数据中的主从关系
  • 报表模板中的主从关系
  • Context 属性
  • 隐式主从关系
  • Range 属性

下一期,小编将为大家介绍数据展开等其他设置是如何在模板中使用的。

扩展链接:

轻松构建低代码工作流程:简化繁琐任务的利器

数据驱动创新:以Java编程方式定制数据透视表

Java批量操作Excel文件实践

本文分享自华为云社区《
Ascend C 自定义算子 Kernel Launch调用入门
》,作者: jackwangcumt。

1 Kernel Launch概述

根据官方说明文档的介绍,Ascend C对外开放核函数的基础调用(Kernel Launch)方式,是为了简化Ascend C 自定义算子的开发流程,提供更易用的调试调优功能。当开发者完成算子核函数的开发和Tiling实现后,即可通过AscendCL运行时接口,完成算子的调用并实现自己的推理应用;同时提供简易的kernel开发工程,开发者仅需提供kernel侧实现,基于工程框架可以快速实现Kernel Launch。本文实验前提是完成了《Ascend C 自定义PRelu算子》博文的相关算子开发工程。网址为:https://bbs.huaweicloud.com/blogs/425244 。请注意:

  • 8.0.RC1.alpha002
    当前版本,Kernel Launch开放式编程为试用特性,不支持应用于商用产品中。
  • 8.0.RC1.alpha002
    当前版本暂不支持获取用户workspace特性。

2 Kernel Launch调用方式

ACLRT_LAUNCH_KERNEL调用方式对内核调用符方式进行了功能加强,核函数的调用是异步的,调用接口的使用方法如下:

ACLRT_LAUNCH_KERNEL(kernel_name)(blockDim, stream, argument list);
  • kernel_name:算子核函数的名称。
  • blockDim:规定了核函数将会在几个核上执行。每个执行该核函数的核会被分配一个逻辑ID,即block_idx,可以在核函数的实现中调用
    GetBlockIdx
    来获取block_idx。
  • stream,类型为aclrtStream,stream用于维护一些异步操作的执行顺序,确保按照应用程序中的代码调用顺序在Device上执行。
  • argument list:参数列表,与核函数的参数列表保持一致。

为帮助开发者快速的完成算子的Kernel Launch调试,官方提供了简易的算子工程,我们可以基于该算子工程中的样例代码和工程框架进行算子开发。算子工程支持的如下:

  • 该工程支持调试功能,如
    PRINTF
    功能、
    DumpTensor
  • 工程编译生成的应用程序,可通过msprof命令行方式采集和解析性能数据。

可以参考工程样例:https://gitee.com/ascend/samples/blob/master/operator/AddCustomSample/KernelLaunch/AddKernelInvocationTilingNeo ,其目录结构如下所示:

AddKernelInvocationNeo|-- cmake                                                 //CMake编译文件
|--scripts|  ├── gen_data.py                                     //输入数据和真值数据生成脚本文件
|  ├── verify_result.py                                //验证输出数据和真值数据是否一致的验证脚本
|-- CMakeLists.txt                                        //CMake编译配置文件
|-- add_custom.cpp                                     //矢量算子kernel实现
|-- data_utils.h                                          //数据读入写出函数
|-- main.cpp                                              //主函数,调用算子的应用程序,含CPU域及NPU域调用
|-- run.sh                                                //编译运行算子的脚本

基于该算子工程,开发者进行算子开发的步骤如下:

  • 完成算子kernel侧实现。
  • 编写算子调用应用程序main.cpp。
  • 编写CMake编译配置文件CMakeLists.txt。

  • 根据实际需要修改输入数据和真值数据生成脚本文件gen_data.py和验证输出数据和真值数据是否一致的验证脚本verify_result.py。
  • 根据实际需要修改编译运行算子的脚本run.sh并执行该脚本,完成算子的编译运行和结果验证。

3 Kernel Launch实现

在PReluSample目录下新建一个目录KernelLaunch,用于存放Kernel Launch调用方式的工程代码,我这里参考官方的https://gitee.com/ascend/samples/tree/master/operator/LeakyReluCustomSample/KernelLaunch/

LeakyReluKernelInvocation样例工程,并修改了相关参数,p_relu_custom.cpp 代码如下所示:

#include "kernel_operator.h"
using namespaceAscendC;

constexpr int32_t BUFFER_NUM
= 2;
constexpr int32_t TOTAL_LENGTH
= 8 * 200 * 1024;
constexpr int32_t TILE_NUM
= 32;
constexpr
float alpha = 0.002;classKernelPRelu {public:
__aicore__ inline KernelPRelu() {}
__aicore__ inline
void Init(GM_ADDR x, GM_ADDR y, uint32_t totalLength, uint32_t tileNum, floatalpha)
{
PRINTF(
"[npu debug] >>> GetBlockNum() %d", GetBlockNum());
ASSERT(GetBlockNum()
!= 0 && "block dim can not be zero!");this->blockLength = totalLength /GetBlockNum();this->tileNum =tileNum;this->alpha = static_cast<float>(alpha);
ASSERT(tileNum
!= 0 && "tile num can not be zero!");this->tileLength = this->blockLength / tileNum /BUFFER_NUM;//get start index for current core, core parallel xGm.SetGlobalBuffer((__gm__ float*)x + this->blockLength * GetBlockIdx(), this->blockLength);
yGm.SetGlobalBuffer((__gm__
float*)y + this->blockLength * GetBlockIdx(), this->blockLength);//pipe alloc memory to queue, the unit is Bytes pipe.InitBuffer(inQueueX, BUFFER_NUM, this->tileLength * sizeof(float));
pipe.InitBuffer(outQueueY, BUFFER_NUM,
this->tileLength * sizeof(float));
pipe.InitBuffer(tmpBuffer1,
this->tileLength * sizeof(float));//pipe.InitBuffer(tmpBuffer2, this->tileLength * sizeof(float)); }
__aicore__ inline
voidProcess()
{
//loop count need to be doubled, due to double buffer int32_t loopCount = this->tileNum *BUFFER_NUM;//tiling strategy, pipeline parallel for (int32_t i = 0; i < loopCount; i++) {
CopyIn(i);
Compute(i);
CopyOut(i);
}
}
private:
__aicore__ inline
voidCopyIn(int32_t progress)
{
//alloc tensor from queue memory LocalTensor<float> xLocal = inQueueX.AllocTensor<float>();//copy progress_th tile from global tensor to local tensor DataCopy(xLocal, xGm[progress * this->tileLength], this->tileLength);//enque input tensors to VECIN queue inQueueX.EnQue(xLocal);
}
__aicore__ inline
voidCompute(int32_t progress)
{
//deque input tensors from VECIN queue LocalTensor<float> xLocal = inQueueX.DeQue<float>();
LocalTensor
<float> yLocal = outQueueY.AllocTensor<float>();
LocalTensor
<float> tmpTensor1 = tmpBuffer1.Get<float>();float inputVal = 0.0;
Maxs(tmpTensor1, xLocal, inputVal,
this->tileLength); //x >= 0 --> x//x < 0 Mins(xLocal, xLocal, inputVal, this->tileLength);
Muls(xLocal, xLocal,
this->alpha, this->tileLength);
Add(yLocal, xLocal, tmpTensor1,
this->tileLength);
outQueueY.EnQue
<float>(yLocal);//free input tensors for reuse inQueueX.FreeTensor(xLocal);
}
__aicore__ inline
voidCopyOut(int32_t progress)
{
//deque output tensor from VECOUT queue LocalTensor<float> yLocal = outQueueY.DeQue<float>();//copy progress_th tile from local tensor to global tensor DataCopy(yGm[progress * this->tileLength], yLocal, this->tileLength);//free output tensor for reuse outQueueY.FreeTensor(yLocal);
}
private:
TPipe pipe;
TBuf
<QuePosition::VECCALC>tmpBuffer1;//TBuf<QuePosition::VECCALC> tmpBuffer1, tmpBuffer2;//create queues for input, in this case depth is equal to buffer num TQue<QuePosition::VECIN, BUFFER_NUM>inQueueX;//create queue for output, in this case depth is equal to buffer num TQue<QuePosition::VECOUT, BUFFER_NUM>outQueueY;
GlobalTensor
<float>xGm, yGm;
uint32_t blockLength;
uint32_t tileNum;
uint32_t tileLength;
floatalpha;
};
extern "C" __global__ __aicore__ voidp_relu_custom(GM_ADDR x, GM_ADDR y) {//GET_TILING_DATA(tiling_data, tiling);//TODO: user kernel impl KernelPRelu op;
op.Init(x, y, TOTAL_LENGTH, TILE_NUM, alpha);
op.Process();
}

#ifndef __CCE_KT_TEST__
//call of kernel function void p_relu_custom_do(uint32_t blockDim, void* l2ctrl, void* stream, uint8_t* x, uint8_t*y)
{
p_relu_custom
<<<blockDim, l2ctrl, stream>>>(x, y);
}
#endif

main.cpp 代码如下所示 :

/** Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
* This file constains code of cpu debug and npu code.We read data from bin file
* and write result to file.
*/#include"data_utils.h"#ifndef __CCE_KT_TEST__
#include
"acl/acl.h" extern void p_relu_custom_do(uint32_t coreDim, void* l2ctrl, void* stream, uint8_t* x, uint8_t*y);#else#include"tikicpulib.h" extern "C" __global__ __aicore__ voidp_relu_custom(GM_ADDR x, GM_ADDR y);#endifint32_t main(int32_t argc,char*argv[])
{
uint32_t blockDim
= 8;
size_t inputByteSize
= 8 * 200 * 1024 * sizeof(float);
size_t outputByteSize
= 8 * 200 * 1024 * sizeof(float);

#ifdef __CCE_KT_TEST__
//CPU uint8_t* x = (uint8_t*)AscendC::GmAlloc(inputByteSize);
uint8_t
* y = (uint8_t*)AscendC::GmAlloc(outputByteSize);
printf(
"[cpu debug]>>> inputByteSize: %d\n", inputByteSize);

ReadFile(
"./input/input_x.bin", inputByteSize, x, inputByteSize);
AscendC::SetKernelMode(KernelMode::AIV_MODE);
ICPU_RUN_KF(p_relu_custom, blockDim, x, y);
//use this macro for cpu debug WriteFile("./output/output_y.bin", y, outputByteSize);
AscendC::GmFree((
void *)x);
AscendC::GmFree((
void *)y);#else //NPU//CHECK_ACL(aclInit(nullptr)); CHECK_ACL(aclInit("./acl.json"));
aclrtContext context;
int32_t deviceId
= 0;
CHECK_ACL(aclrtSetDevice(deviceId));
CHECK_ACL(aclrtCreateContext(
&context, deviceId));
aclrtStream stream
=nullptr;
CHECK_ACL(aclrtCreateStream(
&stream));

uint8_t
*xHost, *yHost;
uint8_t
*xDevice, *yDevice;
CHECK_ACL(aclrtMallocHost((
void**)(&xHost), inputByteSize));
CHECK_ACL(aclrtMallocHost((
void**)(&yHost), outputByteSize));
CHECK_ACL(aclrtMalloc((
void**)&xDevice, inputByteSize, ACL_MEM_MALLOC_HUGE_FIRST));
CHECK_ACL(aclrtMalloc((
void**)&yDevice, outputByteSize, ACL_MEM_MALLOC_HUGE_FIRST));

ReadFile(
"./input/input_x.bin", inputByteSize, xHost, inputByteSize);
CHECK_ACL(aclrtMemcpy(xDevice, inputByteSize, xHost, inputByteSize, ACL_MEMCPY_HOST_TO_DEVICE));

p_relu_custom_do(blockDim, nullptr, stream, xDevice, yDevice);
CHECK_ACL(aclrtSynchronizeStream(stream));

CHECK_ACL(aclrtMemcpy(yHost, outputByteSize, yDevice, outputByteSize, ACL_MEMCPY_DEVICE_TO_HOST));
WriteFile(
"./output/output_y.bin", yHost, outputByteSize);

CHECK_ACL(aclrtFree(xDevice));
CHECK_ACL(aclrtFree(yDevice));
CHECK_ACL(aclrtFreeHost(xHost));
CHECK_ACL(aclrtFreeHost(yHost));

CHECK_ACL(aclrtDestroyStream(stream));
CHECK_ACL(aclrtDestroyContext(context));
CHECK_ACL(aclrtResetDevice(deviceId));
CHECK_ACL(aclFinalize());
#endif return 0;
}

执行如下代码进行NPU上板调试和CPU调试:

#npu
bash run.sh Ascend310P1 npu_onboard
# cpu
bash run.sh Ascend310P1 cpu

QQ截图20240408155058.png

QQ截图20240408155212.png

点击关注,第一时间了解华为云新鲜技术~

在项目开发,迭代交付过程中开发人员,测试人员需要针对系统微服务API做调试,回归测试,性能测试。自动化测试,一个好的平台本质上需要解决API测试的5大基本问题。

  • 1.支持不同的项目,角色,技术人员多人协作
  • 2.支持定义多个不同的测试环境
  • 3.支持定义各种被测系统,API,功能,性能用例
  • 4.支持功能,性能,回归,自动化测试,对比
  • 5.功能/性能明细报告,统计报告,通知

今天要推荐的这款自动化测试平台:
AutoMeter-API
,就完美的解决了这些问题。

1、介绍

AutoMeter-API
是一款专门为测试人员准备的针对分布式服务,微服务API,接口,功能和性能一体的自动化测试平台,一站式解决项目管理,微服务,API接口,环境管理,测试用例,前置条件,变量管理,测试集合,测试计划,测试报告,支持分布式多机执行功能/性能测试兼容的一体化测试工作平台。

项目地址:

https://gitee.com/season-fan/autometer-api
https://github.com/AutoMeter/AutoMeter-API

2、功能使用

2.1 特性运行测试的环境如何定义?

一般个人,公司在使用分布式,微服务架构,从开发到发布上线可能会经过多套环境测试验证,比如开发环境,测试环境,准生产环境,生产环境,其中测试环境又可能分为多套功能测试环境和性能测试环境,多套环境分开管理,可以有序而不相互干扰进行测试工作 每套环境由开发的分布式服务,微服务,即提供api服务能力的实体,中间件(数据库,nosql,web服务器等等)这些元素组成 对于测试来说以上的元素我们需要部署到指定的服务器或者容器中整体来作为一套环境做测试工作。

2.2 针对什么来做测试?

针对采用分布式服务,微服务架构开发的服务实体,我们可以定义此服务的访问协议,端口。 这些服务包含了若干个API接口,对外提供了不同的功能,每个API会有对应的参数,我们可以定义这些API接口,参数来进行自动化测试。

2.3 设计测试用例

设计API接口用例基础要素:用例数据,用例的断言,快捷调试等基础,支持复制,批量复制,后续支持自动生成基础用例 从个人或者公司的角度看,用例的数量和类型来决定需要做怎么样的执行,如果用例数量庞大,并且需要快速得到结果,本质上我们需要拆分用例由多机并行执行满足需求,也就是多点执行,如果需要性能的测试,执行性能的机器我们可以是低性能的多台机器发起或者是高性能的少量机器发起,所以说怎么运行是根据需要来定制执行用例的类型和机器数量。

2.4 运行用例

根据测试业务需要,定义成多个测试集合来满足不同的测试需要,功能测试,性能测试,回归测试,CI对接自动化测试 支持立即执行,定时执行,某天执行等多种方式。

2.5 获得什么样的反馈报告?

对于用例执行完,我们希望看到什么反馈,对于开发,测试,或者其他技术人员,我们希望看到运行的统计信息 以及用例详细信息:结果状态,运行时间,请求数据,API的具体响应,我的期望,断言的详细信息,以及用例运行时的信息 对应性能来说,我们还希望能得到统计的信息,比如整体性能的时间,tps,响应时间,99%pct等,以及被测系统优化前后性能结果的多次对比。

更多使用指南,实践场景,可参考:
https://gitee.com/season-fan/autometer-api/wikis/AutoMeter%E6%96%87%E6%A1%A3/%E5%AE%9E%E8%B7%B5%E5%9C%BA%E6%99%AF

近年来,生成式人工智能(AI)不仅在技术界引起了广泛关注,更成为了推动多个行业革新的关键力量。这种技术之所以备受瞩目,不仅在于其独特的创造性和高效性,还在于它对未来商业模式和社会结构可能产生的深远影响。在这篇文章中,我们将全面介绍生成式AI的概念、定义、应用以及潜在风险,并对比Chat和Agent两种形式,分析它们在未来的发展前景中谁更具优势。

生成式AI

生成式AI,即AI-Generated Content,指的是利用人工智能技术自动生成内容,这些内容可以是文本、图像、音视频等多种形式。生成式AI与其他内容生产模式(如职业生产内容OGC、专业生产内容PGC和用户生产内容UGC)相比,具有更高的自动化程度和创新性。

在国内,生成式AI技术的迅猛发展已经催生了一系列备受瞩目的产品和应用。例如,盘古、文心一言、千问、混元以及Kimi等聊天机器人应用,它们通过模拟人类的对话方式,为用户提供了前所未有的交互体验。这些应用不仅在智能问答和日常对话中表现出色,更在特定领域展现了其独特的优势,如文心一言在古诗词创作方面的精湛技艺,以及Kimi在处理长文本方面的技术突破。

除此之外,国内外还涌现出了众多以生成音乐、视频和图片为主的创新产品,如Midjourney和Stable Diffusion等,它们利用先进的算法生成令人惊叹的视觉艺术作品。通义舞王和suna等产品则在舞蹈和音乐创作领域展现了AI的无限潜力。

尽管国内的生成式AI产品在某些方面已经能够与国际上的GPT技术相媲美,但我们也必须认识到,国外的GPT技术是在多年的深入研究和大量数据积累的基础上发展起来的。国内的AI技术虽然起步较晚,但发展速度迅猛,已经在多个细分领域展现出了强大的竞争力和创新能力。随着技术的不断进步和市场的日益成熟,我们有理由相信,国内的生成式AI产品将在未来的竞争中占据更加重要的地位,并在全球AI领域中发挥更加关键的作用。

image

生成式AI技术虽然在多个领域展现出了其强大的能力和广泛的应用前景,但与此同时,我们也不得不正视它所面临的一些挑战和局限性。首先,能源消耗问题是生成式AI技术的一个重要缺点。这些模型通常需要大量的计算资源来进行训练和运行,这不仅涉及到庞大的电力需求,还关系到能源的可持续性和环境影响。即便是在业界领先的OpenAI等机构,也面临着高昂的运营成本和资源投入,这在一定程度上限制了技术的普及和应用。

其次,生成式AI模型的体积问题也不容忽视。当前的AI模型往往拥有数十亿甚至数百亿的参数,这使得模型的存储和运算要求变得极为苛刻。这种对大规模数据和计算力的依赖,不仅增加了模型部署的复杂性,也限制了其在资源受限环境下的应用可能性。此外,为了实现全方位的性能提升,模型的规模和复杂度不断增加,这进一步加剧了能源消耗和存储需求的问题。这两个缺点在本质上是相互关联的。

AIGC与Agent的区别

AIGC通常指的是基于规则或机器学习模型的系统,能够与用户进行交互式对话。Chat的主要目的是提供信息、解答疑问或进行娱乐对话。它通常不具备长期记忆和复杂的个性化特征。

在开发和使用生成式AI模型的过程中,尤其是通过API进行封装和应用时,我们经常会面临上下文管理的挑战。这里的上下文限制并不仅仅是指代token数量的消耗,而是更深层次地涉及到模型对于长对话记忆的能力。尽管一些国内的AI产品,如Kimi,已经在处理长文本对话方面取得了显著进展,但要让这些chat模型像人类一样记住并理解整个对话历史仍然是一个巨大的挑战。

这是因为,目前的chat模型大多是基于短期记忆设计的,它们主要依赖于最近的对话片段来生成回应。这些模型在底层训练时,并没有接触到需要长期记忆和复杂上下文管理的数据集,因此它们在处理跨越长时间跨度的对话时,往往无法像人类那样保持连贯性和一致性。这意味着,无论chat模型在技术上如何进步,它们在处理长对话记忆方面仍然存在局限性。

Agent则更为复杂,它不仅能够进行交互对话,还具备一定的个性化特征、长期记忆和情感表达能力。Agent的设计初衷是通过模拟一个具有独立个性和情感的完整个体,来与用户在多种不同的情境中进行深入的互动和交流,从而建立起更加丰富和有意义的情感联系。在当前众多的Agent产品中,coze无疑是其中的佼佼者,它通过先进的技术和创新的设计理念,成功地吸引了大量用户的关注和喜爱。同时,我也投身于这一领域,尝试开发了自己的bot应用,以期探索和实现更加智能化和个性化的用户体验。

在这个过程中,我深刻地认识到,尽管大型的AI模型宛如一个强大的大脑,提供了丰富的知识和处理能力,但Agent则更进一步,它不仅具备智能,还拥有模拟人类行为和情感的能力。Agent可以通过集成各种专门的插件和工具,来解决那些即使是最强大的AI模型也难以应对的问题。这种方法不仅极大地扩展了Agent的功能范围,还显著降低了能源消耗,并且在很大程度上减少了模型训练的时间和成本。

image

在当前的人工智能应用市场中,基于知识库的对话系统无疑是最为广泛部署和使用的解决方案之一。这类系统的核心机制通常涉及使用向量数据库来存储和检索知识,进而利用大型语言模型(LLM)对检索到的信息进行深入分析和对话生成。虽然语言模型的质量和效能直接影响着对话的准确性和流畅性,但这种基于知识库的方法已经在很大程度上解决了长期记忆的问题。得益于向量数据库的高效存储能力,系统能够维护大量信息,并保持快速响应。

此外,为了进一步提升AI系统的功能性和用户体验,各种插件的开发和集成成为了关键环节。在国内,许多领先的大型语言模型都已经建立了自己的插件商店,使得开发者可以轻松地为AI系统添加新的功能和服务。以coze为例,它提供了一套完整的插件开发、发布和上架流程,极大地扩展了AI系统的能力范围。其中,最初引起广泛关注的插件之一就是代码执行器,它使得AI不仅能够理解代码,还能实际执行编程任务。

除了单一功能的插件,更为复杂的应用场景往往需要通过工作流开发来实现。通过将不同的功能和插件进行组合,可以创建出更加人性化和前沿化的对话流程。这样的工作流不仅能够提高机器人回答的质量和相关性,还能够根据用户的特定需求进行定制化,使得AI系统在各种复杂情境下都能提供恰当、及时且富有创意的响应。

生成式AI的应用与风险

生成式AI的应用范围非常广泛,从自动写作、艺术创作到虚拟助手和游戏角色设计等。它能够极大地提高内容生产的效率,降低成本,并为用户提供个性化的体验。

image

然而,生成式AI也带来了一些风险,包括信息的真实性、版权问题、以及可能的伦理和社会影响。至今各个国家也没有对AI发布相应的版权法律法规。因为这也是一个比较难缠的问题,我们就不多说了。但是AI确实影响了我们大部分生活。我简单说说风险:

  • 信息真实性:生成式AI可能会产生虚假或误导性的内容,尤其是在新闻和社交媒体上,这可能导致信息的误传和信任危机。
  • 版权问题:AI生成的内容可能涉及版权争议,尤其是当AI学习并模仿特定作者或艺术家的风格时,版权归属可能变得模糊。
  • 伦理与隐私:生成式AI可能会无意中泄露敏感信息,尤其是在处理个人数据时,需要确保遵守隐私保护法规和伦理标准。
  • 工作替代:在某些领域,生成式AI可能会替代人类的工作,引发就业问题和职业转型的挑战。
  • 社会影响:生成式AI可能会加剧社会分化,特别是在教育资源和经济机会分配不均的情况下,技术的发展可能不惠及所有人。
  • 技术滥用:生成式AI技术可能被用于制造虚假证据、网络钓鱼和其他恶意活动,对社会秩序和个人安全构成威胁。

总结

Chat的优势在于其简单、易于实现和部署。它适用于提供快速响应和标准化服务的场景,如客户支持和常见问题解答。然而,Chat的局限性在于缺乏深度个性化和长期记忆,这限制了它在建立深度用户关系方面的潜力。

Agent则提供了更为丰富和复杂的交互体验。它可以模拟真实人物的行为和情感,为用户提供更为沉浸式的体验。Agent的挑战在于其开发和维护的复杂性,以及对大量数据和计算资源的需求。

随着时间的推移,将生成式人工智能无缝融入现有产品已经成为一种普遍的趋势。这意味着人工智能不再仅仅是独立存在的应用,而是与其他产品和服务紧密结合,为用户提供更加智能化和个性化的体验。举例来说,各种小助手等等层出不穷,为用户提供帮助。这只是冰山一角,还有更多类似的应用。

尽管生成式AI在实际应用中面临着一系列挑战和局限性,但这并不足以否定其广阔的发展前景。实际上,AI生成内容(AIGC)与智能代理(Agent)之间的关系更像是一种互为补充的伙伴关系,而非相互排斥的竞争关系。AIGC的高效内容生产能力和大规模数据处理能力使其成为资金雄厚的大公司的理想选择,这些公司通常拥有足够的资源来投资于复杂的AI系统,并从中获得商业价值。相比之下,智能代理(Agent)以其灵活性和个性化服务的特点,更受到资源有限的小型公司的青睐,它们可以利用Agent的定制化交互和易于集成的特性来提升用户体验和服务质量。

在AI技术的发展过程中,不存在万能的解决方案,即所谓的“银弹”。每个组织都需要根据自身的具体情况,包括技术能力、资金状况、市场需求等因素,来选择最适合自己的技术路径和实施方案。关键在于能否准确识别自身的优势和劣势,并在此基础上做出明智的决策。只要所选择的发展策略能够为组织带来价值,无论是采用AIGC还是Agent,或是两者的结合,都是值得探索和实施的。