首页 > 其他分享 >深入解析Semantic Kernel的编码误区:如何让本地模型“不再犯傻”

深入解析Semantic Kernel的编码误区:如何让本地模型“不再犯傻”

时间:2024-10-11 19:20:24浏览次数:10  
标签:Kernel Semantic string request uriBuilder host 犯傻 port match

        Semantic Kernel在AI领域的应用越来越广泛。然而,在使用Semantic Kernel时,如果不注意一些细节问题,可能会导致你的模型表现异常,甚至出现“胡说八道”的情况。今天,我将分享一个关于使用Semantic Kernel的小细节,这个问题曾让我一度陷入困惑,幸好最终找到了问题的根源。

问题背景

        在我的一个项目中,我遇到了一个奇怪的问题:当我使用OpenAI时,模型表现非常智能,但是一旦切换到本地模型,输出结果就变得非常“弱智”。起初,我怀疑这是模型自身的问题,但通过使用Postman进行调试,发现并非如此。

        为了排查问题,我决定从请求报文入手,使用HttpClientHandler进行请求拦截。这一步非常关键,揭示了问题的所在。

抓取请求报文

        首先,我们通过如下代码来创建一个OpenAI的HttpClientHandler,用以拦截和修改请求:

var handler = new OpenAIHttpClientHandler();
services.AddTransient<Kernel>((serviceProvider) =>
{
    if (_kernel == null)
    {
        _kernel = Kernel.CreateBuilder()
            .AddOpenAIChatCompletion(
                modelId: OpenAIOption.ChatModel,
                apiKey: OpenAIOption.Key,
                httpClient: new HttpClient(handler)
)
            .Build();
    }
    return _kernel;
});

然后,通过自定义的OpenAIHttpClientHandler来拦截和修改请求内容。代码如下:

public class OpenAIHttpClientHandler : HttpClientHandler
{
    private string _endPoint { get; set; }

    public OpenAIHttpClientHandler(string endPoint)
    {
        this._endPoint = endPoint;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        UriBuilder uriBuilder;
        Regex regex = new Regex(@"(https?)://([^/:]+)(:\d+)?/(.*)");
        Match match = regex.Match(_endPoint);

        if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development" && request.Content != null)
        {
            string requestBody = await request.Content.ReadAsStringAsync();
            //便于调试查看请求prompt
            Log.Information(requestBody);
        }

        // Modify request based on endpoint
        if (match.Success)
        {
            string xieyi = match.Groups[1].Value;
            string host = match.Groups[2].Value;
            string port = match.Groups[3].Value;
            port = string.IsNullOrEmpty(port) ? port : port.Substring(1);
            var hostnew = string.IsNullOrEmpty(port) ? host : $"{host}:{port}";

            switch (request.RequestUri.LocalPath)
            {
                case "/v1/chat/completions":
                    uriBuilder = new UriBuilder(request.RequestUri)
                    {
                        Scheme = $"{xieyi}://{hostnew}/",
                        Host = host,
                        Path = $"{route}v1/chat/completions",
                    };
                    if (port.ConvertToInt32() != 0) uriBuilder.Port = Convert.ToInt32(port);
                    request.RequestUri = uriBuilder.Uri;
                    break;

                case "/v1/embeddings":
                    uriBuilder = new UriBuilder(request.RequestUri)
                    {
                        Scheme = $"{xieyi}://{host}/",
                        Host = host,
                        Path = $"{route}v1/embeddings",
                    };
                    if (port.ConvertToInt32() != 0) uriBuilder.Port = Convert.ToInt32(port);
                    request.RequestUri = uriBuilder.Uri;
                    break;
            }
        }

        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
        return response;
    }
}
发现问题:Unicode编码

图片

图片

        通过拦截并查看请求报文内容,我发现请求中的内容并非直接的中文,而是Unicode编码。这对OpenAI服务而言,并不会影响识别和处理,但对本地模型来说,表现出明显的智力差异。

        于是,我决定对Unicode编码进行解码,通过下面的方法来实现:

public static string Unescape(this string value)
{
    if (value.IsNull()) return "";

    try
    {
        Formatting formatting = Formatting.None;
        object jsonObj = JsonConvert.DeserializeObject(value);
        string unescapeValue = JsonConvert.SerializeObject(jsonObj, formatting);
        return unescapeValue;
    }
    catch (Exception ex)
    {
        Log.Error(ex.ToString());
        return "";
    }
}

接下来,我们对HttpClientHandler进行修改,以便在发送请求前进行解码:

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
    UriBuilder uriBuilder;
    Regex regex = new Regex(@"(https?)://([^/:]+)(:\d+)?/(.*)");
    Match match = regex.Match(_endPoint);
    string guid = Guid.NewGuid().ToString();
    var mediaType = request.Content.Headers.ContentType.MediaType;
    string requestBody = (await request.Content.ReadAsStringAsync()).Unescape();
    var uncaseBody = new StringContent(requestBody, Encoding.UTF8, mediaType);
    request.Content = uncaseBody;

    if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != "Production")
    {
        Log.Information("{Message}", $"【模型服务接口调用-{guid},host:{_endPoint}】: {Environment.NewLine}{requestBody}");
    }

    // Modify request based on endpoint
    if (match.Success)
    {
        string xieyi = match.Groups[1].Value;
        string host = match.Groups[2].Value;
        string port = match.Groups[3].Value;
        port = string.IsNullOrEmpty(port) ? port : port.Substring(1);
        var hostnew = string.IsNullOrEmpty(port) ? host : $"{host}:{port}";

        switch (request.RequestUri.LocalPath)
        {
            case "/v1/chat/completions":
                uriBuilder = new UriBuilder(request.RequestUri)
                {
                    Scheme = $"{xieyi}://{hostnew}/",
                    Host = host,
                    Path = $"{route}v1/chat/completions",
                };
                if (Convert.ToInt32(port) != 0) uriBuilder.Port = Convert.ToInt32(port);
                request.RequestUri = uriBuilder.Uri;
                break;

            case "/v1/embeddings":
                uriBuilder = new UriBuilder(request.RequestUri)
                {
                    Scheme = $"{xieyi}://{host}/",
                    Host = host,
                    Path = $"{route}v1/embeddings",
                };
                if (Convert.ToInt32(port) != 0) uriBuilder.Port = Convert.ToInt32(port);
                request.RequestUri = uriBuilder.Uri;
                break;
        }
    }

    HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
    
    if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != "Production")
    {
        string responseContent = response.Content.ReadAsStringAsync().Result.Unescape();
        Log.Information("{Message}", $"【模型服务接口返回-{guid},host:{_endPoint}】: {Environment.NewLine}{responseContent}");
    }
    
    return response;
}
最终测试

图片

        经过以上调整后,我们再次验证请求内容,可以发现请求已经恢复为正常的中文编码,本地模型的表现也随之恢复正常。

        接下来,我们来探究一下为什么Semantic Kernel在传输过程中会使用Unicode编码。目前我的猜测是,在使用Function Call时,函数参数可能包含各种不规则的JSON数据。这些数据在进行序列化和反序列化时可能会引发问题,因此,Semantic Kernel可能为了确保数据的完整性和一致性,选择将参数进行Unicode编码后再进行解码处理。这样一来可以减少因不规则JSON导致的错误。

总结

        通过修改请求内容的编码方式,我们成功解决了由于Unicode编码导致本地模型表现不佳的问题。这次的经验告诉我,编码方式在AI模型中的影响不可忽视。希望通过本文的分享,能够帮助到遇到类似问题的开发者,提升你们使用Semantic Kernel的体验。

        如果你有任何问题或想法,欢迎关注我的公众号加入我们的交流群,与我和其他开发者一起探讨更多技术细节。我们下期再见!

标签:Kernel,Semantic,string,request,uriBuilder,host,犯傻,port,match
From: https://blog.csdn.net/u012094427/article/details/142789358

