hanbinChen commited on
Commit
6c8af71
·
1 Parent(s): f1664d6

Refactor LLM service and analysis logic for improved structure and error handling

Browse files

- Updated `llm_service.py` to streamline LLM calls and response handling.
- Enhanced `analyse_service.py` with Pydantic models for structured analysis results.
- Improved JSON parsing in `_parse_analysis_response` to handle various response formats.
- Refined prompt templates in `prompt_templates.py` for better clarity and structure.
- Modified `generation_service.py` to support direct job description input for cover letter generation.
- Added test cases in service modules to validate functionality and ensure robustness.
- Updated documentation in `spec.md` to reflect changes in service architecture and functionality.

docs/spec.md CHANGED
@@ -46,50 +46,66 @@ apply-helper/
46
 
47
  ### 2.2 LLM 集成层
48
 
49
- #### `src/services/llm_service.py` — 核心LLM交互逻辑
50
- - **核心功能**: 封装所有LLM调用,处理JSON解析和错误处理
51
  - **主要函数**:
52
- - `analyse_llm(jd: str, user_info: str) -> Dict[str, Any]`: 工作分析,返回结构化JSON
53
- - `refine_llm(summary: Dict, feedback: str) -> Dict[str, Any]`: 基于反馈优化分析
54
- - `generate_resume_llm(summary: Dict, user_info: str) -> str`: 生成Markdown格式简历
55
- - `generate_cover_letter_llm(summary: Dict, user_info: str) -> str`: 生成纯文本求职信
56
- - `_parse_json_response(response: str) -> Dict`: 解析LLM返回的JSON(处理代码块包装)
57
-
58
- #### `src/llm/litellm_client.py` Azure OpenAI集成
59
- - **当前实现**: 仅支持Azure OpenAI(尽管配置支持多provider)
60
- - **自动配置**: 基于环境变量自动检测和配置Azure客户端
61
  - **主要函数**:
62
  - `call_llm(prompt: str, model: str = None, max_tokens: int = 800, temperature: float = 1.0) -> str`
63
- - `get_azure_client() -> Any`: 配置Azure OpenAI客户端
64
- - 包含连接测试和调试模式
 
65
 
66
  #### `src/llm/prompt_templates.py` — 提示词模板管理
67
  - **模板类型**: analyse, refine, generate_resume, generate_cover_letter
68
  - **函数**: `get_template(name: str) -> str`
69
- - **特点**: 字典存储,包含详细的JSON输出格式要求
 
 
 
 
70
 
71
  #### 核心处理逻辑
72
- - **JSON响应解析**: 自动处理LLM返回的```json代码块包装格式
73
- - **提示词管理**: 从模板获取提示词,动态替换参数
74
- - **错误处理**: 捕获JSON解析错误和API调用异常
75
- - **多任务协调**: 分析→生成→返回结果的完整流程
76
 
77
 
78
  ### 2.3 业务服务层
79
 
80
  #### `src/services/analyse_service.py` — 分析服务
81
- - **功能**: 职位描述与用户背景的匹配分析
 
 
 
 
 
 
82
  - **主要逻辑**:
83
  - 验证输入参数非空
84
  - 委托LLM服务进行分析
85
- - analyse() - 初始分析
86
- - refine() - 基于反馈优化分析
 
87
 
88
  #### `src/services/generation_service.py` — 文档生成服务
89
  - **功能**: 编排简历和求职信的生成
 
 
 
 
90
  - **主要逻辑**:
91
- - 基于分析结果生成文档
92
- - 并行调用LLM服务生成简历(Markdown)和求职信(文本)
 
93
  - 返回生成的文档元组
94
 
95
  ### 2.4 模拟数据系统
@@ -164,11 +180,17 @@ apply-helper/
164
 
165
  ### 4.2 LLM集成配置
166
 
167
- **当前实现**: 仅支持Azure OpenAI
168
 
169
- **必需配置**: AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_MODEL, AZURE_OPENAI_API_VERSION
170
 
171
- **配置逻辑**: 自动检测Azure配置,无配置时报错,不支持其他provider
 
 
 
 
 
 
172
 
173
  ### 4.3 开发和测试
174
 
@@ -177,9 +199,11 @@ apply-helper/
177
  - `src/mock_data.py` 包含完整测试数据集
178
  - 点击"Mock Analyse"按钮即可加载预设结果
179
 
180
- **调试模式**:
181
- - `src/llm/litellm_client.py` 包含连接测试代码
182
- - 运行 `python src/llm/litellm_client.py` 测试LLM连接
 
 
183
 
184
  ### 4.4 架构特点
185
 
@@ -189,7 +213,8 @@ apply-helper/
189
  - 服务层薄包装,便于替换实现
190
 
191
  **扩展点**:
192
- - 添加新的LLM provider: 修改 `litellm_client.py`
193
- - 添加PDF导出: 实现 `pdf_service.py`
194
- - 添加新的生成模板: 扩展 `prompt_templates.py`
195
- - 添加数据验证: 使用已配置的Pydantic
 
 
46
 
47
  ### 2.2 LLM 集成层
48
 
49
+ #### `src/services/llm_service.py` — LLM服务包装层
50
+ - **核心功能**: 提供LLM调用的统一接口,负责提示词模板格式化和LiteLLM调用
51
  - **主要函数**:
52
+ - `analyse_llm(jd: str, user_info: str) -> str`: 工作分析,返回原始LLM响应字符串
53
+ - `refine_llm(summary_json: str, feedback: str) -> str`: 基于反馈优化分析,返回原始LLM响应
54
+ - `generate_resume_llm(summary_json: str, user_info: str) -> str`: 生成Markdown格式简历
55
+ - `generate_cover_letter_llm(job_description: str, user_info: str) -> str`: 生成德语求职信
56
+
57
+ #### `src/llm/litellm_client.py` — LiteLLM客户端集成
58
+ - **多Provider支持**: 优先支持Azure OpenAI,备用支持标准OpenAI
59
+ - **自动配置**: 基于环境变量自动检测和配置最优客户端
 
60
  - **主要函数**:
61
  - `call_llm(prompt: str, model: str = None, max_tokens: int = 800, temperature: float = 1.0) -> str`
62
+ - `get_azure_client() -> dict`: 配置Azure OpenAI客户端
63
+ - `get_openai_client() -> dict`: 配置标准OpenAI客户端
64
+ - 包含连接测试和调试模式,支持litellm debug
65
 
66
  #### `src/llm/prompt_templates.py` — 提示词模板管理
67
  - **模板类型**: analyse, refine, generate_resume, generate_cover_letter
68
  - **函数**: `get_template(name: str) -> str`
69
+ - **特点**:
70
+ - 字典存储,包含详细的JSON输出格式要求
71
+ - ANALYSE_PROMPT: 德国科技市场专家角色,8-12个技能提取,4-6个匹配点分析
72
+ - GENERATE_COVER_LETTER_PROMPT: 德语商务求职信生成,符合德国商务信函规范
73
+ - 所有模板包含明确的输出格式要求和角色设定
74
 
75
  #### 核心处理逻辑
76
+ - **提示词管理**: 从模板获取提示词,使用Python字符串format动态替换参数
77
+ - **LLM调用**: 通过LiteLLM统一接口调用不同provider的模型
78
+ - **错误处理**: LiteLLM层处理API调用异常,服务层处理业务逻辑异常
79
+ - **响应处理**: 返回原始字符串响应,由上层服务负责结构化解析
80
 
81
 
82
  ### 2.3 业务服务层
83
 
84
  #### `src/services/analyse_service.py` — 分析服务
85
+ - **功能**: 职位描述与用户背景的匹配分析,使用Pydantic数据模型
86
+ - **数据模型**: `AnalysisResult(BaseModel)` - 结构化分析结果
87
+ - key_skills: List[str] - 关键技能列表
88
+ - match_points: List[str] - 匹配优势点
89
+ - gap_points: List[str] - 技能差距点
90
+ - suggestions: List[str] - 改进建议
91
+ - pitch: str - 价值主张
92
  - **主要逻辑**:
93
  - 验证输入参数非空
94
  - 委托LLM服务进行分析
95
+ - `_parse_analysis_response()` - 复杂JSON解析逻辑,处理代码块包装
96
+ - `analyse()` - 初始分析,返回AnalysisResult对象
97
+ - `refine()` - 基于反馈优化分析,使用model_dump_json()序列化
98
 
99
  #### `src/services/generation_service.py` — 文档生成服务
100
  - **功能**: 编排简历和求职信的生成
101
+ - **主要接口**:
102
+ - `generate_resume(summary: AnalysisResult, user_info: str) -> str` - 生成简历
103
+ - `generate_cover_letter(job_description: str, user_info: str) -> str` - 生成求职信
104
+ - `generate_both(summary: AnalysisResult, user_info: str, job_description: str) -> Tuple[str, str]` - 生成完整文档对
105
  - **主要逻辑**:
106
+ - 使用AnalysisResult对象和原始文本作为输入
107
+ - 调用LLM服务生成简历(Markdown)和求职信(德语文本)
108
+ - 支持动态导入处理,兼容模块和独立运行
109
  - 返回生成的文档元组
110
 
111
  ### 2.4 模拟数据系统
 
180
 
181
  ### 4.2 LLM集成配置
182
 
183
+ **多Provider支持**: 优先Azure OpenAI,备用标准OpenAI
184
 
185
+ **Azure OpenAI配置**: AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_MODEL, AZURE_OPENAI_API_VERSION(默认2024-12-01-preview)
186
 
187
+ **OpenAI配置**: OPENAI_API_KEY(使用gpt-3.5-turbo模型)
188
+
189
+ **配置逻辑**:
190
+ - 优先检测Azure配置,如果完整则使用Azure
191
+ - Azure不可用时回退到标准OpenAI
192
+ - 支持模型参数覆盖,但仅支持azure/前缀的模型
193
+ - 包含详细的配置验证和错误提示
194
 
195
  ### 4.3 开发和测试
196
 
 
199
  - `src/mock_data.py` 包含完整测试数据集
200
  - 点击"Mock Analyse"按钮即可加载预设结果
201
 
202
+ **调试和测试模式**:
203
+ - `python src/llm/litellm_client.py` - 测试LLM连接和配置验证
204
+ - `python src/services/analyse_service.py` - 测试分析服务完整流程
205
+ - `python src/services/generation_service.py` - 测试文档生成流程
206
+ - 包含litellm._turn_on_debug()调试支持
207
 
208
  ### 4.4 架构特点
209
 
 
213
  - 服务层薄包装,便于替换实现
214
 
215
  **扩展点**:
216
+ - 添加新的LLM provider: 扩展 `litellm_client.py` 中的客户端配置函数
217
+ - 添加PDF导出: 实现 `pdf_service.py` 集成WeasyPrint
218
+ - 添加新的生成模板: `prompt_templates.py` 中添加新模板常量
219
+ - 数据验证增强: 当前使用Pydantic AnalysisResult模型,可扩展更多验证模型
220
+ - 多语言支持: 扩展prompt_templates支持不同语言的求职信生成
src/README.md CHANGED
@@ -69,4 +69,8 @@ apply-helper/
69
  │ └── prompt_templates.py
70
  ├── exports/ # Directory for exported PDF files
71
  └── tests/ # Unit tests
72
- ```
 
 
 
 
 
69
  │ └── prompt_templates.py
70
  ├── exports/ # Directory for exported PDF files
71
  └── tests/ # Unit tests
72
+ ```
73
+
74
+
75
+ PYTHONPATH=src python -m services.analyse_service
76
+
src/llm/litellm_client.py CHANGED
@@ -92,7 +92,7 @@ def call_llm(prompt: str, model: Optional[str] = None, max_tokens: int = 800, te
92
 
93
  except Exception as e:
94
  print(f"An error occurred while calling the LLM: {e}")
95
- raise
96
 
97
 
98
  if __name__ == "__main__":
 
92
 
93
  except Exception as e:
94
  print(f"An error occurred while calling the LLM: {e}")
95
+ raise e # Re-raise the original exception
96
 
97
 
98
  if __name__ == "__main__":
src/llm/prompt_templates.py CHANGED
@@ -2,27 +2,66 @@
2
  Manages all prompt templates for the application.
3
  """
4
 
5
- PROMPT_TEMPLATES = {
6
- "analyse": """
7
- You are a professional career coach. Analyze the provided Job Description (JD) and User Information.
8
- Your goal is to extract key skills, identify matches and gaps, and provide actionable suggestions.
9
 
10
  **Job Description:**
11
  {jd}
12
 
13
- **User Information:**
14
  {user}
15
 
16
- **Your analysis should be in the following JSON format:**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  {{
18
- "key_skills": ["skill1", "skill2", ...],
19
- "match_points": ["point1", "point2", ...],
20
- "gap_points": ["point1", "point2", ...],
21
- "suggestions": ["suggestion1", "suggestion2", ...],
22
- "pitch": "A short, compelling pitch for the user."
23
  }}
24
- """,
25
- "refine": """
 
 
26
  You are a professional career coach. A summary has been generated based on a Job Description and User Information.
27
  Now, refine this summary based on the user's feedback.
28
 
@@ -32,16 +71,21 @@ Now, refine this summary based on the user's feedback.
32
  **User Feedback:**
33
  {feedback}
34
 
 
 
35
  **Your refined analysis should be in the same JSON format as the original:**
 
36
  {{
37
- "key_skills": ["skill1", "skill2", ...],
38
- "match_points": ["point1", "point2", ...],
39
- "gap_points": ["point1", "point2", ...],
40
- "suggestions": ["suggestion1", "suggestion2", ...],
41
  "pitch": "A short, compelling pitch for the user."
42
  }}
43
- """,
44
- "generate_resume": """
 
 
45
  You are a professional resume writer. Based on the following analysis and user information, generate a professional resume in Markdown format.
46
  The resume should be tailored to the job description implied in the analysis.
47
 
@@ -56,30 +100,64 @@ The resume should be tailored to the job description implied in the analysis.
56
  - Highlight the most relevant skills and experiences.
57
  - Use professional and action-oriented language.
58
  - Do not include any introductory text like "Here is the resume:".
59
- """,
60
- "generate_cover_letter": """
61
- You are a professional cover letter writer. Based on the following analysis and user information, generate a compelling cover letter.
62
- The cover letter should be tailored to the job description implied in the analysis.
63
 
64
- **Analysis Summary:**
65
- {summary}
66
 
67
- **User Information:**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  {user}
69
 
70
- **Instructions:**
71
- - The output must be plain text.
72
- - The tone should be professional and enthusiastic.
73
- - Address the key requirements from the job description.
74
- - Do not include any introductory text like "Here is the cover letter:".
75
  """
76
- }
77
 
78
  def get_template(name: str) -> str:
79
  """
80
  Retrieves a prompt template by name.
81
  """
82
- template = PROMPT_TEMPLATES.get(name)
 
 
 
 
 
 
 
83
  if not template:
84
  raise ValueError(f"Template '{name}' not found.")
85
  return template
 
2
  Manages all prompt templates for the application.
3
  """
4
 
5
+ ANALYSE_PROMPT = """
6
+ You are an expert career strategist specializing in the German tech job market with deep understanding of software development roles and industry requirements.
7
+
8
+ **Task:** Analyze the Job Description against the candidate's background to provide comprehensive career guidance.
9
 
10
  **Job Description:**
11
  {jd}
12
 
13
+ **Candidate Information:**
14
  {user}
15
 
16
+ **Analysis Requirements:**
17
+
18
+ 1. **Key Skills Extraction** - Identify 8-12 critical technical and soft skills from the JD, prioritizing:
19
+ - Programming languages and frameworks specifically mentioned
20
+ - Cloud platforms and DevOps tools
21
+ - Development methodologies (Agile, TDD, etc.)
22
+ - Domain-specific requirements (e.g., scalability, testing)
23
+ - Soft skills (teamwork, communication, learning agility)
24
+
25
+ 2. **Match Analysis** - Identify 4-6 strong alignment points where candidate excels:
26
+ - Direct technology matches with concrete project examples
27
+ - Relevant experience depth and breadth
28
+ - Methodological alignment (Agile, CI/CD, etc.)
29
+ - Cultural fit indicators
30
+ - Language requirements fulfillment
31
+
32
+ 3. **Gap Analysis** - Identify 3-5 key areas for improvement:
33
+ - Missing or limited experience with required technologies
34
+ - Lack of specific domain knowledge
35
+ - Experience depth gaps (junior vs senior expectations)
36
+ - Certification or formal training gaps
37
+
38
+ 4. **Strategic Recommendations** - Provide 4-6 actionable, specific suggestions:
39
+ - Concrete learning paths for technology gaps
40
+ - Project ideas to demonstrate missing skills
41
+ - Networking or certification opportunities
42
+ - Interview preparation focus areas
43
+ - Portfolio enhancement recommendations
44
+
45
+ 5. **Value Proposition** - Craft a compelling 2-3 sentence pitch highlighting:
46
+ - Unique combination of candidate's strengths
47
+ - How their background solves company's specific needs
48
+ - Growth potential and learning agility
49
+
50
+ **CRITICAL: Response must be valid JSON only. No additional text.**
51
+
52
+ **Required JSON format:**
53
+ ```json
54
  {{
55
+ "key_skills": ["skill1", "skill2", "skill3", "skill4", "skill5", "skill6", "skill7", "skill8"],
56
+ "match_points": ["point1", "point2", "point3", "point4"],
57
+ "gap_points": ["gap1", "gap2", "gap3"],
58
+ "suggestions": ["suggestion1", "suggestion2", "suggestion3", "suggestion4"],
59
+ "pitch": "Compelling 2-3 sentence value proposition for this specific role."
60
  }}
61
+ ```
62
+ """
63
+
64
+ REFINE_PROMPT = """
65
  You are a professional career coach. A summary has been generated based on a Job Description and User Information.
66
  Now, refine this summary based on the user's feedback.
67
 
 
71
  **User Feedback:**
72
  {feedback}
73
 
74
+ **IMPORTANT: Your response must be valid JSON only. Do not include any text before or after the JSON.**
75
+
76
  **Your refined analysis should be in the same JSON format as the original:**
77
+ ```json
78
  {{
79
+ "key_skills": ["skill1", "skill2", "skill3"],
80
+ "match_points": ["point1", "point2", "point3"],
81
+ "gap_points": ["point1", "point2", "point3"],
82
+ "suggestions": ["suggestion1", "suggestion2", "suggestion3"],
83
  "pitch": "A short, compelling pitch for the user."
84
  }}
85
+ ```
86
+ """
87
+
88
+ GENERATE_RESUME_PROMPT = """
89
  You are a professional resume writer. Based on the following analysis and user information, generate a professional resume in Markdown format.
90
  The resume should be tailored to the job description implied in the analysis.
91
 
 
100
  - Highlight the most relevant skills and experiences.
101
  - Use professional and action-oriented language.
102
  - Do not include any introductory text like "Here is the resume:".
103
+ """
 
 
 
104
 
105
+ GENERATE_COVER_LETTER_PROMPT = """
106
+ You are an expert HR recruiter in the German IT job market.
107
 
108
+ Generate a professional, tailored German Cover Letter (Anschreiben) for the given Job Description and Candidate CV. Follow these updated style rules:
109
+
110
+ ### Structure
111
+ 1. **Opening**
112
+ - 1–2 sentences only.
113
+ - First sentence: Mention the company/job role and express motivation.
114
+ - Second sentence: Short self-introduction (degree, focus, current status).
115
+
116
+ 2. **Main Section**
117
+ - Use **bullet points** (•) to highlight 3–5 most relevant skills and experiences from the CV, directly aligned with the Job Description.
118
+ - Each bullet point should be max. 2 lines, clear and specific.
119
+ - Where possible, mention concrete results/outcomes (STAR method).
120
+
121
+ 3. **Summary Paragraph**
122
+ - 1 short paragraph (2–3 sentences).
123
+ - Summarize why the candidate fits the role, combining motivation and competence.
124
+
125
+ 4. **Closing**
126
+ - Avoid overly flattering phrases.
127
+ - Clearly state availability (e.g., "Ab Oktober stehe ich Ihnen flexibel zur Verfügung.").
128
+ - Express interest in an interview in a confident, neutral tone (e.g., "Gerne erläutere ich Ihnen in einem persönlichen Gespräch, wie ich Ihr Team unterstützen kann.").
129
+ - End with: "Mit freundlichen Grüßen, [Name]"
130
+
131
+ ### Style Guidelines
132
+ - Professional, concise, maximum 1 page.
133
+ - Use active voice and confident tone.
134
+ - Avoid generic filler sentences.
135
+ - Balance between motivation and technical skills.
136
+ - Output in proper German business letter style.
137
+
138
+ ### Inputs
139
+ - Candidate CV:
140
  {user}
141
 
142
+ - Job Description:
143
+ {jd}
144
+
145
+ ### Output
146
+ - A complete Cover Letter (Anschreiben) in German, following the structure and style above.
147
  """
 
148
 
149
  def get_template(name: str) -> str:
150
  """
151
  Retrieves a prompt template by name.
152
  """
153
+ templates = {
154
+ "analyse": ANALYSE_PROMPT,
155
+ "refine": REFINE_PROMPT,
156
+ "generate_resume": GENERATE_RESUME_PROMPT,
157
+ "generate_cover_letter": GENERATE_COVER_LETTER_PROMPT
158
+ }
159
+
160
+ template = templates.get(name)
161
  if not template:
162
  raise ValueError(f"Template '{name}' not found.")
163
  return template
src/services/analyse_service.py CHANGED
@@ -1,18 +1,235 @@
1
- from typing import Dict, Any
 
 
2
  from . import llm_service
3
 
4
- def analyse(jd: str, user_info: str) -> Dict[str, Any]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  """
6
  Analyzes the job description and user information.
7
  """
8
  if not jd or not user_info:
9
  raise ValueError("Job Description and User Info cannot be empty.")
10
- return llm_service.analyse_llm(jd, user_info)
 
11
 
12
- def refine(summary: Dict[str, Any], feedback: str) -> Dict[str, Any]:
13
  """
14
  Refines the analysis based on user feedback.
15
  """
16
  if not summary or not feedback:
17
  raise ValueError("Summary and feedback cannot be empty.")
18
- return llm_service.refine_llm(summary, feedback)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from typing import List
3
+ from pydantic import BaseModel, Field
4
  from . import llm_service
5
 
6
+
7
+ class AnalysisResult(BaseModel):
8
+ """Structured result from job description and user analysis."""
9
+ key_skills: List[str] = Field(description="Key skills required for the job")
10
+ match_points: List[str] = Field(description="Points where user matches job requirements")
11
+ gap_points: List[str] = Field(description="Areas where user lacks required skills")
12
+ suggestions: List[str] = Field(description="Actionable suggestions for improvement")
13
+ pitch: str = Field(description="A compelling pitch for the user")
14
+
15
+
16
+ def _parse_analysis_response(response: str) -> AnalysisResult:
17
+ """
18
+ Parses a JSON string from the LLM response into AnalysisResult.
19
+ """
20
+ try:
21
+ # Remove code block markers if present
22
+ if "```json" in response:
23
+ start = response.find("```json") + 7
24
+ end = response.find("```", start)
25
+ if end != -1:
26
+ json_block = response[start:end].strip()
27
+ else:
28
+ json_block = response[start:].strip()
29
+ elif "```" in response and "{" in response:
30
+ # Handle cases where code blocks exist but without json marker
31
+ start = response.find("```") + 3
32
+ end = response.find("```", start)
33
+ if end != -1:
34
+ json_block = response[start:end].strip()
35
+ else:
36
+ json_block = response[start:].strip()
37
+ else:
38
+ # Find JSON object boundaries
39
+ start = response.find('{')
40
+ end = response.rfind('}') + 1
41
+ if start == -1 or end == 0:
42
+ raise ValueError("No JSON object found in response")
43
+ json_block = response[start:end]
44
+
45
+ # Clean up the JSON block
46
+ json_block = json_block.strip()
47
+
48
+ data = json.loads(json_block)
49
+ return AnalysisResult(**data)
50
+ except (json.JSONDecodeError, IndexError, ValueError) as e:
51
+ print(f"Failed to parse JSON response: {e}\nResponse was: {response[:500]}...")
52
+ raise ValueError("LLM response was not in the expected JSON format.")
53
+
54
+ def analyse(jd: str, user_info: str) -> AnalysisResult:
55
  """
56
  Analyzes the job description and user information.
57
  """
58
  if not jd or not user_info:
59
  raise ValueError("Job Description and User Info cannot be empty.")
60
+ response_text = llm_service.analyse_llm(jd, user_info)
61
+ return _parse_analysis_response(response_text)
62
 
63
+ def refine(summary: AnalysisResult, feedback: str) -> AnalysisResult:
64
  """
65
  Refines the analysis based on user feedback.
66
  """
67
  if not summary or not feedback:
68
  raise ValueError("Summary and feedback cannot be empty.")
69
+ summary_json = summary.model_dump_json(indent=2)
70
+ response_text = llm_service.refine_llm(summary_json, feedback)
71
+ return _parse_analysis_response(response_text)
72
+
73
+
74
+ if __name__ == "__main__":
75
+ # Test data
76
+ test_jd = """
77
+ Seit 25 Jahren unterstützen wir bei inovex mit rund 500 IT-Expert:innen Unternehmen bei der Digitalisierung und Agilisierung ihres Kerngeschäfts an 8 Standorten deutschlandweit.
78
+
79
+ Im Team Application Development entwickeln wir die verschiedensten Anwendungen, von skalierbaren Backends über performante Web Frontends bis hin zu modernen Apps, die teilweise von mehreren Millionen Menschen täglich benutzt werden. Unsere Konzepte implementieren wir bevorzugt mit Open Source Software im Rahmen agiler Projekte. Bei uns verläuft kein Projekt wie das andere – je nach Kunde, Team und Technologie-Stack ist jede Lösung einzigartig.
80
+
81
+ *Wir bei inovex mögen Vielfalt und Individualität. Deshalb freuen wir uns, wenn du unser Team noch bunter machst.
82
+
83
+ Was du bei uns bewegen kannst
84
+ Gemeinsam mit deinem agilen Projektteam setzt du Projekte für Kunden unterschiedlicher Branchen um. Ihr seid cross-funktional aufgestellt und liefert alles aus einer Hand – von der Architektur über die Entwicklung bis zum DevOps-Betrieb. You build it, you run it.
85
+
86
+ Dein Job beginnt nicht erst mit der Entwicklung von Software: Du unterstützt bei der Aufnahme und Analyse der Kundenanforderungen und stimmst dich eng mit deinem Team ab.
87
+
88
+ Du fühlst dich gleichermaßen im Frontend wie im Backend zu Hause und bringst deine Erfahrung in beiden Bereichen ein – egal, ob es um Architekturen oder die Gestaltung von Single Page Applications geht.
89
+
90
+ Du unterstützt deine Kolleg:innen mithilfe automatisierter Testing-Verfahren (Unit Testing, CI/CD, Ende-zu-Ende-Tests u. v. m.) und sorgst somit für eine kontinuierliche Qualitätskontrolle und -optimierung.
91
+
92
+
93
+
94
+ In unseren Projekten verwenden wir häufig folgende Technologien:
95
+
96
+ Kotlin, Java, Go, Python oder C#
97
+
98
+ Angular, React, TypeScript, Vue.js., Next.js
99
+
100
+ Docker, Gitlab
101
+
102
+ AWS, GCP, Azure, Kubernetes
103
+
104
+ Wer gut zu uns passen würde
105
+ Ob du ein Studium oder eine Ausbildung im IT-Bereich abgeschlossen hast, ist für uns nebensächlich – für uns zählen deine fachlichen Skills und deine Persönlichkeit:
106
+
107
+ Du hast mehr als ein Jahr Berufserfahrung und fundierte Kenntnisse in mehreren der oben genannten Technologien. Idealerweise hast du bereits erste Berührungspunkte mit Cloud-Umgebungen gesammelt.
108
+
109
+ Du hast den Anspruch, dich in neue Technologien einzuarbeiten und sie in Bezug auf den Projekteinsatz zu prüfen.
110
+
111
+ Du beteiligst dich gerne an Code Reviews und setzt dich konstruktiv mit deiner Arbeit auseinander.
112
+
113
+ Es ist dir wichtig, ein umfassendes Verständnis der Standpunkte deiner Team-Mitglieder und Kunden zu entwickeln.
114
+
115
+ Sehr gute Deutschkenntnisse sind Voraussetzung (mind. Level C1), gute Englischkenntnisse runden dein Profil ab.
116
+
117
+ Ab Oktober freuen wir uns auf Verstärkung in unserem Team!
118
+
119
+ Was wir dir bieten
120
+ inovex Culture: Kommunikation auf Augenhöhe, flache Hierarchien, Arbeiten in selbstorganisierten und agilen Teams, Vertrauen und Offenheit, starker Zusammenhalt
121
+ Level Up: Weiterbildungsbudget, eigene inovex Academy, Onboarding- und Mentoring-Programm, enger Austausch mit der Community
122
+ Balance: Flexibles und mobiles Arbeiten, keine Kernarbeitszeiten, Workation, 30 Tage Urlaub
123
+ Familienfreundlichkeit: Flexibles Arbeitszeitmodell, Zuschuss zur Kinderbetreuung, Überstunden nur im Ausnahmefall
124
+ Individualität: Betriebliche Altersvorsorge, E-Gym Wellpass, inovex Sportgruppen/ Sport-Community
125
+ Mobilität: JobRad, Firmenwagen-Leasing
126
+ """
127
+
128
+ test_user_info = """
129
+ Start Hanbin Chen
130
+ Master Informatik-Absolvent | LLM-Forscher | Full-Stack-Softwareentwickler
131
+ Ausbildung
132
+ RWTH Aachen University, Master | Informatik 2020.10 - 2025.07
133
+ Masterarbeit: MedKGC (Medical Knowledge Graph Construction)
134
+ • Entwicklung einer End-to-End-Pipeline für die medizinische Wissensextraktion aus Ra-
135
+ diologieberichten mittels LLMs und deren Darstellung in Knowledge Graphs.
136
+ • Implementierung von Named Entity Recognition mit GPT-4o und Llama3 unter Verwen-
137
+ dung eines neuartigen RAG-Ansatzes und Integration von UMLS-Ontologien.
138
+ • Durchführung von Few-Shot Prompting zur Bewertung der LLM-Leistung.
139
+ Fachhochschule Hannover(HsH), Bachelor | Angewandte Informatik 2017.09 - 2020.09
140
+ Berufsprofil
141
+ Erfahrener Softwareentwickler
142
+ spezialisiert auf skalierbare Agenten-
143
+ basierte LLM-Anwendungen.
144
+ Vertraut mit modernen KI-Tools
145
+ und nutzt diese zur Steigerung
146
+ von Produktivität und Qualität.
147
+ Projekterfahrung
148
+ Technische Fähigkeiten
149
+ Programmiersprachen
150
+ Python JavaScript Java
151
+ Webtechnologien & Frameworks
152
+ React Next.js TailwindCSS
153
+ Backend & Frameworks
154
+ RESTful API FASTapi Spring Boot
155
+ Datenbank PostgreSQL MySQL
156
+ ORM SQLAlchemy Hibernate
157
+ Entwicklung & Project
158
+ Agile Scrum TDD DDD
159
+ KI & LLMs
160
+ RAG Agent n8n Dify
161
+ GitHub Copilot Cursor Claude Code
162
+ DevOps & Cloud
163
+ Docker CI/CD GitHub Actions
164
+ AWS Vercel Supabase
165
+ Sprachen
166
+ • Full-Stack Entwickler | EmAilX - KI-gestützte E-Mail-Management SaaS (2025.07)
167
+ – Aufbau einer Web Anwendung mit React, Next.js und TailwindCSS.
168
+ – Integration von Azure OpenAI und Agent LLM für intelligente E-Mail-Erstellung.
169
+ – Implementierung Unternehmens-Authentifizierung und Deployment auf Vercel.
170
+ • Full-Stack-Entwickler | KI-verstärkte Scrum-Management-Plattform (2025.06)
171
+ – Aufbau einer Full-Stack-Plattform mit React, Next.js und TailwindCSS.
172
+ – Implementierung interaktiver Kanban-Boards und Chatbot-UI.
173
+ – Aufbau eines LLM-Agent-Systems zur Anforderungsanalyse und automatischen
174
+ Generierung von Projekt-Issues.
175
+ • Full-Stack-Entwickler | AWS Hackathon (Top 25/1000+ auf Devpost) (2024.09–
176
+ 2024.10)
177
+ – Aufbau eines Vue.js-Frontends und Gradio-Chatbots mit Amazon Bedrock und LLaMA
178
+ 3.1, ermöglicht RAG-basierte Teambildung und Spielereinblicke für VALORANT.
179
+ – Leitung der Datenvorverarbeitung und des SQL-Agent-Designs.
180
+ – Deployment der Full-Stack-App auf AWS EC2.
181
+ • Android-Anwendungsentwickler | Should I Stay or Should I Go (2019.09 - 2020.09)
182
+ – Entwicklung einer standortbasierten Anwendung mit MVVM-Architektur und Kotlin.
183
+ – Anwendung der Test-Driven Development (TDD)-Methodik, mit Tests vor der Im-
184
+ plementierung zur Gewährleistung der Codequalität.
185
+ • Java-Desktop-Entwickler | Vier-Gewinnt-Spiel (2018.09 - 2019.05)
186
+ – Implementierung eines Brettspiels in Java mit KI-Gegner (Minimax-Algorithmus).
187
+ – Anwendung von TDD mit Unit-Tests für robuste Entwicklung und Codequalität.
188
+ Studentische Tätigkeiten
189
+ Deutsch (C1) Englisch (Fließend)
190
+ Kontaktinformationen
191
+  +49 15732636211
192
193
+  HanbinChen97
194
+  Hanbin Chen
195
+  Persönliche Website
196
+ • Full-Stack-Entwickler | MedAgent (RWTH Aachen University) (2024.03 -2025.07)
197
+ – Architektur und Entwicklung des Frontends mit React und Next.js auf Basis einer
198
+ modernen Architektur.
199
+ – Implementierung eines RESTful-API-Backends mit Fokus auf Skalierbarkeit und
200
+ Wartbarkeit.
201
+ – Nutzung von CI/CD-Pipelines mit GitHub Actions und Docker.
202
+ • Datenanalyst | KlimDim (ISAC GmbH) (2023.03 - 2023.12)
203
+ – Entwicklung von Datenverarbeitungs-Pipelines und -visualisierung.
204
+ – Nutzung von GitLab-Pipeline und Docker für Entwicklung und automatisierte Tests.
205
+ """
206
+
207
+ try:
208
+ # Test analyse function
209
+ print("Testing analyse function...")
210
+ analysis_result = analyse(test_jd, test_user_info)
211
+
212
+ print("Analysis Result:")
213
+ print("=" * 50)
214
+ print(f"key_skills: {analysis_result.key_skills}")
215
+ print(f"match_points: {analysis_result.match_points}")
216
+ print(f"gap_points: {analysis_result.gap_points}")
217
+ print(f"suggestions: {analysis_result.suggestions}")
218
+ print(f"pitch: {analysis_result.pitch}")
219
+ print("\n")
220
+
221
+ # Test refine function
222
+ print("Testing refine function...")
223
+ test_feedback = "Please emphasize more on my Python skills and cloud experience"
224
+
225
+ refined_result = refine(analysis_result, test_feedback)
226
+ print("Refined Result:")
227
+ print("=" * 50)
228
+ print(f"key_skills: {refined_result.key_skills}")
229
+ print(f"match_points: {refined_result.match_points}")
230
+ print(f"gap_points: {refined_result.gap_points}")
231
+ print(f"suggestions: {refined_result.suggestions}")
232
+ print(f"pitch: {refined_result.pitch}")
233
+
234
+ except Exception as e:
235
+ print(f"Error during analysis: {e}")
src/services/generation_service.py CHANGED
@@ -1,25 +1,37 @@
1
- from typing import Tuple, Dict, Any
2
- from . import llm_service
 
 
3
 
