learn

OpenAI SDK - Message와 Response 구조

OpenAI SDK - Message와 Response 구조

Message 구조

messages 배열에 들어가는 각 메시지는 role로 구분된다.

system

{"role": "system", "content": "너는 검색 전문가야"}

user

{"role": "user", "content": "파이썬 GIL이 뭔지 검색해줘"}

assistant — 두 가지 형태

# 텍스트 응답
{"role": "assistant", "content": "GIL은..."}

# tool call 요청 (content는 None일 수 있음)
{
    "role": "assistant",
    "content": None,
    "tool_calls": [
        {
            "id": "call_abc123",
            "type": "function",
            "function": {
                "name": "search_web",
                "arguments": '{"query": "python GIL"}'  # JSON 문자열
            }
        }
    ]
}

tool — 실행 결과

{
    "role": "tool",
    "tool_call_id": "call_abc123",  # assistant의 tool_call id와 매칭
    "content": "검색 결과: ..."
}

tool_call_id로 "이 결과가 어느 요청에 대한 것인지" 매칭한다. 모델이 여러 tool을 동시에 요청할 수 있기 때문에 필수.

Response 구조

response.choices[0].finish_reason              # "stop" or "tool_calls"
response.choices[0].message                    # assistant message 객체
response.choices[0].message.content            # 텍스트 (tool_calls면 None)
response.choices[0].message.tool_calls         # tool call 요청 목록 (없으면 None)

# tool_call 하나
tool_call.id                    # "call_abc123"
tool_call.function.name         # "search_web"
tool_call.function.arguments    # '{"query": "python GIL"}' ← JSON 문자열

arguments문자열이다. json.loads()로 파싱해야 dict가 된다.

루프 안에서 messages 추가 순서

순서가 틀리면 모델이 맥락을 잃는다. assistant 메시지 먼저, tool 결과 나중.

# 1. assistant 응답 추가 (tool_calls 포함)
messages.append(response.choices[0].message)

# 2. 각 tool 결과 추가
for tool_call in response.choices[0].message.tool_calls:
    result = execute(tool_call.function.name,
                     json.loads(tool_call.function.arguments))
    messages.append({
        "role": "tool",
        "tool_call_id": tool_call.id,
        "content": str(result)
    })

모델 입장에서 "내가 요청했고 → 결과가 왔다"는 순서가 messages에 그대로 있어야 한다.

Multi-agent에서 다음 agent에게 뭘 넘길까

| 후보 | 내용 | 크기 | |------|------|------| | message.content (최종) | 모델의 최종 텍스트 답변 | 작음 | | tool 결과들 | raw 검색 결과 등 | 클 수 있음 | | 전체 messages 배열 | 모든 왕복 | 큼 |

대부분의 경우 최종 message.content만 넘기는 게 맞다. 모델이 tool 결과를 이미 소화해서 content에 정리해줬으니, raw 데이터까지 넘기면 다음 agent의 context만 커진다.

tool-call-loop-and-multi-agent 참고