ce76046a29
- consolidate API ownership and remove dead vendor code - add Stripe billing, learning paths, and public CV hardening - add migration, recovery, security, audit, and browser gates
94 lines
5.6 KiB
Markdown
94 lines
5.6 KiB
Markdown
# AI Career Assistant (Phase 5)
|
|
|
|
> Phase 5 (2026-07-18). The per-application AI Workspace: modules, prompt flow, provider abstraction,
|
|
> history model, extension points. Companion to `cv-builder.md`, `career-profile-model.md`, and
|
|
> `MASTER_IMPLEMENTATION_GUIDE.md` (AI philosophy). Verified against code + a running container.
|
|
|
|
## Principle
|
|
|
|
AI **assists** the application workflow — it never replaces the user. Every module is **suggestion
|
|
only**: it returns markdown the user reviews and copies. Nothing is applied automatically to the master
|
|
profile, a CV variant, or the application. Every prompt carries the guardrail *"preserve every factual
|
|
claim — never invent employers, titles, dates, qualifications, or metrics."* Job tracking stays the
|
|
core product; the assistant improves the *application* it hangs off.
|
|
|
|
## Where it lives
|
|
|
|
Each job application gains an **AI Workspace** tab (`AiWorkspacePanel`) in the job details dialog. It
|
|
is the central place for all AI on that application. Pre-existing per-tab AI (candidate-fit, focus-plan,
|
|
interview-prep) is untouched — the workspace is the unified, history-backed home for the Phase 5 modules.
|
|
|
|
## Modules
|
|
|
|
`AiWorkspaceService.Modules`, all via `ISummarizerService.SummarizeSectionAsync`:
|
|
|
|
| Module | Produces |
|
|
|---|---|
|
|
| `job-analysis` | Company/role/skills/tech/experience/education/soft-skills/responsibilities/salary/benefits/work-model/visa/language + summary + likely interview topics + confidence |
|
|
| `career-match` | Match % + reasoning, strengths, weaknesses, missing skills, most-relevant experience, suggested improvements |
|
|
| `cover-letter` | A tailored letter in one of 6 tones (professional, friendly, short, detailed, modern, traditional) |
|
|
| `interview` | Company research, likely/behavioural/technical questions, STAR answer outlines, prep checklist |
|
|
| `application-review` | Overall strength (rating), missing info, weak areas, ATS issues, grammar/clarity, formatting |
|
|
|
|
Tailored CV is **not** re-implemented here — it is the Phase 4 CV Builder (`CvVariant` linked to the
|
|
job application). The workspace links to it rather than duplicating it.
|
|
|
|
## Prompt flow
|
|
|
|
```
|
|
job (JobApplication + Company) ─┐
|
|
├─► AiWorkspaceService builds { instruction + guardrail, source }
|
|
master profile text ────────────┘ │
|
|
▼
|
|
ISummarizerService.SummarizeSectionAsync ──► ai-service (active provider)
|
|
│
|
|
▼
|
|
AiInteraction (append-only history) ──► markdown suggestion to the UI
|
|
```
|
|
|
|
`source` = the job context (`BuildJobContext`) plus the user's master profile text
|
|
(`ApplicationUser.ProfileCvText`). No profile fields are written; the text is read-only input.
|
|
|
|
## History model
|
|
|
|
`AiInteraction` (`JobTrackerApi/Models/AiInteraction.cs`) is **append-only** — one row per generation, never
|
|
overwritten. This is deliberately distinct from `AiWorkspaceNote` (a one-row-per-type *cache* for
|
|
candidate-fit/focus-plan). History gives the user restore/reuse (re-surface a past result), compare
|
|
(view two side by side), copy, and delete. `ResultJson` is `{ text, meta? }`; `Provider` records which
|
|
provider produced it. Each row also stores input/output character counts and a conservative estimated
|
|
token count (characters ÷ 4); the estimate is provider-neutral because the sidecar currently returns
|
|
text rather than provider billing metadata. Tenant-scoped (owner query filter), cascades with the application.
|
|
|
|
API (`AiWorkspaceController`, `/api/jobapplications/{id}/ai`): `GET modules` (+ current provider),
|
|
`POST generate`, `GET history?module=`, `DELETE history/{id}`. `GET /api/ai/usage` returns current-month
|
|
and all-time totals; the workspace displays the monthly calls and estimated tokens.
|
|
|
|
## Provider abstraction
|
|
|
|
Generation goes through the existing `ISummarizerService` → ai-service, which routes to the active
|
|
provider (`AI_PROVIDER`: ollama | gemini | groq) — production can offload a weak local GPU to a cloud
|
|
provider. Each `AiInteraction` records the resolved provider for transparency, and `GET …/ai/modules`
|
|
returns the current provider so the UI can show it.
|
|
|
|
**Per-request user-selectable providers** (module 8's "users can choose provider") is a plumbing
|
|
extension, not yet wired end-to-end: it needs (a) ai-service to accept a per-request `provider`
|
|
override and (b) an API **key configured for each selectable provider**. Both are deployment/credential
|
|
concerns (a live paid key per provider), so the code path is left as a documented extension point
|
|
rather than shipped half-configured. The abstraction already isolates the change to one method.
|
|
|
|
## Extension points
|
|
|
|
- **New module**: add a key to `AiWorkspaceService.Modules` + a prompt builder + a `switch` arm. No
|
|
new storage, controller, or UI wiring — the panel enumerates modules from `AI_MODULES`.
|
|
- **New cover-letter tone**: add to `CoverLetterModes` + `ModeGuidance`.
|
|
- **Structured (JSON) results**: swap a module's prompt for JSON and parse into `ResultJson.meta`; the
|
|
UI already renders `result.text` as markdown and can read `meta`.
|
|
- **User-selectable provider**: thread a `provider` param through `ISummarizerService` →
|
|
ai-service; gate on the provider having a configured key (see above).
|
|
|
|
## Security
|
|
|
|
Suggestion only; never overwrites user content; everything requires the user to copy it in. The
|
|
ai-service stays backend-only (`ai_internal` network, `X-Ai-Service-Token`) — see `current.md` §16.
|
|
`Markdown` renders React nodes (no `dangerouslySetInnerHTML`), so AI output has no HTML-injection path.
|