Files

12 KiB

AI Agent Knowledge Analysis Design

Overview

Implement the MVP as a server-side LangChain AI module with PostgreSQL/pgvector-backed knowledge retrieval and a structured elder AI analysis panel in the existing elder list workflow.

The MVP is not a chat assistant. It is a deterministic product workflow:

  1. Authorized user opens an elder's AI analysis dialog from the elder list.
  2. Server loads only the resident data families the user can read.
  3. Server retrieves enabled platform shared knowledge and enabled active-organization knowledge.
  4. LangChain generates a fixed structured analysis.
  5. Server saves completed or failed history, writes audit logs, and returns structured JSON.
  6. UI renders latest analysis, history, citations, recommendations, and restricted placeholders where permissions do not allow detail access.

Module Boundaries

New Module

Create modules/ai/:

  • modules/ai/types.ts
    • Zod-compatible validation helpers or TypeScript runtime validators for knowledge entries, analysis output, history status, risk levels, citation shapes, and data scopes.
  • modules/ai/server/config.ts
    • Reads AI_BASE_URL, AI_API_KEY, AI_CHAT_MODEL, AI_EMBEDDING_MODEL, and optional timeout/limit settings.
  • modules/ai/server/knowledge.ts
    • CRUD for knowledge entries.
    • Chunking and embedding on save.
    • Scope-filtered retrieval from pgvector.
  • modules/ai/server/elder-context.ts
    • Builds full resident graph with permission-aware optional data families.
  • modules/ai/server/analysis.ts
    • Runs LangChain analysis flow.
    • Validates structured output.
    • Saves completed/failed history.
  • modules/ai/components/KnowledgeManagementClient.tsx
    • Settings UI for manual knowledge entries.
  • modules/ai/components/ElderAiAnalysisDialog.tsx
    • Elder row dialog panel for generation, latest analysis, history, and citations.

Existing Integration Points

  • modules/core/server/schema.ts
    • Add AI enums/tables.
  • modules/core/types.ts
    • Add permissions and exported AI-related shared types as needed.
  • modules/core/server/permissions.ts
    • Seed AI/knowledge permission definitions and default role grants.
  • app/api/ai/...
    • New API routes for knowledge and elder analysis.
  • app/(app)/app/settings/knowledge/page.tsx
    • New settings route.
  • app/(app)/app/[organizationSlug]/settings/knowledge/page.tsx
    • Scoped route re-export.
  • modules/shared/lib/navigation.ts
    • Add settings navigation item visible with knowledge:read.
  • modules/elders/components/EldersClient.tsx
    • Add per-row AI analysis action and dialog.

Dependencies

Add runtime dependencies:

  • @langchain/openai
  • @langchain/core
  • langchain

Add pgvector support through Drizzle/Postgres:

  • Use PostgreSQL vector extension.
  • Prefer a pgvector-capable Postgres image for local development instead of plain postgres:17-alpine.
  • If Drizzle vector column support is insufficient for the exact version, use custom SQL migration for vector extension/index while keeping typed table metadata in schema.

Vercel AI SDK remains out of the MVP runtime path.

Data Model

Permissions

Add permission IDs:

  • ai:read
  • ai:manage
  • knowledge:read
  • knowledge:manage

Default grants:

  • platform_admin: all four.
  • org_admin: all four.
  • manager: all four.
  • caregiver: ai:read, knowledge:read.
  • viewer, family, resident: none.

Knowledge Entries

Table: ai_knowledge_entries

Fields:

  • id uuid primary key
  • scope enum('platform', 'organization')
  • organizationId uuid null references organizations(id) on delete cascade
  • title text not null
  • category text not null default ''
  • tags text not null default ''
  • body text not null
  • status enum('enabled', 'disabled') not null default 'enabled'
  • createdByAccountId uuid null references accounts(id)
  • updatedByAccountId uuid null references accounts(id)
  • createdAt timestamptz not null default now()
  • updatedAt timestamptz not null default now()

