前言

前几日在浏览devblogs.microsoft.com的时候,看到了一篇名为Image to Text with Semantic Kernel and HuggingFace的文章。这篇文章大致的内容讲的是,使用Semantic Kernel结合HuggingFace来实现图片内容识别。注意,这里说的是图片内容识别,并非是OCR,而是它可以大致的描述图片里的主要内容。我个人对这些还是有点兴趣的,于是就尝试了一下,本文就是我体验过程的记录。

示例

话不多说,直接展示代码。按照文档上说的,使用HuggingFace ImageToText构建自己的应用程序时,需要使用以下的包

  • Microsoft.SemanticKernel
  • Microsoft.SemanticKernel.Connectors.HuggingFace

第一个包是SemanticKernel包,提供构建AI应用的基础能力。第二个包是HuggingFace包,提供HuggingFaceAPI,方便我们调用HuggingFace的模型。需要注意的是这个包是预发行版,所以在用VS添加的时候需要在VS勾选包括预发行版。使用起来也非常简单,代码如下所示

var kernel = Kernel.CreateBuilder().AddHuggingFaceImageToText("Salesforce/blip-image-captioning-base").Build();
IImageToTextService service = kernel.GetRequiredService<IImageToTextService>();
var imageBinary = File.ReadAllBytes(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "demo.jpg"));
var imageContent = new ImageContent(imageBinary) { MimeType = "image/jpeg" };
var textContent = await service.GetTextContentAsync(imageContent);
Console.WriteLine($"已识别图片中描述的内容: {textContent.Text}");

代码很简单,运行起来试试效果,发现是直接报错了,报错信息如下:

Microsoft.SemanticKernel.HttpOperationException:“由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。 (api-inference.huggingface.co:443)”

原因也很简单,我本地连接不了huggingface,这个需要换种上网方式才能解决。看来默认是请求的api-inference.huggingface.co:443这个地址,在源码中求证了一下HuggingFaceClient.cs#L41,发现确实是这样

internal sealed class HuggingFaceClient
{
private readonly IStreamJsonParser _streamJsonParser;
private readonly string _modelId;
private readonly string? _apiKey;
private readonly Uri? _endpoint;
private readonly string _separator;
private readonly HttpClient _httpClient;
private readonly ILogger _logger; internal HuggingFaceClient(
string modelId,
HttpClient httpClient,
Uri? endpoint = null,
string? apiKey = null,
IStreamJsonParser? streamJsonParser = null,
ILogger? logger = null)
{
Verify.NotNullOrWhiteSpace(modelId);
Verify.NotNull(httpClient);
//默认请求地址
endpoint ??= new Uri("https://api-inference.huggingface.co");
this._separator = endpoint.AbsolutePath.EndsWith("/", StringComparison.InvariantCulture) ? string.Empty : "/";
this._endpoint = endpoint;
this._modelId = modelId;
this._apiKey = apiKey;
this._httpClient = httpClient;
this._logger = logger ?? NullLogger.Instance;
this._streamJsonParser = streamJsonParser ?? new TextGenerationStreamJsonParser();
}
}

它只是默认情况下请求的api-inference.huggingface.co这个地址,如果想要请求其他地址的话,需要自己实现一个api,然后通过SemanticKernel调用。

曲线实现

上面提到了既然是huggingfaceapi我们访问不到,而且我不是很喜欢这种在线方式,太依赖三方接口的稳定性了,我更喜欢本地可以部署的,这样的话就不用考虑网络和稳定性问题了。于是想到了一个曲线的方式,那是不是可以自己实现一个api,然后通过SemanticKernel调用呢?答案是肯定的。

blip-image-captioning-base模型

通过上面的示例我们可以看到它使用ImageToText图片识别模型使用的是Salesforce/blip-image-captioning-base这个模型,我们可以自行下载这个模型到本地。上面说了huggingface需要换种上网方式,不过没关系这个国内是有镜像网站的https://hf-mirror.com/,找到模型地址Salesforce/blip-image-captioning-base选择Files and versions标签把里面的所有文件下载到本地文件夹即可,大概是1.84 G左右。比如我是放到我的D:\Users\User\blip-image-captioning-base文件夹内,目录结构如下所示

