Библиотека интеграции CHATGPT для .NET, поддерживающая как OpenAI, так и Azure Open.
Библиотека доступна на Nuget. Просто найдите Chatgptnet в графическом графике диспетчера пакетов или запустите следующую команду в CLI .NET :
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 Open Service, поэтому необходимо установить правильные настройки конфигурации на основе выбранного провайдера:
CHATGPT можно использовать с различными моделями для завершения чата, как на OpenAI, так и в Service Azure OpenAI. С помощью свойства DefaultModel вы можете указать модель по умолчанию, которая будет использоваться, если вы не пропустите явное значение в методах Askasync или Asystreamasync .
Даже если это не является строго необходимым для беседы в чате, библиотека поддерживает также API встраивания, как на OpenAI, так и на Azure OpenAI. Что касается завершения чата, встраивание можно сделать с разными моделями. С помощью свойства DefaultEmbeddingModel вы можете указать модель по умолчанию, которая будет использоваться, если вы не пропустите явное значение в методе GeteMbeddingAsync .
В настоящее время доступные модели:
У них есть фиксированные имена, доступные в файле openaichatgptmodels.cs.
В Service Azure Openai вам необходимо сначала развернуть модель, прежде чем вы сможете совершать звонки. Когда вы развернете модель, вам нужно присвоить ей имя, которое должно соответствовать имени, которое вы используете с Chatgptnet .
Обратите внимание, что некоторые модели недоступны во всех регионах. Вы можете обратиться к сводной таблице моделей и странице доступности региона, чтобы проверить текущие доступности.
CHATGPT направлен на поддержку разговорных сценариев: Пользователь может поговорить с CHATGPT, не указав полный контекст для каждого взаимодействия. Тем не менее, история разговоров не управляется Spoinai или Azure Open Service, поэтому мы должны сохранить текущее состояние. По умолчанию Chatgptnet обрабатывает это требование, используя MemoryCache, которая хранит сообщения для каждого разговора. Поведение может быть установлено с использованием следующих свойств:
При необходимости, возможно предоставить пользовательский кэш, реализуя интерфейс 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, используя, например, раздел CHATGPT в файле appsettings.json :
"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
//}
}
А затем используйте соответствующую перегрузку метода Addchatgpt :
// Adds ChatGPT service using settings from IConfiguration.
builder . Services . AddChatGpt ( builder . Configuration ) ;Метод AddChatgpt также имеет перегрузку, которая принимает IserviceProvider в качестве аргумента. Например, его можно использовать, если мы находимся в веб -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 ) ;
} ) ;В более сложных сценариях можно настроить Chatgptnet , используя как код, так и Iconfiguration. Это может быть полезно, если мы хотим установить кучу общих свойств, но в то же время нам нужна логика конфигурации. Например:
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, построенного с .NET 6.0 или более поздней версии. Например, мы можем создать минимальный 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прежде чем пытаться получить доступ к фактическому контенту.
Используя конфигурацию, можно установить параметры по умолчанию для завершения чата. Тем не менее, мы также можем указать параметры для каждого запроса, используя перегрузки Askasync или Askstreamasync , которые принимают объект ChatgptParameters:
var response = await chatGptClient . AskAsync ( conversationId , message , new ChatGptParameters
{
MaxTokens = 150 ,
Temperature = 0.7
} ) ;Нам не нужно указывать все параметры, только те, которые мы хотим переопределить. Другие будут взяты из конфигурации по умолчанию.
Chatgpt известен не детерминированным. Это означает, что один и тот же вход может создавать разные выходы. Чтобы попытаться контролировать это поведение, мы можем использовать параметры температуры и TOPP . Например, установка температуры для значений вблизи 0 делает модель более детерминированной, одновременно устанавливая ее на значения, близкие к 1, делает модель более креативной. Тем не менее, этого недостаточно, чтобы получить один и тот же вывод для одного и того же входа. Чтобы решить эту проблему, OpenAI представил параметр семян . Если указана, модель должна определять образец, так что повторные запросы с тем же семенем и параметрами должны вернуть тот же результат. Тем не менее, детерминизм не гарантируется ни в этом случае, и вам следует обратиться к параметру отклика SystemFingerPrint для мониторинга изменений в бэкэнде. Изменения в этих значениях означают, что конфигурация бэкэнд изменилась, и это может повлиять на детерминизм.
Как всегда, свойство семян может быть указано в конфигурации по умолчанию или в перегрузках AskAsync или Askstreamasync , которые принимают ChatgptParameters.
Примечание Seed и Systemfingerprint поддерживаются только самыми последними моделями, такими как GPT-4-1106-Preview .
Если вы хотите отдать ответ в формате JSON, вы можете использовать параметр repplyFormat :
var response = await chatGptClient . AskAsync ( conversationId , message , new ChatGptParameters
{
ResponseFormat = ChatGptResponseFormat . Json ,
} ) ;Таким образом, ответ всегда будет действительным JSON. Примечание, которое также должно указать модели производить JSON через систему или пользовательское сообщение. Если вы этого не сделаете, модель вернет ошибку.
Как всегда, свойство RecsomeFormat может быть указано в конфигурации по умолчанию или в перегрузках AskAsync или AskStreamAsync , которые принимают ChatgptParameters.
Примечание Recasonformat поддерживается только самыми последними моделями, такими как GPT-4-1106-Preview .
Методы Askasync и Askstreamasync (см. Ниже) обеспечивают перегрузки, которые требуют параметра разговора . Если мы перенесем пустое значение, генерируется и возвращается случайный. Мы можем передать это значение в последующих вызовах AskAsync или AskStreamasync , чтобы библиотека автоматически извлекает предыдущие сообщения текущего разговора (в соответствии с настройками MessageLimit и MessageExpiration ) и отправляет их в API завершения чата.
Это поведение по умолчанию для всех взаимодействий в чате. Если вы хотите выделить конкретное взаимодействие из истории разговора, вы можете установить аргумент AddToConversationHistory на false :
var response = await chatGptClient . AskAsync ( conversationId , message , addToConversationHistory : false ) ;Таким образом, сообщение будет отправлено в API завершения чата, но это и соответствующий ответ от CHATGPT не будут добавлены в историю разговора.
С другой стороны, в некоторых сценариях было бы полезно вручную добавить взаимодействие в чате (то есть вопрос, за которым следует ответ) в историю разговоров. Например, мы можем захотеть добавить сообщение, которое было сгенерировано ботом. В этом случае мы можем использовать метод AddInteractionAsync :
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 ) ;
}Примечание Если ответ был отфильтрован с помощью системы фильтрации контента, метод GetContent в Foreach вернет нулевые строки. Таким образом, вы всегда должны проверять свойство
response.IsContentFilteredпрежде чем пытаться получить доступ к фактическому контенту.
Потоковая передача ответов работает путем возвращения iAsyncenumerable, поэтому его можно использовать даже в проекте веб -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 ( ) ;Примечание Если ответ был отфильтрован с помощью системы фильтрации содержимого, метод Asdeltas в Foreach вернет строку Nulls .
Библиотека на 100% совместима также с приложениями Blazor Webassembly:
Проверьте папку «Образцы» для получения дополнительной информации о различных реализациях.
CHATGPT поддерживает сообщения с роли системы , чтобы повлиять на то, как должен вести себя помощник. Например, мы можем сказать Chatgpt что -то подобное:
Chatgptnet предоставляет эту функцию, используя метод Setupasync :
var conversationId await = chatGptClient . SetupAsync ( " Answer in rhyme " ) ;Если мы используем один и тот же разговор при вызове Askasync , то системное сообщение будет автоматически отправлено вместе с каждым запросом, чтобы помощник узнал, как вести себя.
Обратите внимание, что системное сообщение не учитывается для ограниченного номера сообщений.
История разговора автоматически удаляется, когда достигается время истечения срока действия (указано в свойстве MessageExpiration ). Однако, если необходимо, можно немедленно очистить историю:
await chatGptClient . DeleteConversationAsync ( conversationId , preserveSetup : false ) ;Аргумент Searervestup позволяет решить, является ли 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 на самом деле не выполнит каких -либо вызовов функций. Разработчики должны выполнять вызовы функций с помощью выходов моделей.
После фактического выполнения нам нужно вызвать метод AddToolResponseAsync в ChatgptClient , чтобы добавить ответ на историю разговоров, как и стандартное сообщение, чтобы оно было автоматически использовано для завершения чата:
// Calls the remote function API.
var functionResponse = await GetWeatherAsync ( functionCall . Arguments ) ;
await chatGptClient . AddToolResponseAsync ( conversationId , functionCall , functionResponse ) ;Новые модели, такие как GPT-4-Turbo, поддерживают более общий подход к функциям, инструмент вызов . При отправке запроса вы можете указать список инструментов, которые может позвонить модель. В настоящее время поддерживаются только функции, но в будущем будет доступно другие типы инструментов.
Чтобы использовать вызов инструмента вместо прямого вызова функции, вам необходимо установить свойства инструмента и свойства инструментов в объекте ChatgptToolParameters (вместо функциональной и функции , как в предыдущем примере):
var toolParameters = new ChatGptToolParameters
{
ToolChoice = ChatGptToolChoices . Auto , // This is the default if functions are present.
Tools = functions . ToTools ( )
} ;Метод расширения Totools используется для преобразования списка ChatgptFunction в список инструментов.
Если вы используете этот новый подход, конечно, вам все еще нужно проверить, выбрала ли модель инструмент, используя тот же подход, который показано ранее. Затем, после фактического выполнения функции, вы должны вызвать метод AddToolResponseAsync , но в этом случае вам необходимо указать инструмент (не функцию), к которому относится ответ:
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 Open Service мы автоматически получаем фильтрацию контента бесплатно. Для получения подробной информации о том, как это работает, ознакомьтесь с документацией. Эта информация возвращается для всех сценариев при использовании API версии 2023-06-01-preview или более поздних. Chatgptnet полностью поддерживает эту модель объекта, предоставляя соответствующие свойства в классах Chatgptresponse и Chatgptchoice.
Enterdings позволяет преобразовать текст в векторное пространство. Это может быть полезно для сравнения сходства двух предложений, например. Chatgptnet полностью поддерживает эту функцию, предоставляя метод GetembeddingAsync :
var response = await chatGptClient . GenerateEmbeddingAsync ( message ) ;
var embeddings = response . GetEmbedding ( ) ;Этот код даст вам поплавок, содержащий все вставки для указанного сообщения. Длина массива зависит от используемой модели:
| Модель | Выходной размер |
|---|---|
| Текст-эмблдинг-ADA-002 | 1536 |
| Текст-эмбединг-3-ял | 1536 |
| Текст-эмблдинг-3-картанный | 3072 |
Более новые модели, такие как текстовые вмазчивания-3-оборудования и вмазочно-том, позволяют разработчикам производить производительность компромисса и затраты на использование внедрения. В частности, разработчики могут сокращать встраиваемые встраиваемые, не теряя встраивания своих концептуальных свойств.
Что касается CHATGPT, эти настройки могут быть сделаны различными способами:
builder . Services . AddChatGpt ( options =>
{
// ...
options . DefaultEmbeddingParameters = new EmbeddingParameters
{
Dimensions = 256
} ;
} ) ; "ChatGPT": {
"DefaultEmbeddingParameters": {
"Dimensions": 256
}
}
Затем, если вы хотите изменить измерение для конкретного запроса, вы можете указать аргумент EnterdingParameters в GeteMbeddingAsync .
var response = await chatGptClient . GenerateEmbeddingAsync ( request . Message , new EmbeddingParameters
{
Dimensions = 512
} ) ;
var embeddings = response . GetEmbedding ( ) ; // The length of the array is 512Если вам необходимо рассчитать сходство косинуса между двумя встроенными, вы можете использовать метод EnfemdingUtility.cosinesimiLaity .
Полная техническая документация доступна здесь.
Проект постоянно развивается. Взносы приветствуются. Не стесняйтесь подавать проблемы и получать запросы в репо, и мы обратимся к ним как можно.
Предупреждение не забудьте работать над разработкой филиала, не используйте главную филиал напрямую. Создать запросы на то, что нацеливание на то, чтобы разработка .