4
- def generate_resume(summary: Dict[str, Any], user_info: str) -> str:
 
 
 
 
 
 
 
5
  """
6
  Generates the resume (Markdown).
7
  """
8
  if not summary:
9
  raise ValueError("Analysis summary cannot be empty.")
10
 
11
- return llm_service.generate_resume_llm(summary, user_info)
 
12
 
13
- def generate_cover_letter(summary: Dict[str, Any], user_info: str) -> str:
14
  """
15
- Generates the cover letter (text).
16
  """
17
- if not summary:
18
- raise ValueError("Analysis summary cannot be empty.")
 
 
19
 
20
- return llm_service.generate_cover_letter_llm(summary, user_info)
21
 
22
- def generate_both(summary: Dict[str, Any], user_info: str) -> Tuple[str, str]:
23
  """
24
  Generates both the resume (Markdown) and cover letter (text).
25
  """
@@ -27,6 +39,157 @@ def generate_both(summary: Dict[str, Any], user_info: str) -> Tuple[str, str]:
27
  raise ValueError("Analysis summary cannot be empty.")
28
 
29
  resume_md = generate_resume(summary, user_info)
30
- cover_letter_txt = generate_cover_letter(summary, user_info)
31
 
32
  return resume_md, cover_letter_txt
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Tuple
2
+ import sys
3
+ import os
4
+ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
5
 
