모델은 에이전트의 추론 엔진입니다. 어떤 도구를 호출할지, 결과를 어떻게 해석할지, 언제 최종 답변을 제공할지 결정하는 에이전트의 의사결정 프로세스를 주도합니다.
선택하는 모델의 품질과 기능은 에이전트의 신뢰성과 성능에 직접적인 영향을 미칩니다. 모델마다 뛰어난 작업이 다릅니다. 복잡한 지시사항을 따르는 데 뛰어난 모델도 있고, 구조화된 추론에 능한 모델도 있으며, 더 많은 정보를 처리하기 위해 더 큰 컨텍스트 윈도우를 지원하는 모델도 있습니다.
LangChain의 표준 모델 인터페이스는 다양한 제공업체 통합에 대한 액세스를 제공하므로 모델을 쉽게 실험하고 전환하여 사용 사례에 가장 적합한 모델을 찾을 수 있습니다.
⚠️ 참고: 제공업체별 통합 정보 및 기능에 대해서는 해당 제공업체의 통합 페이지를 참조하세요.
invoke: 모델은 메시지를 입력으로 받아 완전한 응답을 생성한 후 메시지를 출력합니다.
stream: 모델을 호출하되, 실시간으로 생성되는 출력을 스트리밍합니다.
batch: 여러 요청을 일괄 처리하여 모델에 전송하여 더 효율적으로 처리합니다.
Parameter(매개변수)
chat model(채팅 모델)은 동작을 구성하는 데 사용할 수 있는 매개변수를 사용합니다. 지원되는 매개변수의 전체 집합은 모델 및 제공업체에 따라 다르지만 표준 매개변수는 다음과 같습니다:
model(필수): 제공업체와 함께 사용하려는 특정 모델의 이름 또는 식별자입니다.
api_key: 모델 제공업체의 인증에 필요한 키입니다. 일반적으로 모델에 대한 액세스 권한을 등록할 때 발급됩니다. environment variable(환경 변수)을 설정하여 액세스하는 경우가 많습니다.
temperature: 모델 출력의 무작위성을 제어합니다. 높은 값은 응답을 더 창의적으로 만들고, 낮은 값은 더 결정적으로 만듭니다.
timeout: 요청을 취소하기 전에 모델의 응답을 기다리는 최대 시간(초)입니다.
max_tokens: 응답의 총 token(토큰) 수를 제한하여 출력 길이를 효과적으로 제어합니다.
max_retries: 네트워크 시간 초과 또는 rate limit(속도 제한)과 같은 문제로 인해 요청이 실패할 경우 시스템이 요청을 재전송하는 최대 시도 횟수입니다.
model = init_chat_model( "openai:gpt-4.1-mini", temperature=0.7, timeout=30, max_tokens=1000, max_retries=2,)
⚠️ 참고: 각 chat model(채팅 모델) 통합에는 제공업체별 기능을 제어하는 데 사용되는 추가 매개변수가 있을 수 있습니다. 예를 들어, ChatOpenAI에는 OpenAI Responses API 또는 Completions API를 사용할지 여부를 결정하는 use_responses_api가 있습니다.
chat model(채팅 모델)은 출력을 생성하기 위해 호출되어야 합니다. 각각 다른 사용 사례에 적합한 세 가지 주요 호출 메서드가 있습니다.
Invoke
모델을 호출하는 가장 간단한 방법은 단일 메시지 또는 메시지 목록과 함께 invoke()를 사용하는 것입니다.
response = model.invoke("앵무새는 왜 화려한 깃털을 가지고 있을까요?")response.pretty_print()
==================================[1m Ai Message [0m==================================
앵무새가 화려한 깃털을 가진 이유는 여러 가지가 있습니다:
1. **짝짓기와 번식**: 화려한 깃털은 건강하고 유전적으로 우수한 개체임을 나타내는 신호로 작용합니다. 밝고 선명한 색깔은 다른 앵무새들에게 매력적으로 보이며, 짝을 찾는 데 중요한 역할을 합니다.
2. **종 간 의사소통**: 깃털의 색깔과 무늬는 같은 종 내에서 개체를 구별하거나, 사회적 신호를 전달하는 데 도움을 줍니다.
3. **서식지와 위장**: 일부 앵무새는 화려한 색깔이 주변 환경과 조화를 이루어 위장 효과를 낼 수 있습니다. 예를 들어, 열대 우림의 다채로운 꽃과 잎 사이에 숨을 때 도움이 됩니다.
4. **포식자 회피**: 때로는 눈에 띄는 색깔이 포식자를 혼란스럽게 하거나, 위험 신호를 보내는 역할을 하기도 합니다.
이처럼 앵무새의 화려한 깃털은 생존과 번식에 유리한 여러 기능을 수행합니다.
대화 기록을 나타내기 위해 모델에 메시지 목록을 제공할 수 있습니다. 각 메시지에는 모델이 대화에서 메시지를 보낸 사람을 나타내는 데 사용하는 role(역할)이 있습니다. role, type 및 content에 대한 자세한 내용은 message(메시지) 가이드를 참조하세요.
# Dictionary formatconversation = [ { "role": "system", "content": "당신은 한국어를 프랑스어로 번역하는 유용한 어시스턴트입니다.", }, {"role": "user", "content": "번역: 나는 프로그래밍을 좋아합니다."}, {"role": "assistant", "content": "J'adore la programmation."}, {"role": "user", "content": "번역: 나는 애플리케이션 구축을 좋아합니다."},]response = model.invoke(conversation)response.pretty_print()
==================================[1m Ai Message [0m==================================
J'aime créer des applications.
# Message objectsfrom langchain_core.messages import AIMessage, HumanMessage, SystemMessageconversation = [ SystemMessage("당신은 한국어를 프랑스어로 번역하는 유용한 어시스턴트입니다."), HumanMessage("번역: 나는 프로그래밍을 좋아합니다."), AIMessage("J'adore la programmation."), HumanMessage("번역: 나는 애플리케이션 구축을 좋아합니다."),]response = model.invoke(conversation)response.pretty_print()
==================================[1m Ai Message [0m==================================
J'aime créer des applications.
Stream
대부분의 모델은 생성되는 동안 출력 콘텐츠를 스트리밍할 수 있습니다. 출력을 점진적으로 표시함으로써 스트리밍은 특히 긴 응답의 경우 사용자 경험을 크게 향상시킵니다.
stream()을 호출하면 생성되는 출력 청크를 생성하는 iterator를 반환합니다. 루프를 사용하여 각 청크를 실시간으로 처리할 수 있습니다:
# 기본 텍스트 스트리밍for chunk in model.stream("앵무새는 왜 화려한 깃털을 가지고 있을까요?"): print(chunk.text, end="", flush=True)
앵무새가 화려한 깃털을 가지고 있는 이유는 주로 다음과 같은 이유들 때문입니다:
1. **짝짓기와 구애**: 화려한 깃털은 건강함과 유전적 우수성을 나타내는 신호로 작용합니다. 밝고 다양한 색깔은 암컷에게 매력적으로 보이며, 좋은 짝을 선택하는 데 도움을 줍니다.
2. **종 간 식별**: 다양한 색깔과 무늬는 같은 종끼리 쉽게 인식할 수 있도록 도와줍니다. 이는 교배 시 종의 혼합을 방지하는 역할을 합니다.
3. **서식지 적응**: 일부 앵무새는 화려한 색이 주변 환경과 어울려 위장 역할을 하기도 합니다. 예를 들어, 열대 우림의 다채로운 색깔과 어우러져 천적에게서 숨기 쉽습니다.
4. **사회적 신호**: 깃털의 색상과 상태는 사회적 지위나 기분을 나타낼 수 있습니다. 예를 들어, 스트레스나 건강 상태가 깃털에 반영되기도 합니다.
요약하면, 앵무새의 화려한 깃털은 생존과 번식에 유리한 여러 기능을 수행하기 위해 진화해 온 특징입니다.
# 도구 호출, 추론, 기타 콘텐츠 스트리밍reasoning_started = Falsereasoning_model = init_chat_model("groq:openai/gpt-oss-20b")for chunk in reasoning_model.stream("하늘은 무슨 색인가요?"): for block in chunk.content_blocks: if block["type"] == "reasoning" and (reasoning := block.get("reasoning")): if not reasoning_started: print("🧠 추론:", end="\n", flush=True) reasoning_started = True print(reasoning, end="", flush=True) elif block["type"] == "tool_call_chunk": print(f"\n도구 호출 청크: {block}") elif block["type"] == "text": if reasoning_started: print("\n\n💬 답변:", end="\n") reasoning_started = False print(block["text"], end="", flush=True) else: print(block) # 그 외
🧠 추론:
The user asks in Korean: "하늘은 무슨 색인가요?" meaning "What color is the sky?" The answer: typically blue. Could mention variations: blue during day, red/orange at sunrise/sunset, gray at overcast, etc. Provide answer in Korean.
💬 답변:
하늘은 보통 **파란색**으로 보입니다.
- **낮**에는 태양빛이 대기 중의 입자에 산란되어 파란색이 가장 눈에 띕니다.
- **해질 무렵**이나 **해돋이**에는 태양이 낮은 각도로 비추어 붉은 빛이 길게 퍼지면서 주황색·분홍색·빨간색으로 물듭니다.
- **구름이 많거나 흐린 날**에는 회색빛을 띠기도 하고, 비가 올 때는 짙은 회색이나 어두운 파란색이 보이기도 합니다.
따라서 가장 일반적인 상황에서는 하늘이 파란색이라고 할 수 있습니다.
모델이 전체 응답을 생성한 후 단일 AIMessage를 반환하는 invoke()와 달리, stream()은 각각 출력 텍스트의 일부를 포함하는 여러 AIMessageChunk 객체를 반환합니다. 중요한 점은 스트림의 각 청크가 합산을 통해 전체 메시지로 수집되도록 설계되었다는 것입니다.
from langchain_core.messages import AIMessageChunkfull: None | AIMessageChunk = Nonefor chunk in model.stream("하늘은 무슨 색인가요? 한문장으로 답변하시오."): full = chunk if full is None else full + chunk print(full.text)
하
하늘
하늘은
하늘은 대
하늘은 대개
하늘은 대개 파
하늘은 대개 파란
하늘은 대개 파란색
하늘은 대개 파란색입니다
하늘은 대개 파란색입니다.
하늘은 대개 파란색입니다.
하늘은 대개 파란색입니다.
하늘은 대개 파란색입니다.
print(full.content_blocks)
[{'type': 'text', 'text': '하늘은 대개 파란색입니다.'}]
결과 메시지는 invoke()로 생성된 메시지와 동일하게 처리할 수 있습니다. 예를 들어 메시지 기록에 집계하여 대화 컨텍스트로 모델에 다시 전달할 수 있습니다.
⚠️ 중의: 스트리밍은 프로그램의 모든 단계가 청크 스트림을 처리하는 방법을 알고 있는 경우에만 작동합니다. 예를 들어, 스트리밍이 불가능한 애플리케이션은 처리하기 전에 전체 출력을 메모리에 저장해야 하는 애플리케이션입니다.
Batch
독립적인 요청 컬렉션을 모델에 일괄 처리하면 처리를 병렬로 수행할 수 있으므로 성능을 크게 향상시키고 비용을 절감할 수 있습니다.
responses = model.batch( [ "앵무새는 왜 화려한 깃털을 가지고 있나요?", "비행기는 어떻게 날 수 있나요?", "양자 컴퓨팅이란 무엇인가요?", ])for response in responses: print(response)
content='앵무새가 화려한 깃털을 가지는 이유는 여러 가지가 있습니다:\n\n1. **짝짓기와 성적 선택**: 화려한 깃털은 건강하고 유전적으로 우수한 개체임을 나타내는 신호로 작용합니다. 밝고 선명한 색깔은 다른 앵무새들에게 매력적으로 보이기 때문에 짝을 찾는 데 유리합니다.\n\n2. **종 내 의사소통**: 깃털의 색상과 무늬는 같은 종 내에서 신호를 주고받는 데 사용됩니다. 예를 들어, 특정 색깔이나 패턴은 공격성, 친화성, 영역 표시 등 다양한 행동을 나타낼 수 있습니다.\n\n3. **서식지 적응**: 일부 앵무새의 화려한 깃털은 주변 환경과 어우러지거나 반대로 눈에 띄어 포식자를 혼란시키는 역할을 할 수도 있습니다.\n\n이처럼 앵무새의 화려한 깃털은 생존과 번식에 중요한 역할을 하는 진화적 결과라고 할 수 있습니다.' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 243, 'prompt_tokens': 24, 'total_tokens': 267, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_4c2851f862', 'id': 'chatcmpl-CX69EOZmIxgUbcqMdqeTJ9avS03xv', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--1a778415-7c7c-43c4-bd28-d9dc37db5800-0' usage_metadata={'input_tokens': 24, 'output_tokens': 243, 'total_tokens': 267, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
content='비행기가 날 수 있는 원리는 주로 **양력(lift)** 때문입니다. 비행기가 하늘을 나는 과정을 간단히 설명하면 다음과 같습니다:\n\n1. **날개 모양 (에어포일)** \n 비행기 날개는 위쪽이 둥글고 아래쪽이 평평하거나 덜 둥근 특수한 모양을 가지고 있습니다. 이를 에어포일(airfoil)이라고 합니다.\n\n2. **공기 흐름과 압력 차이** \n 비행기가 앞으로 움직이면 공기가 날개 위와 아래를 지나갑니다. 날개 위쪽은 둥근 모양 때문에 공기가 더 빠르게 흐르고, 날개 아래쪽은 상대적으로 느리게 흐릅니다. 베르누이의 원리에 따라 빠른 공기 흐름은 압력을 낮추고, 느린 공기 흐름은 압력을 높입니다.\n\n3. **양력 발생** \n 날개 아래의 공기 압력이 날개 위의 압력보다 높아지면서 위로 밀어 올리는 힘, 즉 양력이 발생합니다. 이 양력이 비행기의 무게를 이겨내면 비행기는 하늘로 떠오를 수 있습니다.\n\n4. **추진력과 항력** \n 엔진이 비행기를 앞으로 밀어주는 추진력(thrust)이 있어야 하고, 공기 저항인 항력(drag)을 극복해야 합니다. 추진력이 충분히 크면 비행기는 원하는 속도와 높이를 유지할 수 있습니다.\n\n요약하자면, 비행기는 엔진의 추진력으로 앞으로 이동하며, 날개의 독특한 모양 덕분에 공기의 흐름에 의해 위로 밀어 올리는 양력이 생겨 하늘을 날 수 있습니다.' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 382, 'prompt_tokens': 16, 'total_tokens': 398, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_4c2851f862', 'id': 'chatcmpl-CX69FpqcoFTXL5GVWW9vafs6vLfUT', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--49bc9b3d-6509-4d73-baa1-5b62e28f9443-0' usage_metadata={'input_tokens': 16, 'output_tokens': 382, 'total_tokens': 398, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
content='양자 컴퓨팅(Quantum Computing)은 양자역학의 원리를 이용하여 정보를 처리하는 컴퓨팅 기술입니다. 전통적인 컴퓨터가 비트(bit)를 사용해 0 또는 1의 값을 가지는 반면, 양자 컴퓨터는 큐비트(qubit)를 사용합니다. 큐비트는 0과 1의 상태를 동시에 가질 수 있는 중첩(superposition) 상태를 지닐 뿐만 아니라, 얽힘(entanglement)이라는 특성을 통해 여러 큐비트가 상호 연관된 상태로 작동할 수 있습니다.\n\n이러한 특성 덕분에 양자 컴퓨터는 특정 문제에 대해 기존 컴퓨터보다 훨씬 빠르게 계산할 수 있습니다. 예를 들어, 대규모 소인수분해, 최적화 문제, 양자 시뮬레이션 등에서 강점을 보입니다. 하지만 아직은 기술적 한계와 오류율 문제 등으로 인해 상용화 초기 단계에 있습니다.\n\n요약하자면, 양자 컴퓨팅은 양자역학의 원리를 활용하여 정보를 처리하는 새로운 계산 방식으로, 특정 복잡한 문제 해결에 혁신적인 성능 향상을 기대할 수 있는 기술입니다.' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 265, 'prompt_tokens': 18, 'total_tokens': 283, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_4c2851f862', 'id': 'chatcmpl-CX69Fr3F8NWxFBX7ex5pEU5BgyEms', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--fc5f238a-e9e0-4269-b207-c73827fb1774-0' usage_metadata={'input_tokens': 18, 'output_tokens': 265, 'total_tokens': 283, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
⚠️ 참고: 이 섹션에서는 클라이언트 측에서 모델 호출을 병렬화하는 chat model(채팅 모델) 메서드 batch()에 대해 설명합니다.
이는 OpenAI 또는 Anthropic과 같은 추론 제공업체가 지원하는 batch API(일괄 처리 API)와는 구별됩니다.
기본적으로 batch()는 전체 배치에 대한 최종 출력만 반환합니다. 각 개별 입력이 생성을 완료할 때 출력을 받으려면 batch_as_completed()로 결과를 스트리밍할 수 있습니다:
for response in model.batch_as_completed( [ "앵무새는 왜 화려한 깃털을 가지고 있나요?", "비행기는 어떻게 날 수 있나요?", "양자 컴퓨팅이란 무엇인가요?", ]): print(response)
(2, AIMessage(content='양자 컴퓨팅(Quantum Computing)은 양자역학의 원리를 이용하여 정보를 처리하는 컴퓨팅 기술입니다. 기존의 고전 컴퓨터가 비트(bit)를 사용해 0 또는 1의 값을 가지는 반면, 양자 컴퓨터는 양자 비트(큐비트, qubit)를 사용합니다. 큐비트는 0과 1의 상태를 동시에 가질 수 있는 중첩(superposition) 상태가 가능하며, 얽힘(entanglement)이라는 양자역학적 현상을 통해 큐비트들 간에 강한 상관관계를 형성할 수 있습니다.\n\n이러한 특성 덕분에 양자 컴퓨터는 특정 문제들, 예를 들어 대규모 소인수분해, 최적화 문제, 양자 시뮬레이션 등에서 기존 컴퓨터보다 훨씬 빠른 계산이 가능합니다. 다만, 양자 컴퓨팅 기술은 아직 연구 및 개발 단계에 있으며, 상용화되기까지는 여러 기술적 난제들이 남아 있습니다.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 228, 'prompt_tokens': 18, 'total_tokens': 246, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_4c2851f862', 'id': 'chatcmpl-CX69KzVlx73u2UtnCt8LAQEevgpgQ', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--b1f4fc3f-b5b1-443a-887d-08b3a8585d08-0', usage_metadata={'input_tokens': 18, 'output_tokens': 228, 'total_tokens': 246, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}))
(0, AIMessage(content='앵무새가 화려한 깃털을 가지고 있는 이유는 주로 다음과 같습니다:\n\n1. **짝짓기와 구애**: 화려한 깃털은 건강하고 강한 개체임을 나타내는 신호로 작용합니다. 밝고 선명한 색깔은 짝을 끌어들이는 데 도움이 되며, 이는 자연 선택과 성 선택의 결과입니다.\n\n2. **종 내 식별**: 다양한 색깔과 패턴은 같은 종 내에서 개체를 구별하는 데 유용합니다. 이를 통해 개체들은 서로를 인식하고 사회적 상호작용을 원활하게 할 수 있습니다.\n\n3. **서식지 적응**: 일부 앵무새는 주변 환경에 맞춰 위장하거나, 반대로 눈에 띄는 색깔로 포식자를 혼란시키는 역할을 할 수 있습니다.\n\n4. **사회적 신호**: 깃털의 색과 상태는 사회적 지위나 건강 상태를 나타내는 신호가 되기도 합니다.\n\n종합적으로, 앵무새의 화려한 깃털은 생존과 번식에 유리한 여러 기능을 수행하는 중요한 특징입니다.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 261, 'prompt_tokens': 24, 'total_tokens': 285, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_4c2851f862', 'id': 'chatcmpl-CX69KHEciJHnctDULsQyn4EwY5Nwe', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--f50f9e4d-69a9-4d23-afb5-9d7d76ba69cb-0', usage_metadata={'input_tokens': 24, 'output_tokens': 261, 'total_tokens': 285, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}))
(1, AIMessage(content='비행기가 어떻게 날 수 있는지에 대해 설명해드릴게요.\n\n비행기가 날 수 있는 원리는 주로 **양력(lift)** 때문입니다. 양력은 비행기의 날개가 공기를 가르면서 발생하는 힘으로, 중력(무게)을 이겨내고 비행기를 하늘로 띄우는 역할을 합니다.\n\n### 비행기의 비행 원리\n\n1. **날개의 모양 (에어포일, Airfoil)** \n 비행기 날개의 윗면은 아래쪽보다 더 둥글고 길게 설계되어 있습니다. 날개를 통해 공기가 흐를 때, 윗면을 지나는 공기가 더 빠르게 이동하고, 아랫면을 지나는 공기보다 압력이 낮아집니다. 이것이 바로 **베르누이의 원리**에 의해 발생하는 현상입니다.\n\n2. **양력 발생** \n 날개 윗면의 공기압이 낮아지고, 아랫면의 공기압이 상대적으로 높아지면서 위로 밀어 올리는 힘이 생깁니다. 이 힘이 바로 양력입니다.\n\n3. **추진력 (Thrust)** \n 엔진이나 프로펠러가 앞으로 나아가는 힘을 만들어 냅니다. 이 추진력이 있어야 비행기가 앞으로 움직이며 날개의 공기 흐름이 생겨 양력이 발생합니다.\n\n4. **항력 (Drag)** \n 비행기가 공기 중을 움직일 때 발생하는 저항력입니다. 항력을 줄이는 것이 효율적인 비행을 위해 중요합니다.\n\n5. **중력 (Gravity)** \n 비행기를 아래로 끌어당기는 힘입니다.\n\n### 간단히 정리하면 \n- 엔진이 비행기를 앞으로 밀고(추진력), \n- 날개가 공기의 흐름을 이용해 위로 들어올리는 힘(양력)을 만들어내고, \n- 이 힘이 중력보다 크면 비행기는 하늘을 날 수 있습니다.\n\n이 원리 덕분에 비행기는 하늘을 날면서 사람과 물자를 빠르고 안전하게 이동시킬 수 있습니다.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 466, 'prompt_tokens': 16, 'total_tokens': 482, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_4c2851f862', 'id': 'chatcmpl-CX69K17U8V2v2YjzF5Yffzf1RlVOg', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--8927046f-4ce2-4aff-8946-90b9a27a60c7-0', usage_metadata={'input_tokens': 16, 'output_tokens': 466, 'total_tokens': 482, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}))
⚠️ 참고: batch_as_completed()를 사용할 때 결과가 순서 없이 도착할 수 있습니다. 각각에는 필요에 따라 원래 순서를 재구성하기 위해 일치시키는 입력 인덱스가 포함됩니다.
💡 팁: batch() 또는 batch_as_completed()를 사용하여 많은 수의 입력을 처리할 때 최대 병렬 호출 수를 제어할 수 있습니다. 이는 RunnableConfig 딕셔너리에서max_concurrency 속성을 설정하여 수행할 수 있습니다.
from langchain.tools import tool@tooldef get_weather(city: str) -> str: """도시의 날씨를 가져옵니다.""" return f"{city} 날씨: 맑음, 27°C"model_with_tools = model.bind_tools([get_weather]) # [!code highlight]response = model_with_tools.invoke("보스턴의 날씨는 어때?")for tool_call in response.tool_calls: # 모델이 호출한 도구 확인 print(f"도구: {tool_call['name']}") print(f"인자: {tool_call['args']}")
도구: get_weather
인자: {'city': '보스턴'}
사용자 정의 도구를 바인딩할 때 모델의 응답에는 도구 실행에 대한 요청이 포함됩니다. agent와 별도로 모델을 사용하는 경우, 요청된 작업을 수행하고 후속 추론에 사용할 수 있도록 결과를 모델에 반환하는 것은 사용자의 책임입니다. agent를 사용하는 경우 에이전트 루프가 도구 실행 루프를 처리합니다.
아래에서는 tool calling을 사용할 수 있는 몇 가지 일반적인 방법을 보여줍니다.
Tool execution loop(도구 실행 루프)
모델이 tool call(도구 호출)을 반환하면 도구를 실행하고 결과를 모델에 다시 전달해야 합니다. 이렇게 하면 모델이 도구 결과를 사용하여 최종 응답을 생성할 수 있는 대화 루프가 생성됩니다. LangChain에는 이러한 오케스트레이션을 처리하는 agent 추상화가 포함되어 있습니다.
다음은 간단한 예입니다:
from langchain_core.messages import BaseMessage, HumanMessage# 모델에 도구 바인딩model_with_tools = model.bind_tools([get_weather])# 1단계: 모델이 도구 호출 생성messages: list[BaseMessage] = [HumanMessage("보스턴의 날씨는 어때?")]ai_msg = model_with_tools.invoke(messages)messages.append(ai_msg)# 2단계: 도구 실행 및 결과 수집for tool_call in ai_msg.tool_calls: # 생성된 인자로 도구 실행 tool_result = get_weather.invoke(tool_call) messages.append(tool_result)# 3단계: 결과를 모델에 다시 전달하여 최종 응답 생성final_response = model_with_tools.invoke(messages)print(final_response.content)
보스턴은 현재 맑고 기온은 27도입니다. 더 궁금한 점 있으신가요?
도구가 반환한 각 ToolMessage에는 원래 도구 호출과 일치하는 tool_call_id가 포함되어 있어 모델이 결과를 요청과 연관시키는 데 도움이 됩니다.
Forcing tool calls(도구 호출 강제)
기본적으로 모델은 사용자 입력을 기반으로 사용할 바인딩된 도구를 자유롭게 선택할 수 있습니다. 그러나 특정 tool을 선택하도록 강제하거나 주어진 목록에서 임의의 tool을 사용하도록 보장할 수 있습니다
==================================[1m Ai Message [0m==================================
Tool Calls:
get_weather (call_oFZlf1HuhovweghBbQUAdKrc)
Call ID: call_oFZlf1HuhovweghBbQUAdKrc
Args:
city: 서울
Parallel tool calls(병렬 도구 호출)
많은 모델이 적절한 경우 여러 도구를 병렬로 호출하는 것을 지원합니다. 이를 통해 모델이 동시에 다른 소스에서 정보를 수집할 수 있습니다.
from pprint import pprintfrom langchain_core.messages import BaseMessagemessages: list[BaseMessage] = []model_with_tools = model.bind_tools([get_weather])response = model_with_tools.invoke("보스턴과 도쿄 날씨는 어때요?")messages.append(response)# 모델은 여러 개의 도구 호출을 생성할 수 있습니다pprint(response.tool_calls)# 모든 도구 실행 (async를 사용하여 병렬로 실행 가능)for tool_call in response.tool_calls: if tool_call["name"] == "get_weather": result = get_weather.invoke(tool_call) messages.append(result)
==================================[1m Ai Message [0m==================================
Tool Calls:
get_weather (call_NlHChanfGT5uiw60FF3rnHrF)
Call ID: call_NlHChanfGT5uiw60FF3rnHrF
Args:
city: Boston
get_weather (call_f5WrgfplPINTw5btJmffuNb4)
Call ID: call_f5WrgfplPINTw5btJmffuNb4
Args:
city: Tokyo
=================================[1m Tool Message [0m=================================
Name: get_weather
Boston 날씨: 맑음, 27°C
=================================[1m Tool Message [0m=================================
Name: get_weather
Tokyo 날씨: 맑음, 27°C
Model은 요청된 작업의 독립성을 기반으로 병렬 실행이 적절한 시기를 지능적으로 결정합니다.
💡 팁: tool calling(도구 호출)을 지원하는 대부분의 모델은 기본적으로 parallel tool call(병렬 도구 호출)을 활성화합니다. 일부(OpenAI 및 Anthropic 포함)는 이 기능을 비활성화할 수 있습니다. 이렇게 하려면 parallel_tool_calls=False를 설정하세요:
응답을 스트리밍할 때 도구 호출은 ToolCallChunk를 통해 점진적으로 구축됩니다. 이를 통해 완전한 응답을 기다리지 않고 도구 호출이 생성될 때 확인할 수 있습니다.
for chunk in model_with_tools.stream("보스턴과 도쿄의 날씨는 어때요?"): # 도구 호출 청크가 점진적으로 도착합니다 for tool_chunk in chunk.tool_call_chunks: if name := tool_chunk.get("name"): print(f"Tool: {name}") if id_ := tool_chunk.get("id"): print(f"ID: {id_}") if args := tool_chunk.get("args"): print(f"Args: {args}")
최신 모델은 결론에 도달하기 위해 다단계 추론을 수행할 수 있습니다. 여기에는 복잡한 문제를 더 작고 관리하기 쉬운 단계로 나누는 작업이 포함됩니다.
기본 모델이 지원하는 경우 이 추론 프로세스를 표시하여 모델이 최종 답변에 어떻게 도달했는지 더 잘 이해할 수 있습니다.
reasoning_model = init_chat_model("groq:openai/gpt-oss-20b")for chunk in reasoning_model.stream( "앵무새는 왜 화려한 깃털을 가지고 있을까요? 10단어 문장으로 짧게 답변하세요."): reasoning_steps = [r for r in chunk.content_blocks if r["type"] == "reasoning"] print(reasoning_steps if reasoning_steps else chunk.text)
reasoning_model = init_chat_model("groq:openai/gpt-oss-20b")response = reasoning_model.invoke( "앵무새는 왜 화려한 깃털을 가지고 있을까요? 10단어 문장으로 짧게 답변하세요.")reasoning_steps = [b for b in response.content_blocks if b["type"] == "reasoning"]print(" ".join(step["reasoning"] for step in reasoning_steps))
We need to respond in Korean. The user says: "앵무새는 왜 화려한 깃털을 가지고 있을까요? 10단어 문장으로 짧게 답변하세요."
We need to give a short answer in Korean, 10 words exactly. They want a sentence with 10 words. We must count Korean words. In Korean, words are separated by spaces. So we need 10 words. For example: "성장 경쟁, 짝짓기 매력, 시각적 신호 전달을 위해." Let's count: "성장(1) 경쟁(2), 짝짓기(3) 매력(4), 시각적(5) 신호(6) 전달을(7) 위해(8)." That's 8 words. Need 10. Let's craft: "자연 선택에 의해 짝짓기 매력과 보호를 위해." Count: "자연(1) 선택에(2) 의해(3) 짝짓기(4) 매력과(5) 보호를(6) 위해(7)." 7 words. Need 10. Let's add: "생존 경쟁, 번식 성공을 위해." Count: "생존(1) 경쟁,(2) 번식(3) 성공을(4) 위해(5)." That is 5 words. Combined with previous 7 = 12. We need exactly 10 words. Let's craft a full sentence of 10 words. Example: "성장 경쟁과 짝짓기 매력, 색다른 신호 전달을 위해." Count: "성장(1) 경쟁과(2) 짝짓기(3) 매력,(4) 색다른(5) 신호(6) 전달을(7) 위해(8)." 8 words. Need 2 more words: "자연에서" maybe. Add "자연에서" before? Let's insert: "자연에서 성장 경쟁과 짝짓기 매력, 색다른 신호 전달을 위해." Count: "자연에서(1) 성장(2) 경쟁과(3) 짝짓기(4) 매력,(5) 색다른(6) 신호(7) 전달을(8) 위해(9)." Still 9 words. Need one more. Add "효과적으로" maybe. "효과적으로" before "전달을 위해". Let's count: "자연에서(1) 성장(2) 경쟁과(3) 짝짓기(4) 매력,(5) 색다른(6) 신호(7) 효과적으로(8) 전달을(9) 위해(10)." That is 10 words. Sentence: "자연에서 성장 경쟁과 짝짓기 매력, 색다른 신호 효과적으로 전달을 위해." Wait punctuation: we need a sentence. Maybe we can adjust punctuation but still count words. Word count ignoring punctuation. The sentence: "자연에서 성장 경쟁과 짝짓기 매력, 색다른 신호 효과적으로 전달을 위해." Count: "자연에서(1) 성장(2) 경쟁과(3) 짝짓기(4) 매력,(5) 색다른(6) 신호(7) 효과적으로(8) 전달을(9) 위해(10)." Good. That is 10 words. It's a bit awkward but okay. Or we can produce: "자연에서 성장 경쟁과 짝짓기 매력, 색다른 신호 효과적으로 전달을 위해." It's fine. The user wants "10단어 문장으로 짧게 답변하세요." So we must give a short answer with 10 words. The sentence above is 10 words. Should we add period? It's okay. Let's output that.
모델에 따라 추론에 투입해야 하는 노력 수준을 지정할 수 있는 경우가 있습니다. 마찬가지로 모델이 추론을 완전히 끄도록 요청할 수 있습니다. 이는 추론의 범주형 “tier”(예: 'low' 또는 'high') 또는 정수 토큰 예산(token budget) 형태를 취할 수 있습니다.
많은 chat model 제공업체는 주어진 기간 동안 수행할 수 있는 호출 수에 제한을 부과합니다. rate limit에 도달하면 일반적으로 제공업체로부터 rate limit error 응답을 받게 되며 더 많은 요청을 하기 전에 기다려야 합니다.
rate limit을 관리하는 데 도움이 되도록 chat model 통합은 초기화 중에 제공할 수 있는 rate_limiter 매개변수를 허용하여 요청이 이루어지는 속도를 제어합니다.
속도 제한기 초기화 및 사용
LangChain에는 (선택 사항) 기본 제공 InMemoryRateLimiter가 함께 제공됩니다. 이 제한기는 thread-safe하며 동일한 프로세스의 여러 스레드에서 공유할 수 있습니다.
from langchain_core.rate_limiters import InMemoryRateLimiterrate_limiter = InMemoryRateLimiter( requests_per_second=0.1, # 10초마다 1개의 요청 check_every_n_seconds=0.1, # 100ms마다 요청 허용 여부 확인 max_bucket_size=10, # 최대 버스트 크기 제어)model = init_chat_model( model="gpt-5", model_provider="openai", rate_limiter=rate_limiter,)
⚠️ 경고: 제공된 rate limiter(속도 제한기)는 단위 시간당 요청 수만 제한할 수 있습니다. 요청 크기를 기반으로 제한해야 하는 경우에는 도움이 되지 않습니다.
기본 URL 또는 프록시(Base URL or proxy)
많은 chat model 통합의 경우 API 요청에 대한 base URL을 구성할 수 있으므로 OpenAI 호환 API가 있는 모델 제공업체를 사용하거나 proxy server를 사용할 수 있습니다.
Base URL
많은 모델 제공업체가 OpenAI 호환 API(예: Together AI, vLLM)를 제공합니다. 적절한 base_url 매개변수를 지정하여 이러한 제공업체와 함께 init_chat_model을 사용할 수 있습니다:
model = init_chat_model( model="MODEL_NAME", model_provider="openai", base_url="BASE_URL", api_key="YOUR_API_KEY",)
⚠️ 참고: 직접 chat model 클래스 인스턴스화를 사용하는 경우 매개변수 이름은 제공업체에 따라 다를 수 있습니다. 자세한 내용은 해당 reference를 확인하세요.
프록시 구성
HTTP 프록시가 필요한 배포의 경우 일부 모델 통합이 프록시 구성을 지원합니다:
from langchain_openai import ChatOpenAImodel = ChatOpenAI( model="gpt-4o", openai_proxy="http://proxy.example.com:8080")
⚠️ 참고: 프록시 지원은 통합에 따라 다릅니다. 프록시 구성 옵션은 특정 모델 제공업체의 reference를 확인하세요.
Log probabilities
특정 모델은 모델을 초기화할 때 logprobs 매개변수를 설정하여 주어진 token의 가능성을 나타내는 token-level log probability를 반환하도록 구성할 수 있습니다:
model = init_chat_model(model="gpt-4o", model_provider="openai").bind(logprobs=True)response = model.invoke("앵무새는 왜 말을 할까요?")print(response.response_metadata["logprobs"])
많은 모델 제공업체가 호출 응답의 일부로 token usage 정보를 반환합니다. 사용 가능한 경우 이 정보는 해당 모델에서 생성한 AIMessage 객체에 포함됩니다. 자세한 내용은 message 가이드를 참조하세요.
⚠️ 참고: OpenAI 및 Azure OpenAI chat completion를 포함한 일부 제공업체 API는 사용자가 스트리밍 컨텍스트에서 token usage data를 수신하도록 선택해야 합니다. 자세한 내용은 통합 가이드의 streaming usage metadata 섹션을 참조하세요.
아래와 같이 callback 또는 context manager를 사용하여 애플리케이션의 모델 전체에서 집계 token count를 추적할 수 있습니다:
모델을 호출할 때 RunnableConfig 딕셔너리를 사용하여 config 매개변수를 통해 추가 구성을 전달할 수 있습니다. 이는 실행 동작, callback 및 metadata tracking에 대한 런타임 제어를 제공합니다.
일반적인 구성 옵션은 다음과 같습니다:
from langchain_core.callbacks import UsageMetadataCallbackHandlermy_callback_handler = UsageMetadataCallbackHandler()response = model.invoke( "재미있는 농담 하나 해줘", config={ "run_name": "joke_generation", # 이 실행에 대한 사용자 정의 이름 "tags": ["humor", "demo"], # 분류를 위한 태그 "metadata": {"user_id": "123"}, # 사용자 정의 메타데이터 "callbacks": [my_callback_handler], # 콜백 핸들러 },)response
configurable_fields를 지정하여 런타임 구성 가능한 모델을 만들 수도 있습니다. 모델 값을 지정하지 않으면 기본적으로 'model' 및 'model_provider'를 구성할 수 있습니다.
from langchain.chat_models import init_chat_modelconfigurable_model = init_chat_model(temperature=0)configurable_model.invoke( "당신의 이름은 무엇인가요", config={"configurable": {"model": "gpt-5-nano"}}, # GPT-5-Nano로 실행)configurable_model.invoke( "당신의 이름은 무엇인가요", config={"configurable": {"model": "claude-sonnet-4-5"}}, # Claude로 실행)
기본값이 있는 구성 가능한 모델
기본 모델 값으로 구성 가능한 모델을 만들고 구성 가능한 매개변수를 지정하고 구성 가능한 매개변수에 접두사를 추가할 수 있습니다:
first_model = init_chat_model( model="gpt-4.1-mini", temperature=0, configurable_fields=("model", "model_provider", "temperature", "max_tokens"), config_prefix="first", # 여러 모델이 있는 체인에서 유용함)first_model.invoke("당신의 이름은 무엇인가요?")