feat(tools): reposition as Do Better Legal two-track Norwegian-law MCP
De-family-ify shared JSON tools (persona-aware routing + neutral base prompt), make the verification review pick its engine per track (family/child-welfare -> dbn-legal-agent-v3, others -> gpt-4o interim), and route product-name strings through dbnToolsProductName(). Rebrand the MCP/tools surface (mcp.php + i18n mcp_* strings) to Do Better Legal. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+59
-12
@@ -479,6 +479,39 @@ function dbnToolsDefaultPersonaSlug(): string
|
||||
return $v !== '' ? $v : 'family';
|
||||
}
|
||||
|
||||
/** Product display name for tool-facing copy. Override via .env DBN_PRODUCT_NAME. */
|
||||
function dbnToolsProductName(): string
|
||||
{
|
||||
$v = trim((string)(dbnToolsEnv('DBN_PRODUCT_NAME', 'Do Better Legal') ?? 'Do Better Legal'));
|
||||
return $v !== '' ? $v : 'Do Better Legal';
|
||||
}
|
||||
|
||||
/**
|
||||
* Engine track for a persona slug:
|
||||
* 'family' → family + child-welfare, served/verified by the fine-tuned Qwen.
|
||||
* 'general' → all other Norwegian-law personas, served/verified by the interim engine.
|
||||
*/
|
||||
function dbnToolsPersonaTrack(?string $slug): string
|
||||
{
|
||||
$slug = strtolower(trim((string)$slug));
|
||||
return in_array($slug, ['family', 'child-welfare'], true) ? 'family' : 'general';
|
||||
}
|
||||
|
||||
/**
|
||||
* Reviewer/verification model for a persona's track.
|
||||
* Family track → fine-tuned Qwen (dbn-legal-agent-v3).
|
||||
* General track → interim gpt-4o; swap to the 2nd fine-tune later via .env
|
||||
* DBN_REVIEW_MODEL_GENERAL with no code change.
|
||||
*/
|
||||
function dbnToolsReviewerModel(?string $slug = null): string
|
||||
{
|
||||
if (dbnToolsPersonaTrack($slug) === 'family') {
|
||||
return trim((string)(dbnToolsEnv('DBN_REVIEW_MODEL_FAMILY', 'dbn-legal-agent-v3') ?? 'dbn-legal-agent-v3'))
|
||||
?: 'dbn-legal-agent-v3';
|
||||
}
|
||||
return trim((string)(dbnToolsEnv('DBN_REVIEW_MODEL_GENERAL', 'gpt-4o') ?? 'gpt-4o')) ?: 'gpt-4o';
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a DBN persona (a caveauAI chat profile for client 57) into a normalized
|
||||
* runtime bundle the tools can act on:
|
||||
@@ -544,7 +577,7 @@ function dbnToolsResolvePersona(int $clientId, ?string $slug = null): array
|
||||
dbnToolsAbort('No persona profile and the family-legal corpus package is not active.', 503, 'package_unavailable');
|
||||
}
|
||||
if (!dbnToolsHasActiveSubscription($clientId, (int)$package['id'])) {
|
||||
dbnToolsAbort('Do Better Norge does not have an active corpus subscription.', 503, 'subscription_missing');
|
||||
dbnToolsAbort(dbnToolsProductName() . ' does not have an active corpus subscription.', 503, 'subscription_missing');
|
||||
}
|
||||
$resolved = [
|
||||
'slug' => 'family',
|
||||
@@ -938,7 +971,7 @@ function dbnToolsRequireClient(): array
|
||||
{
|
||||
$client = dbnToolsFetchClient();
|
||||
if (!$client || empty($client['is_active'])) {
|
||||
dbnToolsAbort('Do Better Norge client tenant is not active or was not found.', 503, 'client_unavailable');
|
||||
dbnToolsAbort(dbnToolsProductName() . ' client tenant is not active or was not found.', 503, 'client_unavailable');
|
||||
}
|
||||
return $client;
|
||||
}
|
||||
@@ -1304,26 +1337,33 @@ function dbnToolsLiteLLMEmbedBatch(array $texts, string $model = 'nomic-embed-te
|
||||
// Strategy: ask ONE targeted question per document type, cap tokens at 350 to cut
|
||||
// off before the tool-planning loop kicks in, parse the answer for legal findings.
|
||||
|
||||
function dbnToolsRunLegalCheck(string $brief, string $docType, ?string $personaPrompt = null): array
|
||||
function dbnToolsRunLegalCheck(string $brief, string $docType, ?string $personaPrompt = null, ?string $personaSlug = null): array
|
||||
{
|
||||
$question = dbnToolsSelectCheckQuestion($docType, $brief);
|
||||
$track = dbnToolsPersonaTrack($personaSlug);
|
||||
$model = dbnToolsReviewerModel($personaSlug);
|
||||
|
||||
$question = dbnToolsSelectCheckQuestion($docType, $brief, $track);
|
||||
if ($question === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$opts = [
|
||||
'model' => 'dbn-legal-agent-v3',
|
||||
'model' => $model,
|
||||
'temperature' => 0.1,
|
||||
'max_tokens' => 350,
|
||||
'timeout' => 45,
|
||||
// No 'json' key — plain narrative, no response_format flag
|
||||
];
|
||||
|
||||
// Base prompt from the resolved persona (default = Child-welfare/barnevern).
|
||||
// Base prompt: resolved persona prompt wins; otherwise a track-appropriate default.
|
||||
$personaPrompt = is_string($personaPrompt) ? trim($personaPrompt) : '';
|
||||
$sysMsg = $personaPrompt !== ''
|
||||
? $personaPrompt
|
||||
: 'Du er en ekspert på norsk barnevernsloven og EMD-praksis. Svar alltid på norsk med korrekt juridisk terminologi. Bruk terskler fra barnevernsloven 2021: § 4-25 krever «klar nødvendighet». Strand Lobben mot Norge (37283/13) setter krav om rehabiliteringsplan før adopsjon. Aldri oppfinn paragrafnumre, saksnumre eller dommernavn.';
|
||||
if ($personaPrompt !== '') {
|
||||
$sysMsg = $personaPrompt;
|
||||
} elseif ($track === 'family') {
|
||||
$sysMsg = 'Du er en ekspert på norsk barnevernsloven og EMD-praksis. Svar alltid på norsk med korrekt juridisk terminologi. Bruk terskler fra barnevernsloven 2021: § 4-25 krever «klar nødvendighet». Strand Lobben mot Norge (37283/13) setter krav om rehabiliteringsplan før adopsjon. Aldri oppfinn paragrafnumre, saksnumre eller dommernavn.';
|
||||
} else {
|
||||
$sysMsg = 'Du er en erfaren norsk jurist med bred kompetanse i norsk rett (forvaltningsrett, utlendingsrett, arbeidsrett, husleie- og forbrukerrett). Svar alltid på norsk med korrekt juridisk terminologi. Aldri oppfinn paragrafnumre, saksnumre, dommernavn eller kilder. Hvis du er usikker, si det tydelig.';
|
||||
}
|
||||
|
||||
// Prepend a short snippet of the actual synthesis text so v3 answers in context,
|
||||
// not just as a general law quiz. Strip HTML tags and cap at 350 chars.
|
||||
@@ -1360,16 +1400,23 @@ function dbnToolsRunLegalCheck(string $brief, string $docType, ?string $personaP
|
||||
'severity' => dbnToolsInferCheckSeverity($clean),
|
||||
'legal_basis' => dbnToolsExtractCheckLegalBasis($clean),
|
||||
'source_refs' => [],
|
||||
'what_to_check'=> 'Verifiser med norsk familieretsadvokat',
|
||||
'check_model' => 'dbn-legal-agent-v3',
|
||||
'what_to_check'=> $track === 'family' ? 'Verifiser med norsk familieretsadvokat' : 'Verifiser med norsk advokat',
|
||||
'check_model' => $model,
|
||||
]];
|
||||
}
|
||||
|
||||
function dbnToolsSelectCheckQuestion(string $docType, string $brief): ?string
|
||||
function dbnToolsSelectCheckQuestion(string $docType, string $brief, string $track = 'family'): ?string
|
||||
{
|
||||
$t = mb_strtolower($docType);
|
||||
$b = mb_strtolower($brief);
|
||||
|
||||
// Track 2 (other Norwegian law): neutral verification question, always fires.
|
||||
if ($track !== 'family') {
|
||||
return 'Vurder teksten ovenfor opp mot gjeldende norsk rett: er de rettslige påstandene korrekte, '
|
||||
. 'er det vist til riktige lover og paragrafer, og mangler det viktige rettslige vilkår, frister '
|
||||
. 'eller prosessuelle krav som burde vært nevnt?';
|
||||
}
|
||||
|
||||
if (str_contains($t, 'akutt') || str_contains($t, 'emergency')) {
|
||||
return 'Hva er den korrekte rettslige terskelen for midlertidig plassering utenfor hjemmet '
|
||||
. 'etter barnevernsloven § 4-25 (2021-loven)? Er det forskjell mellom § 4-6 (1992-loven) '
|
||||
|
||||
Reference in New Issue
Block a user