6
+ try:
7
+ from .analyse_service import AnalysisResult
8
+ from . import llm_service
9
+ except ImportError:
10
+ from services.analyse_service import AnalysisResult
11
+ import services.llm_service as llm_service
12
+
13
+ def generate_resume(summary: AnalysisResult, user_info: str) -> str:
14
  """
15
  Generates the resume (Markdown).
16
  """
17
  if not summary:
18
  raise ValueError("Analysis summary cannot be empty.")
19
 
20
+ summary_json = summary.model_dump_json(indent=2)
21
+ return llm_service.generate_resume_llm(summary_json, user_info)
22
 
23
+ def generate_cover_letter(job_description: str, user_info: str) -> str:
24
  """
25
+ Generates the cover letter (text) using direct text inputs.
26
  """
27
+ if not job_description:
28
+ raise ValueError("Job description cannot be empty.")
29
+ if not user_info:
30
+ raise ValueError("User info cannot be empty.")
31
 
32
+ return llm_service.generate_cover_letter_llm(job_description, user_info)
33
 
34
+ def generate_both(summary: AnalysisResult, user_info: str, job_description: str) -> Tuple[str, str]:
35
  """
36
  Generates both the resume (Markdown) and cover letter (text).
37
  """
 
39
  raise ValueError("Analysis summary cannot be empty.")
40
 
41
  resume_md = generate_resume(summary, user_info)
42
+ cover_letter_txt = generate_cover_letter(job_description, user_info)
43
 
44
  return resume_md, cover_letter_txt