Rules:

  • scope='platform' requires organizationId IS NULL.
  • scope='organization' requires organizationId to match the active organization.
  • Platform entries require platform-level authorization to manage.
  • Organization entries require active organization plus knowledge:manage.

Knowledge Chunks

Table: ai_knowledge_chunks

Fields:

  • id uuid primary key
  • entryId uuid not null references ai_knowledge_entries(id) on delete cascade
  • organizationId uuid null
  • scope enum('platform', 'organization')
  • chunkIndex integer not null
  • content text not null
  • embedding vector(<embedding_dimensions>) not null
  • sourceTitle text not null
  • sourceCategory text not null default ''
  • createdAt timestamptz not null default now()

Indexes:

  • B-tree on (scope, organization_id).
  • Vector index for embedding similarity, using pgvector operator/index supported by deployment.

Embedding dimensions are tied to AI_EMBEDDING_MODEL; document the chosen default in env configuration.

Elder AI Analyses

Table: elder_ai_analyses

Fields:

  • id uuid primary key
  • organizationId uuid not null references organizations(id) on delete cascade
  • elderId uuid not null references elders(id) on delete cascade
  • actorAccountId uuid null references accounts(id)
  • status enum('completed', 'failed') not null
  • dataScopes text not null
  • resultJson jsonb null
  • citationsJson jsonb not null default '[]'::jsonb
  • modelSummaryJson jsonb not null default '{}'::jsonb
  • errorCategory text not null default ''
  • errorReason text not null default ''
  • createdAt timestamptz not null default now()

Rules:

  • Completed rows require a valid resultJson.
  • Failed rows must not store prompt, full resident context, or raw provider errors.
  • dataScopes stores a stable comma-separated list or JSON array; use JSONB if Drizzle ergonomics are acceptable.

Authorization Model

Generate Analysis

Required:

  • Authenticated session.
  • Active organization.
  • ai:read.
  • elder:read.
  • Target elder belongs to active organization.

Optional data families:

  • health only if health:read.
  • care only if care:read.
  • family only if family:read.
  • admission only if admission:read.
  • facility only if facility:read.
  • alert only if alert:read.
  • incident only if incident:read.
  • knowledge only if knowledge:read.

dataScopes records exactly which families were included.

View Analysis History

History list can show safe metadata for rows in the active organization and elder when the user has ai:read and elder:read.

History detail requires all permissions implied by dataScopes. If missing any scope, return or render a restricted placeholder and do not include resultJson/citations content.

Knowledge Retrieval

Retrieval requires knowledge:read.

Eligible entries:

  • scope='platform', status='enabled'.
  • scope='organization', organizationId = activeOrganizationId, status='enabled'.

No organization-private chunks from other organizations may be retrievable.

LangChain Flow

Provider

Use OpenAI-compatible configuration:

  • AI_BASE_URL
  • AI_API_KEY
  • AI_CHAT_MODEL
  • AI_EMBEDDING_MODEL

config.ts validates required settings before generation/embedding and returns structured failures for missing configuration.

Retrieval

  1. Convert resident query/context summary into embedding.
  2. Query ai_knowledge_chunks with pgvector similarity.
  3. Filter by allowed scopes before vector ranking.
  4. Return top K chunks with source metadata:
    • source type: knowledge
    • entry id
    • chunk id
    • title
    • category
    • scope

Resident Context Assembly

Build one typed object:

  • Elder basics.
  • Current bed/admission context.
  • Admission history.
  • Health profile.
  • Recent vitals.
  • Chronic conditions.
  • Health anomaly reviews.
  • Care tasks.
  • Alert triggers/rules where relevant.
  • System incidents matched by elder/bed where relevant.
  • Family contacts/visits/feedback.
  • Retrieved knowledge snippets.

Use batch queries and Promise.all where independent. No await in loops.

Structured Output

