feat(job-search): extract keywords from LLM match call instead of heuristics
Piggybacks keyword extraction onto the existing CV-to-job LLM call — no extra API calls. The system prompt now instructs the model to return 8-12 English job-search terms (job titles, technologies, skills, domains) in a new `keywords` field alongside the existing score/summary fields. Keywords flow: LLM JSON → JobMatchResponse.Keywords → CreateJobSearchTokenRequest → JobSearchTokenEntity.Keywords (stored comma-separated) → JobSearchSessionEntity.Keywords (copied at session-creation time, no RAG call needed). Changes: - Add Keywords to JobMatchResponse, CreateJobSearchTokenRequest, JobSearchTokenEntity - IJobTokenService.CreateTokenAsync now accepts IReadOnlyList<string> keywords - JobTokenService: store keywords on token; TriggerStartAsync reads token.Keywords instead of fetching CV text from RAG — removes IRagApiClient dependency - Remove heuristic ExtractKeywords method - Migration AddKeywordsToJobSearchTokens: adds Keywords column to cvSearch.JobSearchTokens - Migration UpdateCvMatchSystemPromptKeywords: updates ai.cv-match.system-prompt seed to include keywords in the JSON shape Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using CvMatcher.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CvMatcher.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UpdateCvMatchSystemPromptKeywords : Migration
|
||||
{
|
||||
private const string OldPrompt =
|
||||
"You are a strict CV-to-job matching engine. Return JSON only. Score realistically from 0 to 100.\n" +
|
||||
"Penalize missing required skills. Do not invent experience. Use concise business language.\n" +
|
||||
"Respond entirely in {{languageName}} — all text fields in the JSON must be in {{languageName}}.\n" +
|
||||
"JSON shape: {\"score\":number,\"summary\":\"...\",\"strengths\":[\"...\"],\"gaps\":[\"...\"],\"recommendations\":[\"...\"],\"evidence\":[\"...\"]}";
|
||||
|
||||
private const string NewPrompt =
|
||||
"You are a strict CV-to-job matching engine. Return JSON only. Score realistically from 0 to 100.\n" +
|
||||
"Penalize missing required skills. Do not invent experience. Use concise business language.\n" +
|
||||
"Respond entirely in {{languageName}} — all text fields in the JSON must be in {{languageName}}.\n" +
|
||||
"Also extract 8 to 12 English job search keywords from the CV — job titles, technologies, skills, and domains.\n" +
|
||||
"The keywords array must always be in English regardless of {{languageName}}. Exclude names, emails, phone numbers, and locations.\n" +
|
||||
"JSON shape: {\"score\":number,\"summary\":\"...\",\"strengths\":[\"...\"],\"gaps\":[\"...\"],\"recommendations\":[\"...\"],\"evidence\":[\"...\"],\"keywords\":[\"term1\",\"term2\"]}";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.UpdateData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "AiPrompts",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: new object[] { "ai.cv-match.system-prompt", "*" },
|
||||
column: "Value",
|
||||
value: NewPrompt);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.UpdateData(
|
||||
schema: MigrationConstants.SchemaName,
|
||||
table: "AiPrompts",
|
||||
keyColumns: ["Key", "Language"],
|
||||
keyValues: new object[] { "ai.cv-match.system-prompt", "*" },
|
||||
column: "Value",
|
||||
value: OldPrompt);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user