45
+
46
+
47
+ if __name__ == "__main__":
48
+ # Test data
49
+ test_summary = """
50
+ key_skills: ['Full-Stack Development', 'Frontend Technologies (React, Next.js, Vue.js, Angular, TypeScript)', 'Backend Development (Java, Kotlin, Go, Python, C#, Spring Boot, FASTAPI)', 'DevOps (Docker, CI/CD, Gitlab/GitHub Actions)', 'Cloud Platforms (AWS, GCP, Azure, Kubernetes)', 'Agile Methodologies (Scrum, TDD, DDD)', 'Automated Testing (Unit Testing, End-to-End, CI/CD)', 'RESTful APIs', 'Database Management (PostgreSQL, MySQL, ORM)', 'German (C1) & English language skills']
51
+ match_points: ['Mehrjährige Erfahrung in Full-Stack-Entwicklung mit Fokus auf Frontend (React, Next.js, Vue.js) und Backend (Java, Spring Boot, FASTAPI, Python-Systeme)', 'Nachgewiesene Cloud-Erfahrung (AWS, Vercel, Supabase) sowie Kenntnisse in DevOps und Deployment mit Docker, CI/CD und GitHub/GitLab Actions', 'Angewendete agile Methoden (Scrum, TDD, DDD) und umfassende Nutzung moderner Softwareentwicklungspraktiken', 'Fundierte Erfahrung im Bereich automatisiertes Testen (TDD, Unit-Tests) in mehreren Projekten', 'Sehr gute Deutschkenntnisse (C1) und fließendes Englisch']
52
+ gap_points: ['Kaum direkte Projekterfahrung mit Angular, TypeScript oder Vue.js als primärem Framework (nur kurz im AWS Hackathon erwähnt)', 'Wenig oder keine explizite Erfahrung mit Kotlin, Go oder C# in aktuellen Projekten (Kotlin vor allem für Android in einer älteren Tätigkeit)', 'Keine Nennung von GCP, Azure oder Kubernetes im Stack (lediglich AWS und Vercel/Supabase)', 'Wenig explizite Erfahrung im Bereich DevOps-Betrieb auf Gitlab (meist GitHub Actions, einmal GitLab-Pipeline)', 'Unit Test/End-to-End Testverfahren werden zwar erwähnt, aber Umfang und Tiefe könnten konkreter dargestellt werden']
53
+ suggestions: ['Vertiefe dein Praxiswissen in Angular, TypeScript und Vue.js durch kleinere Side-Projekte oder Online-Kurse, um gegenüber inovex als breiter aufgestellter Frontend-Experte aufzutreten.', 'Baue gezielt Kenntnisse in Kubernetes und, falls möglich, zumindest Grundkenntnisse in einem weiteren Cloud-Anbieter (z.B. GCP oder Azure) auf.', 'Arbeite aktiv an Projekten, bei denen die Implementierung und Wartung von Gitlab-basierten DevOps-Pipelines und Cloud-Betrieb gefordert ist.', 'Dokumentiere und präsentiere mehr Beispiele für die Anwendung von End-to-End-Tests und komplexerer Testautomatisierung in deinen letzten Projekten.', 'Sprich im Bewerbungsgespräch gezielt über deine Cross-Functionality und wie du dich zügig in neue Technologien (z.B. Go, C#) einarbeiten kannst.']
54
+ pitch: Hanbin Chen bringt als Master-Absolvent und erfahrener Full-Stack-Entwickler die perfekte Mischung aus fundierten Software-Engineering-Skills, Cloud-Expertise und Innovationsgeist mit. Seine Projekterfolge im Bereich LLMs, agiler Full-Stack-Entwicklung sowie CI/CD und Cloud-Deployment zeichnen ihn als modernen Teamplayer mit Wachstumspotenzial aus. Er ist bereit, seine Vielseitigkeit und Begeisterung für neue Technologien in Ihr cross-funktionales Team bei inovex einzubringen und weiter auszubauen.
55
+ """
56
+
57
+ test_jd = """
58
+ Seit 25 Jahren unterstützen wir bei inovex mit rund 500 IT-Expert:innen Unternehmen bei der Digitalisierung und Agilisierung ihres Kerngeschäfts an 8 Standorten deutschlandweit.
59
+
60
+ Im Team Application Development entwickeln wir die verschiedensten Anwendungen, von skalierbaren Backends über performante Web Frontends bis hin zu modernen Apps, die teilweise von mehreren Millionen Menschen täglich benutzt werden. Unsere Konzepte implementieren wir bevorzugt mit Open Source Software im Rahmen agiler Projekte. Bei uns verläuft kein Projekt wie das andere – je nach Kunde, Team und Technologie-Stack ist jede Lösung einzigartig.
61
+
62
+ *Wir bei inovex mögen Vielfalt und Individualität. Deshalb freuen wir uns, wenn du unser Team noch bunter machst.
63
+
64
+ Was du bei uns bewegen kannst
65
+ Gemeinsam mit deinem agilen Projektteam setzt du Projekte für Kunden unterschiedlicher Branchen um. Ihr seid cross-funktional aufgestellt und liefert alles aus einer Hand – von der Architektur über die Entwicklung bis zum DevOps-Betrieb. You build it, you run it.
66
+
67
+ Dein Job beginnt nicht erst mit der Entwicklung von Software: Du unterstützt bei der Aufnahme und Analyse der Kundenanforderungen und stimmst dich eng mit deinem Team ab.
68
+
69
+ Du fühlst dich gleichermaßen im Frontend wie im Backend zu Hause und bringst deine Erfahrung in beiden Bereichen ein – egal, ob es um Architekturen oder die Gestaltung von Single Page Applications geht.
70
+
71
+ Du unterstützt deine Kolleg:innen mithilfe automatisierter Testing-Verfahren (Unit Testing, CI/CD, Ende-zu-Ende-Tests u. v. m.) und sorgst somit für eine kontinuierliche Qualitätskontrolle und -optimierung.
72
+
73
+
74
+
75
+ In unseren Projekten verwenden wir häufig folgende Technologien:
76
+
77
+ Kotlin, Java, Go, Python oder C#
78
+
79
+ Angular, React, TypeScript, Vue.js., Next.js
80
+
81
+ Docker, Gitlab
82
+
83
+ AWS, GCP, Azure, Kubernetes
84
+
85
+ Wer gut zu uns passen würde
86
+ Ob du ein Studium oder eine Ausbildung im IT-Bereich abgeschlossen hast, ist für uns nebensächlich – für uns zählen deine fachlichen Skills und deine Persönlichkeit:
87
+
88
+ Du hast mehr als ein Jahr Berufserfahrung und fundierte Kenntnisse in mehreren der oben genannten Technologien. Idealerweise hast du bereits erste Berührungspunkte mit Cloud-Umgebungen gesammelt.
89
+
90
+ Du hast den Anspruch, dich in neue Technologien einzuarbeiten und sie in Bezug auf den Projekteinsatz zu prüfen.
91
+
92
+ Du beteiligst dich gerne an Code Reviews und setzt dich konstruktiv mit deiner Arbeit auseinander.
93
+
94
+ Es ist dir wichtig, ein umfassendes Verständnis der Standpunkte deiner Team-Mitglieder und Kunden zu entwickeln.
95
+
96
+ Sehr gute Deutschkenntnisse sind Voraussetzung (mind. Level C1), gute Englischkenntnisse runden dein Profil ab.
97
+
98
+ Ab Oktober freuen wir uns auf Verstärkung in unserem Team!
99
+
100
+ Was wir dir bieten
101
+ inovex Culture: Kommunikation auf Augenhöhe, flache Hierarchien, Arbeiten in selbstorganisierten und agilen Teams, Vertrauen und Offenheit, starker Zusammenhalt
102
+ Level Up: Weiterbildungsbudget, eigene inovex Academy, Onboarding- und Mentoring-Programm, enger Austausch mit der Community
103
+ Balance: Flexibles und mobiles Arbeiten, keine Kernarbeitszeiten, Workation, 30 Tage Urlaub
104
+ Familienfreundlichkeit: Flexibles Arbeitszeitmodell, Zuschuss zur Kinderbetreuung, Überstunden nur im Ausnahmefall
105
+ Individualität: Betriebliche Altersvorsorge, E-Gym Wellpass, inovex Sportgruppen/ Sport-Community
106
+ Mobilität: JobRad, Firmenwagen-Leasing
107
+ """
108
+
109
+ test_user_info = """
110
+ Start Hanbin Chen
111
+ Master Informatik-Absolvent | LLM-Forscher | Full-Stack-Softwareentwickler
112
+ Ausbildung
113
+ RWTH Aachen University, Master | Informatik 2020.10 - 2025.07
114
+ Masterarbeit: MedKGC (Medical Knowledge Graph Construction)
115
+ • Entwicklung einer End-to-End-Pipeline für die medizinische Wissensextraktion aus Ra-
116
+ diologieberichten mittels LLMs und deren Darstellung in Knowledge Graphs.
117
+ • Implementierung von Named Entity Recognition mit GPT-4o und Llama3 unter Verwen-
118
+ dung eines neuartigen RAG-Ansatzes und Integration von UMLS-Ontologien.
119
+ • Durchführung von Few-Shot Prompting zur Bewertung der LLM-Leistung.
120
+ Fachhochschule Hannover(HsH), Bachelor | Angewandte Informatik 2017.09 - 2020.09
121
+ Berufsprofil
122
+ Erfahrener Softwareentwickler
123
+ spezialisiert auf skalierbare Agenten-
124
+ basierte LLM-Anwendungen.
125
+ Vertraut mit modernen KI-Tools
126
+ und nutzt diese zur Steigerung
127
+ von Produktivität und Qualität.
128
+ Projekterfahrung
129
+ Technische Fähigkeiten
130
+ Programmiersprachen
131
+ Python JavaScript Java
132
+ Webtechnologien & Frameworks
133
+ React Next.js TailwindCSS
134
+ Backend & Frameworks
135
+ RESTful API FASTapi Spring Boot
136
+ Datenbank PostgreSQL MySQL
137
+ ORM SQLAlchemy Hibernate
138
+ Entwicklung & Project
139
+ Agile Scrum TDD DDD
140
+ KI & LLMs
141
+ RAG Agent n8n Dify
142
+ GitHub Copilot Cursor Claude Code
143
+ DevOps & Cloud
144
+ Docker CI/CD GitHub Actions
145
+ AWS Vercel Supabase
146
+ Sprachen
147
+ • Full-Stack Entwickler | EmAilX - KI-gestützte E-Mail-Management SaaS (2025.07)
148
+ – Aufbau einer Web Anwendung mit React, Next.js und TailwindCSS.
149
+ – Integration von Azure OpenAI und Agent LLM für intelligente E-Mail-Erstellung.
150
+ – Implementierung Unternehmens-Authentifizierung und Deployment auf Vercel.
151
+ • Full-Stack-Entwickler | KI-verstärkte Scrum-Management-Plattform (2025.06)
152
+ – Aufbau einer Full-Stack-Plattform mit React, Next.js und TailwindCSS.
153
+ – Implementierung interaktiver Kanban-Boards und Chatbot-UI.
154
+ – Aufbau eines LLM-Agent-Systems zur Anforderungsanalyse und automatischen
155
+ Generierung von Projekt-Issues.
156
+ • Full-Stack-Entwickler | AWS Hackathon (Top 25/1000+ auf Devpost) (2024.09–
157
+ 2024.10)
158
+ – Aufbau eines Vue.js-Frontends und Gradio-Chatbots mit Amazon Bedrock und LLaMA
159
+ 3.1, ermöglicht RAG-basierte Teambildung und Spielereinblicke für VALORANT.
160
+ – Leitung der Datenvorverarbeitung und des SQL-Agent-Designs.
161
+ – Deployment der Full-Stack-App auf AWS EC2.
162
+ • Android-Anwendungsentwickler | Should I Stay or Should I Go (2019.09 - 2020.09)
163
+ – Entwicklung einer standortbasierten Anwendung mit MVVM-Architektur und Kotlin.
164
+ – Anwendung der Test-Driven Development (TDD)-Methodik, mit Tests vor der Im-
165
+ plementierung zur Gewährleistung der Codequalität.
166
+ • Java-Desktop-Entwickler | Vier-Gewinnt-Spiel (2018.09 - 2019.05)
167
+ – Implementierung eines Brettspiels in Java mit KI-Gegner (Minimax-Algorithmus).
168
+ – Anwendung von TDD mit Unit-Tests für robuste Entwicklung und Codequalität.
169
+ Studentische Tätigkeiten
170
+ Deutsch (C1) Englisch (Fließend)
171
+ Kontaktinformationen
172
+  +49 15732636211
173
174
+  HanbinChen97
175
+  Hanbin Chen
176
+  Persönliche Website
177
+ • Full-Stack-Entwickler | MedAgent (RWTH Aachen University) (2024.03 -2025.07)
178
+ – Architektur und Entwicklung des Frontends mit React und Next.js auf Basis einer
179
+ modernen Architektur.
180
+ – Implementierung eines RESTful-API-Backends mit Fokus auf Skalierbarkeit und
181
+ Wartbarkeit.
182
+ – Nutzung von CI/CD-Pipelines mit GitHub Actions und Docker.
183
+ • Datenanalyst | KlimDim (ISAC GmbH) (2023.03 - 2023.12)
184
+ – Entwicklung von Datenverarbeitungs-Pipelines und -visualisierung.
185
+ – Nutzung von GitLab-Pipeline und Docker für Entwicklung und automatisierte Tests.
186
+ """
187
+
188
+
189
+ try:
190
+ cover_letter = generate_cover_letter(test_jd, test_user_info)
191
+ print("Generated Cover Letter:")
192
+ print("=" * 50)
193
+ print(cover_letter)
194
+ except Exception as e:
195
+ print(f"Error generating cover letter: {e}")
src/services/llm_service.py CHANGED
@@ -1,54 +1,38 @@
1
- import json
2
- from typing import Tuple, Dict, Any
 
