-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathapp.js
More file actions
90 lines (77 loc) · 3.16 KB
/
app.js
File metadata and controls
90 lines (77 loc) · 3.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai';
import { createOpenAIFunctionsAgent, AgentExecutor } from 'langchain/agents';
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts';
import { DynamicTool } from '@langchain/core/tools';
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import { Document } from '@langchain/core/documents';
import * as fs from 'fs/promises';
import * as dotenv from 'dotenv';
dotenv.config();
// Read and split the resume into chunks
async function loadResumeChunks() {
const content = await fs.readFile('documents/resume.md', 'utf8');
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 500,
chunkOverlap: 50,
});
const documents = await splitter.createDocuments([content]);
return documents;
}
// Create vector store from resume chunks
async function createVectorStore(documents) {
const embeddings = new OpenAIEmbeddings();
const vectorStore = await MemoryVectorStore.fromDocuments(documents, embeddings);
return vectorStore;
}
// Define the ResumeRetriever tool
function createResumeRetrieverTool(vectorStore) {
return new DynamicTool({
name: 'ResumeRetriever',
description:
'Use this tool to fetch relevant experience, skills, or achievements from the resume based on job requirements. Use it only ONCE per task.',
func: async (input) => {
const results = await vectorStore.similaritySearch(input, 3);
return results.map((doc) => doc.pageContent).join('\n---\n');
},
});
}
// Set up the Chat model
const model = new ChatOpenAI({
modelName: 'gpt-4o',
temperature: 0.3,
openAIApiKey: process.env.OPENAI_API_KEY,
});
// Define the prompt template
const prompt = ChatPromptTemplate.fromMessages([
[
'system',
'You are a career assistant that writes job-tailored cover letters. If the user input includes a job description, use the ResumeRetriever tool ONCE to extract relevant experience/skills. Do not guess.',
],
new MessagesPlaceholder('agent_scratchpad'),
['user', '{input}'],
]);
// Main execution function
async function main() {
const documents = await loadResumeChunks();
const vectorStore = await createVectorStore(documents);
const resumeRetrieverTool = createResumeRetrieverTool(vectorStore);
const agent = await createOpenAIFunctionsAgent({
llm: model,
tools: [resumeRetrieverTool],
prompt,
});
const executor = new AgentExecutor({
agent,
tools: [resumeRetrieverTool],
verbose: true,
maxIterations: 5,
});
const input = `Write a cover letter tailored to the following job description:
"We are looking for a skilled and passionate React.js Developer with a strong understanding of TypeScript, unit testing, and micro-frontend architecture. As part of our dynamic team, you will be responsible for designing, developing, and maintaining scalable, high-performance web applications, focusing on the frontend architecture and improving the user experience."
`;
const result = await executor.invoke({ input });
console.log('\n📝 Final Cover Letter:\n');
console.log(result.output);
}
main();