相关文章

  • 自动加载类文件时发生错误,类名【core\basic\Kernel】
    当你在使用PBootCMS时遇到“自动加载类文件时发生错误,类名【core\basicKernel】”的问题,通常是因为Kernel.php文件被误删除或丢失。特别是在阿里云虚拟主机上,这类文件可能会被误判为风险文件而被删除。以下是如何解决这一问题的具体步骤:解决方法重新下载PBootCMS模板访问PBo......
  • LInux基础——裸金属主机重装系统进入kernel Panic
    裸金属主机重装系统进入kernelPanic1、问题描述       租户裸金属主机重装系统,重启后进入”endKernelpanic–notsyncing:Fatalexception”; 2、问题分析  i.内存问题(非此类问题,忽略)     按照问题描述内核回退Calltrace是__pte_alloc内核物理内存调......
  • 《活侠传》游戏启动时闪退提示“找不到kernel32.dll”文件该怎么处理?活侠传游戏崩溃弹
    玩《活侠传》时,游戏启动就闪退,还提示“找不到kernel32.dll”文件,实在让人烦恼。此问题的解决或许有一定难度。现在为您仔细阐述处理这种情况的有效办法,助您解决难题,顺利开启游戏。本篇将为大家带来《活侠传》游戏启动时闪退提示“找不到kernel32.dll”文件该怎么处理的内容,......
  • Bad or missing usercopy whitelist? Kernel memory overwrite attempt detected to S
    Linux内核有一个usercopywhitelist机制,只允许这里面的region来做usercopy。如果是用kmem_cache_create申请的kmem_cache申请的内存空间来copytouser或者copyfromuser,那么就会报这个错。这时要用kmem_cache_create_usercopy,来将申请的区域加入到usercopywhitelist中。/***......
  • /sys/kernel/debug/binder/目录下主要节点含义
    /sys/kernel/debug/binder/目录下主要节点含义state显示binder设备的整体状态信息包括进程数量、线程数量、待处理事务数量等stats展示binder操作的统计信息如事务数量、内存使用情况等transactions列出当前正在处理的binder事务包括发送方、接收方、数据大小......
  • systemtap semantic error: no match (similar functions:
    用户态probe文档:https://sourceware.org/systemtap/SystemTap_Beginners_Guide/userspace-probing.html这是process("PATH").function("function")报的错,意思是没有在ELF文件中找到这个函数对应的符号。大概率是因为ELF文件是用C++编译来的,符号名跟函数名不对应。例如对于以下程......
  • 加速clone linux kernel
    tunagitclonehttps://mirrors.tuna.tsinghua.edu.cn/git/linux.gitgiteegitee.com有一个码云极速下载的用户,id是mirrors。这个用户维护了很多github的仓库的镜像,其中就有linuxkernel:[email protected]:mirrors/linux.git实测可以跑满带宽。建议不要用https的方式......
  • 自动加载类文件时发生错误,类名【core\\basic\\Kernel】
    当你使用PbootCMS时遇到了自动加载类文件时发生的错误,具体错误信息如下:自动加载类文件时发生错误,类名【core\\basic\\Kernel】这个问题通常是由于Kernel.php文件丢失或被误删除导致的。特别是在阿里云虚拟主机环境下,可能会因为安全策略而删除某些文件。以下是详细的解决......
  • 【实用教程】如何使用kernelbase.dll修复工具解决常见问题?利用KernelBase.dll修复工具
    引言:在使用Windows操作系统时,有时会遇到因KernelBase.dll文件损坏或缺失而导致的系统崩溃、程序无响应等问题。KernelBase.dll是Windows核心库文件之一,负责提供许多基本的系统级功能。当这个文件出现问题时,系统的稳定性和性能会受到影响。幸运的是,通过使用KernelBase.dll修复......
  • Photoshop CS8.0启动难题:专家支招Photoshop CS8.0 kernel32.dll文件丢失的应急处理与
    PhotoshopCS8.0(注意:实际上AdobePhotoshop的命名中并没有直接称为“CS8.0”的版本,这里可能是对某个版本或假设版本的指代)启动时遇到kernel32.dll文件丢失的问题,确实是一个令人头疼的难题。针对这一问题,专家提供了以下应急处理与预防措施:应急处理使用系统文件检查器(SFC):......