这个模型没有特殊要求,我的电脑是16G内存i5处理器都可以运行起来。接下来用调用这个模型试一试,该模型是适配了transformers框架,所以调用起来比较加单,代码如下所示

from PIL import Image
from transformers import BlipProcessor, BlipForConditionalGeneration processor = BlipProcessor.from_pretrained("D:\\Users\\User\\blip-image-captioning-base")
model = BlipForConditionalGeneration.from_pretrained("D:\\Users\\User\\blip-image-captioning-base") img_url = '01f8115545963d0000019ae943aaad.jpg@1280w_1l_2o_100sh.jpg'
raw_image = Image.open(img_url).convert('RGB') inputs = processor(raw_image, return_tensors="pt")
out = model.generate(**inputs)
en_text = processor.decode(out[0], skip_special_tokens=True)
print(f'已识别图片中描述的内容:{en_text}')

然后我使用了我本地的一张图片

运行这段代码之后输出信息如下所示

已识别图片中描述的内容:a kitten is standing on a tree stump

识别的结果描述的和图片内容大致来说是一致的,看来简单的图片效果还是不错的。不过美中不足的是,它说的是英文,给中国人看说英文这明显不符合设定。所以还是得想办法把英文翻译成中文。

opus-mt-en-zh模型

上面我们看到了blip-image-captioning-base模型效果确实还可以,只是它返回的是英文内容,这个对于英文不足六级的人来说读起来确实不方便。得想办法解决把英文翻译成中文的问题。因为不想调用翻译接口,所以这里我还是想使用模型的方式来解决这个问题。使用Bing搜索了一番,发现推荐的opus-mt-en-zh模型效果不错,于是打算试一试。还是在hf-mirror.com上下载模型到本地文件夹内,方式方法如上面的blip-image-captioning-base模型一致。它的大小大概在1.41 GB左右,也是CPU可运行的,比如我的是下载到本地D:\Users\User\opus-mt-en-zh路径下,内容如下所示

接下来还是老规矩,调用一下这个模型看看效果,不过在huggingface对应的仓库里并没有给出如何使用模型的示例,于是去stackoverflow上找到两个类似的内容参考了一下

通过上面的连接可以看到,非常好的地方就是,这个模型也是兼容transformers框架的,所以调用起来非常简单,把上面的英文内容拿过来试一试, 代码如下所示

from transformers import AutoTokenizer, AutoModelWithLMHead

model = AutoModelWithLMHead.from_pretrained("D:\\Users\\User\\opus-mt-en-zh")
tokenizer = AutoTokenizer.from_pretrained("D:\\Users\\User\\opus-mt-en-zh")
# 英文文本
en_text='a kitten is standing on a tree stump' encoded = tokenizer([en_text], return_tensors="pt")
translation = model.generate(**encoded)
# 翻译后的中文内容
zh_text = tokenizer.batch_decode(translation, skip_special_tokens=True)[0]
print(f'已识别图片中描述的内容:\r\n英文:{en_text}\r\n中文:{zh_text}')

运行这段代码之后输出信息如下所示

已识别图片中描述的内容:
英文:a kitten is standing on a tree stump
中文:一只小猫站在树桩上

这下看着舒服了,至少不用借助翻译工具了。模型的部分到此就差不多了,接下来看如何整合一下模型的问题。

结合Microsoft.SemanticKernel.Connectors.HuggingFace

上面我们调研了图片内容识别的模型和英文翻译的模型,接下来我们看一下如何使用Microsoft.SemanticKernel.Connectors.HuggingFace去整合我们本地的模型。我们通过上面了解到了他说基于http的方式去调用了,这就很明确了。只需要知道调用的路径、请求参数、返回参数就可以自己写接口来模拟了。这个就需要去看一下SemanticKernel里面涉及的代码了。核心类就是HuggingFaceClient类,我们来看下它的GenerateTextAsync方法的代码

