IModerationService
This commit is contained in:
parent
069b9df478
commit
1aafb7cc7d
10 changed files with 162 additions and 10 deletions
12
.env.sample
12
.env.sample
|
|
@ -4,6 +4,14 @@ POSTGRES_PORT=5432
|
||||||
POSTGRES_DB=yavsc
|
POSTGRES_DB=yavsc
|
||||||
POSTGRES_USER=yavsc
|
POSTGRES_USER=yavsc
|
||||||
POSTGRES_PASSWORD=lame-YAVSC_CONNECTION_PASSWORD
|
POSTGRES_PASSWORD=lame-YAVSC_CONNECTION_PASSWORD
|
||||||
DESTDIR=/srv/www/yavsc
|
|
||||||
|
|
||||||
ASPNETCORE_ConnectionStrings__YavscConnection=Server=$POSTGRES_HOST;Port=$POSTGRES_PORT;Database=$POSTGRES_DB;Username=$POSTGRES_USER;Password=$POSTGRES_PASSWORD;
|
ASPNETCORE_ConnectionStrings__YavscConnection=Server=$POSTGRES_HOST;Port=$POSTGRES_PORT;Database=$POSTGRES_DB;Username=$POSTGRES_USER;Password=$POSTGRES_PASSWORD;
|
||||||
|
|
||||||
|
ANTHROPIC_API_KEY=<votre-clé-api-anthropic>
|
||||||
|
MODERATION_AUTO_REJECT_THRESHOLD=0.9
|
||||||
|
MODERATION_AUTO_APPROVE_THRESHOLD=0.7
|
||||||
|
# Limite par appel
|
||||||
|
ANTHROPIC_MAX_TOKENS=256 # la modération n'a pas besoin de plus
|
||||||
|
|
||||||
|
# Choisis le bon modèle selon l'usage
|
||||||
|
# - Modération : claude-haiku-4-5 (rapide, pas cher)
|
||||||
|
# - Aide rédaction devis : claude-sonnet-4-6 (meilleur équilibre)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageVersion Include="Anthropic.SDK" Version="5.10.0" />
|
||||||
<PackageVersion Include="AsciiDocSharp" Version="0.2.0" />
|
<PackageVersion Include="AsciiDocSharp" Version="0.2.0" />
|
||||||
<PackageVersion Include="AsciiDocSharp.Converters.Html" Version="0.2.0" />
|
<PackageVersion Include="AsciiDocSharp.Converters.Html" Version="0.2.0" />
|
||||||
<PackageVersion Include="Avalonia" Version="12.0.4" />
|
<PackageVersion Include="Avalonia" Version="12.0.4" />
|
||||||
|
|
@ -63,10 +64,10 @@
|
||||||
<PackageVersion Include="Selenium.WebDriver" Version="4.44.0" />
|
<PackageVersion Include="Selenium.WebDriver" Version="4.44.0" />
|
||||||
<PackageVersion Include="Serilog.AspNetCore" Version="10.0.0" />
|
<PackageVersion Include="Serilog.AspNetCore" Version="10.0.0" />
|
||||||
<PackageVersion Include="SixLabors.ImageSharp" Version="4.0.0" />
|
<PackageVersion Include="SixLabors.ImageSharp" Version="4.0.0" />
|
||||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="10.1.7" />
|
<PackageVersion Include="Swashbuckle.AspNetCore" Version="10.2.0" />
|
||||||
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="10.0.8" />
|
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="10.0.8" />
|
||||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
|
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
|
||||||
<PackageVersion Include="Yavsc.Abstract" Version="1.0.6-rc18" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
8
src/Yavsc.Abstract/Interfaces/IModerationService.cs
Normal file
8
src/Yavsc.Abstract/Interfaces/IModerationService.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
using Yavsc.Moderation;
|
||||||
|
|
||||||
|
namespace Yavsc.Abstract.Interfaces;
|
||||||
|
|
||||||
|
public interface IModerationService
|
||||||
|
{
|
||||||
|
Task<ModerationResult> ModerateAsync(string content, string context);
|
||||||
|
}
|
||||||
3
src/Yavsc.Abstract/Moderation/ModerationAction.cs
Normal file
3
src/Yavsc.Abstract/Moderation/ModerationAction.cs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
namespace Yavsc.Moderation;
|
||||||
|
|
||||||
|
public enum ModerationAction { Approved, Rejected, NeedsReview }
|
||||||
7
src/Yavsc.Abstract/Moderation/ModerationResult.cs
Normal file
7
src/Yavsc.Abstract/Moderation/ModerationResult.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Yavsc.Moderation;
|
||||||
|
|
||||||
|
public record ModerationResult(
|
||||||
|
ModerationAction Action,
|
||||||
|
string Reason,
|
||||||
|
float ConfidenceScore
|
||||||
|
);
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
|
||||||
|
using Anthropic.SDK;
|
||||||
using Microsoft.AspNetCore;
|
using Microsoft.AspNetCore;
|
||||||
|
using Yavsc.Abstract.Interfaces;
|
||||||
using Yavsc.Extensions;
|
using Yavsc.Extensions;
|
||||||
|
|
||||||
namespace Yavsc
|
namespace Yavsc
|
||||||
|
|
@ -10,11 +12,28 @@ namespace Yavsc
|
||||||
{
|
{
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// Anthropic client
|
||||||
|
builder.Services.AddSingleton<AnthropicClient>(_ =>
|
||||||
|
new AnthropicClient(
|
||||||
|
new APIAuthentication(
|
||||||
|
builder.Configuration["ANTHROPIC_API_KEY"]
|
||||||
|
?? throw new InvalidOperationException("ANTHROPIC_API_KEY manquante")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Service de modération
|
||||||
|
if (builder.Environment.IsDevelopment())
|
||||||
|
builder.Services.AddScoped<IModerationService, MockModerationService>();
|
||||||
|
else
|
||||||
|
builder.Services.AddScoped<IModerationService, ClaudeModerationService>();
|
||||||
|
|
||||||
builder.Configuration
|
builder.Configuration
|
||||||
.AddJsonFile("appsettings.json")
|
.AddJsonFile("appsettings.json")
|
||||||
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true)
|
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true)
|
||||||
.AddEnvironmentVariables()
|
.AddEnvironmentVariables()
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var app = await builder.ConfigureWebAppServices().ConfigurePipeline();
|
var app = await builder.ConfigureWebAppServices().ConfigurePipeline();
|
||||||
app.Run();
|
app.Run();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,5 +77,9 @@
|
||||||
},
|
},
|
||||||
"Kyc": {
|
"Kyc": {
|
||||||
"HmacSecret": "*** via dotnet user-secrets ou variable d'environnement ***"
|
"HmacSecret": "*** via dotnet user-secrets ou variable d'environnement ***"
|
||||||
|
},
|
||||||
|
"Moderation": {
|
||||||
|
"AutoApproveThreshold": 0.7,
|
||||||
|
"AutoRejectThreshold": 0.9
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
90
src/Yavsc.Server/Services/ClaudeModerationService.cs
Normal file
90
src/Yavsc.Server/Services/ClaudeModerationService.cs
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
using Yavsc.Moderation;
|
||||||
|
using Yavsc.Abstract.Interfaces;
|
||||||
|
using Anthropic.SDK;
|
||||||
|
using Anthropic.SDK.Messaging;
|
||||||
|
using Anthropic.SDK.Constants;
|
||||||
|
using Anthropic.SDK.Models;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using System.Text.Json;
|
||||||
|
public class ClaudeModerationService : IModerationService
|
||||||
|
{
|
||||||
|
private readonly AnthropicClient _client;
|
||||||
|
private readonly float _autoApproveThreshold;
|
||||||
|
private readonly float _autoRejectThreshold;
|
||||||
|
|
||||||
|
public ClaudeModerationService(
|
||||||
|
AnthropicClient client,
|
||||||
|
IConfiguration config)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
_autoApproveThreshold = config.GetValue<float>(
|
||||||
|
"Moderation:AutoApproveThreshold", 0.7f);
|
||||||
|
_autoRejectThreshold = config.GetValue<float>(
|
||||||
|
"Moderation:AutoRejectThreshold", 0.9f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ModerationResult> ModerateAsync(string content, string context)
|
||||||
|
{
|
||||||
|
var prompt = $$"""
|
||||||
|
Tu es un modérateur pour une plateforme de mise en relation prestataires/clients.
|
||||||
|
Contexte : {{context}}
|
||||||
|
Contenu à modérer : {{content}}
|
||||||
|
|
||||||
|
Réponds UNIQUEMENT en JSON :
|
||||||
|
{
|
||||||
|
"action": "approved" | "rejected" | "needs_review",
|
||||||
|
"reason": "explication courte",
|
||||||
|
"confidence": 0.0 à 1.0
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
var response = await _client.Messages.GetClaudeMessageAsync(
|
||||||
|
new MessageParameters
|
||||||
|
{
|
||||||
|
Model = AnthropicModels.Claude45Haiku, // Haiku : rapide et économique
|
||||||
|
MaxTokens = 256,
|
||||||
|
Messages = [new Message(RoleType.User, prompt)]
|
||||||
|
});
|
||||||
|
|
||||||
|
var text = response.Content
|
||||||
|
.OfType<TextContent>()
|
||||||
|
.FirstOrDefault()?.Text ?? string.Empty;
|
||||||
|
|
||||||
|
return ParseResult(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModerationResult ParseResult(string json)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var doc = JsonDocument.Parse(json);
|
||||||
|
var root = doc.RootElement;
|
||||||
|
|
||||||
|
var actionStr = root.GetProperty("action").GetString();
|
||||||
|
var reason = root.GetProperty("reason").GetString() ?? string.Empty;
|
||||||
|
var confidence = root.GetProperty("confidence").GetSingle();
|
||||||
|
|
||||||
|
var action = actionStr switch
|
||||||
|
{
|
||||||
|
"approved" => confidence >= _autoApproveThreshold
|
||||||
|
? ModerationAction.Approved
|
||||||
|
: ModerationAction.NeedsReview,
|
||||||
|
"rejected" => confidence >= _autoRejectThreshold
|
||||||
|
? ModerationAction.Rejected
|
||||||
|
: ModerationAction.NeedsReview,
|
||||||
|
"needs_review" => ModerationAction.NeedsReview,
|
||||||
|
_ => ModerationAction.NeedsReview
|
||||||
|
};
|
||||||
|
|
||||||
|
return new ModerationResult(action, reason, confidence);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new ModerationResult(
|
||||||
|
ModerationAction.NeedsReview,
|
||||||
|
"Erreur de parsing de la réponse",
|
||||||
|
0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/Yavsc.Server/Services/MockModerationService.cs
Normal file
11
src/Yavsc.Server/Services/MockModerationService.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
using Yavsc.Abstract.Interfaces;
|
||||||
|
using Yavsc.Moderation;
|
||||||
|
|
||||||
|
public class MockModerationService : IModerationService
|
||||||
|
{
|
||||||
|
public Task<ModerationResult> ModerateAsync(string content, string context)
|
||||||
|
=> Task.FromResult(new ModerationResult(
|
||||||
|
ModerationAction.Approved,
|
||||||
|
"Mock modération — API non configurée",
|
||||||
|
1.0f));
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
<RepositoryUrl>https://github.com/pazof/yavsc</RepositoryUrl>
|
<RepositoryUrl>https://github.com/pazof/yavsc</RepositoryUrl>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Anthropic.SDK" />
|
||||||
<PackageReference Include="HigginsSoft.IdentityServer8.EntityFramework" />
|
<PackageReference Include="HigginsSoft.IdentityServer8.EntityFramework" />
|
||||||
<PackageReference Include="Magick.NET-Q8-AnyCPU" />
|
<PackageReference Include="Magick.NET-Q8-AnyCPU" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue