ChatOpenAI 통합

ChatOpenAI 통합

LangChain JavaScript로 ChatOpenAI 채팅 모델과 통합하는 방법을 안내할게요.

출처: 문서

본문

OpenAI는 인공지능(AI) 연구소예요.

이 가이드는 OpenAI 채팅 모델을 시작하는 데 도움을 줘요. 모든 ChatOpenAI 기능과 구성에 대한 자세한 문서는 API 레퍼런스를 참고하세요.

**Chat Completions API compatibility**

ChatOpenAI is fully compatible with OpenAI's (legacy) Chat Completions API. If you are looking to connect to other model providers that support the Chat Completions API, you can do so – see instructions.

**OpenAI models hosted on Azure**

Note that certain OpenAI models can also be accessed via the Microsoft Azure platform.

개요

통합 세부 정보

클래스 패키지 Serializable PY 지원 Downloads Version
ChatOpenAI @langchain/openai NPM - Downloads NPM - Version

모델 기능

아래 표 헤더의 링크에서 특정 기능을 사용하는 방법에 대한 가이드를 확인할 수 있어요.

Tool calling Structured output Image input Audio input Video input Token-level streaming Token usage Logprobs

설정

OpenAI 채팅 모델에 접근하려면 OpenAI 계정을 만들고 API 키를 받은 뒤 @langchain/openai 통합 패키지를 설치해야 해요.

자격 증명

OpenAI 웹사이트에서 OpenAI에 가입하고 API 키를 생성하세요. 완료되면 OPENAI_API_KEY 환경 변수를 설정하세요:

export OPENAI_API_KEY="your-api-key"

모델 호출의 자동 추적(tracing)을 원한다면 아래 주석을 해제해 LangSmith API 키를 설정할 수도 있어요:

# export LANGSMITH_TRACING="true"
# export LANGSMITH_API_KEY="your-api-key"

설치

LangChain ChatOpenAI 통합은 @langchain/openai 패키지에 있어요:

```bash npm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}} npm install @langchain/openai @langchain/core ```
yarn add @langchain/openai @langchain/core
pnpm add @langchain/openai @langchain/core

인스턴스 생성

이제 모델 객체를 생성하고 채팅 완성을 생성할 수 있어요:

import { ChatOpenAI } from "@langchain/openai"

const llm = new ChatOpenAI({
  model: "gpt-5.5",
  temperature: 0,
  // other params...
})

호출

const aiMsg = await llm.invoke([
  {
    role: "system",
    content: "You are a helpful assistant that translates English to French. Translate the user sentence.",
  },
  {
    role: "user",
    content: "I love programming."
  },
])
aiMsg
AIMessage {
  "id": "chatcmpl-ADItECqSPuuEuBHHPjeCkh9wIO1H5",
  "content": "J'adore la programmation.",
  "additional_kwargs": {},
  "response_metadata": {
    "tokenUsage": {
      "completionTokens": 5,
      "promptTokens": 31,
      "totalTokens": 36
    },
    "finish_reason": "stop",
    "system_fingerprint": "fp_5796ac6771"
  },
  "tool_calls": [],
  "invalid_tool_calls": [],
  "usage_metadata": {
    "input_tokens": 31,
    "output_tokens": 5,
    "total_tokens": 36
  }
}
console.log(aiMsg.content)
J'adore la programmation.

커스텀 URL

다음과 같이 configuration 매개변수를 전달해 SDK가 요청을 보내는 기본 URL을 사용자 지정할 수 있어요:

import { ChatOpenAI } from "@langchain/openai";

const llmWithCustomURL = new ChatOpenAI({
  model: "gpt-5.5",
  temperature: 0.9,
  configuration: {
    baseURL: "https://your_custom_url.com",
  },
});

await llmWithCustomURL.invoke("Hi there!");

configuration 필드는 공식 SDK가 받아들이는 다른 ClientOptions 매개변수도 받아들여요.

Azure OpenAI에서 호스팅한다면 전용 페이지를 대신 참고하세요.

커스텀 헤더

동일한 configuration 필드에서 커스텀 헤더를 지정할 수 있어요:

import { ChatOpenAI } from "@langchain/openai";

const llmWithCustomHeaders = new ChatOpenAI({
  model: "gpt-5.5",
  temperature: 0.9,
  configuration: {
    defaultHeaders: {
      "Authorization": `Bearer SOME_CUSTOM_VALUE`,
    },
  },
});

await llmWithCustomHeaders.invoke("Hi there!");

스트리밍 사용량 메타데이터 비활성화

일부 프록시나 제3자 제공자는 OpenAI와 거의 동일한 API 인터페이스를 제공하지만, 스트리밍 사용량을 반환하기 위해 최근 추가된 stream_options 매개변수를 지원하지 않아요. 다음과 같이 스트리밍 사용량을 비활성화해 ChatOpenAI로 이러한 제공자에 접근할 수 있어요:

import { ChatOpenAI } from "@langchain/openai";

const llmWithoutStreamUsage = new ChatOpenAI({
  model: "gpt-5.5",
  temperature: 0.9,
  streamUsage: false,
  configuration: {
    baseURL: "https://proxy.com",
  },
});

await llmWithoutStreamUsage.invoke("Hi there!");

파인튜닝 모델 호출

