Files
jobtrackingapp/docs/architecture/ai-career-assistant.md
T
cesnimda 5eb9b3cb96 feat(ai): enforce local-first routing
Keep external providers behind server consent, task, and prompt-cost gates while persisting actual provider provenance.
2026-08-09 12:30:11 +02:00

5.4 KiB

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 ISummarizerService to the ai-service. Ollama is primary; AI_PROVIDER names only the optional external fallback candidate. Fallback is sequential and requires administrator enablement, task approval, live Pro/user consent and the prompt cost/privacy ceiling. Each AiInteraction records the provider returned by the sidecar, plus bounded model/route metadata in ResultJson.meta; configuration alone is not treated as proof that a provider executed.

Per-request user-selectable providers remain intentionally unsupported. The server-side privacy policy selects a route, not the browser, and provider credentials remain deployment-only.

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.
  • Provider policy: add task types to the explicit server-side allowlist only after their payload, accounting and production checks pass; do not add browser provider overrides.

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.