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:
- Authorized user opens an elder's AI analysis dialog from the elder list.
- Server loads only the resident data families the user can read.
- Server retrieves enabled platform shared knowledge and enabled active-organization knowledge.
- LangChain generates a fixed structured analysis.
- Server saves completed or failed history, writes audit logs, and returns structured JSON.
- 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.
- Reads
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.
- Add settings navigation item visible with
modules/elders/components/EldersClient.tsx- Add per-row AI analysis action and dialog.
Dependencies
Add runtime dependencies:
@langchain/openai@langchain/corelangchain
Add pgvector support through Drizzle/Postgres:
- Use PostgreSQL
vectorextension. - 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:readai:manageknowledge:readknowledge: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 keyscope enum('platform', 'organization')organizationId uuid null references organizations(id) on delete cascadetitle text not nullcategory text not null default ''tags text not null default ''body text not nullstatus 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'requiresorganizationId IS NULL.scope='organization'requiresorganizationIdto 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 keyentryId uuid not null references ai_knowledge_entries(id) on delete cascadeorganizationId uuid nullscope enum('platform', 'organization')chunkIndex integer not nullcontent text not nullembedding vector(<embedding_dimensions>) not nullsourceTitle text not nullsourceCategory 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 keyorganizationId uuid not null references organizations(id) on delete cascadeelderId uuid not null references elders(id) on delete cascadeactorAccountId uuid null references accounts(id)status enum('completed', 'failed') not nulldataScopes text not nullresultJson jsonb nullcitationsJson jsonb not null default '[]'::jsonbmodelSummaryJson jsonb not null default '{}'::jsonberrorCategory 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.
dataScopesstores 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:
healthonly ifhealth:read.careonly ifcare:read.familyonly iffamily:read.admissiononly ifadmission:read.facilityonly iffacility:read.alertonly ifalert:read.incidentonly ifincident:read.knowledgeonly ifknowledge: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_URLAI_API_KEYAI_CHAT_MODELAI_EMBEDDING_MODEL
config.ts validates required settings before generation/embedding and returns structured failures for missing configuration.
Retrieval
- Convert resident query/context summary into embedding.
- Query
ai_knowledge_chunkswith pgvector similarity. - Filter by allowed scopes before vector ranking.
- Return top K chunks with source metadata:
- source type:
knowledge - entry id
- chunk id
- title
- category
- scope
- source type:
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 | unknownsummary: stringkeyFindings: array of:category: stringseverity:info | warning | criticalevidence: stringcitationIds: string[]
recommendations: array of:priority:low | normal | high | urgentaction: stringreason: stringcitationIds: string[]
dataGaps: string[]citations: array of:id: stringsourceType:record | knowledgesourceId: stringtitle: stringexcerpt: string
confidence: number from 0 to 1modelSummary: 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
dataScopesexceed 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.
- Requires
POST /api/ai/knowledge- Requires
knowledge:manage. - Creates entry, chunks, embeds, writes audit log.
- Requires
PATCH /api/ai/knowledge/[id]- Requires
knowledge:manage. - Updates entry and rebuilds chunks/embeddings.
- Requires
DELETE /api/ai/knowledge/[id]- Requires
knowledge:manage. - Deletes entry and cascading chunks.
- Requires
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
Dialogwithwidth="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.generateai.elderAnalysis.generateFailedai.knowledge.createai.knowledge.updateai.knowledge.deleteai.knowledge.disableai.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_configurationprovider_errortimeoutschema_validation_failedretrieval_failedunknown
On failure:
- Save failed analysis history with sanitized category/reason.
- Write audit log.
- Return structured API failure.
- 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.yamlshould use a pgvector-capable PostgreSQL image or document manual extension installation. - Production deployment must verify
CREATE EXTENSION vectorsupport 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.