feat(tools): persona-driven multi-domain corpus + model routing
Generalize the family-locked legal tools into caveauAI persona profiles (client 57 chat profiles, resolved in-process via the chat_profiles bridge). Each tool accepts an optional `profile` slug that scopes the corpus package(s), search method, system prompt and synthesis model; omitting it falls back to the family-legal package so existing behaviour is unchanged. - dbnToolsResolvePersona / dbnToolsListPersonas / dbnToolsBootChatProfiles in bootstrap.php; new api/personas.php + dbn.list_personas MCP tool. - LegalTools search/ask/corpusContextForSummarize and the BvjAnalyzer / LegalAnalysis / translate paths take the persona's packages + prompt + model. - Persona <select> on ask/search/summarize (populated from api/personas.php). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+4
-1
@@ -16,5 +16,8 @@ dbnToolsWithChargedTelemetry('ask', $language, $ftUid, function () use ($input,
|
||||
if (mb_strlen(trim($question), 'UTF-8') < 5) {
|
||||
dbnToolsAbort('Enter a question or select a document before running.', 422, 'empty_text');
|
||||
}
|
||||
return (new DbnLegalToolsService())->ask($question, $language, $engine);
|
||||
$persona = (isset($input['profile']) && is_string($input['profile']) && trim($input['profile']) !== '')
|
||||
? trim($input['profile'])
|
||||
: null;
|
||||
return (new DbnLegalToolsService())->ask($question, $language, $engine, $persona);
|
||||
});
|
||||
|
||||
+7
-1
@@ -122,7 +122,13 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
$result = (new DbnBvjAnalyzerAgent())->run(
|
||||
$personaSlug = (isset($input['profile']) && is_string($input['profile']) && trim($input['profile']) !== '')
|
||||
? trim($input['profile'])
|
||||
: null;
|
||||
|
||||
$result = (new DbnBvjAnalyzerAgent())
|
||||
->withPersona($personaSlug)
|
||||
->run(
|
||||
$uploadedFiles,
|
||||
$advocateRole,
|
||||
$engine,
|
||||
|
||||
@@ -13,6 +13,9 @@ $mode = in_array($rawMode, ['hybrid', 'bm25', 'vector', 'azure'], true) ? $r
|
||||
$language = dbnToolsNormalizeLanguage($input['language'] ?? 'en');
|
||||
$limit = max(1, min(20, (int)($input['limit'] ?? 8)));
|
||||
$category = isset($input['category']) && $input['category'] !== '' ? trim((string)$input['category']) : null;
|
||||
$persona = (isset($input['profile']) && is_string($input['profile']) && trim($input['profile']) !== '')
|
||||
? trim($input['profile'])
|
||||
: null;
|
||||
|
||||
const EXCLUDED_DOMAIN = 'dobetternorge.no';
|
||||
|
||||
@@ -23,7 +26,7 @@ if (mb_strlen($query, 'UTF-8') < 3) {
|
||||
try {
|
||||
// ── HYBRID: delegate to the existing RAG pipeline ──────────────────────
|
||||
if ($mode === 'hybrid') {
|
||||
$result = (new DbnLegalToolsService())->search($query, $language, $limit, 'disabled', null);
|
||||
$result = (new DbnLegalToolsService())->search($query, $language, $limit, 'disabled', null, 'both', $persona);
|
||||
$hits = array_map(fn($h) => [
|
||||
'title' => $h['title'] ?? '',
|
||||
'category' => $h['category'] ?? '',
|
||||
|
||||
+13
-3
@@ -55,7 +55,16 @@ try {
|
||||
'chars' => mb_strlen($text, 'UTF-8'),
|
||||
]);
|
||||
|
||||
$agent = new DbnLegalAnalysisAgent();
|
||||
// Resolve the persona (default = child-welfare for this barnevern tool) so
|
||||
// the targeted legal-check step uses the persona's system prompt + model.
|
||||
$client = dbnToolsRequireClient();
|
||||
$personaSlug = (isset($input['profile']) && is_string($input['profile']) && trim($input['profile']) !== '')
|
||||
? trim($input['profile'])
|
||||
: 'child-welfare';
|
||||
$personaResolved = dbnToolsResolvePersona((int)$client['id'], $personaSlug);
|
||||
|
||||
$agent = (new DbnLegalAnalysisAgent())
|
||||
->withPersonaPrompt($personaResolved['system_prompt'] ?? null);
|
||||
|
||||
// Pass 1 — extract issues (Azure, fast); deduct credit AFTER this succeeds
|
||||
$emit('progress', ['step' => 'extracting_issues', 'detail' => 'Identifying distinct legal issues…']);
|
||||
@@ -101,7 +110,7 @@ try {
|
||||
'issue_id' => $issue['id'],
|
||||
]);
|
||||
$corpusQuery = $issue['question'] . "\n" . $issue['brief_context'];
|
||||
$corpusContext = $svc->corpusContextForSummarize($corpusQuery, 3);
|
||||
$corpusContext = $svc->corpusContextForSummarize($corpusQuery, 3, $personaResolved['slug'] ?? null);
|
||||
|
||||
$emit('progress', [
|
||||
'step' => 'issue_answering',
|
||||
@@ -122,7 +131,8 @@ try {
|
||||
try {
|
||||
$legalCheck = dbnToolsRunLegalCheck(
|
||||
mb_strimwidth((string)($synth['overall_assessment'] ?? ''), 0, 800),
|
||||
$docType
|
||||
$docType,
|
||||
$personaResolved['system_prompt'] ?? null
|
||||
);
|
||||
} catch (Throwable) {}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../includes/LegalTools.php';
|
||||
|
||||
dbnToolsRequireMethod('GET');
|
||||
dbnToolsRequireAuth();
|
||||
|
||||
$client = dbnToolsRequireClient();
|
||||
$personas = dbnToolsListPersonas((int)$client['id']);
|
||||
|
||||
dbnToolsRespond([
|
||||
'ok' => true,
|
||||
'personas' => $personas,
|
||||
'default_persona' => dbnToolsDefaultPersonaSlug(),
|
||||
]);
|
||||
+4
-1
@@ -20,5 +20,8 @@ dbnToolsWithTelemetry('search', $language, function () use ($input, $language):
|
||||
$scope = in_array($input['corpus_scope'] ?? '', ['shared', 'private', 'both'], true)
|
||||
? $input['corpus_scope']
|
||||
: 'both';
|
||||
return (new DbnLegalToolsService())->search($query, $language, $limit, $temporalMode, $asOfDate, $scope);
|
||||
$persona = (isset($input['profile']) && is_string($input['profile']) && trim($input['profile']) !== '')
|
||||
? trim($input['profile'])
|
||||
: null;
|
||||
return (new DbnLegalToolsService())->search($query, $language, $limit, $temporalMode, $asOfDate, $scope, $persona);
|
||||
});
|
||||
|
||||
+4
-1
@@ -51,7 +51,10 @@ try {
|
||||
]);
|
||||
$svc = new DbnLegalToolsService();
|
||||
$query = mb_substr(trim($text), 0, 600, 'UTF-8');
|
||||
$corpusContext = $svc->corpusContextForSummarize($query, 8);
|
||||
$persona = (isset($input['profile']) && is_string($input['profile']) && trim($input['profile']) !== '')
|
||||
? trim($input['profile'])
|
||||
: null;
|
||||
$corpusContext = $svc->corpusContextForSummarize($query, 8, $persona);
|
||||
$emit('progress', [
|
||||
'step' => 'corpus_done',
|
||||
'detail' => $corpusContext !== ''
|
||||
|
||||
+12
-2
@@ -70,12 +70,22 @@ try {
|
||||
$sourceName = dbnToolsLanguageName($sourceLang);
|
||||
$targetName = dbnToolsLanguageName($targetLang);
|
||||
|
||||
// Persona-aware domain framing (default = Norwegian family law for back-compat).
|
||||
$client = dbnToolsRequireClient();
|
||||
$personaSlug = (isset($input['profile']) && is_string($input['profile']) && trim($input['profile']) !== '')
|
||||
? trim($input['profile'])
|
||||
: null;
|
||||
$persona = dbnToolsResolvePersona((int)$client['id'], $personaSlug);
|
||||
$domainLabel = (isset($persona['name']) && is_string($persona['name']) && trim($persona['name']) !== '' && ($persona['source'] ?? '') === 'chat_profile')
|
||||
? trim($persona['name'])
|
||||
: 'Norwegian family law, ECHR, and child-welfare proceedings';
|
||||
|
||||
$docTypeHint = $docType !== 'auto'
|
||||
? "The document is of type: {$docType}. Apply appropriate Norwegian family-law terminology for this context."
|
||||
? "The document is of type: {$docType}. Apply appropriate {$domainLabel} terminology for this context."
|
||||
: '';
|
||||
|
||||
$systemPrompt = <<<PROMPT
|
||||
You are a professional legal translator specialising in Norwegian family law, ECHR, and child-welfare proceedings.
|
||||
You are a professional legal translator specialising in {$domainLabel}.
|
||||
|
||||
Task: Translate the provided text from {$sourceName} into {$targetName}.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user