맞춤 사용자 인터페이스를 구현하고 API를 사용하여 대화 메시지를 보내려는 경우 API 액세스 배포 옵션을 사용할 수 있습니다.
런타임에 각 대화 차례에 대해 runSession 메서드를 사용하여 사용자 메시지를 보내고 에이전트 애플리케이션 응답을 받습니다.
API 액세스 채널 만들기
API 액세스 채널을 만들려면 다음 단계를 따르세요.
- 에이전트 빌더 상단에서 배포를 클릭합니다.
- 새 채널을 클릭합니다.
- API 액세스 설정을 선택합니다.
- 채널 이름을 입력합니다.
- 에이전트 애플리케이션 버전을 선택하거나 만듭니다.
- 이 특정 채널의 특정 에이전트 애플리케이션 설정을 재정의하려면 선택사항인 채널별 동작 설정을 사용하세요.
- 채널 만들기를 클릭합니다.
배포 ID와 샘플 curl 명령어를 보여주는 대화상자 창이 열립니다.
Linux 및 macOS 시스템에서는 이 샘플 명령어를 사용하여 에이전트 애플리케이션에 테스트 메시지를 보낼 수 있습니다.
인증
제공된 curl 샘플 명령어는 gcloud를 사용하여 사용자 인증 정보로 요청을 인증합니다.
프로덕션 시스템의 경우 인증에 서비스 계정을 사용해야 합니다.
자세한 내용은 CX Agent Studio 인증을 참고하세요.
필수 ID
요청을 보내려면 PROJECT_ID, REGION_ID, APPLICATION_ID, DEPLOYMENT_ID 정적 ID가 필요합니다.
이 ID는 채널의 대화상자 창 명령 샘플에 제공된 배포 ID 필드에서 확인할 수 있습니다.
값은 이름이 지정된 경로 세그먼트에 정의됩니다.
projects/PROJECT_ID/locations/REGION_ID/apps/APPLICATION_ID/deployments/DEPLOYMENT_ID
또한 고유한 대화를 나타내는 동적 SESSION_ID가 필요합니다.
ID가 정규 표현식 [a-zA-Z0-9][a-zA-Z0-9-_]{4,62}와 일치하는 생성된 문자열을 선택할 수 있습니다.
runSession에 전화 걸기
다음 샘플은 세션에서 에이전트 애플리케이션과의 단일 턴 상호작용을 시작하는 데 사용되는 runSession 메서드를 호출합니다.
REST
요청 데이터를 사용하기 전에 다음을 바꿉니다.
- PROJECT_ID: 프로젝트 ID입니다.
- REGION_ID: 리전 ID
- APPLICATION_ID: 애플리케이션 ID
- SESSION_ID: 세션 ID
- DEPLOYMENT_ID: 애플리케이션 배포 ID
HTTP 메서드 및 URL:
POST https://ces.googleapis.com/v1/projects/PROJECT_ID/locations/REGION_ID/apps/APPLICATION_ID/sessions/SESSION_ID:runSession
JSON 요청 본문:
{
"config": {
"session": "projects/PROJECT_ID/locations/REGION_ID/apps/APPLICATION_ID/sessions/SESSION_ID",
"deployment": "projects/PROJECT_ID/locations/REGION_ID/apps/APPLICATION_ID/deployments/DEPLOYMENT_ID",
},
"inputs": [
{
"text": "hi"
}
]
}
요청을 보내려면 다음 옵션 중 하나를 펼칩니다.
다음과 비슷한 JSON 응답이 표시됩니다.
{
"outputs": [
{
"text": "Hello there!",
"turnCompleted": true,
"turnIndex": 1,
"diagnosticInfo": {...}
}
]
}
Java
CX Agent Studio에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.
/* * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.cloud.ces.v1.samples; import com.google.cloud.ces.v1.RunSessionRequest; import com.google.cloud.ces.v1.RunSessionResponse; import com.google.cloud.ces.v1.SessionConfig; import com.google.cloud.ces.v1.SessionInput; import com.google.cloud.ces.v1.SessionServiceClient; import java.util.ArrayList; public class SyncRunSession { public static void main(String[] args) throws Exception { syncRunSession(); } public static void syncRunSession() throws Exception { // This may require specifying regional endpoints when creating the service client as shown in // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library try (SessionServiceClient sessionServiceClient = SessionServiceClient.create()) { RunSessionRequest request = RunSessionRequest.newBuilder() .setConfig(SessionConfig.newBuilder().build()) .addAllInputs(new ArrayList<SessionInput>()) .build(); RunSessionResponse response = sessionServiceClient.runSession(request); } } }
Python
CX Agent Studio에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.
from google.cloud import ces_v1 from google.protobuf import field_mask_pb2 def async_create_app(): with ces_v1.AgentServiceClient() as agent_service_client: list_apps_request = ces_v1.ListAppsRequest( parent="projects/project-name/locations/us" ) try: list_apps_page = agent_service_client.list_apps(request=list_apps_request) app_name = next(iter(list_apps_page)).name except StopIteration: print("No apps found.") return print(f"Using appName {app_name} to grab App") get_app_request = ces_v1.GetAppRequest(name=app_name) app = agent_service_client.get_app(request=get_app_request) print(f"Using app to either createAgent / updateApp {app}") # If there is no agent, please uncomment this part # # Create Agent # create_agent_request = ces_v1.CreateAgentRequest( # parent=app_name, # agent=ces_v1.Agent(display_name="agent-1"), # ) # agent_response = agent_service_client.create_agent(request=create_agent_request) # List Agents list_agents_request = ces_v1.ListAgentsRequest(parent=app_name) try: list_agents_page = agent_service_client.list_agents(request=list_agents_request) agent = next(iter(list_agents_page)) agent_name = agent.name except StopIteration: print(f"No agents found for app {app_name}.") return print(f"Using agent {agent_name} to update Agent") # Update App to have root agent app.root_agent = agent_name update_app_request = ces_v1.UpdateAppRequest( app=app, update_mask=field_mask_pb2.FieldMask(paths=["root_agent"]), ) update_app_response = agent_service_client.update_app(request=update_app_request) print(f"Update App response: {update_app_response}") # runSession with ces_v1.SessionServiceClient() as session_service_client: session_name = f"{app_name}/sessions/session-1" config = ces_v1.SessionConfig(session=session_name) input_ = ces_v1.SessionInput(text="Hello World!") inputs = [input_] session_request = ces_v1.RunSessionRequest(config=config, inputs=inputs) session_response = session_service_client.run_session(request=session_request) print(f"Run session response: {session_response}") if __name__ == "__main__": async_create_app()
Node.js
CX Agent Studio에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.
import {AgentServiceClient, SessionServiceClient} from '../src/v1'; async function main() { const agentServiceClient = new AgentServiceClient(); const sessionServiceClient = new SessionServiceClient(); try { const listAppsRequest = { parent: 'projects/project-name/locations/us', }; const listAppsIterator = agentServiceClient.listAppsAsync(listAppsRequest); let firstApp; for await (const app of listAppsIterator) { firstApp = app; break; } if (!firstApp) { console.log('No apps found.'); return; } const appName = firstApp.name!; console.log(`Using appName ${appName} to grab App`); const [app] = await agentServiceClient.getApp({name: appName}); console.log('Using app to either createAgent / updateApp', app); // If there is no agent, please uncomment this part // // Create Agent // const createAgentRequest = { // parent: appName, // agent: {displayName: 'agent-1'}, // }; // const [agentResponse] = // await agentServiceClient.createAgent(createAgentRequest); // List Agents const listAgentsRequest = {parent: appName}; const listAgentsIterator = agentServiceClient.listAgentsAsync(listAgentsRequest); let agent; for await (const a of listAgentsIterator) { agent = a; break; } if (!agent) { console.log(`No agents found for app ${appName}.`); return; } const agentName = agent.name!; console.log(`Using agent ${agentName} to update Agent`); // Update App to have root agent app.rootAgent = agentName; const updateAppRequest = { app: app, updateMask: { paths: ['root_agent'], }, }; const [updateAppResponse] = await agentServiceClient.updateApp(updateAppRequest); console.log('Update App response:', updateAppResponse); // runSession const sessionName = `${appName}/sessions/session-1`; const config = {session: sessionName}; const input = {text: 'Hello World!'}; const inputs = [input]; const sessionRequest = {config: config, inputs: inputs}; const [sessionResponse] = await sessionServiceClient.runSession(sessionRequest); console.log('Run session response:', sessionResponse); } catch (err) { console.error('An error occurred:', err); } finally { await agentServiceClient.close(); await sessionServiceClient.close(); } } main();
BidiRunSession에 전화 걸기
runSession 대신 BidiRunSession를 사용하여 에이전트 애플리케이션과의 양방향 스트리밍 연결을 설정할 수 있습니다.
Go
CX Agent Studio에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.
// A simple websocket client to interact with BidiRunSession with audio input // and output. // // Initialize your new module: // - mkdir mypackage // - cd mypackage // - go mod init mypackage // // Install non-standard dependencies: // - Install PortAudio C library using instructions at // https://files.portaudio.com/docs/v19-doxydocs/tutorial_start.html // - go get azul3d.org/engine // - go get github.com/gordonklaus/portaudio // - go get github.com/gorilla/websocket // // Usage // // go run main.go -access_token=$(gcloud auth print-access-token) \ // -location=YOUR_LOCATION \ // -app=YOUR_APP_NAME \ // -deployment=YOUR_DEPLOYMENT_NAME package main import ( "bytes" "crypto/rand" "encoding/base64" "encoding/binary" "encoding/hex" "encoding/json" "flag" "fmt" "log" "net/http" "net/url" "os" "os/signal" "strings" "sync" "azul3d.org/engine/audio" "github.com/gordonklaus/portaudio" "github.com/gorilla/websocket" ) var addr = flag.String("addr", "ces.googleapis.com", "API service address.") var location = flag.String("location", "us", "API service location.") var accessToken = flag.String("access_token", "", "Access token.") var app = flag.String("app", "", "App name in the form "+ "projects/PROJECT_ID/locations/REGION_ID/apps/APPLICATION_ID.") var deployment = flag.String("deployment", "", "Deployment name in the form "+ "projects/PROJECT_ID/locations/REGION_ID/apps/APPLICATION_ID/deployments/DEPLOYMENT_ID.") var audioSampleRate = flag.Int("audio_sample_rate", 16000, "Desired audio sample rate in Hz (e.g., 8000, 16000, 24000).") var ( audioBuffer bytes.Buffer bufferMutex sync.Mutex wg sync.WaitGroup ) type BidiSessionServerMessage struct { SessionOutput *SessionOutput `json:"sessionOutput"` RecognitionResult *RecognitionResult `json:"recognitionResult"` InterruptionSignal *InterruptionSignal `json:"interruptionSignal"` EndSession *EndSession `json:"endSession"` } type SessionOutput struct { Audio string `json:"audio"` Text string `json:"text"` } type RecognitionResult struct { Transcript string `json:"transcript"` } type InterruptionSignal struct { } type EndSession struct { Metadata struct{} `json:"metadata"` } const defaultSampleRateHertz = 16000 // 100ms const defaultChunkLength = 100 // 16000 (sample rate hertz) * 100/1000 (100ms) * 2 (16 bit) const defaultChunkSize = defaultSampleRateHertz * defaultChunkLength / 1000 * 2 func main() { flag.Parse() log.SetFlags((log.LstdFlags | log.Lshortfile | log.Lmicroseconds) &^ log.Ldate) u := url.URL{Scheme: "wss", Host: *addr, Path: "/ws/google.cloud.ces.v1.SessionService/BidiRunSession/locations/" + *location} if strings.Contains(*addr, "localhost") { u = url.URL{Scheme: "ws", Host: *addr, Path: "/ws"} } h := make(http.Header) h.Add("Content-Type", "application/json") h.Add("Authorization", "Bearer "+*accessToken) conn, _, err := websocket.DefaultDialer.Dial(u.String(), h) if err != nil { log.Fatal("dial:", err) } interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) portaudio.Initialize() defer portaudio.Terminate() inDev, err := portaudio.DefaultInputDevice() if err != nil { log.Fatal(err) } log.Printf("Input device: %v", inDev.Name) outDev, err := portaudio.DefaultOutputDevice() if err != nil { log.Fatal(err) } log.Printf("Output device: %v", outDev.Name) randomBytes := make([]byte, 8) if _, err := rand.Read(randomBytes); err != nil { log.Fatal(err) } session := fmt.Sprintf("%v/sessions/%v", *app, hex.EncodeToString(randomBytes)) log.Printf("Session: %v", session) configMessage := map[string]any{ "config": map[string]any{ "session": session, "inputAudioConfig": map[string]any{ "audioEncoding": "LINEAR16", "sampleRateHertz": *audioSampleRate, }, "outputAudioConfig": map[string]any{ "audioEncoding": "LINEAR16", "sampleRateHertz": *audioSampleRate, }, "deployment": *deployment, // "debugMode": true, }, } jsonConfigMsg, err := json.Marshal(configMessage) if err != nil { log.Fatal(err) } if err = conn.WriteMessage(websocket.TextMessage, []byte(jsonConfigMsg)); err != nil { log.Fatal(err) } log.Printf("Sent config: %s", string(jsonConfigMsg)) framesPerBuffer := defaultChunkSize / 2 stream, err := portaudio.OpenDefaultStream(1, 1, float64(*audioSampleRate), framesPerBuffer, func(in, out []float32) { // Lock the buffer to avoid data race bufferMutex.Lock() defer bufferMutex.Unlock() // Output processing (Server to Speaker) bytesPerSample := 2 bytesToRead := framesPerBuffer * bytesPerSample audioChunk := make([]byte, bytesToRead) nBytes, _ := audioBuffer.Read(audioChunk) if nBytes > 0 && audioBuffer.Len() == 0 { wg.Done() } audioChunk = audioChunk[:nBytes] // LINEAR16 audio encoding var speakerPcm16 []int16 numPcm16Samples := nBytes / 2 speakerPcm16 = make([]int16, numPcm16Samples) for i := 0; i < numPcm16Samples; i++ { if (i*2 + 2) <= len(audioChunk) { speakerPcm16[i] = int16( binary.LittleEndian.Uint16(audioChunk[i*2 : (i+1)*2])) } } for i := 0; i < framesPerBuffer; i++ { if i < len(speakerPcm16) { out[i] = float32(audio.Int16ToFloat64(speakerPcm16[i])) } else { out[i] = 0 } } // Input processing (Mic to Server) micPcm16 := make([]int16, len(in)) for i, f := range in { micPcm16[i] = audio.Float64ToInt16(float64(f)) } // LINEAR16 mic audio var inputBuf []byte buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, micPcm16) inputBuf = buf.Bytes() audioMessage := map[string]any{ "realtimeInput": map[string]any{ "audio": base64.StdEncoding.EncodeToString(inputBuf), }, } jsonAudioMsg, err := json.Marshal(audioMessage) if err != nil { log.Fatal(err) } if err = conn.WriteMessage(websocket.TextMessage, []byte(jsonAudioMsg)); err != nil { // log.Print(err) } }) if err != nil { log.Fatalf("Error opening stream: %v", err) } defer stream.Close() if err := stream.Start(); err != nil { log.Fatalf("Error starting stream: %v", err) } serverHalfClose := make(chan struct{}) go func() { // Signal completion when server half-close is received. defer close(serverHalfClose) for { _, message, err := conn.ReadMessage() if err != nil { log.Printf("websocket read error:%v", err) break } bidiSessionServerMessage := BidiSessionServerMessage{} json.Unmarshal(message, &bidiSessionServerMessage) if bidiSessionServerMessage.SessionOutput != nil && bidiSessionServerMessage.SessionOutput.Audio != "" { } else { log.Println(string(message)) } if bidiSessionServerMessage.InterruptionSignal != nil { bufferMutex.Lock() if audioBuffer.Len() != 0 { audioBuffer.Reset() wg.Done() } bufferMutex.Unlock() } if bidiSessionServerMessage.EndSession != nil { log.Printf("Received EndSession signal, closing the stream...") interrupt <- os.Interrupt break } if bidiSessionServerMessage.SessionOutput != nil && bidiSessionServerMessage.SessionOutput.Audio != "" { audioBytes, err := base64.StdEncoding.DecodeString( bidiSessionServerMessage.SessionOutput.Audio) if err != nil { log.Fatal(err) } bufferMutex.Lock() if audioBuffer.Len() == 0 { wg.Add(1) } audioBuffer.Write(audioBytes) bufferMutex.Unlock() } } }() <-interrupt if strings.Contains(*addr, "localhost") { log.Println( "Received interrupt. Initiating graceful shutdown (half-close)...") if err = conn.WriteMessage( websocket.TextMessage, []byte("half-close")); err != nil { log.Printf("Error sending half-close: %v", err) } } else { log.Fatal("Terminating...") } <-serverHalfClose wg.Wait() log.Println("Graceful termination complete. Closing connection.") }
Java
CX Agent Studio에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.
/* * Copyright 2025 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.app; import com.google.auth.oauth2.GoogleCredentials; import com.google.cloud.ces.v1.BidiSessionClientMessage; import com.google.cloud.ces.v1.BidiSessionServerMessage; import com.google.cloud.ces.v1.SessionConfig; import com.google.cloud.ces.v1.SessionInput; import com.google.protobuf.util.JsonFormat; import java.net.URI; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; public class Hello { public static void main(String[] args) throws Exception { asyncBidiRunSession(); } public static void asyncBidiRunSession() throws Exception { // This may require specifying regional endpoints when creating the service client as shown in // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library String sessionId = "session-name"; if (sessionId.isEmpty()) { sessionId = UUID.randomUUID().toString(); } String appResourceName = "projects/project-name/locations/us/apps/app-name"; String sessionName = appResourceName + "/sessions/" + sessionId; String query = "Do you have t-shirt with a cat on it?"; String query2 = "What is the weather today?"; URI uri = new URI( "wss://ces.googleapis.com/ws/google.cloud.ces.v1.SessionService/BidiRunSession/locations/us"); GoogleCredentials credentials = GoogleCredentials.getApplicationDefault() .createScoped( Collections.singletonList("https://www.googleapis.com/auth/cloud-platform")); credentials.refreshIfExpired(); String token = credentials.getAccessToken().getTokenValue(); Map<String, String> headers = new HashMap<>(); headers.put("Authorization", "Bearer " + token); CountDownLatch latch = new CountDownLatch(1); WebSocketClient client = new WebSocketClient(uri, headers) { @Override public void onOpen(ServerHandshake handshakedata) { System.out.println("WebSocket connection opened"); try { // 1. Send config message BidiSessionClientMessage configMessage = BidiSessionClientMessage.newBuilder() .setConfig(SessionConfig.newBuilder().setSession(sessionName)) .build(); String configJson = JsonFormat.printer() .preservingProtoFieldNames() .omittingInsignificantWhitespace() .print(configMessage); System.out.println("Sending config: " + configJson); send(configJson); // 2. Send query message BidiSessionClientMessage queryMessage = BidiSessionClientMessage.newBuilder() .setRealtimeInput(SessionInput.newBuilder().setText(query)) .build(); String queryJson = JsonFormat.printer() .preservingProtoFieldNames() .omittingInsignificantWhitespace() .print(queryMessage); System.out.println("Sending query: " + queryJson); send(queryJson); // 3. Send query message 2 BidiSessionClientMessage queryMessage2 = BidiSessionClientMessage.newBuilder() .setRealtimeInput(SessionInput.newBuilder().setText(query2)) .build(); String queryJson2 = JsonFormat.printer() .preservingProtoFieldNames() .omittingInsignificantWhitespace() .print(queryMessage2); System.out.println("Sending query 2: " + queryJson2); send(queryJson2); } catch (Exception e) { e.printStackTrace(); latch.countDown(); } } @Override public void onMessage(String message) { System.out.println("==============="); System.out.println("Received message: " + message); try { BidiSessionServerMessage.Builder builder = BidiSessionServerMessage.newBuilder(); JsonFormat.parser().ignoringUnknownFields().merge(message, builder); BidiSessionServerMessage response = builder.build(); System.out.println("Parsed response: " + response); } catch (Exception e) { System.err.println("Failed to parse message: " + e.getMessage()); } } @Override public void onMessage(ByteBuffer bytes) { System.out.println("==============="); String message = StandardCharsets.UTF_8.decode(bytes).toString(); System.out.println("Received message: " + message); } @Override public void onClose(int code, String reason, boolean remote) { System.out.println( "WebSocket connection closed by " + (remote ? "remote peer" : "us") + " with code " + code + " and reason: " + reason); latch.countDown(); } @Override public void onError(Exception ex) { System.err.println("WebSocket error: " + ex.getMessage()); ex.printStackTrace(); latch.countDown(); } }; try { System.out.println("Connecting to WebSocket: " + uri); client.connect(); latch.await(10, TimeUnit.SECONDS); } finally { client.close(); } } }
Python
CX Agent Studio에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.
import threading import time import uuid import google.auth import google.auth.transport.requests import websocket from google.protobuf import json_format from google.cloud import ces_v1 def async_bidi_run_session(): """Demonstrates how to use the BidiRunSession WebSocket endpoint.""" session_id = "session_name" if not session_id: session_id = str(uuid.uuid4()) app_resource_name = "projects/project_name/locations/us/apps/app_name" session_name = f"{app_resource_name}/sessions/{session_id}" query = "Do you have t-shirt with a cat on it?" query2 = "What is the weather today?" uri = "wss://ces.googleapis.com/ws/google.cloud.ces.v1.SessionService/BidiRunSession/locations/us" try: credentials, _ = google.auth.default( scopes=["https://www.googleapis.com/auth/cloud-platform"] ) request = google.auth.transport.requests.Request() credentials.refresh(request) token = credentials.token headers = {"Authorization": f"Bearer {token}"} except Exception as e: print(f"Failed to get credentials: {e}") return def on_open(ws): print("WebSocket connection opened") try: # 1. Send config message config_message = ces_v1.BidiSessionClientMessage( config=ces_v1.SessionConfig(session=session_name) ) config_json = json_format.MessageToJson( config_message._pb, preserving_proto_field_name=False, indent=0 ).replace("\\n", "") print(f"Sending config: {config_json}") ws.send(config_json) # 2. Send query message query_message = ces_v1.BidiSessionClientMessage( realtime_input=ces_v1.SessionInput(text=query) ) query_json = json_format.MessageToJson( query_message._pb, preserving_proto_field_name=False, indent=0 ).replace("\\n", "") print(f"Sending query: {query_json}") ws.send(query_json) # 3. Send query message 2 query_message2 = ces_v1.BidiSessionClientMessage( realtime_input=ces_v1.SessionInput(text=query2) ) query_json2 = json_format.MessageToJson( query_message2._pb, preserving_proto_field_name=False, indent=0 ).replace("\\n", "") print(f"Sending query 2: {query_json2}") ws.send(query_json2) except Exception as e: print(f"Error during on_open: {e}") ws.close() def on_message(ws, message): print("===============") print(f"Received message: {message}") try: response_pb = ces_v1.BidiSessionServerMessage()._pb json_format.Parse( message, response_pb, ignore_unknown_fields=True, ) response = ces_v1.BidiSessionServerMessage(response_pb) print(f"Parsed response: {response}") except Exception as e: print(f"Failed to parse message: {e}") def on_error(ws, error): print(f"WebSocket error: {error}") def on_close(ws, close_status_code, close_msg): print( "WebSocket connection closed" f" with code {close_status_code}" f" and reason: {close_msg}" ) print(f"Connecting to WebSocket: {uri}") ws_app = websocket.WebSocketApp( uri, header=headers, on_open=on_open, on_message=on_message, on_error=on_error, on_close=on_close, ) wst = threading.Thread(target=ws_app.run_forever) wst.daemon = True wst.start() print("Waiting for 10 seconds for messages...") time.sleep(10) print("10 seconds elapsed, closing connection.") ws_app.close() wst.join() if __name__ == "__main__": async_bidi_run_session()
Node.js
CX Agent Studio에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.
import {GoogleAuth} from 'google-gax'; import {WebSocket} from 'ws'; async function main() { const sessionId = 'session-name'; const appResourceName = 'projects/project-name/locations/us/apps/app-name'; const sessionName = `${appResourceName}/sessions/${sessionId}`; const query = 'Do you have t-shirt with a cat on it?'; const query2 = 'What is the weather today?'; console.log(`Starting bidi session with session name: ${sessionName}`); const uri = 'wss://ces.googleapis.com/ws/google.cloud.ces.v1.SessionService/BidiRunSession/locations/us'; let token; try { const auth = new GoogleAuth({ scopes: ['https://www.googleapis.com/auth/cloud-platform'], }); token = await auth.getAccessToken(); } catch (e) { console.error(`Failed to get credentials: ${e}`); return; } const headers = {Authorization: `Bearer ${token}`}; const ws = new WebSocket(uri, {headers}); ws.on('open', () => { console.log('WebSocket connection opened'); try { // 1. Send config message const configMessage = { config: {session: sessionName}, }; const configJson = JSON.stringify(configMessage); console.log(`Sending config: ${configJson}`); ws.send(configJson); // 2. Send query message const queryMessage = { realtimeInput: {text: query}, }; const queryJson = JSON.stringify(queryMessage); console.log(`Sending query: ${queryJson}`); ws.send(queryJson); // 3. Send query message 2 const queryMessage2 = { realtimeInput: {text: query2}, }; const queryJson2 = JSON.stringify(queryMessage2); console.log(`Sending query 2: ${queryJson2}`); ws.send(queryJson2); } catch (e) { console.error(`Error during on_open: ${e}`); ws.close(); } }); ws.on('message', message => { console.log('==============='); console.log(`Received message: ${message}`); }); ws.on('error', err => { console.error(`WebSocket error: ${err}`); }); ws.on('close', (code, reason) => { console.log( `WebSocket connection closed with code ${code} and reason: ${reason}`); }); console.log('Waiting for 10 seconds for messages...'); await new Promise(resolve => setTimeout(resolve, 10000)); console.log('10 seconds elapsed, closing connection.'); ws.close(); } main();
또한 gRPC 클라이언트 라이브러리와 함께 ces.googleapis.com을 apiEndpoint로 사용하는 경우 otherArgs의 x-goog-request-params도 특정 위치로 설정해야 합니다.
예를 들어 Node.js 클라이언트 라이브러리를 사용하는 경우:
const sessionClient = new SessionServiceClient({
apiEndpoint: 'ces.googleapis.com'
});
const grpc = require('@grpc/grpc-js');
const callOptions = {
otherArgs: {headers: {'x-goog-request-params': `location=locations/us`}}
};
stream = sessionClient.bidiRunSession(callOptions);
ces.us.rep.googleapis.com 또는 ces.eu.rep.googleapis.com를 사용하는 경우 x-goog-request-params를 설정할 필요가 없습니다.