Skip to main content

Open UI + Langgraph Runbook (Langgraph)

create_deep_agent​

기존의 단순 ReAct 에이전트보다 복잡하고 심층적인 분석을 수행하는 에이전트를 쉽게 구축할 수 있는 구조를 제공한다.

  • Deep reasoning 지원(복잡한 문제 해결을 위한 계획 수립, 도구 호출, 평가 루프 등)
  • 서브 에이전트 위임, 파일/컨텍스트 관리 기능 내장
PROMPT_PATH = Path(__file__).with_name("system-prompt.txt")

model = ChatOpenAI(
model=os.environ.get("OPENAI_MODEL", "gpt-4.1-mini"),
streaming=True,
use_responses_api=False,
)
graph = create_deep_agent(
model=model,
tools=[get_weather, get_stock_price, search_web],
system_prompt=PROMPT_PATH.read_text(encoding="utf-8")
+ "\n"
+ (
"You are an OpenUI assistant with weather, finance, and research tools. "
"Use the tools when they help answer the user's request, then answer only in OpenUI Lang. "
"Weather, stock and search tools return demo data; do not present it as live data."
),
)

# AG-UI 변환 레이어
def stream_transformers():
"""LangGraph API creates a fresh transformer per run (protocol v2)."""
return [OpenUIStreamTransformer]

Middleware​

LangGraph 구조에 훅이 추가되었다.

  • before agent, after agent: 그래프 실행 전후에 미들웨어로 개입할 수 있다.
  • before/after llm, tool도 가능하다.

Deep Agent를 사용하면 기본적으로 아래 미들웨어가 제공된다.

deepagent_middleware.extend([
#비동기 하위 에이전트가 끝나면 자신을 실행한 부모 에이전트에게 결과·실패를 알림
_ParentNotifierMiddleware(),
# 대화가 길어지면 이전 내용을 요약해 모델 입력 크기를 관리
create_summarization_middleware(model, backend),
# 도구 호출은 있는데 결과가 빠져 있는지 검사해서 누락된 도구 호출 이력을 보충 (tool다시 실행을 하지는 않는다.)
PatchToolCallsMiddleware()
])

@tool 데코레이터​

에이전트가 사용할 수 있는 도구를 정의하는 방법이다.

@tool
def get_weather(city: str) -> str:
"""Get the current weather for a city."""
...

모델에게는 대략 다음 정보가 전달됩니다.

{
"name": "get_weather", // 함수 이름
"description": "Get the current weather for a city.", // 하위 주석
"parameters": { // 넘겨야 하는 인자
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"]
}
}

AG-UI OpenUIStreamTransformer​

목적: 모델·도구 실행 이벤트를 OpenUI 프런트엔드가 이해하는 AG-UI 이벤트로 변환한다.

  • OpenUIStreamTransformer는 LangGraph의 StreamTransformer를 상속한다.
  • 단, langgraph dev로 실행한 langgraph-api 서버에서 사용하는 경우다.

이벤트 규약​

실행 중 발생한 내용AG-UI 이벤트화면에서 가능한 처리
모델 응답 시작TEXT_MESSAGE_START새 답변 영역 생성
텍스트 조각TEXT_MESSAGE_CONTENT답변을 실시간으로 이어 붙이기
모델 응답 종료TEXT_MESSAGE_END해당 메시지 출력 완료 처리
도구 호출 시작TOOL_CALL_START호출할 도구 표시
도구 인자 조각TOOL_CALL_ARGS호출 인자 누적
도구 호출 정보 전송 종료TOOL_CALL_END인자 수신 완료 처리
도구 실행 결과TOOL_CALL_RESULT결과 표시 및 실행 상태 갱신
  • TOOL_CALL_END는 도구 실행 완료를 의미하지 않는다.
  • TOOL_CALL_START -> TOOL_CALL_ARGS -> TOOL_CALL_END -> TOOL_CALL_RESULT 순서다.

Langgraph 출력 결과물 w/Open lang​

root = Stack([card])
card = Card([header, price, change, details], "card", "column", "m", "start", "center")
header = TextContent("Apple Stock (AAPL)", "large-heavy")
price = TextContent("$189.32", "large-heavy")
change = TextContent("-0.52 (-0.27%)", "default")
details = EntityList([
{left: "Volume", right: "45.1M"},
{left: "Day High", right: "$191.86"},
{left: "Day Low", right: "$188.12"}
], "default")
{
"values": {
"messages": [
{
"content": [
{
"type": "text",
"text": "What's the current Apple stock price?\n"
}
],
"additional_kwargs": {},
"response_metadata": {},
"type": "human",
"name": null,
"id": "01a16e43-b3f3-769c-af68-a05594329ebd"
},
{
"content": "",
"additional_kwargs": {
"invalid_tool_calls": [],
"usage_metadata": {
"input_tokens": 11323,
"output_tokens": 16,
"total_tokens": 11339,
"input_token_details": {
"audio": 0,
"cache_read": 11264
},
"output_token_details": {
"audio": 0,
"reasoning": 0
}
}
},
"response_metadata": {
"finish_reason": "tool_calls",
"model_name": "gpt-4.1-mini-2025-04-14",
"system_fingerprint": "fp_98a8cc63cd",
"service_tier": "default",
"model_provider": "openai"
},
"type": "ai",
"name": null,
"id": "lc_run--01a16e43-b7d1-7f13-a6eb-0354be053d77",
"tool_calls": [
{
"name": "get_stock_price",
"args": {
"symbol": "AAPL"
},
"id": "call_3kV5vbHcpu6NB6d58d4LBSBx",
"type": "tool_call"
}
],
"invalid_tool_calls": [],
"usage_metadata": null
},
{
"content": "{\"symbol\": \"AAPL\", \"price\": 189.32, \"change\": -0.52, \"change_percent\": -0.27, \"volume\": \"45.1M\", \"day_high\": 191.86, \"day_low\": 188.12}",
"additional_kwargs": {},
"response_metadata": {},
"type": "tool",
"name": "get_stock_price",
"id": "feb2e6ea-524b-4fb7-a418-c5f63219651b",
"tool_call_id": "call_3kV5vbHcpu6NB6d58d4LBSBx",
"artifact": null,
"status": "success"
},
{
"content": "root = Stack([card])\ncard = Card([header, price, change, details], \"card\", \"column\", \"m\", \"start\", \"center\")\nheader = TextContent(\"Apple Stock (AAPL)\", \"large-heavy\")\nprice = TextContent(\"$189.32\", \"large-heavy\")\nchange = TextContent(\"-0.52 (-0.27%)\", \"default\")\ndetails = EntityList([\n {left: \"Volume\", right: \"45.1M\"},\n {left: \"Day High\", right: \"$191.86\"},\n {left: \"Day Low\", right: \"$188.12\"}\n], \"default\")",
"additional_kwargs": {
"invalid_tool_calls": [],
"usage_metadata": {
"input_tokens": 11407,
"output_tokens": 133,
"total_tokens": 11540,
"input_token_details": {
"audio": 0,
"cache_read": 11264
},
"output_token_details": {
"audio": 0,
"reasoning": 0
}
}
},
"response_metadata": {
"finish_reason": "stop",
"model_name": "gpt-4.1-mini-2025-04-14",
"system_fingerprint": "fp_98a8cc63cd",
"service_tier": "default",
"model_provider": "openai"
},
"type": "ai",
"name": null,
"id": "lc_run--01a16e43-bc67-7651-a319-a3794165e3dc",
"tool_calls": [],
"invalid_tool_calls": [],
"usage_metadata": null
}
],
"files": {}
},
}

한계점 및 운영 시 고려 사항​

  1. OpenUI 응답을 일반 텍스트로 요약하면 구조 정보가 손실될 위험이 있다.

  2. Autofix: 생성 완료 후 유효하지 않은 OpenUI Lang 코드를 단 한 번의 API 호출로 자동으로 수정한다(Repair). 기존 모델 공급자, 추론 환경, 에이전트 프레임워크를 그대로 유지한 채 적용 가능하다.

  3. 신뢰성 모니터링 (Reliability Monitoring): 생성된 인터페이스의 실패율, 반복되는 에러 유형, 실패 원인이 된 생성 내역을 추적·분석한다. Gateway/Autofix 사용 시 자동 활성화되며, 자체 추론 환경에서는 Observability SDK를 통해 연동할 수 있다.

  1. 플리커링 이슈 및 validator failover 이슈(오픈소스의 프리미엄 영역은 모니터링 부분이다.)

  2. LangGraph JS 예제에는 LLM이 만든 OpenUI Lang을 서버에서 검증하고, 오류를 모델에 돌려보내 수정시키는 단계는 없다. (Gateway에서 검증·보정을 맡는 구조로, 유료 서비스 이용이 필요하다.)

  • 모델이 잘못된 UI 크기를 출력한 경우에도 검증이 가능한지는 모르겠다. width, height가 명시된 경우 validator를 통과한다. 그러나 높이와 너비가 적절하지 않으면 화면이 깨질 수 있다.
  1. 렌더링 성능을 측정할 필요가 있다.