diff --git a/README.md b/README.md index b854926..c40b353 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # TechCase +서비스 링크: [https://techcase.dadamda.site/](https://techcase.dadamda.site/) + TechCase는 기업 기술 블로그를 기반으로, 개발자가 새로운 기술 스택 도입이나 기술적 이슈 해결을 고민할 때 실제 기업 적용 사례를 빠르게 탐색할 수 있도록 돕는 기술 사례 검색 서비스입니다. 기존 기술 블로그 모음 서비스는 여러 글을 한곳에서 볼 수 있다는 장점이 있지만, 검색 품질이 단순 키워드 매칭에 머무르는 경우가 많습니다. 예를 들어 Kafka, Kubernetes, Terraform, Redis, Elasticsearch 같은 기술을 검색하더라도 해당 키워드가 포함된 글만 나열될 뿐, 그 기술이 어떤 문제를 해결하기 위해 도입되었는지, 어떤 아키텍처 안에서 사용되었는지, 실제 서비스 맥락에서 어떤 의미를 가졌는지 파악하기 어렵습니다. diff --git a/apps/backend/app/keywords/dictionaries.py b/apps/backend/app/keywords/dictionaries.py index 882402a..6103cdc 100644 --- a/apps/backend/app/keywords/dictionaries.py +++ b/apps/backend/app/keywords/dictionaries.py @@ -60,6 +60,11 @@ class KeywordRule: KeywordRule("Spring Boot", "technology", ("spring boot", "springboot", "스프링 부트")), KeywordRule("JPA", "technology", ("jpa", "java persistence api")), KeywordRule("LLM", "technology", ("llm", "large language model", "대규모 언어 모델")), + KeywordRule( + "OpenAI Codex", + "technology", + ("openai codex", "codex", "codex cli", "openai codex cli"), + ), KeywordRule( "RAG", "architecture", diff --git a/apps/backend/app/search/evaluation/queries.json b/apps/backend/app/search/evaluation/queries.json index 42b0ac3..80bf5bb 100644 --- a/apps/backend/app/search/evaluation/queries.json +++ b/apps/backend/app/search/evaluation/queries.json @@ -1046,6 +1046,24 @@ } ] }, + { + "id": "technology-openai-codex", + "query": "codex", + "category": "technology", + "intent": "OpenAI Codex와 Codex CLI를 활용한 AI 코딩 에이전트, 코드 리뷰, 개발 자동화 사례를 찾는다.", + "expectedResults": [ + { + "url": "https://techblog.musinsa.com/ai-%EC%8A%A4%ED%8E%98%EC%85%9C%EB%A6%AC%EC%8A%A4%ED%8A%B8%EC%99%80-%EC%9E%90%EB%8F%99%EC%82%AC%EB%83%A5-%ED%95%98%EB%84%A4%EC%8A%A4%EB%A1%9C-%EC%A0%9C%EC%96%B4%ED%95%98%EB%8A%94-ai-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-6c578f8bd1fb?source=rss----f107b03c406e---4", + "title": "AI 스페셜리스트와 자동사냥 - 하네스로 제어하는 AI 파이프라인", + "relevance": 3 + }, + { + "url": "https://meetup.nhncloud.com/posts/407", + "title": "2025 프론트엔드 뉴스 한 방에 몰아 보기 : NHN Cloud Meetup", + "relevance": 2 + } + ] + }, { "id": "architecture-rag", "query": "rag", diff --git a/apps/backend/tests/test_search_query_rules.py b/apps/backend/tests/test_search_query_rules.py new file mode 100644 index 0000000..09b128b --- /dev/null +++ b/apps/backend/tests/test_search_query_rules.py @@ -0,0 +1,28 @@ +from app.search.service import build_search_query, expand_query, matched_rule_keywords + + +def test_codex_matches_openai_codex_keyword() -> None: + assert matched_rule_keywords("codex") == ["OpenAI Codex"] + assert expand_query("codex") == "codex openai codex" + + +def test_codex_search_does_not_use_fuzzy_matching() -> None: + query = build_search_query("codex") + + assert not contains_key(query, "fuzziness") + + +def test_generic_code_search_keeps_fuzzy_fallback() -> None: + query = build_search_query("code") + + assert contains_key(query, "fuzziness") + + +def contains_key(value: object, key: str) -> bool: + if isinstance(value, dict): + return key in value or any(contains_key(child, key) for child in value.values()) + + if isinstance(value, list): + return any(contains_key(child, key) for child in value) + + return False