🤖 AI Resume Assistant, helping you tailor your resume and find your ideal job quickly.
Features
More aligned with practices within China
Analyzes the 3 most important responsibilities from the input job posting link
Strategically highlights key personal strengths
Quantifies past work achievements
ATS (Applicant Tracking System) friendly format
Customizable resume templates
Ready-to-use default resume template style
Supports multiple API integrations
Supports langsmith observability
Generate HTML file and PDF file
Installation
To set up the project locally, follow these steps:
Clone the repository:
1 2
git clone https://github.com/alphaqiu/curriculum_vitae_ai_agent.git cd curriculum_vitae_ai_agent
Create a virtual environment:
1
python -m venv venv
Activate the virtual environment:
On Windows:
1
venv\Scripts\activate
On macOS and Linux:
1
source venv/bin/activate
Install the required packages:
1
pip install -r requirements.txt
Usage
Create a .env file in the same directory as main.py. The content of the .env file should be as follows:
1 2 3 4 5 6 7 8 9 10
# ifenable dashscope, uncomment the following line DASHSCOPE_API_KEY="your-aliyun-tongyi-key" # ifenable openai, uncomment the following line #OPENAI_API_KEY="openai-api-key or deepseek-api-key" # ifenable langsmith, uncomment the following lines #LANGCHAIN_API_KEY="langchain-api-key" #LANGCHAIN_TRACING_V2="true" #LANGCHAIN_ENDPOINT="https://api.smith.langchain.com"
The configuration file (config.yml) contains settings for the resume generation process, including model usage and file paths. Customize it according to your needs.
The config.yml file supports the following key parameters:
model: Specifies the AI model to use for resume generation
Currently supports: qwen-plus (Alibaba Cloud’s Tongyi Qianwen model)
This parameter determines which AI service will process your resume
task_path: Specifies the directory path for task-related files
Default value: ./data
This directory contains necessary files like templates, configurations, and resume information
Example configuration:
1 2
model:qwen-plus task_path:./data
Resume Information
Resume information file is located at data/info.yml, you can customize it or use the default information.
Basic Profile (resume_profile)
name: Your full name
gender: Your gender
age: Your age
email: Contact email address
education_background: Brief education summary
work_experience: Years of work experience
skills: Key technical skills, separated by commas
projects: Brief project summary
awards: Notable achievements or awards
mobile_phone: Contact phone number
job_seeking_status: Current employment status
desired_positions: List of target positions
position: Job title
location: Preferred location
salary: Expected salary range
industry: Target industry
employment_type: Type of employment (e.g., full-time, part-time)
Personal Advantages (personal_advantage)
List of key strengths and competencies that make you stand out
Education Details (education_details)
school: Institution name
major: Field of study
degree: Degree type
start_date: Start date (YYYY-MM format)
graduation_date: End date (YYYY-MM format)
school_experience: Notable activities during education
(6).非传递性(Non-transitive): 在代理重加密方案中,如果代理者拥有将 Alice 的密文转化成 Bob 的密文的代理重加密密钥rka-B和将 Bob 的密文转化成 Carlo 的密文的代理重加密钥rkB-C,但是代理者不能够自己计算出将 Alice 的密文转化成 Carlo 的密文的代理重加密密钥rkA-C,则该代理重加密方案是非传递性的。
(7).非转移性(Non-transferable): 在代理重加密方案中,如果代理者拥有将 Alice 的密文转化成 Bob 的密文的代理重加密密钥rkA-B,即使代理者和受理者合谋,也不能够获得将Alice 的密文转化成 Carlo 的密文的代理重加密密钥rkA-C,则该代理重加密方案是非转移性的注意,非传递性指代理者自己不能生成新的代理重加密密钥,而非转移性指,代理者不能和受理者合谋生成新的代理重加密密钥。
-- 在主表上创建索引,会在分表上自动创建对应的索引。 -- 在创建新的分区表时,也会自动创建好对应的索引。 CREATEUNIQUE INDEX example_table_epoch ON example_table(epoch DESC); -- 在主表上执行删除索引时,也会自动在分区表上删除对应的索引。 DROP INDEX example_table_epoch;
CREATETRIGGER show_notice_before_insert_stmt BEFORE INSERT ON example_table FOR STATEMENT EXECUTEFUNCTION notice_trigger(); CREATETRIGGER show_notice_after_insert_stmt AFTER INSERT ON example_table FOR STATEMENT EXECUTEFUNCTION notice_trigger(); CREATETRIGGER show_notice_before_insert_row BEFORE INSERT ON example_table FORROW EXECUTEFUNCTION notice_trigger(); CREATETRIGGER show_notice_after_insert_row AFTER INSERT ON example_table FORROW EXECUTEFUNCTION notice_trigger();
CREATETRIGGER show_notice_before_update_stmt BEFORE UPDATE ON example_table FOR STATEMENT EXECUTEFUNCTION notice_trigger(); CREATETRIGGER show_notice_after_update_stmt AFTER UPDATE ON example_table FOR STATEMENT EXECUTEFUNCTION notice_trigger(); CREATETRIGGER show_notice_before_update_row BEFORE UPDATE ON example_table FORROW EXECUTEFUNCTION notice_trigger(); CREATETRIGGER show_notice_after_update_row AFTER UPDATE ON example_table FORROW EXECUTEFUNCTION notice_trigger();
通过观察表结构定义我们可以看到,主表上创建出8个触发器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
example=# \d+ example_table Partitioned table "public.example_table" Column| Type |Collation| Nullable |Default| Storage | Stats target | Description --------+---------+-----------+----------+---------+---------+--------------+------------- col1 |integer|||| plain || Partition key: RANGE (col1) Triggers: show_notice_after_insert_row AFTER INSERTON example_table FOREACHROWEXECUTEFUNCTION notice_trigger() show_notice_after_insert_stmt AFTER INSERTON example_table FOREACH STATEMENT EXECUTEFUNCTION notice_trigger() show_notice_after_update_row AFTER UPDATEON example_table FOREACHROWEXECUTEFUNCTION notice_trigger() show_notice_after_update_stmt AFTER UPDATEON example_table FOREACH STATEMENT EXECUTEFUNCTION notice_trigger() show_notice_before_insert_row BEFORE INSERTON example_table FOREACHROWEXECUTEFUNCTION notice_trigger() show_notice_before_insert_stmt BEFORE INSERTON example_table FOREACH STATEMENT EXECUTEFUNCTION notice_trigger() show_notice_before_update_row BEFORE UPDATEON example_table FOREACHROWEXECUTEFUNCTION notice_trigger() show_notice_before_update_stmt BEFORE UPDATEON example_table FOREACH STATEMENT EXECUTEFUNCTION notice_trigger() Partitions: example_table_1 FORVALUESFROM (0) TO (10), example_table_2 FORVALUESFROM (11) TO (20), example_table_3 FORVALUESFROM (21) TO (30)
分区表上也自动创建出了4个行级触发器。
1 2 3 4 5 6 7 8 9 10 11
example=# \d example_table_1 Table "public.example_table_1" Column| Type |Collation| Nullable |Default --------+---------+-----------+----------+--------- col1 |integer||| Partitionof: example_table FORVALUESFROM (0) TO (10) Triggers: show_notice_after_insert_row AFTER INSERTON example_table_1 FOREACHROWEXECUTEFUNCTION notice_trigger(), ONTABLE example_table show_notice_after_update_row AFTER UPDATEON example_table_1 FOREACHROWEXECUTEFUNCTION notice_trigger(), ONTABLE example_table show_notice_before_insert_row BEFORE INSERTON example_table_1 FOREACHROWEXECUTEFUNCTION notice_trigger(), ONTABLE example_table show_notice_before_update_row BEFORE UPDATEON example_table_1 FOREACHROWEXECUTEFUNCTION notice_trigger(), ONTABLE example_table
-- 根据当前时间计算出对应的FileCoin高度,如果不传高度,默认高度为0,即链的起始高度。 CREATEOR REPLACE FUNCTION calc_timestamp_by_epoch(height BIGINT=0) RETURNSTIMESTAMPWITHTIME ZONE IMMUTABLE STRICT LANGUAGE plpgsql AS $$ DECLARE baseEpoch INTEGER=extract(EPOCH FROM TIMESTAMPTZ '2020-08-25T06:00:00+08:00')::INTEGER; BEGIN -- 根据高度计算时间 RETURN to_timestamp(baseEpoch + height *30); END $$;
时间转换高度
1 2 3 4 5 6 7 8 9 10 11 12 13
-- 根据FileCoin epoch计算出对应的时间,传入的参数如不填,默认当前时间 CREATEOR REPLACE FUNCTION calc_epoch_by_ts(ts timestampwithtime zone =current_timestamp) RETURNSbigint IMMUTABLE STRICT LANGUAGE plpgsql AS $$ DECLARE baseTime TIMESTAMP= TIMESTAMPTZ '2020-08-25T06:00:00+08:00'; BEGIN -- 根据时间计算高度 RETURNextract(EPOCH FROM (ts - baseTime))::INTEGER/30; END $$;
获取分区名称
通过系统数据库(pg_catalog)中的表获得分区表名称。如果不存在分区表,返回NULL。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
CREATEOR REPLACE FUNCTION pg_get_part_table_name(main_table VARCHAR, suffix VARCHAR) RETURNSVARCHAR IMMUTABLE AS $$ DECLARE part_table_name VARCHAR; BEGIN WITH o AS (SELECT inhrelid FROM pg_inherits WHERE inhparent = (concat_ws('.','public', main_table))::REGCLASS) SELECT relname FROM pg_class WHERE oid IN (SELECT*FROM o) AND relname = concat_ws('_', main_table, suffix) INTO part_table_name; RETURN part_table_name; END $$ LANGUAGE plpgsql;
SELECT DISTINCT kcu.column_name FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name WHERE constraint_type ='UNIQUE'AND tc.table_name ='miner_actors';
CREATEFUNCTION pg_compute_part_table_update_columns(main_table CHARACTERVARYING) RETURNS TEXT IMMUTABLE LANGUAGE plpgsql AS $$ DECLARE update_columns TEXT; BEGIN SELECT string_agg(concat(column_name, '=$1.', column_name), ', ') AS columns INTO update_columns FROM information_schema.columns WHERE table_schema ='public' AND table_name = main_table AND column_name NOTIN (SELECT DISTINCT kcu.column_name FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name WHERE constraint_type ='UNIQUE'AND tc.table_name = main_table); RETURN update_columns; END $$;
CREATEOR REPLACE FUNCTION pg_compute_constraint_keys(main_table VARCHAR) RETURNS TEXT IMMUTABLE LANGUAGE plpgsql AS $$ DECLARE ukeys TEXT; BEGIN WITH t AS (SELECTDISTINCT kcu.column_name FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name WHERE constraint_type ='UNIQUE' AND tc.table_name = main_table) SELECT string_agg(concat(t.column_name, '=$1.', t.column_name), ' AND ') FROM t INTO ukeys; RETURN ukeys; END $$;
CREATEOR REPLACE FUNCTION create_new_partition_table(main_table VARCHAR, part_table_type SMALLINT, part_def PARTITION_TABLE_RANGE_DEFINITION) RETURNSVARCHAR AS $$ DECLARE part_table_name TEXT; BEGIN part_table_name = concat_ws('_', main_table, part_def.suffix); EXECUTE format('CREATE TABLE IF NOT EXISTS %s PARTITION OF %s FOR VALUES FROM (%s) TO (%s)', part_table_name, main_table, part_def.begin_epoch, part_def.end_epoch);
CREATE TABLE IF NOTEXISTS part_tables ( id SERIAL NOT NULLPRIMARY KEY, tb_name VARCHAR(100), part_type SMALLINTNOT NULLDEFAULT0, part_table VARCHAR(100), begin_time TIMESTAMPTZ, end_time TIMESTAMPTZ, created_time TIMESTAMPTZ DEFAULTcurrent_timestamp, -- 分区表需要唯一 CONSTRAINT uk_part_table_name UNIQUE (tb_name, part_table), CHECK (part_type IN (0, 1, 2)) -- 0 range / 1 hash /2 list ); INSERT INTO part_tables(tb_name, part_type, part_table, begin_time, end_time, created_time) VALUES (main_table, part_table_type, part_table_name, part_def.begin_time, part_def.end_time, current_timestamp) ON CONFLICT (tb_name, part_table) DO NOTHING ;
-- delete 动作要放在这里,这里有移动操作。 EXECUTE format('DELETE FROM %s WHERE %s', old_part_table_name, constraint_keys) USING old_row; EXECUTE format('INSERT INTO %s SELECT $1.*', new_part_table_name) USING new_row; RETURN; END $$ LANGUAGE plpgsql;