Output schema:

  • overallRiskLevel: low | medium | high | critical | unknown
  • summary: string
  • keyFindings: array of:
    • category: string
    • severity: info | warning | critical
    • evidence: string
    • citationIds: string[]
  • recommendations: array of:
    • priority: low | normal | high | urgent
    • action: string
    • reason: string
    • citationIds: string[]
  • dataGaps: string[]
  • citations: array of:
    • id: string
    • sourceType: record | knowledge
    • sourceId: string
    • title: string
    • excerpt: string
  • confidence: number from 0 to 1
  • modelSummary: object with provider base URL host, chat model, embedding model, and generation timestamp, excluding API key.

Use LangChain structured output where possible. Validate again server-side before saving.

API Design

Elder Analysis

  • GET /api/ai/elders/[id]/analyses
    • Returns latest allowed analysis metadata/detail and history list.
    • Redacts detail for records whose dataScopes exceed current permissions.
  • POST /api/ai/elders/[id]/analyses
    • Synchronously generates a new analysis.
    • Returns { success: true, reason, analysis } on completion.
    • On failure, saves failed history and audit log, then returns { success: false, reason }.

Knowledge

  • GET /api/ai/knowledge
    • Requires knowledge:read.
    • Lists platform and active organization entries visible to caller.
  • POST /api/ai/knowledge
    • Requires knowledge:manage.
    • Creates entry, chunks, embeds, writes audit log.
  • PATCH /api/ai/knowledge/[id]
    • Requires knowledge:manage.
    • Updates entry and rebuilds chunks/embeddings.
  • DELETE /api/ai/knowledge/[id]
    • Requires knowledge:manage.
    • Deletes entry and cascading chunks.

All routes return the existing API response shape with Cache-Control: no-store.

UI Design

Elder List

In EldersClient:

  • Add AI analysis button per row for users with ai:read.
  • Use lucide icon such as Sparkles.
  • Open Dialog with width="wide".
  • Dialog sections:
    • Resident summary.
    • Generate button with loading state.
    • Latest analysis summary.
    • Findings and recommendations.
    • Data gaps.
    • Citations.
    • History list with restricted placeholders.

No chat input and no one-click business creation.

Knowledge Settings Page

Route: /settings/knowledge

Features:

  • List entries by scope/status/category/search.
  • Create/edit dialog with title, body, category, tags, scope, enabled/disabled.
  • Disable mutations when missing knowledge:manage.
  • Show embedding/rebuild status through response messages; no background jobs.

Audit And Logging

Audit actions:

  • ai.elderAnalysis.generate
  • ai.elderAnalysis.generateFailed
  • ai.knowledge.create
  • ai.knowledge.update
  • ai.knowledge.delete
  • ai.knowledge.disable
  • ai.knowledge.enable

Audit records include actor, organization, target type/id, result, and sanitized reason.

Do not store:

  • Full prompts.
  • Full resident context snapshots.
  • Raw provider errors.
  • API keys.

Failure Handling

Generation failures are classified:

  • missing_configuration
  • provider_error
  • timeout
  • schema_validation_failed
  • retrieval_failed
  • unknown

On failure:

  1. Save failed analysis history with sanitized category/reason.
  2. Write audit log.
  3. Return structured API failure.
  4. UI shows a concise error state and keeps prior completed history visible.

Rollout And Compatibility

  • Requires database migration for pgvector extension, AI enums, knowledge tables, chunk table, analysis table, and permissions.
  • Local compose.yaml should use a pgvector-capable PostgreSQL image or document manual extension installation.
  • Production deployment must verify CREATE EXTENSION vector support before migration.
  • If AI env vars are missing, knowledge CRUD can still work except embedding-dependent save should fail gracefully; analysis generation should return a structured configuration failure.

Open Trade-Offs

  • Embedding dimensions depend on the selected embedding model. The implementation should define a default and document migration impact if changed.
  • Synchronous generation is acceptable for MVP but can be replaced later by async jobs using the same history table status field.
  • Platform shared knowledge increases utility but requires careful scope filters in every retrieval query.