.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の埋め込みもサポートしています。チャットの完了に関しては、埋め込みは異なるモデルで実行できます。 defaultembeddingModelプロパティを使用すると、 GetembedingSyncメソッドで明示的な値を渡さない限り、使用されるデフォルトモデルを指定できます。
現在利用可能なモデルは次のとおりです。
Openaichatgptmodels.csファイルで入手可能な名前が固定されています。
Azure Openaiサービスでは、電話をかける前に最初にモデルを展開する必要があります。モデルを展開するときは、名前を割り当てる必要があります。これは、 chatgptnetで使用する名前と一致する必要があります。
注モデルはすべての地域で利用できません。モデルサマリーテーブルと領域の可用性ページを参照して、現在の可用性を確認できます。
ChatGptは、会話のシナリオをサポートすることを目的としています。ユーザーは、すべてのインタラクションの完全なコンテキストを指定せずにChatGPTに相談できます。ただし、会話の履歴はOpenaiまたはAzure Openaiサービスによって管理されていないため、現在の状態を保持するのは私たち次第です。デフォルトでは、 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パラメーターを設定することもできます。利用可能なパラメーターとその意味のリストについては、公式ドキュメントを確認してください。
構成は、 appsettings.jsonファイルのchatgptセクションを使用して、iconfigurationから自動的に読み取ることができます。
"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
プロパティを確認する必要があります。
構成を使用すると、チャット完了のためにデフォルトのパラメーターを設定することができます。ただし、 AskaSyncまたはAskStreamAsyncオーバーロードを使用して、ChatGPtParametersオブジェクトを使用して、各要求のパラメーターを指定することもできます。
var response = await chatGptClient . AskAsync ( conversationId , message , new ChatGptParameters
{
MaxTokens = 150 ,
Temperature = 0.7
} ) ;
すべてのパラメーターを指定する必要はありません。これは、オーバーライドしたいパラメーターのみです。他のものは、デフォルトの構成から取得されます。
ChatGptは非決定論的であることが知られています。これは、同じ入力が異なる出力を生成できることを意味します。この動作を制御するために、温度とTOPPパラメーターを使用できます。たとえば、温度を0に近くの値に設定すると、モデルがより決定的になりますが、値は1に近い値に設定すると、モデルがより創造的になります。ただし、これは常に同じ入力に対して同じ出力を取得するのに十分ではありません。この問題に対処するために、OpenaiはSeedパラメーターを導入しました。指定されている場合、モデルは決定論的にサンプリングする必要があります。そのため、同じシードとパラメーターを使用した繰り返しの要求が同じ結果を返すようにします。それにもかかわらず、この場合でも決定論は保証されていません。バックエンドの変更を監視するには、 SystemFingerPrint応答パラメーターを参照する必要があります。この値の変更は、バックエンド構成が変更されたことを意味し、これが決定論に影響を与える可能性があります。
いつものように、シードプロパティは、デフォルトの構成、またはchatGptParametersを受け入れるAskaSyncまたはAskStreamAsyncオーバーロードで指定できます。
注SeedとSystemFingerPrintは、 GPT-4-1106-PREVIEWなどの最新のモデルによってのみサポートされています。
JSON形式で応答を見捨てたい場合は、 ResponseFormatパラメーターを使用できます。
var response = await chatGptClient . AskAsync ( conversationId , message , new ChatGptParameters
{
ResponseFormat = ChatGptResponseFormat . Json ,
} ) ;
このようにして、応答は常に有効なJSONになります。また、システムまたはユーザーメッセージを介してJSONを生成するようにモデルに指示する必要があることに注意してください。これを行わない場合、モデルはエラーを返します。
いつものように、 ResponseFormatプロパティは、デフォルトの構成またはAskAsyncまたはAskStreamAsyncのOverloadsでChatGptParametersを受け入れることで指定できます。
Note ResponseFormatは、 GPT-4-1106-PREVIEWなどの最新のモデルによってのみサポートされています。
AskasyncおよびAskStreamasync (以下を参照)メソッドは、会話のパラメーターを必要とする過負荷を提供します。空の値を渡すと、ランダムな値が生成されて返されます。 AskasyncまたはAskStreamasyncのその後の呼び出しでこの値を渡すことができます。これにより、ライブラリは現在の会話の以前のメッセージを自動的に取得し( MessageLimitおよびMessageExpiration設定に従って)、Chat Rection 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は、応答ストリーミングをサポートします。この機能を使用すると、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メソッドはヌル文字列を返します。したがって、実際のコンテンツにアクセスしようとする前に、常に
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文字列を返します。
ライブラリは、Blazor WebAssemblyアプリケーションとも100%互換性があります。
さまざまな実装の詳細については、Samplesフォルダーをご覧ください。
ChatGPTは、アシスタントがどのように動作するかに影響を与えるために、システムの役割を持つメッセージをサポートします。たとえば、私たちはそのようなことをchatgptに伝えることができます:
chatgptnetは、 setupasyncメソッドを使用してこの機能を提供します。
var conversationId await = chatGptClient . SetupAsync ( " Answer in rhyme " ) ;
Askasyncを呼び出すときに同じConversationIDを使用すると、システムメッセージがすべてのリクエストとともに自動的に送信されるため、アシスタントが動作方法を知るようになります。
メモシステムメッセージは、メッセージの制限番号にカウントされません。
会話履歴は、有効期限( 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スキーマ参照に従って、任意の数の関数を渡すことができます。ボンネットの下では、機能がシステムメッセージに注入され、モデルがトレーニングされている構文の構文になります。これは、関数がモデルのコンテキスト制限に対してカウントされ、入力トークンとして請求されることを意味します。
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のAddToolResponseAsyncメソッドを呼び出して、標準メッセージのように会話履歴に応答を追加する必要があります。そうすれば、チャットの完了に自動的に使用されます。
// Calls the remote function API.
var functionResponse = await GetWeatherAsync ( functionCall . Arguments ) ;
await chatGptClient . AddToolResponseAsync ( conversationId , functionCall , functionResponse ) ;
GPT-4-ターボのような新しいモデルは、機能に対するより一般的なアプローチ、ツール呼び出しをサポートしています。リクエストを送信すると、モデルが呼び出す可能性のあるツールのリストを指定できます。現在、機能のみがサポートされていますが、将来的には他のタイプのツールが利用可能になります。
直接関数呼び出しの代わりにツール呼び出しを使用するには、 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 Openaiサービスを使用する場合、自動的にコンテンツフィルタリングを無料で取得します。その仕組みの詳細については、ドキュメントをご覧ください。この情報は、APIバージョン2023-06-01-preview
以降を使用する場合、すべてのシナリオに対して返されます。 ChatGptNetは、CHATGPTRESPONSEおよびCHATGPTCHOICEクラスで対応するプロパティを提供することにより、このオブジェクトモデルを完全にサポートします。
埋め込みにより、テキストをベクトル空間に変換できます。これは、たとえば、2つの文の類似性を比較するのに役立ちます。 chatgptnetは、 GetembeddingSyncメソッドを提供することにより、この機能を完全にサポートしています。
var response = await chatGptClient . GenerateEmbeddingAsync ( message ) ;
var embeddings = response . GetEmbedding ( ) ;
このコードは、指定されたメッセージのすべての埋め込みを含むフロートアレイを提供します。配列の長さは、使用されるモデルによって異なります。
モデル | 出力寸法 |
---|---|
Text-rembedding-ada-002 | 1536 |
Text-embedding-3-Small | 1536 |
Text-rembedding-3-large | 3072 |
Text-embedding-3-SmallやText-rembedding-3-Largeなどの新しいモデルにより、開発者はパフォーマンスと埋め込みコストをトレードオフできます。具体的には、開発者は、埋め込みがコンセプトを代表する特性を失うことなく、埋め込みを短くすることができます。
chatgptに関しては、この設定はさまざまな方法で実行できます。
builder . Services . AddChatGpt ( options =>
{
// ...
options . DefaultEmbeddingParameters = new EmbeddingParameters
{
Dimensions = 256
} ;
} ) ;
"ChatGPT": {
"DefaultEmbeddingParameters": {
"Dimensions": 256
}
}
次に、特定のリクエストのディメンションを変更する場合は、 Getembedingsyncの呼び出しで埋め込みパラメーター引数を指定できます。
var response = await chatGptClient . GenerateEmbeddingAsync ( request . Message , new EmbeddingParameters
{
Dimensions = 512
} ) ;
var embeddings = response . GetEmbedding ( ) ; // The length of the array is 512
2つの埋め込み間のコサインの類似性を計算する必要がある場合は、 Embeddingutility.cosinesimilarityメソッドを使用できます。
完全な技術文書はこちらから入手できます。
このプロジェクトは常に進化しています。貢献は大歓迎です。リポジトリに問題やリクエストを提出してください。できる限り説明します。
警告開発ブランチで作業することを忘れないでください。マスターブランチを直接使用しないでください。ターゲットをターゲットにするプル要求を作成します。