feat: add AI knowledge analysis MVP
This commit is contained in:
@@ -349,3 +349,96 @@ GOOGLE_GENERATIVE_AI_API_KEY=...
|
||||
# Anthropic
|
||||
ANTHROPIC_API_KEY=sk-ant-...
|
||||
```
|
||||
|
||||
## Scenario: Elder AI Analysis MVP With LangChain And pgvector
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
- Trigger: AI features that inspect resident, care, health, family, admission, alert, incident, or knowledge data.
|
||||
- Current project contract: the elder analysis MVP uses LangChain on the server with an OpenAI-compatible provider, not the Vercel AI SDK runtime path.
|
||||
- Keep provider calls under `modules/ai/server/*`; feature modules and API routes must not instantiate model clients directly.
|
||||
|
||||
### 2. Signatures
|
||||
|
||||
- API:
|
||||
- `GET /api/ai/elders/[id]/analyses`
|
||||
- `POST /api/ai/elders/[id]/analyses`
|
||||
- `GET /api/ai/knowledge`
|
||||
- `POST /api/ai/knowledge`
|
||||
- `PATCH /api/ai/knowledge/[id]`
|
||||
- `DELETE /api/ai/knowledge/[id]`
|
||||
- DB:
|
||||
- `ai_knowledge_entries`
|
||||
- `ai_knowledge_chunks` with `vector(1536)`
|
||||
- `elder_ai_analyses`
|
||||
- Runtime env:
|
||||
- `AI_API_KEY` required for generation and embedding.
|
||||
- `AI_BASE_URL` optional OpenAI-compatible endpoint.
|
||||
- `AI_CHAT_MODEL` optional, defaults in `modules/ai/server/config.ts`.
|
||||
- `AI_EMBEDDING_MODEL` optional, defaults in `modules/ai/server/config.ts`.
|
||||
|
||||
### 3. Contracts
|
||||
|
||||
- All API responses use the project API shape: `{ success: boolean, reason: string, ...payload }`.
|
||||
- Knowledge entry input fields:
|
||||
- `scope`: `"platform" | "organization"`
|
||||
- `title`: non-empty string
|
||||
- `category`: string
|
||||
- `tags`: string
|
||||
- `body`: non-empty string
|
||||
- `status`: `"enabled" | "disabled"`
|
||||
- Elder analysis history stores:
|
||||
- `status`: `"completed" | "failed"`
|
||||
- `dataScopes`: JSON array of included families
|
||||
- `resultJson`: only for completed rows
|
||||
- `errorCategory` / `errorReason`: sanitized only for failed rows
|
||||
- Failed rows must not store prompts, full resident context snapshots, API keys, or raw provider errors.
|
||||
- Knowledge retrieval may return enabled platform knowledge and enabled active-organization knowledge only.
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
- Missing `ai:read` -> `403 权限不足`.
|
||||
- Missing `elder:read` -> `403 权限不足`.
|
||||
- Missing active organization for elder analysis -> `400`.
|
||||
- Target elder outside active organization -> `404`.
|
||||
- Missing `knowledge:read` -> knowledge scope is omitted from analysis context.
|
||||
- Missing `knowledge:manage` -> knowledge mutation routes return `403`.
|
||||
- Platform knowledge mutation without `platform:manage` -> `403`.
|
||||
- Missing `AI_API_KEY` -> save failed analysis with `missing_config`, return structured failure.
|
||||
- Provider failure -> save failed analysis with `provider_error` or `timeout`.
|
||||
- Invalid model output -> save failed analysis with `schema_validation_failed`.
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
- Good: org manager generates elder analysis; context includes only data families allowed by the manager's current permissions; history stores matching `dataScopes`.
|
||||
- Base: caregiver generates analysis; caregiver gets `ai:read` and `knowledge:read` by default, but cannot mutate knowledge.
|
||||
- Bad: viewer has `elder:read` but no `ai:read`; the AI row action must not be exposed and API must return `403`.
|
||||
- Bad: organization user attempts to retrieve another organization's private knowledge; the retrieval query must not include those chunks.
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
- Permission seeding assertions for `ai:*` and `knowledge:*` default role grants.
|
||||
- Knowledge retrieval tests for platform shared, own organization, other organization, and disabled entry filtering.
|
||||
- Analysis tests for missing config, schema validation failure, failed-history sanitization, and data-scope redaction.
|
||||
- Route tests for auth/permission failures and standard response shape.
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
#### Wrong
|
||||
|
||||
```typescript
|
||||
// Do not call a model from a feature route and pass raw resident rows directly.
|
||||
const model = new ChatOpenAI({ apiKey: process.env.AI_API_KEY });
|
||||
await model.invoke(JSON.stringify(residentRows));
|
||||
```
|
||||
|
||||
#### Correct
|
||||
|
||||
```typescript
|
||||
// Keep model calls in modules/ai/server and build permission-aware context first.
|
||||
const result = await generateElderAiAnalysis(auth.context, elderId);
|
||||
if (!result.success) {
|
||||
return jsonFailure(result.reason, result.status);
|
||||
}
|
||||
return jsonSuccess("AI 分析已生成", { analysis: result.data.analysis }, 201);
|
||||
```
|
||||
|
||||
@@ -244,6 +244,79 @@ if (type === "tool-output-available") {
|
||||
}
|
||||
```
|
||||
|
||||
## Scenario: Elder AI Analysis Panel MVP
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
- Trigger: UI surfaces that render elder AI analysis, knowledge retrieval results, or AI history.
|
||||
- Current project contract: the MVP elder workflow is a fixed structured analysis panel, not chat and not streaming.
|
||||
- Do not use `@ai-sdk/react` hooks for this MVP panel; use the existing project API result shape through fetch until a generated client exists for these routes.
|
||||
|
||||
### 2. Signatures
|
||||
|
||||
- Elder row action:
|
||||
- Visible only when current permissions include both `ai:read` and `elder:read`.
|
||||
- Opens `ElderAiAnalysisDialog` with `width="wide"`.
|
||||
- Client APIs:
|
||||
- `GET /api/ai/elders/[id]/analyses` -> `{ success, reason, history }`
|
||||
- `POST /api/ai/elders/[id]/analyses` -> `{ success, reason, analysis }`
|
||||
- `GET /api/ai/knowledge` -> `{ success, reason, entries }`
|
||||
- Knowledge mutations -> `{ success, reason, entry }`
|
||||
|
||||
### 3. Contracts
|
||||
|
||||
- Render completed analysis from fixed fields:
|
||||
- `overallRiskLevel`
|
||||
- `summary`
|
||||
- `keyFindings[]`
|
||||
- `recommendations[]`
|
||||
- `dataGaps[]`
|
||||
- `citations[]`
|
||||
- `confidence`
|
||||
- `modelSummary`
|
||||
- Render `restricted: true` history items as placeholders only; do not assume `result` or citations exist.
|
||||
- Render failed history from sanitized `errorCategory` / `errorReason`; never display raw provider errors.
|
||||
- The MVP UI must not provide one-click business mutations or confirmable drafts from recommendations.
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
- Missing `ai:read` or `elder:read` -> hide row action.
|
||||
- API failure -> show `reason` in the dialog or settings page message area.
|
||||
- `restricted: true` history -> show an access-restricted placeholder.
|
||||
- `status === "failed"` -> show failed badge and sanitized reason.
|
||||
- Empty history -> show empty state.
|
||||
- Generation pending -> disable generate button and show loading label.
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
- Good: authorized manager opens dialog, generates analysis, sees latest completed result, citations, data gaps, and history list.
|
||||
- Base: generation fails because provider is not configured; user sees a structured failure message and failed history remains visible.
|
||||
- Bad: viewer without `ai:read` sees an `AI 分析` row action.
|
||||
- Bad: frontend renders recommendations as buttons that create care tasks, alert handling notes, or health review records.
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
- Component or route smoke coverage for hidden AI action when permissions are missing.
|
||||
- History rendering coverage for completed, failed, restricted, and empty states.
|
||||
- Knowledge settings coverage for read-only users and `knowledge:manage` users.
|
||||
- Build check must include both unscoped `/app/settings/knowledge` and scoped `/app/[organizationSlug]/settings/knowledge`.
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
#### Wrong
|
||||
|
||||
```tsx
|
||||
// Do not expose an AI action based only on elder read permission.
|
||||
{permissions.includes("elder:read") ? <button>AI 分析</button> : null}
|
||||
```
|
||||
|
||||
#### Correct
|
||||
|
||||
```tsx
|
||||
const canUseAi = permissions.includes("ai:read") && permissions.includes("elder:read");
|
||||
{canUseAi ? <Button onClick={() => setAiTarget(elder)}>AI 分析</Button> : null}
|
||||
```
|
||||
|
||||
## 7. Best Practices Summary
|
||||
|
||||
| Rule | Description |
|
||||
|
||||
Reference in New Issue
Block a user