public async Task<IReadOnlyList<TextContent>> GenerateTextAsync(
string prompt,
PromptExecutionSettings? executionSettings,
CancellationToken cancellationToken)
{
string modelId = executionSettings?.ModelId ?? this._modelId;
var endpoint = this.GetTextGenerationEndpoint(modelId);
var request = this.CreateTextRequest(prompt, executionSettings);
using var httpRequestMessage = this.CreatePost(request, endpoint, this._apiKey); string body = await this.SendRequestAndGetStringBodyAsync(httpRequestMessage, cancellationToken)
.ConfigureAwait(false); var response = DeserializeResponse<TextGenerationResponse>(body);
var textContents = GetTextContentFromResponse(response, modelId); return textContents;
} //组装请求路径方法
private Uri GetTextGenerationEndpoint(string modelId)
=> new($"{this._endpoint}{this._separator}models/{modelId}"); private HttpRequestMessage CreateImageToTextRequest(ImageContent content, PromptExecutionSettings? executionSettings)
{
var endpoint = this.GetImageToTextGenerationEndpoint(executionSettings?.ModelId ?? this._modelId); var imageContent = new ByteArrayContent(content.Data?.ToArray());
imageContent.Headers.ContentType = new(content.MimeType); var request = new HttpRequestMessage(HttpMethod.Post, endpoint)
{
Content = imageContent
}; this.SetRequestHeaders(request); } private Uri GetImageToTextGenerationEndpoint(string modelId)
=> new($"{this._endpoint}{this._separator}models/{modelId}");

通过上面的GenerateTextAsync方法代码我们可以得到我们自定义接口时所需要的全部信息

  • 首先是请求路径问题, 我们通过GetTextGenerationEndpointGetImageToTextGenerationEndpoint方法可以看到,拼接的路径地址服务地址/models/模型id,比如我们上面调用的是Salesforce/blip-image-captioning-base模型,拼接的路径就是models/Salesforce/blip-image-captioning-base
  • 其次通过CreateImageToTextRequest方法我们可以得知,请求参数的类型是ByteArrayContent,请求参数的ContentTypeimage/jpeg。也就是把我们的图片内容转换成字节数组放到请求body请求体里即可,然后POST到具体的服务里即可。
  • 通过TextGenerationResponse返回类型我们可以知道这个承载的是返回参数的类型里。

我们来看下TextGenerationResponse类的定义

internal sealed class TextGenerationResponse : List<GeneratedTextItem>
{
internal sealed class GeneratedTextItem
{
[JsonPropertyName("generated_text")]
public string? GeneratedText { get; set; }
}
}

这个参数比较简单,就是返回一个包含generated_text字段的数组即可对应成json格式的话就是[{"generated_text":"识别结果"}]。接下来我们需要做的是把模型整合换成http接口,这样的话Microsoft.SemanticKernel.Connectors.HuggingFace就可以调用这个接口了。这里我选择使用的是python的fastapiweb框架去整合成webapi服务,其他框架也可以,只要入参返回的结果把握住就可以,整合后效果如下所示

import io
import uvicorn
from fastapi import FastAPI, Request
from PIL import Image
from transformers import BlipProcessor, BlipForConditionalGeneration, AutoTokenizer, AutoModelWithLMHead app = FastAPI() # 图片内容识别模型
processor = BlipProcessor.from_pretrained("D:\\Users\\User\\blip-image-captioning-base")
blipModel = BlipForConditionalGeneration.from_pretrained("D:\\Users\\User\\blip-image-captioning-base") # 英文翻译模型
tokenizer = AutoTokenizer.from_pretrained("D:\\Users\\User\\opus-mt-en-zh")
opusModel = AutoModelWithLMHead.from_pretrained("D:\\Users\\User\\opus-mt-en-zh") # 定义接口函数
@app.post("/models/Salesforce/blip-image-captioning-base", summary="图片内容识别")
async def blip_image_captioning_base(request: Request):
# 获取请求参数
request_object_content: bytes = await request.body()
# 转换图片内容
raw_image = Image.open(io.BytesIO(request_object_content)).convert('RGB') # 识别图片内容
inputs = processor(raw_image, return_tensors="pt")
out = blipModel.generate(**inputs)
en_text = processor.decode(out[0], skip_special_tokens=True) # 英译汉
encoded = tokenizer([en_text], return_tensors="pt")
translation = opusModel.generate(**encoded)
zh_text = tokenizer.batch_decode(translation, skip_special_tokens=True)[0]
return [{"generated_text": zh_text}] if __name__ == '__main__':
# 运行fastapi程序
uvicorn.run(app="snownlpdemo:app", host="0.0.0.0", port=8000, reload=True)