해당하는 modelName 매개변수를 전달해 파인튜닝된 OpenAI 모델을 호출할 수 있어요.

이것은 일반적으로 ft:{OPENAI_MODEL_NAME}:{ORG_NAME}::{MODEL_ID} 형식을 취해요. 예를 들어:

import { ChatOpenAI } from "@langchain/openai";

const fineTunedLlm = new ChatOpenAI({
  temperature: 0.9,
  model: "ft:gpt-3.5-turbo-0613:{ORG_NAME}::{MODEL_ID}",
});

await fineTunedLlm.invoke("Hi there!");

생성 메타데이터

logprobs나 토큰 사용량 같은 추가 정보가 필요하다면 메시지의 response_metadata 필드 내 invoke 응답에서 직접 반환돼요.

**Requires `@langchain/core>=0.1.48`.**
import { ChatOpenAI } from "@langchain/openai";

// See https://cookbook.openai.com/examples/using_logprobs for details
const llmWithLogprobs = new ChatOpenAI({
  model: "gpt-5.5",
  logprobs: true,
  // topLogprobs: 5,
});

const responseMessageWithLogprobs = await llmWithLogprobs.invoke("Hi there!");
console.dir(responseMessageWithLogprobs.response_metadata.logprobs, { depth: null });
{
  content: [
    {
      token: 'Hello',
      logprob: -0.0004740447,
      bytes: [ 72, 101, 108, 108, 111 ],
      top_logprobs: []
    },
    {
      token: '!',
      logprob: -0.00004334534,
      bytes: [ 33 ],
      top_logprobs: []
    },
    {
      token: ' How',
      logprob: -0.000030113732,
      bytes: [ 32, 72, 111, 119 ],
      top_logprobs: []
    },
    {
      token: ' can',
      logprob: -0.0004797665,
      bytes: [ 32, 99, 97, 110 ],
      top_logprobs: []
    },
    {
      token: ' I',
      logprob: -7.89631e-7,
      bytes: [ 32, 73 ],
      top_logprobs: []
    },
    {
      token: ' assist',
      logprob: -0.114006,
      bytes: [
         32,  97, 115,
        115, 105, 115,
        116
      ],
      top_logprobs: []
    },
    {
      token: ' you',
      logprob: -4.3202e-7,
      bytes: [ 32, 121, 111, 117 ],
      top_logprobs: []
    },
    {
      token: ' today',
      logprob: -0.00004501419,
      bytes: [ 32, 116, 111, 100, 97, 121 ],
      top_logprobs: []
    },
    {
      token: '?',
      logprob: -0.000010206721,
      bytes: [ 63 ],
      top_logprobs: []
    }
  ],
  refusal: null
}

커스텀 도구

커스텀 도구는 임의의 문자열 입력을 가진 도구를 지원해요. 특히 문자열 인자가 길거나 복잡할 것으로 예상될 때 유용할 수 있어요.

커스텀 도구를 지원하는 모델을 사용한다면 ChatOpenAI 클래스와 customTool 함수로 커스텀 도구를 만들 수 있어요.

import { ChatOpenAI, customTool } from "@langchain/openai";
import { createAgent, HumanMessage } from "langchain";

const codeTool = customTool(
  async () => {
    // ... Add code to execute the input
    return "Code executed successfully";
  },
  {
    name: "execute_code",
    description: "Execute a code snippet",
    format: { type: "text" },
  }
);

const model = new ChatOpenAI({ model: "gpt-5.5" });

const agent = createAgent({
  model,
  tools: [codeTool],
});

const result = await agent.invoke({
  messages: [new HumanMessage("Use the tool to execute the code")],
});
console.log(result);
Context-free grammars

OpenAI supports the specification of a context-free grammar for custom tool inputs in lark or regex format. See OpenAI docs for details. The format parameter can be passed into customTool as shown below:

import { ChatOpenAI, customTool } from "@langchain/openai";
import { createAgent, HumanMessage } from "langchain";

const MATH_GRAMMAR = `
start: expr
expr: term (SP ADD SP term)* -> add
| term
term: factor (SP MUL SP factor)* -> mul
| factor
factor: INT
SP: \" \"
ADD: \"+\"
MUL: \"*\"
%import common.INT
`;

const doMath = customTool(
  async () => {
    // ... Add code to parse and execute the input
    return "27";
  },
  {
    name: "do_math",
    description: "Evaluate a math expression",
    format: { type: "grammar", definition: MATH_GRAMMAR, syntax: "lark" },
  }
);

const model = new ChatOpenAI({ model: "gpt-5.5" });

const agent = createAgent({
  model,
  tools: [doMath],
});

const result = await agent.invoke({
  messages: [new HumanMessage("Use the tool to calculate 3^3")],
});
console.log(result);

strict: true

2024년 8월 6일 기준으로, OpenAI는 도구 호출 시 모델이 도구 인자 스키마를 준수하도록 강제하는 strict 인자를 지원해요. 더 보기.

