sip 를 통한 agent worker에서 session.on("close") 이벤트가 발...
# ask-ai
l
sip 를 통한 agent worker에서 session.on("close") 이벤트가 발생하지 않은 케이스가 있는데, 이건 왜 그런거야?
t
Thanks for reaching out, Dan(송희웅). I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
요청하신 “SIP로 동작하는 agent worker에서
session.on('close')
이벤트가 발생하지 않는” 문제에 대해 답변드릴게요 — 왜 그럴 수 있는지와 빠르게 확인할 수 있는 항목들을 안내합니다. 먼저 가능한 원인 (주요 포인트) • 네트워크 단절이나 클라이언트의 비정상 종료로 인해 정상적인 종료 시퀀스가 실행되지 않으면 `close`가 발생하지 않습니다. • 에이전트의 종료 훅(예:
session.shutdown()
또는 유사 호출)이 호출되지 않거나 실패한 경우
close
이벤트가 방출되지 않습니다. • 워커 프로세스가 충돌(crash)하거나 강제 종료되면 정리 루틴이 실행되지 않아 이벤트가 생략될 수 있습니다. • 세션/룸에서 발생하는
disconnected
같은 다른 이벤트 처리 로직에 문제가 있으면 기대하던
close
흐름이 차단될 수 있습니다. 빠른 점검 항목 (순서대로 확인 권장) 1. 로그 확인: 에이전트 종료 시점 전후의 에러/예외 로그, SIP 종료 시그널(BYE 등) 수신 기록을 확인하세요. 2. 종료 호출 확인: 에이전트 코드에서 정상 종료 시 `session.shutdown()`(또는 사용 중인 API의 종료 메서드)을 명시적으로 호출하는지 확인하세요. 3. 프로세스 종료 방식: 워커가 SIGINT/SIGTERM 등 정상 시그널로 종료되는지, 아니면 OOM/Crash로 죽는지 확인하세요. 4. Shutdown 콜백 등록:
ctx.add_shutdown_callback()
같은 종료 콜백으로 세션 정리 로직을 강제 실행하도록 구성했는지 확인하세요. 5. 대체 이벤트 감지:
disconnected
등 다른 룸/세션 이벤트를 수신해 비정상 종료를 감지하고 별도 정리 로직을 실행하는지 확인하세요. 추가로 도와드리려면 아래 정보 중 일부를 알려주세요 • 에이전트 종료/세션 정리 관련 코드(종료 부분) 스니펫 • 워커가 종료될 때 보이는 로그(오류 포함)나 SIP 신호(예: BYE 수신 여부) • 워커가 실행되는 환경(컨테이너인지, 프로세스 관리 도구 사용 여부) • 문제가 발생한 재현 조건(항상 발생하는지, 간헐적인지) 원하시면 제공하신 코드/로그를 기반으로 더 구체적인 원인 진단과 수정 예시(종료 훅 추가/로그 포인트 등)를 바로 제안해드릴게요. Sources: Connecting to LiveKit | LiveKit docs | Job lifecycle | LiveKit docs | Collect recording consent with tasks | LiveKit docs | Company directory phone assistant | LiveKit docs
l
@room.on("participant_disconnected") 이벤트 까지는 발생을 했고 여기에서 job_ctx.shutdown(reason="Session ended") 을 하고 있다. 그런데 @session.on("close") 이벤트는 발생이 안되고 있는데, 이 문제를 어떻게 해결할수 있지?
t
좋아요 — 말씀하신 상황( `@room.on("participant_disconnected")`에서 `job_ctx.shutdown(reason="Session ended")`를 호출했지만 `@session.on("close")`가 발생하지 않음)을 기준으로 원인별 진단과 해결책을 정리해드릴게요. 몇 가지 확인·수정 항목과 추가 질문을 드립니다. 가능한 원인 및 확인 방법 • shutdown이 비동기 처리되는 동안 워커가 즉시 강제 종료되어 정리 루틴이 실행되지 않았을 수 있습니다. ◦ 확인:
job_ctx.shutdown()
직후 프로세스가 즉시 exit 되는지 로그로 확인하세요. • `job_ctx.shutdown()`이 실제로 세션의 정리(예:
session.shutdown()
호출)를 트리거하지 않거나 예외로 실패했을 수 있습니다. ◦ 확인:
job_ctx.shutdown()
내부에서 세션 종료 호출이 발생하는지, 예외가 발생했는지 로그/스택트레이스로 확인하세요. •
session.on("close")
리스너가 등록되기 전에 세션이 닫혀 이벤트를 놓쳤을 수 있습니다. ◦ 확인: 리스너 등록 시점이 세션 생성 직후인지, 또는 비동기 흐름에서 늦게 등록되는지 코드 흐름을 검토하세요. • 워커가 크래시(uncaught exception, OOM)로 종료되어 정상적인 종료 훅이 실행되지 않았을 수 있습니다. ◦ 확인: 프로세스 종료 코드, 핵심 덤프, 컨테이너 런타임 로그 확인. 해결 권장 조치 (우선순위 순) 1. shutdown 흐름을 동기적으로 보장 ◦
job_ctx.shutdown()
이후 세션 정리 완료를 기다리는 코드를 추가하거나, shutdown 콜백에서
await session.shutdown()
같은 명시적 호출을 하세요. ◦ 종료 시 예외가 있으면 재시도/로그를 남기도록 하세요. 1. 종료 콜백 강제 등록 ◦ `ctx.add_shutdown_callback()`로 강제 정리 로직(세션 종료 호출 및 이벤트 대기)을 등록해 정상 종료 경로가 항상 실행되게 하세요. 1. `session.on("close")`를 포괄적으로 감지하도록 보완 ◦ `room.on("disconnected")`나 다른 세션/connection 상태 이벤트를 같이 수신해, `close`가 발생하지 않을 때 대체 정리 로직을 실행하도록 하세요. 1. 리스너 등록 시점 고정 ◦ 세션 생성 직후
session.on("close")
리스너를 등록해 이벤트 누락을 방지하세요. 1. 로그와 모니터링 강화 ◦ shutdown 진입/종료,
session.shutdown()
호출,
close
이벤트 수신을 남기는 상세 로그를 추가하세요. 문제 재현 시 로그로 흐름을 추적하면 원인 파악이 빨라집니다. 추가로 필요합니다 — 몇 가지 확인 질문 •
job_ctx.shutdown()
호출 직후 워커 프로세스가 바로 종료되나요, 아니면 정상적으로 몇 초 대기하나요? •
job_ctx.shutdown()
내부에
session.shutdown()
또는 동등한 세션 정리 호출이 있나요? 가능하면 해당 종료 부분 코드(몇 줄)를 공유해 주세요. • 워커가 컨테이너 안에서 돌아가나요(예: Kubernetes), 또는 단일 프로세스로 실행하나요? • 로그에서
session.shutdown()
호출 시점과 `session.on("close")`를 기다리는 로그가 어떻게 찍히는지 샘플 로그가 있으면 도움이 됩니다. 원하시면 코드 스니펫(종료 로직)과 관련 로그를 붙여 주시면, 그걸 기반으로 구체적으로 어떤 위치에 `await`나 shutdown 콜백을 넣을지, 예시 코드 수정까지 제안해드릴게요.
l
{"message": "unhandled exception while running the job task\nTraceback (most recent call last):\n File \"/Users/heewungsong/Desktop/Wise-Ai/Outbound-Agent-Project/outbound-agent/.venv/lib/python3.12/site-packages/opentelemetry/util/_decorator.py\", line 71, in async_wrapper\n return await func(*args, **kwargs) # type: ignore\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/Users/heewungsong/Desktop/Wise-Ai/Outbound-Agent-Project/outbound-agent/.venv/lib/python3.12/site-packages/livekit/agents/ipc/job_proc_lazy_main.py\", line 286, in _traceable_entrypoint\n await self._job_entrypoint_fnc(job_ctx)\n File \"/Users/heewungsong/Desktop/Wise-Ai/Outbound-Agent-Project/outbound-agent/src/agent_server.py\", line 317, in entrypoint\n speech_handle = session.say(\n ^^^^^^^^^^^^\n File \"/Users/heewungsong/Desktop/Wise-Ai/Outbound-Agent-Project/outbound-agent/.venv/lib/python3.12/site-packages/livekit/agents/voice/agent_session.py\", line 881, in say\n raise RuntimeError(\"AgentSession isn't running\")\nRuntimeError: AgentSession isn't running", "level": "ERROR", "name": "livekit.agents", "exc_info": "Traceback (most recent call last):\n File \"/Users/heewungsong/Desktop/Wise-Ai/Outbound-Agent-Project/outbound-agent/.venv/lib/python3.12/site-packages/opentelemetry/util/_decorator.py\", line 71, in async_wrapper\n return await func(*args, **kwargs) # type: ignore\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/Users/heewungsong/Desktop/Wise-Ai/Outbound-Agent-Project/outbound-agent/.venv/lib/python3.12/site-packages/livekit/agents/ipc/job_proc_lazy_main.py\", line 286, in _traceable_entrypoint\n await self._job_entrypoint_fnc(job_ctx)\n File \"/Users/heewungsong/Desktop/Wise-Ai/Outbound-Agent-Project/outbound-agent/src/agent_server.py\", line 317, in entrypoint\n speech_handle = session.say(\n ^^^^^^^^^^^^\n File \"/Users/heewungsong/Desktop/Wise-Ai/Outbound-Agent-Project/outbound-agent/.venv/lib/python3.12/site-packages/livekit/agents/voice/agent_session.py\", line 881, in say\n raise RuntimeError(\"AgentSession isn't running\")\nRuntimeError: AgentSession isn't running", "pid": 23735, "job_id": "AJ_ft7RvKBf5mPw", "room_id": "RM_3SYtsfgJ94nE", "timestamp": "2025-12-23T063438.032769+00:00" session 끝난상태에서 session.say 를 실행해서 나온 에러같은데, session 이 running 상태인지 판단하는 방법이 있어?
t
좋습니다 — 주신 에러 로그와 상황(세션이 끝난 뒤
session.say()
호출로
RuntimeError: AgentSession isn't running
)에 맞춰 바로 적용 가능한 해결책과 안전한 체크 패턴을 정리해드릴게요. 또한, 추가로 확인해주시면 좋은 정보도 요청합니다. 요점(무엇에 답하는지): 세션이 running 상태인지 확인하는 방법과,
session.say()
호출 시점에서 발생하는 예외를 방지하는 안전한 패턴입니다. 해결책 요약 • 세션 상태 확인(호출 전) ◦ 안전한 간단 체크:
Copy code
if getattr(session, "_state", None) != "closed":
        speech_handle = session.say("...")
    else:
        logger.warning("Cannot speak: session is not running")
• 또는 현재 재생 중인지 확인:
Copy code
if session.current_speech and not session.current_speech.done():
        # 현재 재생 중
• shutdown/종료 시퀀스 보장 a.
participant_disconnected
핸들러에서
job_ctx.shutdown()
호출 후 즉시
session.say()
같은 동작을 하면 안 됩니다. 항상 현재 재생이 끝나는 것을 기다리세요:
Copy code
if session.current_speech:
         await session.current_speech.wait_for_playout()
     session.shutdown(reason="Session ended")
1.
job_ctx.shutdown()
내부가 세션 정리를 트리거하지 않는다면, 명시적으로
await session.shutdown()
또는
session.shutdown()
호출을 하세요. 2. 종료 콜백 등록: ◦ `ctx.add_shutdown_callback()`로 세션 정리(및
await
필요 작업)를 보장하세요. • 이벤트 누락 방지 ◦
session.on("close")
리스너는 세션 생성 직후 등록하세요. 리스너 등록이 늦으면 이벤트를 놓칠 수 있습니다. ◦
participant_disconnected
/ SIP 상태 변경 핸들러에서 바로 종료 플래그를 세우고(예:
session._shutdown_scheduled = True
) 실제 종료는 현재 재생이 끝난 뒤 수행하는 패턴을 권장합니다. • 로그/예외 처리 강화 ◦
session.say()
호출 부근에 예외 처리 추가:
Copy code
try:
        speech_handle = session.say("...")
    except RuntimeError:
        logger.warning("Tried to say after session ended")
•
job_ctx.shutdown()
진입/완료,
session.shutdown()
호출,
session.on("close")
수신 시점에 상세 로그를 추가하세요. 추가로 알려주시면 더 구체적으로 도와드릴 수 있습니다 •
participant_disconnected
핸들러의 관련 코드(특히
job_ctx.shutdown()
직전/직후 부분) •
job_ctx.shutdown()
내부에서 세션 종료를 어떻게 처리하는지(코드나 설명) • 워커 환경(컨테이너/K8s 또는 로컬 프로세스) 및 프로세스 종료 로그(있다면) •
session.on("close")
리스너 등록 위치의 코드 스니펫 원하시면 제공해주신 코드에 맞춰 안전한
await
위치, shutdown 콜백 예시, 그리고
session.say()
호출 전 체크용 헬퍼 함수를 바로 만들어 드릴게요. Sources: Agent session | LiveKit docs