这里我们把服务暴露到8000端口上去,等待服务启动成功即可,然后我们去改造Microsoft.SemanticKernel.Connectors.HuggingFace的代码如下所示

//这里我们传递刚才自行构建的fastapi服务地址
var kernel = Kernel.CreateBuilder().AddHuggingFaceImageToText("Salesforce/blip-image-captioning-base", new Uri("http://127.0.0.1:8000")).Build();
IImageToTextService service = kernel.GetRequiredService<IImageToTextService>();
var imageBinary = File.ReadAllBytes(Path.Combine(Directory.GetCurrentDirectory(), "01f8115545963d0000019ae943aaad.jpg@1280w_1l_2o_100sh.jpg"));
var imageContent = new ImageContent(imageBinary) { MimeType = "image/jpeg" };
var textContent = await service.GetTextContentAsync(imageContent);
Console.WriteLine($"已识别图片中描述的内容: {textContent.Text}");

这样的话代码改造完成,需要注意的是得先运行fastapi服务等待服务启动成功之后,再去然后运行dotnet项目,运行起来效果如下所示

已识别图片中描述的内容: 一只小猫站在树桩上

改造成插件

我们使用上面的方式是比较生硬古板的,熟悉SemanticKernel的同学都清楚它是支持自定插件的,这样的话它可以根据我们的提示词来分析调用具体的插件,从而实现调用我们自定义的接口。这是一个非常实用的功能,让SemanticKernel的调用更加灵活,是对AIGC能力的扩展,可以让他调用我们想调用的接口或者服务等等。话不多说,我们定义一个插件让它承载我们识别图片的内容,这样的话就可以通过SemanticKernel的调用方式去调用这个插件了。定义插件的代码如下所示

public class ImageToTextPlugin
{
private IImageToTextService _service;
public ImageToTextPlugin(IImageToTextService service)
{
_service = service;
} [KernelFunction]
[Description("根据图片路径分析图片内容")]
public async Task<string> GetImageContent([Description("图片路径")] string imagePath)
{
var imageBinary = File.ReadAllBytes(imagePath);
var imageContent = new ImageContent(imageBinary) { MimeType = "image/jpeg" };
var textContent = await _service.GetTextContentAsync(imageContent);
return $"图片[{imagePath}]分析内容为:{textContent.Text!}";
}
}

这里需要注意的是我们定义的方法的Description和参数的Description,其中GetImageContent方法的DescriptionSemanticKernel的提示词,这样在调用的时候就可以通过提示词来调用这个方法了。参数imagePathDescription这样OpenAI就知道如何在提示词里提取出来对应的参数信息了。好了接下来我们看下如何使用这个插件

using HttpClient httpClient = new HttpClient(new RedirectingHandler());
var executionSettings = new OpenAIPromptExecutionSettings()
{
ToolCallBehavior = ToolCallBehavior.EnableKernelFunctions,
Temperature = 1.0
};
var builder = Kernel.CreateBuilder().AddHuggingFaceImageToText("Salesforce/blip-image-captioning-base", new Uri("http://127.0.0.1:8000"));
var kernel = builder.Build();
ImageToTextPlugin imageToTextPlugin = new ImageToTextPlugin(kernel.GetRequiredService<IImageToTextService>());
kernel.Plugins.AddFromObject(imageToTextPlugin); var chatCompletionService = new OpenAIChatCompletionService("gpt-3.5-turbo-0125", "你的apiKey", httpClient: httpClient); Console.WriteLine("现在你可以开始和我聊天了,输入quit退出。等待你的问题:");
do
{
var prompt = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(prompt))
{
if (prompt.ToLowerInvariant() == "quit")
{
Console.WriteLine("非常感谢!下次见。");
break;
}
else
{
var history = new ChatHistory();
history.AddUserMessage(prompt);
//调用gpt的chat接口
var result = await chatCompletionService.GetChatMessageContentAsync(history,
executionSettings: executionSettings,
kernel: kernel);
//判断gpt返回的结果是否是调用插件
var functionCall = ((OpenAIChatMessageContent)result).GetOpenAIFunctionToolCalls().FirstOrDefault();
if (functionCall != null)
{
kernel.Plugins.TryGetFunctionAndArguments(functionCall, out KernelFunction? pluginFunction, out KernelArguments? arguments);
var content = await kernel.InvokeAsync(pluginFunction!, arguments);
Console.WriteLine(content);
}
else
{
//不是调用插件这直接输出返回结果
Console.WriteLine(result.Content);
}
}
}
} while (true);

