You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hello, this is my feedback to the Sematik Kernel v1 proposal document.
I don't know if this is the right place to drop that, but I didn't find a better spot so far.
This is only relating to the C# variant.
Feedback to SK v1 propoals:
01.01 ContextVariables to Dictionary<string, object>
Seems more flexible at first, but exposing this through the API is difficult.
As a C# developer, you really want to minimize untyped values as much as you can.
Forcing a weak-typed collection on developers doesn't feel good. Better provide a way
to introduce multiple ContextVariables collections, each strongly typed by using generics.
Better:
Use a ContextVariables carrier object to transfer the values:
var contextVars = new ContextVariables();
contextVars.Set<ChatHistory>("messages", chatHistory);
kernel.RunAsync(function, contextVars);
The generic can be omitted here, as the compiler can infere the type from the
chatHistory variables type. You can also get back a strongly typed instance later on to work
with it:
var history = contextVars.Get<ChatHistory>("messages");
// Since it is typed now, you can easily access all properties of the ChatHistory object.
Ideally, also make it an IContextVariables interface. A developer might decide to
implement a different ContextVariables carrier object which additionally has constraints
on the types that might be registered with it, i.e. force them to be serializable, so that
the Context can be safely stored away and retrieved later on.
When just putting objects in there, you might end up with a type that is not serializable
and then it's impossible to persist the context variables where serialization would be needed.
01.02 SKContext to IKernel
Again, please use a ContextVariables carrier object thet provides a type-safe API here
instead of a Dictionary<string, object>.
02.01. Handlebars
.NET already has powerful template engines that a) most .NET developers know and b)
fit perfectly in the ecosystem. These are T4 templates as well as the Razor engine.
Especially when rendering to a HTML/CML-like structure like you did in the example,
Razor would be a much better fit for .NET developers.
02.03. YAML
No YAML, please. Configuration in the .NET world nowadays is almost exclusively done
in JSON, and we have an extremely powerful, layered JSON configuration system already
in place. Besides that, Json tooling is being optimized in .NET for several years now,
while YAML doesn't really have good tooling at all in the .NET space.
If leveraged correctly, System.Extensions.Configuration also allows you to load changes
to the configuration at runtime, making developing / tuning prompts a breeze. You send
a request, see the answer, tune the prompt, save the file, immidiately send a new request
that - at runtime - picks up the change on the IOptions and you
can directly see the result of the adjusted prompt.
Also, the current configuration system allows you to override only specific values for
prod/dev/env so you can test new prompts in your functions easily by only overriding a
specific prompt in your appsettings.test.json file on your test environment.
If you force adoption of YAML onto .NET developers, they will look for alternatives.
04.01. Functions detached - as singletons?
You suggest that functions can (or should) be instanciated once and be re-used by several kernels.
This is honestly a bad advice. For example, think of an AI-powered API that should provide a function
that looks up stuff in a database with Entity Framework Core.
For EF Core, you need to get an instance of your DbContext that is scoped to the current API request,
because EF Core strongly discourages the use of long-lived DbContextes as they carry a DbConnection
from a pool of multiple connections and these should be as short-lived as possible.
So if your function needs a DbContext, you have to jump through a lot of hoops and your function
oftentimes needs to resort to the ServiceLocator antipattern where your function gets a ServiceProvider,
uses that to create a new scope of its own (which is sadly detached from the scope of the current
request), and resolves the DbContext from that scope.
What you'd actually should provide, is that the developer only registers a Type and not an
function instance with your kernel. The Kernel can then use reflection to determine the functions on
the Type and provide these to the model. If a function is called, then the Kernel uses the DI system
to resolve the function, based on the current scope of the request that also provided the Kernel itself.
This way, singletons are still possible while functions that require some scoped dependencies like a
DbContext will also easily work.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Hello, this is my feedback to the Sematik Kernel v1 proposal document.
I don't know if this is the right place to drop that, but I didn't find a better spot so far.
This is only relating to the C# variant.
Feedback to SK v1 propoals:
01.01 ContextVariables to Dictionary<string, object>
Seems more flexible at first, but exposing this through the API is difficult.
As a C# developer, you really want to minimize untyped values as much as you can.
Forcing a weak-typed collection on developers doesn't feel good. Better provide a way
to introduce multiple ContextVariables collections, each strongly typed by using generics.
Better:
Use a ContextVariables carrier object to transfer the values:
The generic can be omitted here, as the compiler can infere the type from the
chatHistory variables type. You can also get back a strongly typed instance later on to work
with it:
Ideally, also make it an
IContextVariables
interface. A developer might decide toimplement a different
ContextVariables
carrier object which additionally has constraintson the types that might be registered with it, i.e. force them to be serializable, so that
the Context can be safely stored away and retrieved later on.
When just putting
objects
in there, you might end up with a type that is not serializableand then it's impossible to persist the context variables where serialization would be needed.
01.02 SKContext to IKernel
Again, please use a
ContextVariables
carrier object thet provides a type-safe API hereinstead of a
Dictionary<string, object>
.02.01. Handlebars
.NET already has powerful template engines that a) most .NET developers know and b)
fit perfectly in the ecosystem. These are T4 templates as well as the Razor engine.
Especially when rendering to a HTML/CML-like structure like you did in the example,
Razor would be a much better fit for .NET developers.
02.03. YAML
No YAML, please. Configuration in the .NET world nowadays is almost exclusively done
in JSON, and we have an extremely powerful, layered JSON configuration system already
in place. Besides that, Json tooling is being optimized in .NET for several years now,
while YAML doesn't really have good tooling at all in the .NET space.
If leveraged correctly, System.Extensions.Configuration also allows you to load changes
to the configuration at runtime, making developing / tuning prompts a breeze. You send
a request, see the answer, tune the prompt, save the file, immidiately send a new request
that - at runtime - picks up the change on the IOptions and you
can directly see the result of the adjusted prompt.
Also, the current configuration system allows you to override only specific values for
prod/dev/env so you can test new prompts in your functions easily by only overriding a
specific prompt in your appsettings.test.json file on your test environment.
If you force adoption of YAML onto .NET developers, they will look for alternatives.
04.01. Functions detached - as singletons?
You suggest that functions can (or should) be instanciated once and be re-used by several kernels.
This is honestly a bad advice. For example, think of an AI-powered API that should provide a function
that looks up stuff in a database with Entity Framework Core.
For EF Core, you need to get an instance of your DbContext that is scoped to the current API request,
because EF Core strongly discourages the use of long-lived DbContextes as they carry a DbConnection
from a pool of multiple connections and these should be as short-lived as possible.
So if your function needs a DbContext, you have to jump through a lot of hoops and your function
oftentimes needs to resort to the ServiceLocator antipattern where your function gets a ServiceProvider,
uses that to create a new scope of its own (which is sadly detached from the scope of the current
request), and resolves the DbContext from that scope.
What you'd actually should provide, is that the developer only registers a Type and not an
function instance with your kernel. The Kernel can then use reflection to determine the functions on
the Type and provide these to the model. If a function is called, then the Kernel uses the DI system
to resolve the function, based on the current scope of the request that also provided the Kernel itself.
This way, singletons are still possible while functions that require some scoped dependencies like a
DbContext will also easily work.
Beta Was this translation helpful? Give feedback.
All reactions