-
Notifications
You must be signed in to change notification settings - Fork 0
[Test] 로컬 테스트 진행을 위한 LLMClient 클래스 구현 및 README 파일의 LocalStack 사용법 업데이트 #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
6e277be
chore: 이전 버전의 로컬 테스트 코드 제거
hjham0856 8beaa9e
chore: 로컬 환경에서의 테스트를 위한 localstack 및 awscli 의존성 추가
hjham0856 b39a452
chore: gemini 사용을 위한 google-genai 의존성 추가
hjham0856 5b79977
feat: gemini와 bedrock을 자동으로 선택하는 LLMClient 클래스 추가
hjham0856 66fe7d7
refactor: 요약 및 평가 과정에서 LLMClient를 사용하도록 수정
hjham0856 2c50da9
refactor: LLMClient 인스턴스를 생성하여 tos_summarize 및 tos_evaluate에 전달
hjham0856 54ae58e
chore: 잘못된 의존성 제거
hjham0856 f809b30
chore: 로컬 테스트용 임시파일을 git으로 관리하지 않도록 gitignore 목록에 추가
hjham0856 c4815be
docs: README.md에 테스트 관련 내용 추가
hjham0856 91ae860
Merge pull request #27 from TermLens/feature/25-local-test
hjham0856 da70309
docs: README에 LoaclStack의 람다 함수 업데이트에 대한 내용 추가
hjham0856 f890b6d
refactor: tos_content에 타입 힌트 `str` 추가
hjham0856 02542ab
docs: arm 환경 호환성 명령어 예시 추가 및 zsh 호환성 고려 명령어 수정
hjham0856 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,5 @@ | ||
| /venv/ | ||
| src/__pycache__/ | ||
| src/__pycache__/ | ||
| build/ | ||
| test-package.zip | ||
| output.json |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,28 +1,73 @@ | ||
| # TermLens_BE | ||
| 약관 요약과 중요 조항에 대한 평가를 제공합니다. | ||
|
|
||
| # 아키텍처 | ||
| AWS Lambda | ||
| - AWS Lambda | ||
| - AWS Bedrock | ||
| # 테스트 | ||
| LocalStack의 실행을 위해 docker 설치가 필요합니다. | ||
| ## LocalStack 설치 | ||
| ```bash | ||
| # pipx 설치 | ||
| sudo apt update | ||
| sudo apt install pipx | ||
| # 잘 설치되었는지 확인 | ||
| pipx --version | ||
|
|
||
| # Getting Started | ||
| ## 개발환경 구축 | ||
| bash를 기준으로 작성됨 | ||
| # LocalStack, awslocal 설치 | ||
| pipx install localstack --include-deps | ||
| pipx install awscli-local[ver1] --include-deps | ||
| source ~/.bashrc | ||
| # 잘 설치되었는지 확인 | ||
| localstack --version | ||
| awslocal --version | ||
| ``` | ||
| wsl 환경에서 `pip install` 명령을 전역으로 쓸 수 없어 pipx를 사용합니다. 전역으로 localstack 및 awslocal을 설치하여 사용 가능한 경우 pipx의 설치가 필요하지 않습니다. | ||
| ## 로컬 테스트 | ||
| **docker가 실행된 상태에서** `localstack start` 명령으로 LocalStack을 구동합니다. 이후 터미널에 `Ready`가 나타나면 다른 터미널 창을 열고, 프로젝트 디렉토리에서 아래 작업을 수행합니다. | ||
|
|
||
| 우선, LocalStack에 업로드할 zip파일을 생성합니다. | ||
| ```bash | ||
| sudo apt install python3.12-venv | ||
| python3 -m venv venv # 가상환경 생성 | ||
| source venv/bin/activate # 가상환경 사용 | ||
| pip install -r requirements.txt -t build/ --upgrade | ||
| cp src/*.py build/ | ||
| cd build | ||
| zip -r ../test-package.zip . | ||
| cd .. | ||
| ``` | ||
|
|
||
| 이후부터 작업 시 `source venv/bin/activate` 명령으로 가상환경을 실행한 후 작업 | ||
| - `(venv) 사용자명@컴퓨터명:~/.../TermLens_BE` 처럼 앞에 `(venv)`가 붙는지 확인 | ||
| ARM 환경에서는 `pip install...` 명령 대신 아래의 명령을 사용해주세요. LocalStack 및 AWS Lambda 환경에서는 amd64(x86-64)을 기반으로 작동하나, ARM 기반 기기에서 해당 명령으로 설치하게 되면 ARM용 바이너리를 받아와 LocalStack에서 실행하지 못합니다. 아래 명령으로 생성된 `build/` 디렉토리 및 `test-package.zip` 파일은 삭제 시 root 권한이 필요합니다. | ||
| ```bash | ||
| docker run --platform linux/amd64 --rm -v "$(pwd)":/var/task --entrypoint "" public.ecr.aws/lambda/python:3.12 /bin/sh -c "pip install -r requirements.txt -t build/ --upgrade && cp src/*.py build/ && cd build && dnf install -y zip && zip -r ../test-package.zip . && cd .." | ||
| ``` | ||
|
|
||
| ## 컨벤션 | ||
| ### 커밋 메시지 | ||
| 커밋 메시지의 작성법은 [컨벤셔널 커밋](https://www.conventionalcommits.org/ko/v1.0.0/)을 따릅니다. `feat: `, `fix: `, `test: ` 등의 접두사 뒤에 설명을 덧붙이는 방식입니다. 한국어로 작성합니다. | ||
| 그 다음 아래의 명령을 통해 람다 함수를 생성합니다. | ||
| ```bash | ||
| awslocal lambda create-function \ | ||
| --function-name analyzeTermsOfServices \ | ||
| --runtime python3.12 \ | ||
| --timeout 120 \ | ||
| --zip-file fileb://test-package.zip \ | ||
| --handler lambda_function.lambda_handler \ | ||
| --role arn:aws:iam::000000000000:role/lambda-role \ | ||
| --environment Variables='{GEMINI_API_KEY=여기에_KEY값을_넣어주세요,LLM_PROVIDER=GEMINI}' | ||
| ``` | ||
|
|
||
| ### 브랜칭 전략 | ||
| [깃허브 플로우](https://docs.github.com/ko/get-started/using-github/github-flow)와 유사하게, 개별 작업마다 연관된 새로운 브랜치를 생성하고, 해당 브랜치에서 작업 후 main 브랜치에 병합하는 방식으로 개발을 진행합니다. 이때 브랜치의 이름은 `작업 종류/이슈번호-짧은-설명` 으로 합니다. | ||
| 생성된 함수의 호출은 다음과 같이 할 수 있습니다. | ||
| ```bash | ||
| awslocal lambda invoke --function-name analyzeTermsOfServices \ | ||
| --payload '{"queryStringParameters": {"url" : "www.example.com"}, "body" : "약관 텍스트" }' output.json | ||
| ``` | ||
| 이후 `output.json` 파일에서 응답을 확인할 수 있습니다. | ||
|
|
||
| # 테스트 | ||
| 로컬에서 `lambda_handler()` 메서드를 테스트해야하는 경우 `test_local.py`를 통해 실행 | ||
| 함수가 생성된 상태에서 변경하기 위해서는 `update-function-code`를 사용합니다. | ||
| ```bash | ||
| awslocal lambda update-function-code \ | ||
| --function-name analyzeTermsOfServices \ | ||
| --zip-file fileb://test-package.zip | ||
| ``` | ||
| ## AWS 환경에서 테스트 | ||
| 로컬에서는 작동만을 확인하고, 답변 품질에 대한 테스트는 AWS Lambda에, 테스트용 함수에 배포하여 수행합니다. | ||
| # 컨벤션 | ||
| ## 커밋 메시지 | ||
| 커밋 메시지의 작성법은 [컨벤셔널 커밋](https://www.conventionalcommits.org/ko/v1.0.0/)을 따릅니다. `feat: `, `fix: `, `test: ` 등의 접두사 뒤에 설명을 덧붙이는 방식입니다. 한국어로 작성합니다. | ||
| ## 브랜칭 전략 | ||
| [깃허브 플로우](https://docs.github.com/ko/get-started/using-github/github-flow)와 유사하게, 개별 작업마다 연관된 새로운 브랜치를 생성하고, 해당 브랜치에서 작업 후 main 브랜치에 병합하는 방식으로 개발을 진행합니다. 이때 브랜치의 이름은 `작업 종류/이슈번호-짧은-설명` 으로 합니다. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| boto3 | ||
| google-genai | ||
| markdownify |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
|
|
||
| # 로컬 테스트 환경에서는 gemini 사용 | ||
| # AWS Lambda 환경에서는 Bedrock의 Claude 사용 | ||
|
|
||
| # temperature, top_p는 기본값 temperature 0.2, top_p 0.9로 사용 | ||
| # 환각 억제 목적 | ||
|
|
||
| import os | ||
| from google import genai | ||
| from google.genai import types | ||
| import boto3 | ||
|
|
||
| class LLMClient: | ||
|
|
||
| # 환경에 따라 LLM 모델 선정 및 클라이언트 초기화 | ||
| def __init__(self, temperature: float = 0.2, top_p: float = 0.9): | ||
|
|
||
| self.temperature = temperature | ||
| self.top_p = top_p | ||
|
|
||
| self.provider = os.getenv("LLM_PROVIDER") | ||
| if (self.provider == "GEMINI"): | ||
| # 로컬 테스트 환경 - gemini | ||
| self.client = genai.Client() | ||
| else: | ||
| # AWS Lambda 환경 - Bedrock Claude | ||
| self.client = boto3.client( | ||
| service_name="bedrock-runtime", | ||
| region_name="us-west-2" | ||
| ) | ||
|
|
||
| # 응답 생성 | ||
| # gemini와 bedrock claude 분기 처리 | ||
| def generate_response(self, system_instruction: str, message: str) -> str: | ||
|
|
||
| if (self.provider == "GEMINI"): | ||
| return self._generate_response_gemini(system_instruction, message) | ||
| else: | ||
| return self._generate_response_bedrock_claude(system_instruction, message) | ||
|
|
||
| # gemini로부터 응답 생성 | ||
| def _generate_response_gemini(self, system_instruction: str, message: str) -> str: | ||
|
|
||
| response = self.client.models.generate_content( | ||
| model="gemini-2.5-flash-lite", | ||
| config=types.GenerateContentConfig( | ||
| temperature=self.temperature, | ||
| top_p=self.top_p, | ||
| system_instruction=system_instruction | ||
| ), | ||
| contents=message | ||
| ) | ||
|
|
||
| return response.text | ||
|
|
||
| # bedrock Claude로부터 응답 생성 | ||
| # claude 모델은 3.5 haiku 사용. 테스트에 걸리는 시간 줄이기 위함 | ||
| def _generate_response_bedrock_claude(self, system_instruction: str, message: str) -> str: | ||
|
|
||
| response = self.client.converse( | ||
| modelId="us.anthropic.claude-3-5-haiku-20241022-v1:0", | ||
| inferenceConfig={ | ||
| "temperature": self.temperature, | ||
| "topP": self.top_p | ||
| }, | ||
| system=[{"text": system_instruction}], | ||
| messages=[{"role": "user", "content": [{"text": message}]}] | ||
| ) | ||
|
|
||
| return response['output']['message']['content'][0]['text'] |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,8 +1,8 @@ | ||||||||||
| import json | ||||||||||
| import boto3 | ||||||||||
| from llm_client import LLMClient | ||||||||||
|
|
||||||||||
| def tos_evaluate(summarized_tos): | ||||||||||
| system_instruction=[{"text": """ | ||||||||||
| def tos_evaluate(summarized_tos, client: LLMClient) -> dict: | ||||||||||
| system_instruction=""" | ||||||||||
| 당신은 전문적인 약관 분석 AI입니다. 주어진 약관 내용 및 각 조항을 평가합니다. | ||||||||||
| 주어진 약관은 주요 조항을 위주로 요약된 내용입니다. | ||||||||||
| JSON 양식으로, 다음의 key값을 사용합니다. | ||||||||||
|
|
@@ -39,33 +39,16 @@ def tos_evaluate(summarized_tos): | |||||||||
| } | ||||||||||
| ] | ||||||||||
| } | ||||||||||
| """}] | ||||||||||
| client = boto3.client( | ||||||||||
| service_name="bedrock-runtime", | ||||||||||
| region_name="us-west-2" | ||||||||||
| ) | ||||||||||
| """ | ||||||||||
|
|
||||||||||
| model_id = "us.anthropic.claude-sonnet-4-5-20250929-v1:0" | ||||||||||
| messages = [{ | ||||||||||
| "role": "user", | ||||||||||
| "content": [ | ||||||||||
| {"text": summarized_tos} | ||||||||||
| ] | ||||||||||
| }] | ||||||||||
|
|
||||||||||
| response = client.converse( | ||||||||||
| modelId=model_id, | ||||||||||
| system=system_instruction, | ||||||||||
| messages=messages, | ||||||||||
| ) | ||||||||||
| response = client.generate_response(system_instruction, summarized_tos) | ||||||||||
|
|
||||||||||
| print("TOS Evaluation Response:") | ||||||||||
| print(response) | ||||||||||
|
|
||||||||||
| text = response['output']['message']['content'][0]['text'] | ||||||||||
| start = text.find('{') | ||||||||||
| end = text.rfind('}') + 1 | ||||||||||
| json_text = text[start:end] | ||||||||||
| start = response.find('{') | ||||||||||
| end = response.rfind('}') + 1 | ||||||||||
| json_text = response[start:end] | ||||||||||
|
|
||||||||||
|
Comment on lines
+51
to
52
|
||||||||||
| json_text = response[start:end] | |
| if start == -1 or end == 0 or end <= start: | |
| raise ValueError("Response does not contain a valid JSON object: " + repr(response)) | |
| json_text = response[start:end] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,32 +1,15 @@ | ||
| import boto3 | ||
| from llm_client import LLMClient | ||
|
|
||
| def tos_summarize(tos_content): | ||
| system_instruction=[{"text": """ | ||
| def tos_summarize(tos_content: str, client: LLMClient) -> str: | ||
| system_instruction=""" | ||
| 당신은 약관 분석 전문가입니다. | ||
| 주어진 텍스트에서 주요 약관 내용을 요약합니다. | ||
| 한국어로 응답합니다. | ||
| """}] | ||
|
|
||
| client = boto3.client( | ||
| service_name="bedrock-runtime", | ||
| region_name="us-west-2" | ||
| ) | ||
| """ | ||
|
|
||
| model_id = "us.anthropic.claude-sonnet-4-5-20250929-v1:0" | ||
| messages = [{ | ||
| "role": "user", | ||
| "content": [ | ||
| {"text": tos_content} | ||
| ] | ||
| }] | ||
|
|
||
| response = client.converse( | ||
| modelId=model_id, | ||
| system=system_instruction, | ||
| messages=messages, | ||
| ) | ||
| response = client.generate_response(system_instruction, tos_content) | ||
|
|
||
| print("TOS Summarization Response:") | ||
| print(response) | ||
|
|
||
| return response['output']['message']['content'][0]['text'] | ||
| return response |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
summarized_tosparameter is missing a type hint. For consistency with theclientparameter, add a type hint (likelystrbased on usage).