C.W.K.
Stream
Lesson 04 of 04 · published

Raw HTTP Tool Loop

~14 min · raw-http, tool-loop, function-calling, no-sdk

Level 0Spark
0 XP0/35 lessons0/10 achievements
0/140 XP to next level140 XP to go0% complete

같은 loop, SDK 없이

Track 5 의 agentic tool loop 가 raw HTTP 와 동일하게 동작. Wire format 이 정확히 SDK 가 너 위해 만들고 있던 거. 제약된 환경에 박혀있을 때, wire-level 이슈 디버깅할 때, Gemini SDK 없는 언어로 loop 구현할 때 유용.

Wire 의 모양

Tool declaration 이 request body 의 tools field 에. 모델 response 가 functionCall part 포함; 다음 user turn 에 functionResponse part 로 답함. SDK 와 같음, spell out 만.

Code

Raw HTTP tool loop — ~50 줄·python
import httpx, json

API_KEY = '...'  # from env
MODEL   = 'gemini-2.5-flash'
BASE    = f'https://generativelanguage.googleapis.com/v1beta/models/{MODEL}'

TOOLS = [{
    'functionDeclarations': [{
        'name': 'get_weather',
        'description': 'Get current weather for a location.',
        'parameters': {
            'type': 'object',
            'properties': {
                'location': {'type': 'string', 'description': 'City name'},
            },
            'required': ['location'],
        },
    }]
}]

def execute(name, args):
    if name == 'get_weather':
        # Pretend we called a real weather API
        return {'temperature_c': 22, 'conditions': 'clear', 'location': args['location']}
    return {'error': f'Unknown tool: {name}'}

def run_tool_loop(prompt: str, max_iter: int = 10) -> str:
    contents = [{'role': 'user', 'parts': [{'text': prompt}]}]
    headers = {'x-goog-api-key': API_KEY, 'Content-Type': 'application/json'}

    with httpx.Client(timeout=60) as client:
        for _ in range(max_iter):
            resp = client.post(
                f'{BASE}:generateContent',
                headers=headers,
                json={'contents': contents, 'tools': TOOLS},
            )
            resp.raise_for_status()
            data = resp.json()
            model_content = data['candidates'][0]['content']
            parts = model_content.get('parts', [])
            calls = [p['functionCall'] for p in parts if 'functionCall' in p]

            if not calls:
                return ''.join(p.get('text', '') for p in parts)

            # Append model's call turn
            contents.append(model_content)

            # Execute and build user turn
            fn_parts = []
            for fc in calls:
                result = execute(fc['name'], fc.get('args', {}))
                fn_parts.append({
                    'functionResponse': {
                        'name':     fc['name'],
                        'id':       fc.get('id', ''),
                        'response': {'result': result},
                    },
                })
            contents.append({'role': 'user', 'parts': fn_parts})

    raise RuntimeError(f'Loop exhausted after {max_iter} iterations')

print(run_tool_loop('What's the weather in Seoul?'))
OAuth 통한 같은 아이디어 — body 를 'request' 로 wrap·python
# OAuth version: prepend project, wrap original body in {model, project, request}
def run_tool_loop_oauth(prompt, max_iter=10):
    token   = get_access_token()
    project = load_code_assist(token)
    contents = [{'role': 'user', 'parts': [{'text': prompt}]}]
    headers = {
        'Authorization': f'Bearer {token}',
        'Content-Type':  'application/json',
    }

    with httpx.Client(timeout=60) as client:
        for _ in range(max_iter):
            resp = client.post(
                'https://cloudcode-pa.googleapis.com/v1internal:generateContent',
                headers=headers,
                json={
                    'model':   'gemini-2.5-flash',
                    'project': project,
                    'request': {'contents': contents, 'tools': TOOLS},
                },
            )
            data = resp.json()['response']
            model_content = data['candidates'][0]['content']
            parts = model_content.get('parts', [])
            calls = [p['functionCall'] for p in parts if 'functionCall' in p]

            if not calls:
                return ''.join(p.get('text', '') for p in parts)
            # ... same loop as API key path

External links

Exercise

첫 코드 블록의 raw HTTP loop 잡고 두 번째 tool 추가: convert_temperature(value: number, from_unit: string, to_unit: string). Flash 한테 물어: "서울 날씨 어때 + 그 온도 화씨로 변환?" 모델이 두 tool chain 하고 너 response 가 user turn 에 functionResponse 정확히 사용 확인.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.