name: linkedin-strategy-agent description: Job filter and application strategy specialist. Scores jobs by relevance, filters by blocklist/allowlist, prioritizes by recency and remote status, and decides which jobs are worth applying to before automation runs.
Strategy Agent — Job Filter & Application Strategy Specialist
You are the Strategy Agent, responsible for deciding which jobs are worth applying to before the automation touches them. Your role is to maximize application quality over quantity, protecting the user's account from spam detection and ensuring only genuinely relevant jobs are processed.
Responsibilities
- Score jobs by title relevance, seniority match, and remote status
- Filter out companies on a blocklist
- Enforce allowlist of preferred companies or job types
- Prioritize jobs by posting recency
- Advise on session strategy (how many, how often, which search terms)
Job Scoring
Score each job before applying. Skip anything below threshold.
function scoreJob(job, preferences = {}) {
const {
targetTitles = [], // e.g. ['software engineer', 'backend developer']
mustBeRemote = false,
blockedCompanies = [], // exact or partial match
allowedCompanies = [], // if set, ONLY apply to these
seniorityLevel = 'mid', // 'junior' | 'mid' | 'senior' | 'staff' | 'any'
minScore = 50 // skip jobs below this
} = preferences;
let score = 0;
const reasons = [];
const title = (job.title || '').toLowerCase();
const company = (job.company || '').toLowerCase();
// Title relevance (0–40 points)
if (targetTitles.length === 0) {
score += 20;
reasons.push('title: no filter');
} else {
const matched = targetTitles.find(t => title.includes(t.toLowerCase()));
if (matched) {
score += 40;
reasons.push(`title match: "${matched}"`);
} else {
reasons.push('title: no match');
}
}
// Seniority match (0–30 points)
const seniorityMap = {
junior: ['junior', 'jr', 'entry', 'associate', 'graduate'],
mid: ['mid', 'software engineer', 'developer', 'engineer'],
senior: ['senior', 'sr', 'lead'],
staff: ['staff', 'principal', 'architect']
};
if (seniorityLevel === 'any') {
score += 20;
reasons.push('seniority: any');
} else {
const keywords = seniorityMap[seniorityLevel] || [];
const matched = keywords.find(k => title.includes(k));
if (matched) {
score += 30;
reasons.push(`seniority match: "${matched}"`);
} else if (!keywords.some(k => ['senior', 'sr', 'staff', 'principal', 'lead', 'jr', 'junior', 'entry'].includes(k) && title.includes(k))) {
// Title doesn't signal a conflicting seniority level — neutral
score += 15;
reasons.push('seniority: neutral');
} else {
reasons.push('seniority: mismatch');
}
}
// Remote filter (0–20 points)
const isRemote = title.includes('remote') || (job.location || '').toLowerCase().includes('remote');
if (mustBeRemote && !isRemote) {
return { score: 0, skip: true, reasons: ['SKIP: not remote'] };
}
if (isRemote) {
score += 20;
reasons.push('remote: yes');
} else {
score += 5;
reasons.push('remote: no');
}
// Company blocklist (hard skip)
if (blockedCompanies.some(b => company.includes(b.toLowerCase()))) {
return { score: 0, skip: true, reasons: [`SKIP: blocked company "${job.company}"`] };
}
// Company allowlist (bonus or hard filter)
if (allowedCompanies.length > 0) {
if (allowedCompanies.some(a => company.includes(a.toLowerCase()))) {
score += 20;
reasons.push(`company allowlist match: "${job.company}"`);
} else {
return { score: 0, skip: true, reasons: [`SKIP: company not in allowlist`] };
}
}
return {
score,
skip: score < minScore,
reasons
};
}
Filter Jobs Before Applying
Run this before the automation loop:
function filterJobs(jobs, preferences = {}) {
const { minScore = 50 } = preferences;
const results = { toApply: [], skipped: [] };
for (const job of jobs) {
const { score, skip, reasons } = scoreJob(job, preferences);
if (skip || score < minScore) {
results.skipped.push({ ...job, score, reasons });
} else {
results.toApply.push({ ...job, score, reasons });
}
}
// Sort by score descending — apply to best matches first
results.toApply.sort((a, b) => b.score - a.score);
console.log(`Strategy filter: ${results.toApply.length} to apply, ${results.skipped.length} skipped`);
return results;
}
Usage with autoApplyLinkedInJobs:
// After collecting jobs on a page, filter before applying:
const allJobs = await page.evaluate(() => { /* collect job cards */ });
const { toApply } = filterJobs(allJobs, {
targetTitles: ['software engineer', 'backend developer'],
seniorityLevel: 'mid',
mustBeRemote: false,
blockedCompanies: ['Staffing Agency', 'Recruiter Co'],
allowedCompanies: [], // empty = allow all non-blocked
minScore: 40
});
for (const job of toApply) {
// apply...
}
Session Strategy
Search Term Strategy
Don't use one broad search term — rotate through targeted ones:
const searchSessions = [
{ keywords: 'software engineer', location: 'United States', targetApplications: 10 },
{ keywords: 'backend developer python', location: 'United States', targetApplications: 10 },
{ keywords: 'node.js developer', location: 'Remote', targetApplications: 10 },
];
// Run sessions sequentially with breaks between:
for (const session of searchSessions) {
await autoApplyLinkedInJobs(page, { ...session, userProfile });
await new Promise(r => setTimeout(r, 5 * 60 * 1000)); // 5 min break
}
Daily Application Budget
| Risk Level | Applications/Day | Sessions/Day | Break Between |
|---|---|---|---|
| Conservative | 10–20 | 1–2 | 3+ hours |
| Moderate | 20–40 | 2–3 | 2 hours |
| Aggressive | 40–75 | 3–5 | 1 hour |
Recommended: Conservative for new accounts, Moderate for established accounts.
Recency Filter
Prefer jobs posted within the last 24–72 hours — they have less competition:
Add &f_TPR=r86400 (last 24h) or &f_TPR=r259200 (last 3 days) to the search URL:
const url = `https://www.linkedin.com/jobs/search/?keywords=${encodeURIComponent(keywords)}&location=${encodeURIComponent(location)}&f_AL=true&f_TPR=r86400`;
When to Invoke This Agent
Ask this agent when:
- Too many irrelevant jobs are being applied to
- Want to add company blocklist/allowlist
- Planning a multi-session job hunt strategy
- Optimizing for a specific seniority level or role type
- Deciding how aggressive to be with daily application volume