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:
2026-06-01 20:49:58 +02:00
parent 5a0ef89dca
commit 662fbf7d6d
16 changed files with 404 additions and 58 deletions
+23 -3
View File
@@ -34,11 +34,25 @@ final class DbnBvjAnalyzerAgent
private array $uploadVecs = [];
private array $stepTimings = [];
/** Persona slug driving package selection + the legal-check system prompt; default = child-welfare. */
private string $personaSlug = 'child-welfare';
/** Resolved persona system prompt (set in run()); null ⇒ built-in barnevern prompt. */
private ?string $resolvedPersonaPrompt = null;
public function __construct(DbnAzureOpenAiGateway|DbnBedrockGateway|null $azure = null)
{
$this->azure = $azure ?: DbnGatewayFactory::makeForTool('barnevernet-analyze');
}
public function withPersona(?string $slug): self
{
$slug = is_string($slug) ? trim($slug) : '';
if ($slug !== '') {
$this->personaSlug = $slug;
}
return $this;
}
/**
* Main pipeline. At least 1 uploaded file is required.
*
@@ -71,7 +85,12 @@ final class DbnBvjAnalyzerAgent
}
$client = dbnToolsRequireClient();
$package = $this->requireFamilyPackage((int)$client['id']);
$persona = dbnToolsResolvePersona((int)$client['id'], $this->personaSlug);
$package = $persona['package'] ?? $this->requireFamilyPackage((int)$client['id']);
$packageIds = $persona['package_ids'] ?: [(int)$package['id']];
$this->resolvedPersonaPrompt = is_string($persona['system_prompt'] ?? null) && trim($persona['system_prompt']) !== ''
? $persona['system_prompt']
: null;
dbnToolsBootCaveau();
$aiPortalRoot = dbnToolsAiPortalRoot();
@@ -265,7 +284,7 @@ final class DbnBvjAnalyzerAgent
[
'search_private' => false,
'search_shared' => true,
'package_ids' => [(int)$package['id']],
'package_ids' => $packageIds,
'shared_doc_ids' => $sharedDocIds,
'chunk_limit' => $controls['chunk_limit'],
'search_method' => 'hybrid',
@@ -964,7 +983,8 @@ PROMPT;
if ($engine !== 'dbn_legal_v3') {
$checkFindings = dbnToolsRunLegalCheck(
(string)($json['advocacy_brief'] ?? ''),
$docType
$docType,
$this->resolvedPersonaPrompt
);
if (!empty($checkFindings)) {
if (!is_array($json['procedural_red_flags'] ?? null)) {