这里需要注意自定义的RedirectingHandler,如果你不是使用OpenAI的接口而是自己对接或者代理的OpenAI的接口,就需要自行定义HttpClientHandler来修改请求的GPT的服务地址。

public class RedirectingHandler : HttpClientHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.RequestUri = new UriBuilder(request.RequestUri!) { Scheme = "http", Host = "你的服务地址", Path= "/v1/chat/completions" }.Uri;
return base.SendAsync(request, cancellationToken);
}
}

这样的话我们就可以在于GPT的交互中调用我们自定义的插件了,当我们输入相关的提示词OpenAI的接口就可以根据提示词和插件信息返回调用哪个插件。使用了几张我本地的图片试了一下效果还是不错的,能分析出大致的图片内容,如下所示

这样使用起来就比较灵活了,在对话的过程中就可以使用本地的功能,不得不说有了插件化的能力SemanticKernel的功能就更加丰富了。关于插件化的实现原理也是比较简单,这是利用OpenAI对话接口的能力,我们只需要定义好插件和相关的提示词就可以,比如我们上面示例,使用FiddlerCharles拦截一下发出的请求即可,它是发起的HTTP请求,请求格式如下

{
"messages": [
{
"content": "Assistant is a large language model.",
"role": "system"
},
{
"content": "请帮我分析这张图片的内容D:\\Software\\AI.Lossless.Zoomer-2.1.0-x64\\Release\\output\\20200519160906.png",
"role": "user"
}
],
"temperature": 1,
"top_p": 1,
"n": 1,
"presence_penalty": 0,
"frequency_penalty": 0,
"model": "gpt-3.5-turbo-0125",
"tools": [
{
"function": {
"name": "ImageToTextPlugin-GetImageContent",
"description": "根据图片路径分析图片内容",
"parameters": {
"type": "object",
"required": [
"imagePath"
],
"properties": {
"imagePath": {
"type": "string",
"description": "图片路径"
}
}
}
},
"type": "function"
}
],
"tool_choice": "auto"
}

通过请求OpenAI/v1/chat/completions接口的请求参数我们可以大致了解它的工作原理,SemanticKernel通过扫描我们定义的插件的元数据比如类_方法方法的描述参数的描述来放入请求的JSON数据里,我们定义的Description里的描述作为提示词拆分来具体匹配插件的依据。接下来我们再来看一下这个接口的返回参数的内容

{
"id": "chatcmpl-996IuJbsTrXHcHAM3dqtguwNi9M3Z",
"object": "chat.completion",
"created": 1711956212,
"model": "gpt-35-turbo",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_4aN9xUhly2cEbNmzRcIh1it0",
"type": "function",
"function": {
"name": "ImageToTextPlugin-GetImageContent",
"arguments": "{\"imagePath\":\"D:\\\\Software\\\\AI.Lossless.Zoomer-2.1.0-x64\\\\Release\\\\output\\\\20200519160906.png\"}"
}
}
]
},
"finish_reason": "tool_calls"
}
],
"usage": {
"prompt_tokens": 884,
"completion_tokens": 49,
"total_tokens": 933
},
"system_fingerprint": "fp_2f57f81c11"
}