Requires `@langchain/openai >= 0.2.6` If `strict: true` the tool definition will also be validated, and a subset of JSON schema are accepted. Crucially, schema cannot have optional args (those with default values). Read [the full docs](https://platform.openai.com/docs/guides/structured-outputs) on what types of schema are supported.

tool calling이 포함된 예시가 있어요. .bindTools에 추가 strict: true 인자를 전달하면 매개변수가 모든 도구 정의에 전달돼요:

import { ChatOpenAI } from "@langchain/openai";
import { tool } from "@langchain/core/tools";
import * as z from "zod";

const weatherTool = tool((_) => "no-op", {
  name: "get_current_weather",
  description: "Get the current weather",
  schema: z.object({
    location: z.string(),
  }),
})

const llmWithStrictTrue = new ChatOpenAI({
  model: "gpt-5.5",
}).bindTools([weatherTool], {
  strict: true,
  tool_choice: weatherTool.name,
});

// Although the question is not about the weather, it will call the tool with the correct arguments
// because we passed `tool_choice` and `strict: true`.
const strictTrueResult = await llmWithStrictTrue.invoke("What is 127862 times 12898 divided by 2?");

console.dir(strictTrueResult.tool_calls, { depth: null });
[
  {
    name: 'get_current_weather',
    args: { location: 'current' },
    type: 'tool_call',
    id: 'call_hVFyYNRwc6CoTgr9AQFQVjm9'
  }
]

이 매개변수를 선택된 도구에만 적용하려면 OpenAI 형식 도구 스키마를 직접 전달할 수도 있어요:

import { zodToJsonSchema } from "zod-to-json-schema";

const toolSchema = {
  type: "function",
  function: {
    name: "get_current_weather",
    description: "Get the current weather",
    strict: true,
    parameters: zodToJsonSchema(
      z.object({
        location: z.string(),
      })
    ),
  },
};

const llmWithStrictTrueTools = new ChatOpenAI({
  model: "gpt-5.5",
}).bindTools([toolSchema], {
  strict: true,
});

const weatherToolResult = await llmWithStrictTrueTools.invoke([{
  role: "user",
  content: "What is the current weather in London?"
}])

weatherToolResult.tool_calls;
[
  {
    name: 'get_current_weather',
    args: { location: 'London' },
    type: 'tool_call',
    id: 'call_EOSejtax8aYtqpchY8n8O82l'
  }
]

구조화된 출력

.withStructuredOutput()strict: true를 전달할 수도 있어요. 예시는 다음과 같아요:

import { ChatOpenAI } from "@langchain/openai";

const traitSchema = z.object({
  traits: z.array(z.string()).describe("A list of traits contained in the input"),
});

const structuredLlm = new ChatOpenAI({
  model: "gpt-5.4-mini",
}).withStructuredOutput(traitSchema, {
  name: "extract_traits",
  strict: true,
});

await structuredLlm.invoke([{
  role: "user",
  content: `I am 6'5" tall and love fruit.`
}]);
{ traits: [ `6'5" tall`, 'love fruit' ] }

Responses API

**Compatibility**

The below points apply to @langchain/openai>=0.4.5-rc.0.

OpenAI는 에이전틱 애플리케이션 구축을 지향하는 Responses API를 지원해요. 여기에는 웹 및 파일 검색을 포함한 내장 도구 제품군이 포함돼요. 또한 대화 상태 관리를 지원해 이전 메시지를 명시적으로 전달하지 않고도 대화 스레드를 계속할 수 있어요.

ChatOpenAI는 이 기능 중 하나가 사용되면 Responses API로 라우팅해요. ChatOpenAI를 인스턴스화할 때 useResponsesApi: true를 지정할 수도 있어요.

내장 도구

ChatOpenAI에 내장 도구를 장착하면 파일 컨텍스트나 웹 같은 외부 정보로 응답을 기반(grounding)시켜요. 모델에서 생성된 AIMessage는 내장 도구 호출에 대한 정보를 포함해요.

웹 검색

웹 검색을 트리거하려면 다른 도구처럼 {"type": "web_search_preview"}를 모델에 전달하세요.

**You can also pass built-in tools as invocation params:**
llm.invoke("...", { tools: [{ type: "web_search_preview" }] });
import { ChatOpenAI } from "@langchain/openai";

const llm = new ChatOpenAI({ model: "gpt-5.4-mini" }).bindTools([
  { type: "web_search_preview" },
]);

await llm.invoke("What was a positive news story from today?");

응답은 응답 텍스트와 소스를 인용하는 OpenAI annotations를 모두 포함하는 구조화된 콘텐츠 블록을 포함한다는 점에 유의하세요. 출력 메시지에는 도구 호출의 정보도 포함돼요.

파일 검색

파일 검색을 트리거하려면 다른 도구처럼 파일 검색 도구를 모델에 전달하세요. OpenAI 관리형 벡터 저장소를 채우고 도구 정의에 벡터 저장소 ID를 포함해야 해요. 자세한 내용은 OpenAI 문서를 참고하세요.

import { ChatOpenAI } from "@langchain/openai";

const llm = new ChatOpenAI({ model: "gpt-5.4-mini" }).bindTools([
  { type: "file_search", vector_store_ids: ["vs..."] },
]);

await llm.invoke("Is deep research by OpenAI?");

웹 검색과 마찬가지로 응답에는 인용이 포함된 콘텐츠 블록이 포함되고, 내장 도구 호출의 정보도 포함돼요.

컴퓨터 사용

ChatOpenAI는 내장 컴퓨터 사용 도구를 위한 특화 모델인 computer-use-preview 모델을 지원해요. 활성화하려면 다른 도구처럼 컴퓨터 사용 도구를 전달하세요.

현재 컴퓨터 사용 도구 출력은 AIMessage.additional_kwargs.tool_outputs에 있어요. 컴퓨터 사용 도구 호출에 응답하려면 해당하는 ToolMessage를 만들 때 additional_kwargs.type: "computer_call_output"을 설정해야 해요.

자세한 내용은 OpenAI 문서를 참고하세요.

import { AIMessage, ToolMessage } from "@langchain/core/messages";
import { ChatOpenAI } from "@langchain/openai";
import * as fs from "node:fs/promises";

const findComputerCall = (message: AIMessage) => {
  const toolOutputs = message.additional_kwargs.tool_outputs as
    | { type: "computer_call"; call_id: string; action: { type: string } }[]
    | undefined;

  return toolOutputs?.find((toolOutput) => toolOutput.type === "computer_call");
};

const llm = new ChatOpenAI({ model: "computer-use-preview" })
  .bindTools([
    {
      type: "computer-preview",
      display_width: 1024,
      display_height: 768,
      environment: "browser",
    },
  ])
  .bind({ truncation: "auto" });

let message = await llm.invoke("Check the latest OpenAI news on bing.com.");
const computerCall = findComputerCall(message);

if (computerCall) {
  // Act on a computer call action
  const screenshot = await fs.readFile("./screenshot.png", {
    encoding: "base64",
  });

  message = await llm.invoke(
    [
      new ToolMessage({
        additional_kwargs: { type: "computer_call_output" },
        tool_call_id: computerCall.call_id,
        content: [
          {
            type: "computer_screenshot",
            image_url: `data:image/png;base64,${screenshot}`,
          },
        ],
      }),
    ],
    { previous_response_id: message.response_metadata["id"] }
  );
}

코드 인터프리터

ChatOpenAI는 내장 코드 인터프리터 도구를 사용해 코드의 샌드박스 생성과 실행을 지원할 수 있게 해줘요.

import { ChatOpenAI } from "@langchain/openai";

const llm = new ChatOpenAI({
  model: "o4-mini",
  useResponsesApi: true,
});

const llmWithTools = llm.bindToools([
  {
    type: "code_interpreter",
    // Creates a new container
    container: { type: "auto" }
  },
]);

const response = await llmWithTools.invoke(
  "Write and run code to answer the question: what is 3^3?"
);

위 명령은 새 컨테이너를 만든다는 점에 유의하세요. 기존 컨테이너 ID를 지정해 호출 간 컨테이너를 재사용할 수 있어요.

const tool_outputs: Record<string, any>[] = response.additional_kwargs.tool_outputs
const container_id = tool_outputs[0].container_id

const llmWithTools = llm.bindTools([
  {
    type: "code_interpreter",
    // Reuses container from the last call
    container: container_id,
  },
]);

원격 MCP

ChatOpenAI는 내장 원격 MCP 도구를 지원하며, 모델이 생성한 MCP 서버 호출이 OpenAI 서버에서 일어나도록 해줘요.

import { ChatOpenAI } from "@langchain/openai";

const llm = new ChatOpenAI({
    model: "o4-mini",
    useResponsesApi: true,
});

const llmWithMcp = llm.bindTools([
    {
        type: "mcp",
        server_label: "deepwiki",
        server_url: "https://mcp.deepwiki.com/mcp",
        require_approval: "never"
    }
]);

const response = await llmWithMcp.invoke(
    "What transport protocols does the 2025-03-26 version of the MCP spec (modelcontextprotocol/modelcontextprotocol) support?"
);
**MCP Approvals**

When instructed, OpenAI will request approval before making calls to a remote MCP server.

In the above command, we instructed the model to never require approval. We can also configure the model to always request approval, or to always request approval for specific tools:

...
const llmWithMcp = llm.bindTools([
  {
    type: "mcp",
    server_label: "deepwiki",
    server_url: "https://mcp.deepwiki.com/mcp",
    require_approval: {
      always: {
        tool_names: ["read_wiki_structure"],
      },
    },
  },
]);
const response = await llmWithMcp.invoke(
    "What transport protocols does the 2025-03-26 version of the MCP spec (modelcontextprotocol/modelcontextprotocol) support?"
);

With this configuration, responses can contain tool outputs typed as mcp_approval_request. To submit approvals for an approval request, you can structure it into a content block in a followup message:

const approvals = [];
if (Array.isArray(response.additional_kwargs.tool_outputs)) {
  for (const content of response.additional_kwargs.tool_outputs) {
    if (content.type === "mcp_approval_request") {
      approvals.push({
        type: "mcp_approval_response",
        approval_request_id: content.id,
        approve: true,
      });
    }
  }
}

const nextResponse = await model.invoke(
  [
    response,
    new HumanMessage({ content: approvals }),
  ],
);

이미지 생성

ChatOpenAI는 내장 이미지 생성 도구를 통해 responses API를 사용한 다중 턴 대화의 일부로 이미지를 생성할 수 있게 해줘요.

import { ChatOpenAI } from "@langchain/openai";

const llm = new ChatOpenAI({
  model: "gpt-5.5",
  useResponsesApi: true,
});

const llmWithImageGeneration = llm.bindTools([
  {
    type: "image_generation",
    quality: "low",
  }
]);

const response = await llmWithImageGeneration.invoke(
  "Draw a random short word in green font."
)

추론 모델

**Compatibility**: The below points apply to `@langchain/openai>=0.4.0`.

o1 같은 추론 모델을 사용할 때 withStructuredOutput의 기본 메서드는 OpenAI의 내장 구조화 출력 메서드예요(withStructuredOutputmethod: "jsonSchema" 옵션을 전달하는 것과 동일). JSON 스키마는 다른 모델과 거의 동일하게 작동하지만 한 가지 중요한 주의사항이 있어요: 스키마를 정의할 때 z.optional()은 존중되지 않으므로 대신 z.nullable()을 사용해야 해요.

예시:

import * as z from "zod";
import { ChatOpenAI } from "@langchain/openai";

// Will not work
const reasoningModelSchemaOptional = z.object({
  color: z.optional(z.string()).describe("A color mentioned in the input"),
});

const reasoningModelOptionalSchema = new ChatOpenAI({
  model: "o1",
}).withStructuredOutput(reasoningModelSchemaOptional, {
  name: "extract_color",
});

await reasoningModelOptionalSchema.invoke([{
  role: "user",
  content: `I am 6'5" tall and love fruit.`
}]);
{ color: 'No color mentioned' }

그리고 z.nullable()을 사용한 예시:

import * as z from "zod";
import { ChatOpenAI } from "@langchain/openai";

// Will not work
const reasoningModelSchemaNullable = z.object({
  color: z.nullable(z.string()).describe("A color mentioned in the input"),
});

const reasoningModelNullableSchema = new ChatOpenAI({
  model: "o1",
}).withStructuredOutput(reasoningModelSchemaNullable, {
  name: "extract_color",
});

await reasoningModelNullableSchema.invoke([{
  role: "user",
  content: `I am 6'5" tall and love fruit.`
}]);
{ color: null }

프롬프트 캐싱

최신 OpenAI 모델은 입력이 특정 크기(작성 시점 기준 1024 토큰)를 넘어서면 프롬프트 일부를 자동으로 캐시하여 긴 컨텍스트가 필요한 사용 사례의 비용을 줄여요.

참고: 특정 쿼리에 캐시된 토큰 수는 AIMessage.usage_metadata에 아직 표준화되지 않았으며 대신 AIMessage.response_metadata 필드에 포함돼요.

예시는 다음과 같아요

// @lc-docs-hide-cell

const CACHED_TEXT = `## Components

LangChain provides standard, extendable interfaces and external integrations for various components useful for building with LLMs.
Some components LangChain implements, some components we rely on third-party integrations for, and others are a mix.

### Chat models

<span data-heading-keywords="chat model,chat models"></span>

Language models that use a sequence of messages as inputs and return chat messages as outputs (as opposed to using plain text).
These are generally newer models (older models are generally \`LLMs\`, see below).
Chat models support the assignment of distinct roles to conversation messages, helping to distinguish messages from the AI, users, and instructions such as system messages.

Although the underlying models are messages in, message out, the LangChain wrappers also allow these models to take a string as input.
This gives them the same interface as LLMs (and simpler to use).
When a string is passed in as input, it will be converted to a \`HumanMessage\` under the hood before being passed to the underlying model.

LangChain does not host any Chat Models, rather we rely on third party integrations.

We have some standardized parameters when constructing ChatModels:

- \`model\`: the name of the model

Chat Models also accept other parameters that are specific to that integration.

<Warning>
**Some chat models have been fine-tuned for **tool calling** and provide a dedicated API for it.**

Generally, such models are better at tool calling than non-fine-tuned models, and are recommended for use cases that require tool calling.
Please see the [tool calling section](/oss/javascript/langchain/tools) for more information.
</Warning>

For specifics on how to use chat models, see the [relevant how-to guides here](/oss/javascript/langchain/models).

#### Multimodality

Some chat models are multimodal, accepting images, audio and even video as inputs.
These are still less common, meaning model providers haven't standardized on the "best" way to define the API.
Multimodal outputs are even less common. As such, we've kept our multimodal abstractions fairly light weight
and plan to further solidify the multimodal APIs and interaction patterns as the field matures.

In LangChain, most chat models that support multimodal inputs also accept those values in OpenAI's content blocks format.
So far this is restricted to image inputs. For models like Gemini which support video and other bytes input, the APIs also support the native, model-specific representations.

For specifics on how to use multimodal models, see the [relevant how-to guides here](/oss/javascript/how-to/#multimodal).

### LLMs

<span data-heading-keywords="llm,llms"></span>

<Warning>
**Pure text-in/text-out LLMs tend to be older or lower-level. Many popular models are best used as [chat completion models](/oss/javascript/langchain/models),**

even for non-chat use cases.
</Warning>

Language models that takes a string as input and returns a string.
These are traditionally older models (newer models generally are [Chat Models](/oss/javascript/langchain/models), see above).

Although the underlying models are string in, string out, the LangChain wrappers also allow these models to take messages as input.
This gives them the same interface as [Chat Models](/oss/javascript/langchain/models).
When messages are passed in as input, they will be formatted into a string under the hood before being passed to the underlying model.

LangChain does not host any LLMs, rather we rely on third party integrations.

For specifics on how to use LLMs, see the [relevant how-to guides here](/oss/javascript/langchain/models).

### Message types

Some language models take an array of messages as input and return a message.
There are a few different types of messages.
All messages have a \`role\`, \`content\`, and \`response_metadata\` property.

The \`role\` describes WHO is saying the message.
LangChain has different message classes for different roles.

The \`content\` property describes the content of the message.
This can be a few different things:

- A string (most models deal this type of content)
- A List of objects (this is used for multi-modal input, where the object contains information about that input type and that input location)

#### HumanMessage

This represents a message from the user.

#### AIMessage

This represents a message from the model. In addition to the \`content\` property, these messages also have:

**\`response_metadata\`**

The \`response_metadata\` property contains additional metadata about the response. The data here is often specific to each model provider.
This is where information like log-probs and token usage may be stored.

**\`tool_calls\`**

These represent a decision from a language model to call a tool. They are included as part of an \`AIMessage\` output.
They can be accessed from there with the \`.tool_calls\` property.

This property returns a list of \`ToolCall\`s. A \`ToolCall\` is an object with the following arguments:

- \`name\`: The name of the tool that should be called.
- \`args\`: The arguments to that tool.
- \`id\`: The id of that tool call.

#### SystemMessage

This represents a system message, which tells the model how to behave. Not every model provider supports this.

#### ToolMessage

This represents the result of a tool call. In addition to \`role\` and \`content\`, this message has:

- a \`tool_call_id\` field which conveys the id of the call to the tool that was called to produce this result.
- an \`artifact\` field which can be used to pass along arbitrary artifacts of the tool execution which are useful to track but which should not be sent to the model.

#### (Legacy) FunctionMessage

This is a legacy message type, corresponding to OpenAI's legacy function-calling API. \`ToolMessage\` should be used instead to correspond to the updated tool-calling API.

This represents the result of a function call. In addition to \`role\` and \`content\`, this message has a \`name\` parameter which conveys the name of the function that was called to produce this result.

### Prompt templates

<span data-heading-keywords="prompt,prompttemplate,chatprompttemplate"></span>

Prompt templates help to translate user input and parameters into instructions for a language model.
This can be used to guide a model's response, helping it understand the context and generate relevant and coherent language-based output.

Prompt Templates take as input an object, where each key represents a variable in the prompt template to fill in.

Prompt Templates output a PromptValue. This PromptValue can be passed to an LLM or a ChatModel, and can also be cast to a string or an array of messages.
The reason this PromptValue exists is to make it easy to switch between strings and messages.

There are a few different types of prompt templates:

#### String PromptTemplates

These prompt templates are used to format a single string, and generally are used for simpler inputs.
For example, a common way to construct and use a PromptTemplate is as follows:

\`\`\`typescript
import { PromptTemplate } from "@langchain/core/prompts";

const promptTemplate = PromptTemplate.fromTemplate(
  "Tell me a joke about {topic}"
);

await promptTemplate.invoke({ topic: "cats" });
\`\`\`

#### ChatPromptTemplates

These prompt templates are used to format an array of messages. These "templates" consist of an array of templates themselves.
For example, a common way to construct and use a ChatPromptTemplate is as follows:

\`\`\`typescript
import { ChatPromptTemplate } from "@langchain/core/prompts";

const promptTemplate = ChatPromptTemplate.fromMessages([
  ["system", "You are a helpful assistant"],
  ["user", "Tell me a joke about {topic}"],
]);

await promptTemplate.invoke({ topic: "cats" });
\`\`\`

In the above example, this ChatPromptTemplate will construct two messages when called.
The first is a system message, that has no variables to format.
The second is a HumanMessage, and will be formatted by the \`topic\` variable the user passes in.

#### MessagesPlaceholder

<span data-heading-keywords="messagesplaceholder"></span>

This prompt template is responsible for adding an array of messages in a particular place.
In the above ChatPromptTemplate, we saw how we could format two messages, each one a string.
But what if we wanted the user to pass in an array of messages that we would slot into a particular spot?
This is how you use MessagesPlaceholder.

\`\`\`typescript
import {
  ChatPromptTemplate,
  MessagesPlaceholder,
} from "@langchain/core/prompts";
import { HumanMessage } from "@langchain/core/messages";

const promptTemplate = ChatPromptTemplate.fromMessages([
  ["system", "You are a helpful assistant"],
  new MessagesPlaceholder("msgs"),
]);

promptTemplate.invoke({ msgs: [new HumanMessage({ content: "hi!" })] });
\`\`\`

This will produce an array of two messages, the first one being a system message, and the second one being the HumanMessage we passed in.
If we had passed in 5 messages, then it would have produced 6 messages in total (the system message plus the 5 passed in).
This is useful for letting an array of messages be slotted into a particular spot.

An alternative way to accomplish the same thing without using the \`MessagesPlaceholder\` class explicitly is:

\`\`\`typescript
const promptTemplate = ChatPromptTemplate.fromMessages([
  ["system", "You are a helpful assistant"],
  ["placeholder", "{msgs}"], // <-- This is the changed part
]);
\`\`\`

For specifics on how to use prompt templates, see the [relevant how-to guides here](/oss/javascript/how-to/#prompt-templates).

### Example Selectors

One common prompting technique for achieving better performance is to include examples as part of the prompt.
This gives the language model concrete examples of how it should behave.
Sometimes these examples are hardcoded into the prompt, but for more advanced situations it may be nice to dynamically select them.
Example Selectors are classes responsible for selecting and then formatting examples into prompts.

For specifics on how to use example selectors, see the [relevant how-to guides here](/oss/javascript/how-to/#example-selectors).

### Output parsers

<span data-heading-keywords="output parser"></span>

<Note>
**The information here refers to parsers that take a text output from a model try to parse it into a more structured representation.**

More and more models are supporting function (or tool) calling, which handles this automatically.
It is recommended to use function/tool calling rather than output parsing.
See the [LangChain tools documentation](/oss/javascript/langchain/tools).

</Note>

Responsible for taking the output of a model and transforming it to a more suitable format for downstream tasks.
Useful when you are using LLMs to generate structured data, or to normalize output from chat models and LLMs.

There are two main methods an output parser must implement:

- "Get format instructions": A method which returns a string containing instructions for how the output of a language model should be formatted.
- "Parse": A method which takes in a string (assumed to be the response from a language model) and parses it into some structure.

And then one optional one:

- "Parse with prompt": A method which takes in a string (assumed to be the response from a language model) and a prompt (assumed to be the prompt that generated such a response) and parses it into some structure. The prompt is largely provided in the event the OutputParser wants to retry or fix the output in some way, and needs information from the prompt to do so.

Output parsers accept a string or \`BaseMessage\` as input and can return an arbitrary type.

LangChain has many different types of output parsers. This is a list of output parsers LangChain supports. The table below has various pieces of information:

**Name**: The name of the output parser

**Supports Streaming**: Whether the output parser supports streaming.

**Input Type**: Expected input type. Most output parsers work on both strings and messages, but some (like OpenAI Functions) need a message with specific arguments.

**Output Type**: The output type of the object returned by the parser.

**Description**: Our commentary on this output parser and when to use it.

The current date is ${new Date().toISOString()}`;

// Noop statement to hide output
void 0;
import { ChatOpenAI } from "@langchain/openai";

const modelWithCaching = new ChatOpenAI({
  model: "gpt-5.4-mini",
});

// CACHED_TEXT is some string longer than 1024 tokens
const LONG_TEXT = `You are a pirate. Always respond in pirate dialect.

Use the following as context when answering questions:

${CACHED_TEXT}`;

const longMessages = [
  {
    role: "system",
    content: LONG_TEXT,
  },
  {
    role: "user",
    content: "What types of messages are supported in LangChain?",
  },
];

const originalRes = await modelWithCaching.invoke(longMessages);

console.log("USAGE:", originalRes.response_metadata.usage);
USAGE: {
  prompt_tokens: 2624,
  completion_tokens: 263,
  total_tokens: 2887,
  prompt_tokens_details: { cached_tokens: 0 },
  completion_tokens_details: { reasoning_tokens: 0 }
}
const resWitCaching = await modelWithCaching.invoke(longMessages);

console.log("USAGE:", resWitCaching.response_metadata.usage);
USAGE: {
  prompt_tokens: 2624,
  completion_tokens: 272,
  total_tokens: 2896,
  prompt_tokens_details: { cached_tokens: 2432 },
  completion_tokens_details: { reasoning_tokens: 0 }
}

예측 출력 (Predicted output)

일부 OpenAI 모델(예: gpt-4ogpt-4o-mini 시리즈)은 예측 출력(Predicted Outputs)을 지원하며, LLM의 예상 출력 중 알려진 부분을 미리 전달해 지연 시간을 줄일 수 있게 해줘요. 텍스트 편집이나 코드 편집처럼 모델 출력의 작은 부분만 변경되는 경우에 유용해요.

예시:

import { ChatOpenAI } from "@langchain/openai";

const modelWithPredictions = new ChatOpenAI({
  model: "gpt-5.4-mini",
});

const codeSample = `
/// <summary>
/// Represents a user with a first name, last name, and username.
/// </summary>
public class User
{
/// <summary>
/// Gets or sets the user's first name.
/// </summary>
public string FirstName { get; set; }

/// <summary>
/// Gets or sets the user's last name.
/// </summary>
public string LastName { get; set; }

/// <summary>
/// Gets or sets the user's username.
/// </summary>
public string Username { get; set; }
}
`;

// Can also be attached ahead of time
// using `model.bind({ prediction: {...} })`;
await modelWithPredictions.invoke(
  [
    {
      role: "user",
      content:
        "Replace the Username property with an Email property. Respond only with code, and with no markdown formatting.",
    },
    {
      role: "user",
      content: codeSample,
    },
  ],
  {
    prediction: {
      type: "content",
      content: codeSample,
    },
  }
);
AIMessage {
  "id": "chatcmpl-AQLyQKnazr7lEV7ejLTo1UqhzHDBl",
  "content": "/// <summary>\n/// Represents a user with a first name, last name, and email.\n/// </summary>\npublic class User\n{\n/// <summary>\n/// Gets or sets the user's first name.\n/// </summary>\npublic string FirstName { get; set; }\n\n/// <summary>\n/// Gets or sets the user's last name.\n/// </summary>\npublic string LastName { get; set; }\n\n/// <summary>\n/// Gets or sets the user's email.\n/// </summary>\npublic string Email { get; set; }\n}",
  "additional_kwargs": {},
  "response_metadata": {
    "tokenUsage": {
      "promptTokens": 148,
      "completionTokens": 217,
      "totalTokens": 365
    },
    "finish_reason": "stop",
    "usage": {
      "prompt_tokens": 148,
      "completion_tokens": 217,
      "total_tokens": 365,
      "prompt_tokens_details": {
        "cached_tokens": 0
      },
      "completion_tokens_details": {
        "reasoning_tokens": 0,
        "accepted_prediction_tokens": 36,
        "rejected_prediction_tokens": 116
      }
    },
    "system_fingerprint": "fp_0ba0d124f1"
  },
  "tool_calls": [],
  "invalid_tool_calls": [],
  "usage_metadata": {
    "output_tokens": 217,
    "input_tokens": 148,
    "total_tokens": 365,
    "input_token_details": {
      "cache_read": 0
    },
    "output_token_details": {
      "reasoning": 0
    }
  }
}

현재 예측은 추가 토큰으로 청구되며 이 감소된 지연 시간에 대한 대가로 사용량과 비용이 증가한다는 점에 유의하세요.

오디오 출력

일부 OpenAI 모델(예: gpt-4o-audio-preview)은 오디오 출력 생성을 지원해요. 이 예제는 그 기능을 사용하는 방법을 보여줘요:

import { ChatOpenAI } from "@langchain/openai";

const modelWithAudioOutput = new ChatOpenAI({
  model: "gpt-4o-audio-preview",
  // You may also pass these fields to `.bind` as a call argument.
  modalities: ["text", "audio"], // Specifies that the model should output audio.
  audio: {
    voice: "alloy",
    format: "wav",
  },
});

const audioOutputResult = await modelWithAudioOutput.invoke("Tell me a joke about cats.");
const castAudioContent = audioOutputResult.additional_kwargs.audio as Record<string, any>;

console.log({
  ...castAudioContent,
  data: castAudioContent.data.slice(0, 100) // Sliced for brevity
})
{
  id: 'audio_67129e9466f48190be70372922464162',
  data: 'UklGRgZ4BABXQVZFZm10IBAAAAABAAEAwF0AAIC7AAACABAATElTVBoAAABJTkZPSVNGVA4AAABMYXZmNTguMjkuMTAwAGRhdGHA',
  expires_at: 1729277092,
  transcript: "Why did the cat sit on the computer's keyboard? Because it wanted to keep an eye on the mouse!"
}

오디오 데이터가 data 필드에 반환되는 것을 볼 수 있어요. 또한 expires_at 날짜 필드가 제공되는데, 이 필드는 서버에서 오디오 응답을 더 이상 다중 턴 대화에서 사용할 수 없게 되는 날짜를 나타내요.

스트리밍 오디오 출력

OpenAI는 스트리밍 오디오 출력도 지원해요. 예시:

import { AIMessageChunk } from "@langchain/core/messages";
import { concat } from "@langchain/core/utils/stream"
import { ChatOpenAI } from "@langchain/openai";

const modelWithStreamingAudioOutput = new ChatOpenAI({
  model: "gpt-4o-audio-preview",
  modalities: ["text", "audio"],
  audio: {
    voice: "alloy",
    format: "pcm16", // Format must be `pcm16` for streaming
  },
});

const audioOutputStream = await modelWithStreamingAudioOutput.stream("Tell me a joke about cats.");
let finalAudioOutputMsg: AIMessageChunk | undefined;
for await (const chunk of audioOutputStream) {
  finalAudioOutputMsg = finalAudioOutputMsg ? concat(finalAudioOutputMsg, chunk) : chunk;
}
const castStreamedAudioContent = finalAudioOutputMsg?.additional_kwargs.audio as Record<string, any>;

console.log({
  ...castStreamedAudioContent,
  data: castStreamedAudioContent.data.slice(0, 100) // Sliced for brevity
})
{
  id: 'audio_67129e976ce081908103ba4947399a3eaudio_67129e976ce081908103ba4947399a3e',
  transcript: 'Why was the cat sitting on the computer? Because it wanted to keep an eye on the mouse!',
  index: 0,
  data: 'CgAGAAIADAAAAA0AAwAJAAcACQAJAAQABQABAAgABQAPAAAACAADAAUAAwD8/wUA+f8MAPv/CAD7/wUA///8/wUA/f8DAPj/AgD6',
  expires_at: 1729277096
}

오디오 입력

이러한 모델은 입력으로 오디오를 전달하는 것도 지원해요. 이를 위해 아래와 같이 input_audio 필드를 지정해야 해요:

import { HumanMessage } from "@langchain/core/messages";

const userInput = new HumanMessage({
  content: [{
    type: "input_audio",
    input_audio: {
      data: castAudioContent.data, // Reuse the base64 data from the first example
      format: "wav",
    },
  }]
})

// Reuse the same model instance
const userInputAudioRes = await modelWithAudioOutput.invoke([userInput]);

console.log((userInputAudioRes.additional_kwargs.audio as Record<string, any>).transcript);
That's a great joke! It's always fun to imagine why cats do the funny things they do. Keeping an eye on the "mouse" is a creatively punny way to describe it!

API 레퍼런스

모든 ChatOpenAI 기능과 구성에 대한 자세한 문서는 API 레퍼런스를 참고하세요.


[Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers. [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/oss/javascript/integrations/chat/openai.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).

더 알아보기