chore(task): archive 07-04-ai-agent-knowledge-analysis
This commit is contained in:
@@ -0,0 +1,395 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user