OpenAI接口给我们返回了它选择的插件信息,告诉我们可以调用ImageToTextPlugin-GetImageContent这个方法,传递的参数则是{\"imagePath\":\"D:\\\\Software\\\\AI.Lossless.Zoomer-2.1.0-x64\\\\Release\\\\output\\\\20200519160906.png\"},这是GPT帮我们分析的结果,SemanticKernel根据这个信息来调用我们本地的插件,执行具体操作。这里GPT的起到的作用就是,我们请求的时候提交插件的元数据,GPT根据提示词和插件的元数据帮我分析我们可以调用哪个插件,并且把插件参数帮我们分析出来,这样我们就可以根据返回的插件元数据来调用我们本地的插件了。

需要注意的,目前我尝试的是只有OpenAIAzureOpenAI提供的对话接口支持插件的能力,国内的模型我试了一下比如文心一言讯飞星火通义千问百川都不支持,至少通过OneApi对接过来的不支持,不知道是不是我姿势不对。

参考连接

以下是学习研究过程中参考的一些连接,在这里展示出来供大家参考。涉及到学习参考、解决问题、查找资源相关。毕竟人生地不熟的,需要找到方向

总结

本文缘起来于在devblogs上看到的一篇文章,感觉比较有趣,便动手实践一下。其中遇到了问题,便部署本地模型来实现,最终实现了Microsoft.SemanticKernel.Connectors.HuggingFace调用本地模型实现图片内容识别。最终把它定义成一个插件,这样在SemanticKernel中就可以通过调用插件的方式来调用本地模型,实现图片内容识别。这些可以在本地运行的实现特定功能的模型还是比较有意思的,模型本身不大,本地可运行,适合初学者或者有兴趣的人使用。

我始终倡导大家积极接触和学习新技术。这并不意味着我们必须深入钻研,毕竟人的精力有限,无法将所有精力都投入到这些领域。但至少,我们应该保持好奇心,对这些新技术有所了解,理解其基本原理。这样,当有一天我们需要应用这些技术时,就能更加得心应手。即使我们不能成为某个领域的专家,但对这些技术的了解也会成为我们思考的一部分,让我们在解决问题时拥有更多的选择和思路。因此,不要害怕尝试新事物,保持好奇心和学习态度,这将是我们不断进步的关键。

欢迎扫码关注我的公众号

体验Semantic Kernel图片内容识别的更多相关文章

  1. Semantic Kernel 知多少 | 开启面向AI编程新篇章

    引言 在ChatGPT 火热的当下, 即使没有上手亲自体验,想必也对ChatGPT的强大略有耳闻.当一些人在对ChatGPT犹犹豫豫之时,一些敏锐的企业主和开发者们已经急不可耐的开展基于ChatGPT ...

  2. Semantic Kernel 入门系列:🛸LLM降临的时代

    不论你是否关心,不可否认,AGI的时代即将到来了. 在这个突如其来的时代中,OpenAI的ChatGPT无疑处于浪潮之巅.而在ChatGPT背后,我们不能忽视的是LLM(Large Language ...

  3. Semantic Kernel 入门系列:🪄LLM的魔法

    ChatGPT 只是LLM 的小试牛刀,让人类能够看到的是机器智能对于语言系统的理解和掌握. 如果只是用来闲聊,而且只不过是将OpenAI的接口封装一下,那么市面上所有的ChatGPT的换皮应用都差不 ...

  4. Semantic Kernel 入门系列:🔥Kernel 内核和🧂Skills 技能

    理解了LLM的作用之后,如何才能构造出与LLM相结合的应用程序呢? 首先我们需要把LLM AI的能力和原生代码的能力区分开来,在Semantic Kernel(以下简称SK),LLM的能力称为 sem ...

  5. Semantic Kernel 入门系列:💬Semantic Function

    如果把提示词也算作一种代码的话,那么语义技能所带来的将会是全新编程方式,自然语言编程. 通常情况下一段prompt就可以构成一个Semantic Function,如此这般简单,如果我们提前可以组织好 ...

  6. Semantic Kernel 入门系列:💾Native Function

    语义的归语义,语法的归语法. 基础定义 最基本的Native Function定义只需要在方法上添加 SKFunction 的特性即可. using Microsoft.SemanticKernel. ...

  7. Semantic Kernel 入门系列:🥑突破提示词的限制

    无尽的上下文 LLM的语言理解和掌握能力在知识内容的解读和总结方面提供了强大的能力. 但是由于训练数据本身来自于公共领域,也就注定了无法在一些小众或者私有的领域能够足够的好的应答. 因此如何给LLM ...

  8. Semantic Kernel 入门系列:🥑Memory内存

    了解的运作原理之后,就可以开始使用Semantic Kernel来制作应用了. Semantic Kernel将embedding的功能封装到了Memory中,用来存储上下文信息,就好像电脑的内存一样 ...

  9. Semantic Kernel 入门系列:🍋Connector连接器

    当我们使用Native Function的时候,除了处理一些基本的逻辑操作之外,更多的还是需要进行外部数据源和服务的对接,要么是获取相关的数据,要么是保存输出结果.这一过程在Semantic Kern ...

  10. Semantic Kernel 入门系列:📅 Planner 计划管理

    Semantic Kernel 的一个核心能力就是实现"目标导向"的AI应用. 目标导向 "目标导向"听起来是一个比较高大的词,但是却是实际生活中我们处理问题的 ...

随机推荐

  1. 什么是数据同步利器DataX,如何使用?

    转载至我的博客 https://www.infrastack.cn ,公众号:架构成长指南 今天给大家分享一个阿里开源的数据同步工具DataX,在Github拥有14.8k的star,非常受欢迎,官网 ...

  2. Linux驱动开发笔记(二):ubuntu系统从源码编译安装gcc7.3.0编译器

    前言   编译ubuntu驱动之前,发现使用的gcc是7.3.0,而使用apt管理和下载的都无法直接或间接安装gcc7.3.0,于是只能从源码安装gcc7.3.0编译器.   GCC 概述   GCC ...

  3. 【系统选型】企业即时通讯(IM)软件调研及供应商对比评估

    企业即时通讯(IM)软件调研及供应商对比评估 1.概览 1.1 即时通讯 即时通讯(Instant messaging,简称IM)是一个终端服务,允许两人或多人使用网路即时的传递文字讯息.档案.语音与 ...

  4. ABP开发需要用到的命令

    0.命令行在哪里执行? 在Visual Studio的"解决方案资源管理器"的解决方案或者项目上点鼠标右键,选择"在终端中打开". 1.安装abp的命令行 官网 ...

  5. 【Azure 事件中心】使用Kafka消费Azure EventHub中数据,遇见消费慢的情况可以如何来调节呢?

    问题描述 使用Kafka消费Azure EventHub中数据,遇见消费慢的情况可以如何来调节呢? 问题解答 查看Kafka Consumer的配置参数,其中最只要的一个参数为:max.poll.re ...

  6. Nebula Graph|信息图谱在携程酒店的应用

    本文首发于 Nebula Graph Community 公众号 对于用户的每一次查询,都能根据其意图做到相应的场景和产品的匹配",是携程酒店技术团队的目标,但实现这个目标他们遇到了三大问题 ...

  7. Java 重写引入

    1 package com.bytezero.override; 2 /* 3 * 方法的重新(override/overwrite) 4 * 5 * 1.重写:子类继承父类以后,可以对父类中同名同参 ...

  8. 摆脱鼠标系列 - vscode 左右切换文档 快捷键换成 Ctrl + Alt + 左右箭头

    为什么 默认快捷键是 Ctrl + PageUp PageDown 用的不太习惯 Ctrl + Alt + 左右箭头 和 双列的快捷键冲突了,那个就不用了,基本没用过 备份图

  9. koa 文件下载 pdf预览 两个接口 - nodejs - chromeDownload chromePreview

    koa 文件下载 pdf预览 两个接口 - nodejs - chromeDownload chromePreview chrome.js const router = require("k ...

  10. windows10 中为文件添加让自己可以使用查看、修改、运行的权限

    在Win10中添加权限的方法 前一段时间重装了系统,然后,突然间就因为权限原因没法查看一些文件了.所以就想办法添加权限.尝试很多次后终于成功了,这篇文章记录一下如何为自己添加权限. 选中需要添加权限的 ...