GLM과 GitHub 커넥터로 레거시 Python 코드 현대화하기
GLM과 GitHub 커넥터로 레거시 Python 코드 현대화하기 (Modernize legacy Python code using GLM and the GitHub connector)
zai-glm-5-2(GLM) 모델과 GitHub 커넥터를 사용해 리포지토리에서 낡은 Python 코드를 읽고 현대화된 버전을 만드는 방법을 보여주는 쿡북입니다.
출처: 문서
본문
zai-glm-5-2(GLM) 모델과 GitHub 커넥터를 사용해 리포지토리에서 낡은 Python 코드를 읽고 현대화된 버전을 만들어요.
API status: 에이전트는
client.beta.agents를, 대화는client.beta.conversations를 사용해요. 이들은 beta 엔드포인트이며 변경될 수 있어요.
사전 요구사항 (Prerequisites)
- Python 3.10+
- Mistral 계정과 API 키
- GitHub 계정 (스크립트가 OAuth 인증을 처리)
환경 설정 (Environment setup)
설치 (Install)
Mistral Python SDK와 .env 파일에서 API 키를 로드하기 위한 python-dotenv를 설치해요:
pip install mistralai python-dotenv
필요한 환경 변수 (Required environment variables)
이 쿡북을 완료하려면 Mistral API 키가 필요해요. Studio의 API keys 섹션에서 새 API 키를 만드세요.
예제 .env 파일을 복사하고 Mistral API 키를 추가해요:
cp .env.example .env
MISTRAL_API_KEY=your-mistral-api-key
GITHUB_REPO=mistralai/cookbook
GITHUB_BRANCH=main
GITHUB_REPO와 GITHUB_BRANCH는 기본값으로 mistralai/cookbook 리포지토리의 main 브랜치를 가리켜요. 다른 리포지토리의 파일을 현대화하려면 이 값을 바꾸면 돼요.
Step 1 — 클라이언트 초기화 (Initialize the client)
프로젝트 디렉토리에 modernize_code.py를 만드세요:
touch modernize_code.py
파일을 열고 임포트와 클라이언트 초기화를 추가해요. 나머지 단계는 에이전트, 대화, 출력 로직을 만듭니다.
"""Modernize legacy Python code using GLM and the GitHub connector."""
import asyncio
import os
import re
from pathlib import Path
from dotenv import load_dotenv
from mistralai.client import Mistral
load_dotenv()
# Step 1 — Initialize the client
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
async def main() -> None:
# Step 2 — Define the modernization target
# Step 3 — Authenticate the GitHub connector
# Step 4 — Create the agent with the GitHub connector
# Step 5 — Start the conversation and extract results
# Step 6 — Save the modernized code
pass
if __name__ == "__main__":
asyncio.run(main())
Step 2 — 현대화 대상 정의 (Define the modernization target)
현대화할 리포지토리, 브랜치, 파일 경로를 지정해요. 리포지토리와 브랜치는 환경 변수에서 읽으며, 기본값은 mistralai/cookbook 리포지토리의 main 브랜치예요. 이 쿡북은 이 리포지토리의 legacy_app/ 디렉토리에 있는 의도적으로 낡은 Python 파일 두 개를 대상으로 해요.
클라이언트 초기화 아래에 다음 상수를 추가해요:
# Step 2 — Define the modernization target
REPO = os.environ.get("GITHUB_REPO", "mistralai/cookbook")
BRANCH = os.environ.get("GITHUB_BRANCH", "main")
FILE_PATHS = [
"mistral/agents/glm_code_modernizer/legacy_app/app.py",
"mistral/agents/glm_code_modernizer/legacy_app/utils.py",
]
이 파일들은 Python 2/3.5 시대 패턴으로 작성된 CLI to-do 리스트 매니저예요: %-formatting, os.path, 수동 open()/close(), 맨땅 except:, type() 체크, sys.argv 파싱, 타입 힌트 없음.
Step 3 — GitHub 커넥터 인증 (Authenticate the GitHub connector)
github_app 커넥터는 스크립트를 실행할 때마다 OAuth 인증이 필요해요. 스크립트가 get_auth_url_async를 호출해 인증 URL을 얻으면, 브라우저에서 열어 접근 권한을 부여해요.
main의 맨 위에 다음을 추가해요:
# Step 3 — Authenticate the GitHub connector
auth_result = await client.beta.connectors.get_auth_url_async(
connector_id_or_name="github_app",
)
print(f"Authenticate the GitHub connector:\n{auth_result.auth_url}")
input("Press Enter once you've completed the OAuth flow in your browser...")
스크립트를 실행하면 GitHub OAuth URL이 출력돼요. 브라우저에서 열고 권한을 승인한 뒤 터미널로 돌아와 Enter를 누르면 돼요.
Step 4 — GitHub 커넥터로 에이전트 생성 (Create the agent with the GitHub connector)
GLM과 GitHub 커넥터를 결합한 에이전트를 만들어요. 에이전트의 instructions는 정확히 어떤 패턴을 현대화할지, 출력을 어떻게 포맷할지 알려줘요.
인증 단계 다음 main 안에, 정리를 보장하는 try/finally 블록으로 감싸서 다음을 추가해요:
agent_id: str | None = None
try:
# Step 4 — Create the agent with the GitHub connector
file_list = "\n".join(f"- `{path}`" for path in FILE_PATHS)
agent = await client.beta.agents.create_async(
name="code_modernizer",
description="Reads legacy Python files from GitHub and produces modernized versions",
model="zai-glm-5-2",
instructions=(
"You are an expert Python developer who modernizes legacy code.\n\n"
"IMPORTANT: You MUST use the GitHub connector to read each file's "
"actual contents from the repository. Do NOT guess or invent code. "
"Read the real source code first, then modernize it.\n\n"
"Your workflow:\n"
"1. Use the GitHub connector to read each file from the repository\n"
"2. Analyze the actual code you retrieved for legacy patterns\n"
"3. Produce a modernized version of THAT SAME CODE — same logic, "
"same structure, same functionality, but with modern Python idioms\n\n"
"Apply these modernizations to the code you read:\n"
"- Replace %-formatting with f-strings\n"
"- Replace os.path with pathlib.Path\n"
"- Replace manual open()/close() with 'with' statements\n"
"- Replace json.loads(f.read()) with json.load(f)\n"
"- Replace bare except: with specific exceptions\n"
"- Replace type() checks with isinstance()\n"
"- Replace mutable default arguments with None sentinels\n"
"- Replace sys.argv parsing with argparse\n"
"- Add type hints to all functions\n"
"- Replace == True/False comparisons with truthiness checks\n"
"- Replace range(len(...)) with enumerate or direct iteration\n\n"
"Return each modernized file in a separate ```python code fence. "
"Include a comment with the original filename at the top of each block. "
"The modernized code must preserve the original functionality exactly."
),
tools=[
{
"type": "connector",
"connector_id": "github_app",
},
],
)
agent_id = agent.id
print(f"Created agent: {agent.name} ({agent.id})")
두 가지를 알아두세요:
- Model:
zai-glm-5-2는 코드 생성·변환에 설계된 코드 생성 모델이에요. - Connector:
"github_app"은 내장 GitHub 커넥터를 이름으로 가리킵니다. 에이전트가 이를 사용해 서버 쪽에서 리포지토리 파일을 읽어요.
Step 5 — 대화 시작 및 결과 추출
에이전트와 대화를 시작해요. 프롬프트는 어떤 리포지토리와 파일을 읽을지 알려줘요. GitHub 커넥터가 파일 접근을 처리하고, 모델은 파일을 읽고 패턴을 분석해 단일 응답으로 현대화된 코드를 반환해요.
main 위에 다음 헬퍼 함수를 추가해요:
def extract_python_blocks(text: str) -> list[str]:
"""Extract Python code blocks from the model response."""
return re.findall(r"```python\s*\n(.*?)```", text, re.DOTALL)
def get_response_text(response) -> str:
"""Extract text content from a conversation response."""
parts = []
for output in response.outputs:
if output.type == "message.output":
content = output.content
if isinstance(content, str):
parts.append(content)
else:
parts.append(
"".join(
chunk.text if hasattr(chunk, "text") else str(chunk)
for chunk in content
)
)
return "\n".join(parts)
그런 다음 에이전트 생성 후 try 블록 안에서 계속해요:
# Step 5 — Start the conversation
print(f"Reading files from {REPO} and modernizing...")
print("This may take a few minutes.\n")
response = await client.beta.conversations.start_async(
agent_id=agent.id,
inputs=[
{
"role": "user",
"content": (
f"Read these files from the `{REPO}` repository "
f"(branch: `{BRANCH}`) using the GitHub connector, "
f"then produce modernized versions of each:\n\n{file_list}\n\n"
"Return each modernized file in a ```python code fence "
"with a comment at the top indicating the original filename."
),
}
],
timeout_ms=600_000,
)
text = get_response_text(response)
timeout_ms=600_000 파라미터는 10분 타임아웃을 설정해요. GLM이 큰 코드 출력을 생성하므로, 이 여유 시간이 요청이 타임아웃되는 것을 방지해요.
model 대신 agent_id를 넘기면 대화가 에이전트의 모델, instructions, 도구를 자동으로 사용해요. Conversations API가 모든 도구 호출을 서버 쪽에서 처리해요 — 에이전트가 GitHub 커넥터를 호출해 파일을 읽고, 내용을 처리하고, 현대화된 코드를 하나의 응답 주기로 반환해요.
Step 6 — 현대화된 코드 저장 (Save the modernized code)
응답에서 Python 코드 블록을 추출해 각각을 modernized/ 디렉토리에 저장해요:
# Step 6 — Save the modernized code
code_blocks = extract_python_blocks(text)
if not code_blocks:
print("No Python code blocks found in the response.")
print("\nRaw response:\n")
print(text)
return
output_dir = Path("modernized")
output_dir.mkdir(exist_ok=True)
filenames = [Path(p).name for p in FILE_PATHS]
for i, block in enumerate(code_blocks):
name = filenames[i] if i < len(filenames) else f"file_{i}.py"
output_path = output_dir / name
output_path.write_text(block.strip() + "\n", encoding="utf-8")
print(f"Saved: {output_path}")
print(f"\nModernized {len(code_blocks)} file(s) in {output_dir}/")
정리 (Cleanup)
완료 시 에이전트를 삭제해요. finally 블록이 에러가 나도 정리를 보장해요:
finally:
# Cleanup — Delete the agent
if agent_id:
await client.beta.agents.delete_async(agent_id=agent_id)
print(f"Deleted agent: {agent_id}")
실행 (Run)
모든 단계가 준비되면 스크립트를 실행해요:
python modernize_code.py
스크립트는 OAuth로 GitHub 커넥터 인증을 받도록 안내한 다음, GLM 에이전트를 만들고, 리포지토리에서 레거시 파일을 읽고, 현대화된 버전을 만들어 modernized/ 디렉토리에 저장해요.
예시 출력:
Authenticate the GitHub connector:
https://github.com/login/oauth/authorize?client_id=...&scope=repo+...
Press Enter once you've completed the OAuth flow in your browser...
Created agent: code_modernizer (a1b2c3d4-5678-90ab-cdef-1234567890ab)
Reading files from mistralai/cookbook and modernizing...
This may take a few minutes.
Saved: modernized/app.py
Saved: modernized/utils.py
Modernized 2 file(s) in modernized/
Deleted agent: a1b2c3d4-5678-90ab-cdef-1234567890ab
다른 대상 시도 (Try different targets)
.env의 GITHUB_REPO와 GITHUB_BRANCH를 업데이트해 다른 리포지토리의 파일을 현대화할 수 있어요. 스크립트의 FILE_PATHS를 바꾸거나 에이전트 instructions를 조정해 다른 현대화 패턴에 집중할 수도 있어요.
Django views 파일을 현대화하려면:
GITHUB_REPO=your-org/your-django-app
그다음 스크립트의 FILE_PATHS를 바꾸세요:
FILE_PATHS = ["myapp/views.py"]
특정 브랜치를 대상으로 하려면:
GITHUB_BRANCH=feature/legacy-cleanup
Python 대신 JavaScript를 현대화하려면: 에이전트 instructions를 JavaScript 패턴(예: var → const/let, 콜백 → async/await, CommonJS → ES modules)으로 바꾸고, 코드 펜스 추출을 ```javascript 블록을 찾도록 바꾸세요.
요약 (Summary)
이 쿡북은 GLM의 코드 생성과 GitHub 커넥터를 결합해 코드 현대화 파이프라인을 만드는 방법을 보여줬어요 — 에이전트가 리포지토리에서 레거시 파일을 직접 읽고 idiomatic한 현대 Python을 반환해요.
만든 것:
- GitHub에서 낡은 Python 파일을 읽고 현대화된 버전을 생성하는 코드 모더나이저
- GLM(
zai-glm-5-2)과 GitHub 커넥터를 결합한 에이전트 (서버 쪽 파일 접근) - 모델 응답에서 코드 블록을 추출해 로컬로 저장하는 파이프라인
사용한 Mistral 기능:
zai-glm-5-2모델을 사용한 Agents API (beta)- 서버 쪽 도구 실행을 위한 Conversations API (beta)
- OAuth 인증을 사용한 GitHub 커넥터(
github_app) - 긴 코드 생성을 위한 확장 타임아웃(
timeout_ms)
더 알아보기 (Learn more)
- Modernize legacy Python code using GLM and the GitHub connector — 공식 문서
- Agents API — Mistral 에이전트 API
- Connectors — GitHub 등 커넥터 사용법
- Mistral Cookbook — 예시 리포지토리