大家好,欢迎来到IT知识分享网。
问题说明
之前在一些公众号就看到了关于SK的开发文章,然后说自己也试试看。然后就遇到一个关于如何设置baseurl的问题。啥意思呢?同样是SK,用python语言的话,OpenAI的baseurl是可以直接设置的,但是在C#下没法直接设置。
然后,开始调试,找野路子…
解决方式1
官方案例初始化OpenAIClient的构造函数,只有一个OpenAIKey的参数
但是,可以看到,这个构造函数,其实是调用了另一个构造函数,参数有Endpoint(即baseurl信息)、OpenAIKey,以及OPenAIClientOptions。
这个时候,脑海里有了一个想法,我通过下面的构造函数搞起来不就行了,为了方便后续统一调整,自己搞一个通用类实现
public class OpenAIClientExtend { /// <summary> /// 创建一个OpenAIClient对象,通过apikey和baseurl /// </summary> /// <param name="openAIApiKey"></param> /// <param name="openAIApiKeyBaseUrl"></param> /// <returns></returns> public static OpenAIClient CreateOpenAIClient(string openAIApiKey, string openAIApiBaseUrl) { OpenAIClient openAIClient = new OpenAIClient(new Uri(openAIApiBaseUrl), CreateDelegatedToken(openAIApiKey), new OpenAIClientOptions()); return openAIClient; } /// <summary> /// 直接把OPenAIClient代码的相关逻辑拿来,通过apikey生成token /// </summary> /// <param name="token">实际这里是OPenAIKey</param> /// <returns></returns> private static TokenCredential CreateDelegatedToken(string token) { var accessToken = new AccessToken(token, DateTimeOffset.Now.AddDays(180)); return DelegatedTokenCredential.Create((_, _) => accessToken); }
试了下,发现还是不行啊。。
然后又回到那个构造函数那里,想起来它在调用了另一个构造函数后,其实还写了一句:
_isConfiguredForAzureOpenAI = false;
看字面意思,是否是针对AzureOpenAI设置,默认值是true,那就知道为啥了,不过看了下这个字段是private,那么只能通过反射修改了。
//通过反射冬天修改私有字段,否则按照原来的逻辑,会初始化AzureOpenAI,导致无法使用报错 OpenAIClientExtend.ModifyObj<OpenAIClient>(openAIClient, "_isConfiguredForAzureOpenAI", false);
具体修改实例类字段的代码如下:
public static void ModifyObj<T>(object obj,string filedName,object newVal) { Type type = typeof(T); FieldInfo? field = type.GetField(filedName, BindingFlags.NonPublic | BindingFlags.Instance); if (field != null && field.IsPrivate) { object? value = field.GetValue(obj); // 获取私有字段的值 Console.WriteLine("原始私有字段的值为:" + value); field.SetValue(obj, newVal); // 修改私有字段的值 Console.WriteLine("修改后的私有字段的值为:" + field.GetValue(obj)); } }
这个时候,baseurl的设置终于生效了,可以愉快的开始后面的coding了
完整的OPenAIClient初始化代码
public static Kernel CreateKernel() { OpenAIClient openAIClient = OpenAIClientExtend.CreateOpenAIClient(OPENAI_API_KEY, OPENAI_BASE_URL); //通过反射冬天修改私有字段,否则按照原来的逻辑,会初始化AzureOpenAI,导致无法使用报错 OpenAIClientExtend.ModifyObj<OpenAIClient>(openAIClient, "_isConfiguredForAzureOpenAI", false); // Create a kernel var builder = Kernel.CreateBuilder(); // Add a text or chat completion service using either: // builder.Services.AddAzureOpenAIChatCompletion() // builder.Services.AddAzureOpenAITextGeneration() //IServiceCollection serviceCollection = builder.Services.AddLogging(c => c.SetMinimumLevel(LogLevel.Trace).AddDebug()); //这里使用的是OpenAI的聊天模型,不太理想,需要改进,更好的方法是在Add方法中实例化大模型对象 builder.Services.AddOpenAIChatCompletion("gpt-3.5-turbo", openAIClient); // builder.Services.AddOpenAITextGeneration() builder.Plugins.AddFromType<AuthorEmailPlanner>(); builder.Plugins.AddFromType<EmailPlugin>(); var kernel = builder.Build(); return kernel; }
解决方式2
隔了有几天,在公众号看到有大佬也提到了这个问题,不过还是大佬技高一筹,解决方式更好,直接上代码。
var kernel = Kernel.CreateBuilder() .AddOpenAIChatCompletion( modelId: "gpt-3.5-turbo", apiKey: Util.OPENAI_API_KEY, httpClient: new HttpClient(new MyOpenAIHandler()) ).Build();
可以看到,最后一个参数httpClient即动态设置baseurl的
/// <summary> /// 自定义baseurl /// </summary> class MyOpenAIHandler : DelegatingHandler { public MyOpenAIHandler() : base(new HttpClientHandler()) { } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var newUriBuilder = new UriBuilder(request.RequestUri); newUriBuilder.Scheme = "https"; newUriBuilder.Host = "api.xx.com"; //newUriBuilder.Port = 21000; request.RequestUri = newUriBuilder.Uri; return base.SendAsync(request, cancellationToken); } }
测试可用,大概思路,动态修改了原来OPenAI的base地址。
不过和方式1的差别,方式1 直接修改完整地址,方式2在OPenAI地址基础上,修改了http或https标记,修改域名部分,修改端口,即部分修改。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/133248.html