From ce66b01fcb941c52464aa2308e84597db9c1a65e Mon Sep 17 00:00:00 2001 From: tomchccom Date: Mon, 22 Jun 2026 20:43:20 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20=EA=B2=BD=ED=97=98=20=EC=88=9C?= =?UTF-8?q?=EC=B0=A8=20=EC=A4=91=EB=B3=B5=20=ED=8C=90=EC=A0=95=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20-=20=EA=B8=B0=EC=A1=B4=20=EA=B2=BD=ED=97=98?= =?UTF-8?q?=EA=B3=BC=20=EC=95=9E=EC=84=A0=20=EB=B0=B0=EC=B9=98=20=EA=B2=BD?= =?UTF-8?q?=ED=97=98=20=EB=B9=84=EA=B5=90=20-=20batch=20=EC=9D=B8=EB=8D=B1?= =?UTF-8?q?=EC=8A=A4=20=EA=B8=B0=EB=B0=98=20=EC=A4=91=EB=B3=B5=20=ED=9B=84?= =?UTF-8?q?=EB=B3=B4=20=EB=B0=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/services/experience_merge_service.py | 29 +++++++++++ .../tests/test_experience_merge_service.py | 49 ++++++++++++++++++- 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/myeongsung/app/services/experience_merge_service.py b/myeongsung/app/services/experience_merge_service.py index f449288..65c0b83 100644 --- a/myeongsung/app/services/experience_merge_service.py +++ b/myeongsung/app/services/experience_merge_service.py @@ -406,3 +406,32 @@ def apply_merge_results_to_step2( experience.merge_similarity = result.similarity return step2_experiences + + +def apply_sequential_merge_results_to_step2( + step2_experiences: List[dict], + existing_experiences: List[MergeExperiencePayload], + threshold: Optional[float] = None, + embedding_client: Optional[Any] = None, +) -> List[dict]: + accepted_candidates: List[Any] = list(existing_experiences) + + for index, experience in enumerate(step2_experiences): + merge_response = check_merge_candidates( + targets=[experience], + existing_experiences=accepted_candidates, + threshold=threshold, + top_k=1, + embedding_client=embedding_client, + ) + result = merge_response.results[0] + experience["needs_merge"] = result.needs_merge + experience["merge_candidate_id"] = result.merge_candidate_id + experience["merge_similarity"] = result.similarity + + if not result.needs_merge: + accepted_candidate = dict(experience) + accepted_candidate["id"] = f"batch:{index}" + accepted_candidates.append(accepted_candidate) + + return step2_experiences diff --git a/myeongsung/tests/test_experience_merge_service.py b/myeongsung/tests/test_experience_merge_service.py index 8d7b0c5..ff4321c 100644 --- a/myeongsung/tests/test_experience_merge_service.py +++ b/myeongsung/tests/test_experience_merge_service.py @@ -2,7 +2,11 @@ import unittest from app.schemas.resume_dto import MergeExperiencePayload, Step2ExtractedExperience -from app.services.experience_merge_service import build_embedding_text, check_merge_candidates +from app.services.experience_merge_service import ( + apply_sequential_merge_results_to_step2, + build_embedding_text, + check_merge_candidates, +) class FakeEmbeddings: @@ -34,6 +38,20 @@ class LowSimilarityOpenAI: embeddings = LowSimilarityEmbeddings() +class SameEmbeddings: + def create(self, model, input): + return SimpleNamespace( + data=[ + SimpleNamespace(embedding=[1.0, 0.0]) + for _ in input + ] + ) + + +class SameOpenAI: + embeddings = SameEmbeddings() + + class ExperienceMergeServiceTest(unittest.TestCase): def test_check_merge_candidates_marks_similar_target(self): @@ -185,6 +203,35 @@ def test_low_embedding_with_two_key_field_matches_marks_merge(self): self.assertTrue(response.results[0].needs_merge) self.assertEqual("exp-1", response.results[0].merge_candidate_id) + def test_sequential_merge_uses_first_non_duplicate_as_batch_candidate(self): + experiences = [ + { + "experience_name": "캡스톤 프로젝트", + "experience_group": "상세 서술형", + "experience_type": "프로젝트", + "basic_info": {"project_name": "캡스톤 프로젝트"}, + "experience_content": "추천 모델을 개발했습니다.", + }, + { + "experience_name": "캡스톤 프로젝트", + "experience_group": "상세 서술형", + "experience_type": "프로젝트", + "basic_info": {"project_name": "캡스톤 프로젝트"}, + "experience_content": "추천 모델을 개발했습니다.", + }, + ] + + result = apply_sequential_merge_results_to_step2( + experiences, + [], + threshold=0.86, + embedding_client=SameOpenAI(), + ) + + self.assertFalse(result[0]["needs_merge"]) + self.assertTrue(result[1]["needs_merge"]) + self.assertEqual("batch:0", result[1]["merge_candidate_id"]) + if __name__ == "__main__": unittest.main()