From 0f269d078bde5c458019d9f2f924f7296e2ac705 Mon Sep 17 00:00:00 2001 From: jykim Date: Fri, 6 Mar 2026 11:06:23 +0900 Subject: [PATCH] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91?= =?UTF-8?q?=EC=97=85=EB=82=B4=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++ src/tools/stat-tools.ts | 130 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 123 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1597f84..8c60df3 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,7 @@ API_USER_PW={CDA 사용자 PW (필수)} LOGIN_API_URL=https://{내부IP}/login STATS_API_URL=https://{내부IP}/secured/ui/common ``` + +## 샘플 질문 +2021년 6월 한달동안 가장많이 인바운드업무를 한 Agent 누구야? +2021년 6월 한달동안 콜 인입이 가장 많이 들어온 VDN알려줘. \ No newline at end of file diff --git a/src/tools/stat-tools.ts b/src/tools/stat-tools.ts index 8847bb0..a88be74 100644 --- a/src/tools/stat-tools.ts +++ b/src/tools/stat-tools.ts @@ -228,18 +228,20 @@ export function registerStatTools(factory: ToolFactory, config: ApiConfig): void const payload = { _sqlName: ["cda_CallTimeResult_select"], - fromhour: "0000", - tohour: "239959", - use_yn: "Y", - gubun: 3, - org1_cd: "전체", - org2_cd: "전체", - org3_cd: "전체", - user_id: [], - edit_agent_id: "", + parameters: { + fromhour: "0000", + tohour: "239959", + use_yn: "Y", + gubun: 3, + org1_cd: "전체", + org2_cd: "전체", + org3_cd: "전체", + user_id: [], + edit_agent_id: "", + fromdate: fromDate, + todate: toDate, + }, _dataset: [], - fromdate: fromDate, - todate: toDate, }; let body: ApiResponse<{ cda_CallTimeResult_select?: StatsResultSet }>; @@ -311,9 +313,58 @@ export function registerStatTools(factory: ToolFactory, config: ApiConfig): void return `Record ${index + 1}\n${formatRecord(columns, row)}`; }); + const columnSchemas = buildColumnSchema(columns); + const recordObjects = filteredRecords.map((row) => mapRowToObject(columns, row)); + const acdcallsIndex = columns.findIndex((col) => col?.toLowerCase() === "acdcalls"); + const acdtimeIndex = columns.findIndex((col) => col?.toLowerCase() === "acdtime"); + + const totals = filteredRecords.reduce( + (acc, row) => { + acc.acdcalls += acdcallsIndex >= 0 ? toNumber(row[acdcallsIndex]) : 0; + acc.acdtime += acdtimeIndex >= 0 ? toNumber(row[acdtimeIndex]) : 0; + return acc; + }, + { acdcalls: 0, acdtime: 0 }, + ); + + const sampleRecord = recordObjects[0] ?? {}; + const sampleAcdcalls = toNumber(sampleRecord["acdcalls"]); + const sampleAcdtime = toNumber(sampleRecord["acdtime"]); + const sampleAgentName = String(sampleRecord["agent_name"] ?? "-"); + const sampleSuccessRate = + sampleAcdcalls > 0 ? sampleAcdtime / sampleAcdcalls : null; + const totalSuccessRate = totals.acdcalls > 0 ? totals.acdtime / totals.acdcalls : null; + + const successRateExplanation = sampleSuccessRate + ? `예: ${sampleAgentName}의 성공률(초/콜) = ${sampleAcdtime} ÷ ${sampleAcdcalls} = ${sampleSuccessRate.toFixed( + 2, + )}` + : "첫 번째 기록에 acdtime 또는 acdcalls 값이 없어 성공률 예시를 제공할 수 없습니다."; + + const structuredData = { + schema: columnSchemas, + records: recordObjects, + totals, + calculations: { + successRateFormula: "acdtime ÷ acdcalls", + successRateExample: sampleSuccessRate + ? { + agentName: sampleAgentName, + acdtime: sampleAcdtime, + acdcalls: sampleAcdcalls, + result: Number(sampleSuccessRate.toFixed(2)), + } + : null, + totalSuccessRate: totalSuccessRate ? Number(totalSuccessRate.toFixed(2)) : null, + }, + }; + const summary = [ `Agent stats for ${fromDate} to ${toDate}`, `Transactions returned: ${filteredRecords.length}`, + `IB 총 통화콜수(합계): ${totals.acdcalls}`, + `IB 총 통화시간(합계): ${formatDuration(totals.acdtime)} (${totals.acdtime} sec)`, + successRateExplanation, statusMessage, "", formattedRecords.join("\n\n"), @@ -326,6 +377,7 @@ export function registerStatTools(factory: ToolFactory, config: ApiConfig): void text: summary, }, ], + structuredData, }; }, ); @@ -473,3 +525,59 @@ function formatRecord(columns: string[], row: unknown[]): string { }); return values.join("\n"); } + +interface ColumnMeta { + label: string; + description: string; +} + +interface ColumnSchema { + name: string; + label: string; + description: string; +} + +const COLUMN_METADATA: Record = { + workdate: { label: "작업일/기간", description: "날짜 또는 기간" }, + starttime: { label: "콜 시작시간", description: "콜이 시작된 시각" }, + dept_nm: { label: "부서 이름", description: "콜을 담당한 부서 이름" }, + agent_id: { label: "사번/에이전트 ID", description: "에이전트 식별자" }, + user_id: { label: "사용자 아이디", description: "시스템 사용자 ID" }, + agent_name: { label: "에이전트 이름", description: "에이전트의 실명 또는 호칭" }, + acdcalls: { label: "IB 통화콜수", description: "인바운드 통화콜 수" }, + acdtime: { label: "IB 통화시간", description: "인바운드 통화에 소요된 총 시간(초)" }, + abncalls: { label: "IB 포기콜수", description: "인바운드 전화에서 고객이 포기한 횟수" }, + abntime: { label: "IB 포기시간", description: "포기 콜에 소요된 시간(초)" }, + da_acdcalls: { label: "IB-DA 통화콜수", description: "인바운드 DA 통화콜 수" }, + da_acdtime: { label: "IB-DA 통화시간", description: "DA 포함 인바운드 통화시간(초)" }, + da_abncalls: { label: "IB-DA 포기콜수", description: "DA 관련 포기 콜 수" }, + da_abntime: { label: "IB-DA 포기시간", description: "DA 관련 포기 콜에 소요된 시간(초)" }, + auxoutoffcalls: { label: "OB 시도콜수", description: "아웃바운드 시도 콜 수" }, + auxoutofftime: { label: "OB 통화콜수", description: "아웃바운드에서 실제 통화된 콜 수" }, + auxoutoffconncalls: { label: "OB 벨울림 포함시간", description: "OB 벨울림 포함 통화 시간" }, + auxoutofftalktime: { label: "OB 통화시간", description: "아웃바운드 통화에 실제 소요된 시간(초)" }, + acd_rate: { label: "IB 응대율", description: "인바운드 통화 중 응대된 비율(%)" }, + inout_calls: { label: "IB+OB 합계 콜수", description: "인바운드와 아웃바운드 콜의 합" }, + auxincalls: { label: "기타 인입콜수", description: "기타 인입 콜 수" }, + auxintime: { label: "기타 인입시간", description: "기타 인입 콜에 소요된 시간(초)" }, +}; + +function buildColumnSchema(columns: string[]): ColumnSchema[] { + return columns.map((columnName, index) => { + const key = columnName ?? `column${index + 1}`; + const meta = COLUMN_METADATA[key.toLowerCase()]; + return { + name: key, + label: meta?.label ?? key, + description: meta?.description ?? "설명 없음", + }; + }); +} + +function mapRowToObject(columns: string[], row: unknown[]): Record { + return columns.reduce>((acc, columnName, index) => { + const key = columnName ?? `column${index + 1}`; + acc[key] = row[index] ?? null; + return acc; + }, {}); +} -- GitLab