3
 
4
  from llm.litellm_client import call_llm
5
  from llm.prompt_templates import get_template
6
 
7
- def _parse_json_response(response: str) -> Dict[str, Any]:
8
- """
9
- Parses a JSON string from the LLM response.
10
- """
11
- try:
12
- # The LLM might return a string enclosed in ```json ... ```, so we find the JSON block.
13
- json_block = response[response.find('{'):response.rfind('}')+1]
14
- return json.loads(json_block)
15
- except (json.JSONDecodeError, IndexError) as e:
16
- print(f"Failed to parse JSON response: {e}\nResponse was: {response}")
17
- raise ValueError("LLM response was not in the expected JSON format.")
18
-
19
 
20
- def analyse_llm(jd: str, user_info: str) -> Dict[str, Any]:
21
  """
22
  Calls the LLM to analyse the JD and user info.
23
  """
24
  prompt = get_template("analyse").format(jd=jd, user=user_info)
25
- response_text = call_llm(prompt)
26
- return _parse_json_response(response_text)
27
 
28
 
29
- def refine_llm(summary: Dict[str, Any], feedback: str) -> Dict[str, Any]:
30
  """
31
  Calls the LLM to refine an existing analysis based on user feedback.
32
  """
33
- summary_str = json.dumps(summary, indent=2)
34
- prompt = get_template("refine").format(summary=summary_str, feedback=feedback)
35
- response_text = call_llm(prompt)
36
- return _parse_json_response(response_text)
37
 
38
 
39
- def generate_resume_llm(summary: Dict[str, Any], user_info: str) -> str:
40
  """
41
  Calls the LLM to generate a resume.
42
  """
43
- summary_str = json.dumps(summary, indent=2)
44
- prompt = get_template("generate_resume").format(summary=summary_str, user=user_info)
45
  return call_llm(prompt)
46
 
47
 
48
- def generate_cover_letter_llm(summary: Dict[str, Any], user_info: str) -> str:
49
  """
50
  Calls the LLM to generate a cover letter.
51
  """
52
- summary_str = json.dumps(summary, indent=2)
53
- prompt = get_template("generate_cover_letter").format(summary=summary_str, user=user_info)
54
  return call_llm(prompt)
 
1
+ import sys
2
+ import os
3
+ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
4
 
5
  from llm.litellm_client import call_llm
6
  from llm.prompt_templates import get_template
7
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
+ def analyse_llm(jd: str, user_info: str) -> str:
10
  """
11
  Calls the LLM to analyse the JD and user info.
12
  """
13
  prompt = get_template("analyse").format(jd=jd, user=user_info)
14
+ return call_llm(prompt)
 
15
 
16
 
17
+ def refine_llm(summary_json: str, feedback: str) -> str:
18
  """
19
  Calls the LLM to refine an existing analysis based on user feedback.
20
  """
21
+ prompt = get_template("refine").format(summary=summary_json, feedback=feedback)
22
+ return call_llm(prompt)
 
 
23
 
24
 
25
+ def generate_resume_llm(summary_json: str, user_info: str) -> str:
26
  """
27
  Calls the LLM to generate a resume.
28
  """
29
+ prompt = get_template("generate_resume").format(summary=summary_json, user=user_info)
 
30
  return call_llm(prompt)
31
 
32
 
33
+ def generate_cover_letter_llm(job_description: str, user_info: str) -> str:
34
  """
35
  Calls the LLM to generate a cover letter.
36
  """
37
+ prompt = get_template("generate_cover_letter").format(jd=job_description, user=user_info)
 
38
  return call_llm(prompt)