.NET的ChatGpt集成庫,支持OpenAI和Azure OpenAi服務。
該庫可在Nuget上找到。只需在包裝管理器GUI中搜索ChatGptNet或在.NET CLI中運行以下命令:
dotnet add package ChatGptNet在應用程序啟動時註冊CHATGPT服務:
builder . Services . AddChatGpt ( options =>
{
// OpenAI.
//options.UseOpenAI(apiKey: "", organization: "");
// Azure OpenAI Service.
//options.UseAzure(resourceName: "", apiKey: "", authenticationType: AzureAuthenticationType.ApiKey);
options . DefaultModel = " my-model " ;
options . DefaultEmbeddingModel = " text-embedding-ada-002 " ;
options . MessageLimit = 16 ; // Default: 10
options . MessageExpiration = TimeSpan . FromMinutes ( 5 ) ; // Default: 1 hour
options . DefaultParameters = new ChatGptParameters
{
MaxTokens = 800 ,
//MaxCompletionTokens = 800, // o1 series models support this property instead of MaxTokens
Temperature = 0.7
} ;
} ) ;ChatGptNet支持OpenAI和Azure OpenAI服務,因此有必要根據所選提供商設置正確的配置設置:
Chatgpt可以與不同的型號一起聊天,無論是在OpenAI和Azure Openai服務上。使用DefaultModel屬性,您可以指定將使用的默認模型,除非您在AskAsync或AsyStreamAsync方法中傳遞了顯式值。
即使不是嚴格進行聊天對話的必要條件,圖書館也支持OpenAI和Azure Openai上的嵌入API。至於聊天完成,可以使用不同的型號進行嵌入。使用DefaultEmbedModel屬性,您可以指定將使用的默認模型,除非您在GeteMbedDingAsync方法中傳遞了顯式值。
當前可用的型號是:
它們具有固定的名稱,可在OpenAICHATGPTMODELS.CS文件中使用。
在Azure OpenAi服務中,您需要先部署模型才能撥打電話。部署模型時,您需要為其分配一個名稱,該名稱必須與您使用的名稱與ChatGptNet匹配。
注意某些模型在所有區域均不可用。您可以參考模型摘要表和區域可用性頁面以檢查當前可用性。
CHATGPT的目的是支持對話方案:用戶可以與Chatgpt交談,而無需為每次互動指定完整上下文。但是,對話歷史記錄不是由OpenAI或Azure OpenAi服務管理的,因此由我們保留當前狀態。默認情況下, ChatGptNet使用存儲每個對話的消息的內存速度來處理這一要求。可以使用以下屬性設置該行為:
如有必要,可以通過實現IchatgptCache接口,然後調用withCache擴展方法來提供自定義緩存:
public class LocalMessageCache : IChatGptCache
{
private readonly Dictionary < Guid , IEnumerable < ChatGptMessage > > localCache = new ( ) ;
public Task SetAsync ( Guid conversationId , IEnumerable < ChatGptMessage > messages , TimeSpan expiration , CancellationToken cancellationToken = default )
{
localCache [ conversationId ] = messages . ToList ( ) ;
return Task . CompletedTask ;
}
public Task < IEnumerable < ChatGptMessage > ? > GetAsync ( Guid conversationId , CancellationToken cancellationToken = default )
{
localCache . TryGetValue ( conversationId , out var messages ) ;
return Task . FromResult ( messages ) ;
}
public Task RemoveAsync ( Guid conversationId , CancellationToken cancellationToken = default )
{
localCache . Remove ( conversationId ) ;
return Task . CompletedTask ;
}
public Task < bool > ExistsAsync ( Guid conversationId , CancellationToken cancellationToken = default )
{
var exists = localCache . ContainsKey ( conversationId ) ;
return Task . FromResult ( exists ) ;
}
}
// Registers the custom cache at application startup.
builder . Services . AddChatGpt ( /* ... */ ) . WithCache < LocalMessageCache > ( ) ;我們還可以在啟動時設置chatgpt參數以進行聊天完成。檢查官方文檔中是否可用參數列表及其含義。
可以自動從IconFiguration讀取配置,例如AppSettings.json文件中的ChatGpt部分:
"ChatGPT": {
"Provider": "OpenAI", // Optional. Allowed values: OpenAI (default) or Azure
"ApiKey": "", // Required
//"Organization": "", // Optional, used only by OpenAI
"ResourceName": "", // Required when using Azure OpenAI Service
"ApiVersion": "2024-10-21", // Optional, used only by Azure OpenAI Service (default: 2024-10-21)
"AuthenticationType": "ApiKey", // Optional, used only by Azure OpenAI Service. Allowed values: ApiKey (default) or ActiveDirectory
"DefaultModel": "my-model",
"DefaultEmbeddingModel": "text-embedding-ada-002", // Optional, set it if you want to use embedding
"MessageLimit": 20,
"MessageExpiration": "00:30:00",
"ThrowExceptionOnError": true // Optional, default: true
//"User": "UserName",
//"DefaultParameters": {
// "Temperature": 0.8,
// "TopP": 1,
// "MaxTokens": 500,
// "MaxCompletionTokens": null, // o1 series models support this property instead of MaxTokens
// "PresencePenalty": 0,
// "FrequencyPenalty": 0,
// "ResponseFormat": { "Type": "text" }, // Allowed values for Type: text (default) or json_object
// "Seed": 42 // Optional (any integer value)
//},
//"DefaultEmbeddingParameters": {
// "Dimensions": 1536
//}
}
然後使用相應的che addchatgpt方法的超載:
// Adds ChatGPT service using settings from IConfiguration.
builder . Services . AddChatGpt ( builder . Configuration ) ;AddChatGpt方法還具有一個超負荷,它接受IserviceProvider作為參數。例如,如果我們在Web API中,則可以使用它,並且我們需要支持每個用戶具有不同API密鑰的方案,可以通過依賴項注入來檢索這些API密鑰,以訪問數據庫:
builder . Services . AddChatGpt ( ( services , options ) =>
{
var accountService = services . GetRequiredService < IAccountService > ( ) ;
// Dynamically gets the API Key from the service.
var apiKey = " ... "
options . UseOpenAI ( apiKyey ) ;
} ) ;在更複雜的方案中,可以使用代碼和ICONFIGURATION配置ChatGptNet 。如果我們要設置一堆常見屬性,這可能很有用,但是與此同時,我們需要一些配置邏輯。例如:
builder . Services . AddChatGpt ( ( services , options ) =>
{
// Configure common properties (message limit and expiration, default parameters, ecc.) using IConfiguration.
options . UseConfiguration ( builder . Configuration ) ;
var accountService = services . GetRequiredService < IAccountService > ( ) ;
// Dynamically gets the API Key from the service.
var apiKey = " ... "
options . UseOpenAI ( apiKyey ) ;
} ) ;ChatGptNet使用HTTPCLIENT調用聊天完成和嵌入API。如果需要自定義,則可以使用ADDCHATGPT方法的過載,該方法接受操作<ihttpclientbuiler>作為參數。例如,如果要為HTTP客戶端添加彈性(假設重試策略),則可以使用Polly:
// using Microsoft.Extensions.DependencyInjection;
// Requires: Microsoft.Extensions.Http.Resilience
builder . Services . AddChatGpt ( context . Configuration ,
httpClient =>
{
// Configures retry policy on the inner HttpClient using Polly.
httpClient . AddStandardResilienceHandler ( options =>
{
options . AttemptTimeout . Timeout = TimeSpan . FromMinutes ( 1 ) ;
options . CircuitBreaker . SamplingDuration = TimeSpan . FromMinutes ( 3 ) ;
options . TotalRequestTimeout . Timeout = TimeSpan . FromMinutes ( 3 ) ;
} ) ;
} )有關此主題的更多信息,請訪問官方文檔。
該庫可用於使用.NET 6.0或更高版本構建的任何.NET應用程序中。例如,我們可以以這種方式創建最小的API:
app . MapPost ( " /api/chat/ask " , async ( Request request , IChatGptClient chatGptClient ) =>
{
var response = await chatGptClient . AskAsync ( request . ConversationId , request . Message ) ;
return TypedResults . Ok ( response ) ;
} )
. WithOpenApi ( ) ;
// ...
public record class Request ( Guid ConversationId , string Message ) ;如果我們只想檢索響應消息,我們可以調用getContent方法:
var content = response . GetContent ( ) ;請注意,如果響應已通過內容過濾系統過濾,則GetContent將返回null 。因此,在嘗試訪問實際內容之前,您應始終檢查
response.IsContentFiltered。
使用配置,可以設置默認參數以進行聊天完成。但是,我們還可以使用接受ChatGptParameters對象的AskAsync或AskStreamAsync Overloads指定每個請求的參數:
var response = await chatGptClient . AskAsync ( conversationId , message , new ChatGptParameters
{
MaxTokens = 150 ,
Temperature = 0.7
} ) ;我們不需要指定所有參數,而只需要覆蓋的參數。其他將從默認配置中獲取。
眾所周知,CHATGPT是非確定性的。這意味著相同的輸入可以產生不同的輸出。為了控制這種行為,我們可以使用溫度和TOPP參數。例如,將溫度設置為接近0的值使模型更具確定性,同時將其設置為接近1的值會使模型更具創造力。但是,這並不總是足以獲得相同輸入的相同輸出。為了解決這個問題,OpenAI引入了種子參數。如果指定,模型應確定性採樣,以便具有相同種子的重複請求和參數應返回相同的結果。但是,在這種情況下,不能保證確定性,您應該參考SystemFingerPrint響應參數以監視後端的變化。該值的變化意味著後端配置已經改變,這可能會影響確定性。
與往常一樣,可以在默認配置或AskAsync或AskStreamasync Overloads中指定種子屬性。
注意種子和Systemfingerprint僅由最近的模型(例如GPT-4-1106-Preiview)支持。
如果要以JSON格式進行響應,則可以使用ResponseFormat參數:
var response = await chatGptClient . AskAsync ( conversationId , message , new ChatGptParameters
{
ResponseFormat = ChatGptResponseFormat . Json ,
} ) ;這樣,響應將永遠是有效的JSON。請注意,還必須指示模型通過系統或用戶消息產生JSON。如果您不這樣做,則該模型將返回錯誤。
與往常一樣,可以在默認配置或AskAsync或AskStreamAsync Overloads中指定響應Format屬性,該屬性接受ChatGptParameters。
Note ResponseFormat僅由最新模型(例如GPT-4-1106-preiview)支持。
AskAsync和AskStreamAsync (見下文)方法提供了需要對話參數的過載。如果我們傳遞一個空值,則會生成一個隨機的值並返回。我們可以在AskAsync或AskStreamAsync的隨後調用中傳遞此值,以便庫自動檢索當前對話的先前消息(根據MessageLimit和MessageExpiration設置),並將其發送到聊天完成API。
這是所有聊天交互的默認行為。如果您想從對話歷史記錄中忽略特定的交互,則可以將addoconversation history參數設置為false :
var response = await chatGptClient . AskAsync ( conversationId , message , addToConversationHistory : false ) ;這樣,該消息將發送到聊天完成API,但是IT和ChatGpt的相應答案不會添加到對話歷史記錄中。
另一方面,在某些情況下,手動在對話歷史上手動添加聊天互動(即一個問題,然後是答案)可能很有用。例如,我們可能要添加一個由機器人生成的消息。在這種情況下,我們可以使用AddInteractionSASYNC方法:
await chatGptClient . AddInteractionAsync ( conversationId , question : " What is the weather like in Taggia? " ,
answer : " It's Always Sunny in Taggia " ) ;該問題將作為用戶消息添加,答案將在對話歷史記錄中作為助理消息添加。與往常一樣,這些新消息(尊重MessageLimit選項)將在隨後的AskAsync或AskStreamAsync的調用中使用。
聊天完成API支持響應流。使用此功能時,將發送部分消息Deltas,例如在ChatGpt中。令牌將作為僅數據的服務器事件發送時,它們將作為可用。 ChatGptNet使用AskStreamAsync方法提供響應流:
// Requests a streaming response.
var responseStream = chatGptClient . AskStreamAsync ( conversationId , message ) ;
await foreach ( var response in responseStream )
{
Console . Write ( response . GetContent ( ) ) ;
await Task . Delay ( 80 ) ;
}請注意,如果響應已通過內容過濾系統過濾,則foreach中的getContent方法將返回null字符串。因此,在嘗試訪問實際內容之前,您應始終檢查
response.IsContentFiltered。
響應流通過返回iAsyncenumerable來起作用,因此即使在Web API項目中也可以使用:
app . MapGet ( " /api/chat/stream " , ( Guid ? conversationId , string message , IChatGptClient chatGptClient ) =>
{
async IAsyncEnumerable < string ? > Stream ( )
{
// Requests a streaming response.
var responseStream = chatGptClient . AskStreamAsync ( conversationId . GetValueOrDefault ( ) , message ) ;
// Uses the "AsDeltas" extension method to retrieve partial message deltas only.
await foreach ( var delta in responseStream . AsDeltas ( ) )
{
yield return delta ;
await Task . Delay ( 50 ) ;
}
}
return Stream ( ) ;
} )
. WithOpenApi ( ) ;請注意,如果響應已通過內容過濾系統過濾,則Foreach中的Asdeltas方法將返回NULLS字符串。
該庫100%兼容,也與Ellazor WebAssembly應用程序兼容:
查看樣本文件夾以獲取有關不同實現的更多信息。
Chatgpt支持具有系統角色的消息,以影響助手應如何行事。例如,我們可以說出類似的東西:
ChatGptNet使用SetupAsync方法提供此功能:
var conversationId await = chatGptClient . SetupAsync ( " Answer in rhyme " ) ;如果我們在調用AskAsync時使用相同的對話ID ,則系統消息將與每個請求一起自動發送,以便助手知道如何行事。
注意系統消息不計算消息限制號碼。
當達到到期時間(由MessageExpiration屬性指定)時,會自動刪除對話歷史記錄。但是,如有必要,有可能立即清除歷史記錄:
await chatGptClient . DeleteConversationAsync ( conversationId , preserveSetup : false ) ;PreservEsetup參數允許確定Mantain是否還使用setUpasync方法設置的系統消息(默認: false )。
通過函數調用,我們可以描述功能,並讓模型智能選擇輸出包含參數以調用這些函數的JSON對象。這是一種更可靠地將GPT功能與外部工具和API聯繫起來的新方法。
ChatGptNet通過提供允許指定函數定義的AskAsync方法的過載來完全支持函數調用。如果提供此參數,則該模型將決定何時適當地使用函數。例如:
var functions = new List < ChatGptFunction >
{
new ( )
{
Name = " GetCurrentWeather " ,
Description = " Get the current weather " ,
Parameters = JsonDocument . Parse ( """
{
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and/or the zip code"
},
"format": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The temperature unit to use. Infer this from the user's location."
}
},
"required": ["location", "format"]
}
""" )
} ,
new ( )
{
Name = " GetWeatherForecast " ,
Description = " Get an N-day weather forecast " ,
Parameters = JsonDocument . Parse ( """
{
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and/or the zip code"
},
"format": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The temperature unit to use. Infer this from the user's location."
},
"daysNumber": {
"type": "integer",
"description": "The number of days to forecast"
}
},
"required": ["location", "format", "daysNumber"]
}
""" )
}
} ;
var toolParameters = new ChatGptToolParameters
{
FunctionCall = ChatGptToolChoices . Auto , // This is the default if functions are present.
Functions = functions
} ;
var response = await chatGptClient . AskAsync ( " What is the weather like in Taggia? " , toolParameters ) ;我們可以在JSON模式引用之後傳遞一個任意數量的函數,每個功能都具有名稱,描述和JSON模式描述函數參數。在引擎蓋下,將函數注入了系統消息中,該函數已對模型進行了訓練。這意味著功能符合模型的上下文限制,並將其作為輸入令牌計數。
AskAsync方法返回的響應對象提供了一個屬性來檢查模型是否選擇了函數調用:
if ( response . ContainsFunctionCalls ( ) )
{
Console . WriteLine ( " I have identified a function to call: " ) ;
var functionCall = response . GetFunctionCall ( ) ! ;
Console . WriteLine ( functionCall . Name ) ;
Console . WriteLine ( functionCall . Arguments ) ;
}此代碼將打印出這樣的內容:
I have identified a function to call:
GetCurrentWeather
{
"location": "Taggia",
"format": "celsius"
}
請注意,API實際上不會執行任何函數調用。開發人員可以使用模型輸出執行函數調用。
實際執行後,我們需要在ChatGptClient上調用AddToolResponsync方法,以將響應添加到對話歷史記錄中,就像標準消息一樣,以便自動用於聊天完成:
// Calls the remote function API.
var functionResponse = await GetWeatherAsync ( functionCall . Arguments ) ;
await chatGptClient . AddToolResponseAsync ( conversationId , functionCall , functionResponse ) ;諸如GPT-4-Turbo之類的新模型支持一種更通用的功能方法,即工具調用。發送請求時,您可以指定模型可能調用的工具列表。當前,僅支持功能,但是在將來發布的其他類型的工具將提供。
要使用工具調用而不是直接函數調用,您需要在ChatgptToolParameters對像中設置工具選擇和工具屬性(如前所述,而不是functionCall和function ,如上一個示例):
var toolParameters = new ChatGptToolParameters
{
ToolChoice = ChatGptToolChoices . Auto , // This is the default if functions are present.
Tools = functions . ToTools ( )
} ;Totools擴展方法用於將ChatGptFunction的列表轉換為工具列表。
如果使用這種新方法,當然,您仍然需要檢查模型是否已選擇了一個工具調用,則使用以前顯示的相同方法。然後,在函數的實際執行後,您必須調用AddToolResponseansync方法,但是在這種情況下,您需要指定響應所指的工具(不是函數):
var tool = response . GetToolCalls ( ) ! . First ( ) ;
var functionCall = response . GetFunctionCall ( ) ! ;
// Calls the remote function API.
var functionResponse = await GetWeatherAsync ( functionCall . Arguments ) ;
await chatGptClient . AddToolResponseAsync ( conversationId , tool , functionResponse ) ;最後,您需要將原始消息重新發送到聊天完成API,以便該模型可以考慮到功能呼叫響應的對話。查看呼叫示例的函數以完成此工作流的完整實現。
使用Azure OpenAI服務時,我們會自動免費獲得內容過濾。有關其工作原理的詳細信息,請查看文檔。當使用API版本2023-06-01-preview或更高版本時,所有方案都會返回此信息。 ChatGptNet通過在ChatgPtresponse和ChatGptChoice類中提供相應的屬性來完全支持此對像模型。
嵌入允許將文本轉換為向量空間。例如,這對於比較兩個句子的相似性可能很有用。 ChatGptNet通過提供GeteMbedDingAsync方法來完全支持此功能:
var response = await chatGptClient . GenerateEmbeddingAsync ( message ) ;
var embeddings = response . GetEmbedding ( ) ;此代碼將為您提供一個浮點數組,其中包含指定消息的所有嵌入式。數組的長度取決於所使用的模型:
| 模型 | 輸出維度 |
|---|---|
| 文本插入-ADA-002 | 1536年 |
| text-embedding-3-small | 1536年 |
| 文本插入3大 | 3072 |
諸如Text-embedding-3-small和Text-ebbedding-3-large之類的較新型號使開發人員可以權衡性能和使用嵌入的成本。具體而言,開發人員可以縮短嵌入,而不會嵌入失去其概念代表性的屬性。
至於chatgpt,可以通過各種方式完成此設置:
builder . Services . AddChatGpt ( options =>
{
// ...
options . DefaultEmbeddingParameters = new EmbeddingParameters
{
Dimensions = 256
} ;
} ) ; "ChatGPT": {
"DefaultEmbeddingParameters": {
"Dimensions": 256
}
}
然後,如果要更改特定請求的維度,則可以在getembeddingAsync Invocation中指定嵌入式參數參數:
var response = await chatGptClient . GenerateEmbeddingAsync ( request . Message , new EmbeddingParameters
{
Dimensions = 512
} ) ;
var embeddings = response . GetEmbedding ( ) ; // The length of the array is 512如果您需要計算兩個嵌入之間的餘弦相似性,則可以使用嵌入性。Cosineimerity方法。
完整的技術文檔可在此處找到。
該項目不斷發展。歡迎捐款。隨時提出問題並提取回購請求,我們將盡可能地解決。
警告記住要在開發分支上工作,不要直接使用主分支。創建針對目標的拉請請求。