Skip to content

Commit 0f8b6ae

Browse files
authored
Complete Tier 1 time grains and weekly summary (#3)
1 parent 5b7edb1 commit 0f8b6ae

21 files changed

Lines changed: 801 additions & 21 deletions

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ postgres.public.llm_request_events
157157
→ ods_ai_observability_llm_request_events_di
158158
→ paimon_lake.dwd.dwd_ai_llm_request_di
159159
→ paimon_lake.dws.dws_ai_llm_feature_request_1d
160+
→ paimon_lake.dws.dws_ai_llm_feature_request_1h
161+
→ paimon_lake.dws.dws_ai_llm_session_request_1d
160162
```
161163

162164
Flink SQL assets are stored in:

‎docs/data_model.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,8 @@ Tier 1 expands the runtime observability model beyond LLM and Agent execution:
458458
| `dws_ai_retrieval_knowledge_base_request_1d` | One daily row per app, knowledge base, embedding model and strategy | Retrieval volume, zero-result count, hit count, similarity and latency metrics |
459459
| `dws_ai_feedback_feature_action_1d` | One daily row per app, feature and agent | Feedback volume, positive/negative counts, regeneration/report counts and average rating |
460460
| `dws_ai_guardrail_rule_check_1d` | One daily row per app, rule category and action | Guardrail check volume, trigger/action counts, latency and distinct users |
461+
| `dws_ai_llm_feature_request_1h` | One hourly row per app, feature and model | Request, token, cost and latency metrics for near-real-time dashboards |
462+
| `dws_ai_llm_session_request_1d` | One daily row per app and feature | Session count, turns, tokens, duration and positive-resolution count |
461463
| `ads_observability_retrieval_daily_quality` | Daily retrieval quality mart | Hit rate, zero-result rate and latency breach flags |
462464
| `ads_observability_feedback_daily_satisfaction` | Daily satisfaction mart | Satisfaction rate, regeneration rate and breach flags |
463465
| `ads_observability_guardrail_daily_violation` | Daily guardrail violation mart | Trigger/block rates and policy-latency breach flags |
@@ -480,5 +482,6 @@ Tier 2 starts with cost-governance tables:
480482
| `dws_ai_llm_feature_env_request_1d` | One daily row per app, feature, model and environment | Prod/staging/dev split of request volume, success/error counts, tokens, cost and latency |
481483
| `dws_ai_llm_region_request_1d` | One daily row per region, environment, app and model | Regional request volume, success/error counts, tokens, cost and latency |
482484
| `dws_ai_agent_team_run_1d` | One daily row per team, app, agent and task type | Team-attributed agent run volume, success/error counts, tokens, cost, duration and span metrics |
485+
| `ads_observability_executive_weekly_summary` | One weekly row per app | Cross-domain LLM, Agent, retrieval, feedback, guardrail, evaluation and cost summary |
483486

484-
Remaining planned extensions include agent, compliance and platform-health dimensions.
487+
Tier 1 and Tier 2 table inventory is implemented. Remaining planned extensions are Tier 3 compliance, multi-agent orchestration and platform-health facts.

‎docs/metric_definitions.md‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,30 @@ FROM ai_observability.dws_ai_agent_tool_tool_call_1d
236236
GROUP BY tool_name
237237
ORDER BY failure_rate DESC;
238238
```
239+
240+
---
241+
242+
## 14. Resolved Session Count
243+
244+
### Definition
245+
246+
Distinct daily sessions with positive feedback. A session is resolved when it has a `thumbs_up` event or a rating value of at least 4.
247+
248+
Session duration is measured from the first request start to the last request completion within the same date, app, feature, and session.
249+
250+
---
251+
252+
## 15. Executive Weekly Weighted Metrics
253+
254+
### Definition
255+
256+
`ads_observability_executive_weekly_summary` is grouped by ISO week start date and application. Average request latency and evaluation score are weighted by their request/evaluation counts rather than averaging daily averages.
257+
258+
```text
259+
avg_latency_ms = sum(daily_avg_latency_ms * daily_request_count) / sum(daily_request_count)
260+
avg_evaluation_score = sum(daily_avg_score * daily_evaluation_count) / sum(daily_evaluation_count)
261+
retrieval_hit_rate_1w = retrieval_hit_cnt_1w / retrieval_returned_cnt_1w
262+
satisfaction_rate_1w = thumbs_up_cnt_1w / (thumbs_up_cnt_1w + thumbs_down_cnt_1w)
263+
evaluation_pass_rate_1w = evaluation_pass_cnt_1w / evaluation_cnt_1w
264+
total_ai_cost_amt_1w = llm_cost_amt_1w + agent_cost_amt_1w
265+
```

‎docs/runtime_runbook.md‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Expected long-running SQL jobs:
4949
| --- | --- | --- | --- |
5050
| `flink/sql/10_ingest_ods_to_kafka.sql` | CDC ingest | Postgres CDC source table | Kafka ODS topic |
5151
| `flink/sql/20_build_dwd_from_kafka_ods.sql` | DWD transform and data quality filter | Kafka ODS topic | Paimon DWD |
52-
| `flink/sql/30_build_dws_from_dwd.sql` | DWS streaming aggregation | Paimon DWD | Paimon DWS |
52+
| `flink/sql/30_build_dws_from_dwd.sql` | Daily, hourly and session DWS streaming aggregation | Paimon DWD | Paimon DWS |
5353

5454
Submit the streaming jobs:
5555

@@ -137,12 +137,24 @@ The health check verifies:
137137
- Postgres source table `public.llm_request_events` has rows.
138138
- Flink REST API is reachable.
139139
- DWD and DWS streaming jobs are running.
140+
- Hourly feature and daily session DWS jobs are running.
140141
- Doris FE is queryable when serving checks are enabled.
141142

142143
This is a fast operational check. It does not replace deeper table-level
143144
verification queries such as `flink/sql/91_verify_dwd_count.sql` and
144145
`flink/sql/92_verify_dws_metrics.sql`.
145146

147+
The hourly feature job uses a five-second event-time watermark and a one-hour tumble window. The session job resolves sessions from positive feedback (`thumbs_up` or rating >= 4).
148+
149+
Build and load the executive weekly summary after the daily DWS Parquet outputs are available:
150+
151+
```bash
152+
uv run python -m scripts.spark_build_ads_executive_weekly_summary
153+
uv run python -m scripts.load_dws_metrics_to_doris \
154+
--input data/warehouse/ads/ads_observability_executive_weekly_summary.parquet \
155+
--table ads_observability_executive_weekly_summary
156+
```
157+
146158
## Flink Recovery
147159

148160
The local Flink runtime is configured with:

‎docs/three_tier_domain_expansion_plan.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,8 @@ Implementation status: Tier 2 granularity expansions are implemented. `dws_ai_co
532532
| Prompt engineering | Parse prompt registry (Git repo, config DB, or API) | Medium |
533533
| Model deployment | Parse CI/CD events (GitHub Actions, ArgoCD, internal deploy API) | Medium-High |
534534

535+
Implementation status: `ads_observability_executive_weekly_summary` is implemented at week-and-app grain. It combines LLM, Agent, retrieval, feedback, guardrail, evaluation, and cost metrics for management reporting.
536+
535537
---
536538

537539
## 5. Tier 3 — Compliance, Multi-Agent, and Platform Health (Month 5+)
@@ -641,6 +643,8 @@ Source: Kafka JMX, Flink REST API, Paimon catalog metadata, Doris `information_s
641643

642644
Hourly DWS tables use the same schema as daily DWS but with `hour` (int, 0-23) as an additional grouping key. In Flink, hourly aggregation uses a 1-hour tumble window. Daily DWS remains as a rollup from hourly.
643645

646+
Implementation: `dws_ai_llm_feature_request_1h` is implemented with Spark batch aggregation and a Flink 1-hour tumble-window path, with Paimon and Doris serving tables.
647+
644648
### 6.2 Organization Granularity
645649

646650
```text
@@ -671,6 +675,8 @@ Implementation: `dim_team` and `dim_user` (Tier 2) enable team-level and user-le
671675

672676
Built from: `dwd_ai_llm_request_di` grouped by `session_id`, then aggregated to daily.
673677

678+
Implementation: `dws_ai_llm_session_request_1d` is implemented. A session is resolved when it has a `thumbs_up` feedback event or a rating of at least 4. Session duration runs from the first request start to the last request completion within the daily app/feature/session grain.
679+
674680
### 6.4 Region / Environment Granularity
675681

676682
Both `region` and `environment` fields exist in all DWD tables but are not DWS grouping keys.

‎flink/sql/03_dwd_paimon_tables.sql‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ CREATE TABLE IF NOT EXISTS paimon_lake.dwd.dwd_ai_llm_request_di (
4141
environment STRING,
4242
created_at TIMESTAMP(3),
4343
`date` DATE,
44+
WATERMARK FOR created_at AS created_at - INTERVAL '5' SECOND,
4445
PRIMARY KEY (request_id) NOT ENFORCED
4546
) WITH (
4647
'bucket' = '4'

‎flink/sql/04_dws_paimon_tables.sql‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,41 @@ CREATE TABLE IF NOT EXISTS paimon_lake.dws.dws_ai_llm_feature_request_1d (
2020
'bucket' = '4'
2121
);
2222

23+
CREATE TABLE IF NOT EXISTS paimon_lake.dws.dws_ai_llm_feature_request_1h (
24+
`date` DATE,
25+
`hour` INT,
26+
app_name STRING,
27+
feature_name STRING,
28+
model_name STRING,
29+
request_cnt_1h BIGINT,
30+
success_cnt_1h BIGINT,
31+
error_cnt_1h BIGINT,
32+
prompt_token_cnt_1h BIGINT,
33+
completion_token_cnt_1h BIGINT,
34+
total_token_cnt_1h BIGINT,
35+
estimated_cost_amt_1h DOUBLE,
36+
avg_latency_ms DOUBLE,
37+
max_latency_ms BIGINT,
38+
p95_latency_ms BIGINT,
39+
PRIMARY KEY (`date`, `hour`, app_name, feature_name, model_name) NOT ENFORCED
40+
) PARTITIONED BY (`date`) WITH (
41+
'bucket' = '4'
42+
);
43+
44+
CREATE TABLE IF NOT EXISTS paimon_lake.dws.dws_ai_llm_session_request_1d (
45+
`date` DATE,
46+
app_name STRING,
47+
feature_name STRING,
48+
session_cnt_1d BIGINT,
49+
avg_turns_per_session DOUBLE,
50+
avg_tokens_per_session DOUBLE,
51+
avg_duration_per_session_ms DOUBLE,
52+
resolved_session_cnt_1d BIGINT,
53+
PRIMARY KEY (`date`, app_name, feature_name) NOT ENFORCED
54+
) PARTITIONED BY (`date`) WITH (
55+
'bucket' = '4'
56+
);
57+
2358
CREATE TABLE IF NOT EXISTS paimon_lake.dws.dws_ai_retrieval_knowledge_base_request_1d (
2459
`date` DATE,
2560
app_name STRING,

‎flink/sql/30_build_dws_from_dwd.sql‎

Lines changed: 84 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,50 @@
1-
-- Build daily feature metrics from DWD.
1+
-- Build hourly feature metrics from DWD using event-time windows.
22

3-
INSERT INTO paimon_lake.dws.dws_ai_llm_feature_request_1d
3+
INSERT INTO paimon_lake.dws.dws_ai_llm_feature_request_1h
44
SELECT
5-
`date`,
5+
CAST(window_start AS DATE) AS `date`,
6+
CAST(EXTRACT(HOUR FROM window_start) AS INT) AS `hour`,
67
app_name,
78
feature_name,
89
model_name,
9-
COUNT(*) AS request_count,
10-
SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) AS success_count,
11-
SUM(CASE WHEN status = 'error' THEN 1 ELSE 0 END) AS error_count,
12-
SUM(prompt_tokens) AS prompt_tokens,
13-
SUM(completion_tokens) AS completion_tokens,
14-
SUM(total_tokens) AS total_tokens,
15-
SUM(estimated_cost_usd) AS estimated_cost_usd,
10+
COUNT(*) AS request_cnt_1h,
11+
SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) AS success_cnt_1h,
12+
SUM(CASE WHEN status = 'error' THEN 1 ELSE 0 END) AS error_cnt_1h,
13+
SUM(prompt_tokens) AS prompt_token_cnt_1h,
14+
SUM(completion_tokens) AS completion_token_cnt_1h,
15+
SUM(total_tokens) AS total_token_cnt_1h,
16+
SUM(estimated_cost_usd) AS estimated_cost_amt_1h,
1617
AVG(latency_ms) AS avg_latency_ms,
17-
-- Flink 1.20 SQL does not support PERCENTILE_CONT as a streaming aggregate.
18-
-- Store an explicit upper-bound metric instead of naming MAX latency as p95.
1918
CAST(MAX(latency_ms) AS BIGINT) AS max_latency_ms,
2019
CAST(0 AS BIGINT) AS p95_latency_ms
21-
FROM paimon_lake.dwd.dwd_ai_llm_request_di
20+
FROM TABLE(
21+
TUMBLE(
22+
TABLE paimon_lake.dwd.dwd_ai_llm_request_di,
23+
DESCRIPTOR(created_at),
24+
INTERVAL '1' HOUR
25+
)
26+
)
27+
GROUP BY window_start, app_name, feature_name, model_name;
28+
29+
-- Daily feature metrics roll up the hourly table so both grains share one definition.
30+
31+
INSERT INTO paimon_lake.dws.dws_ai_llm_feature_request_1d
32+
SELECT
33+
`date`,
34+
app_name,
35+
feature_name,
36+
model_name,
37+
SUM(request_cnt_1h) AS request_count,
38+
SUM(success_cnt_1h) AS success_count,
39+
SUM(error_cnt_1h) AS error_count,
40+
SUM(prompt_token_cnt_1h) AS prompt_tokens,
41+
SUM(completion_token_cnt_1h) AS completion_tokens,
42+
SUM(total_token_cnt_1h) AS total_tokens,
43+
SUM(estimated_cost_amt_1h) AS estimated_cost_usd,
44+
SUM(avg_latency_ms * request_cnt_1h) / SUM(request_cnt_1h) AS avg_latency_ms,
45+
MAX(max_latency_ms) AS max_latency_ms,
46+
CAST(0 AS BIGINT) AS p95_latency_ms
47+
FROM paimon_lake.dws.dws_ai_llm_feature_request_1h
2248
GROUP BY `date`, app_name, feature_name, model_name;
2349

2450
INSERT INTO paimon_lake.dws.dws_ai_retrieval_knowledge_base_request_1d
@@ -129,3 +155,48 @@ SELECT
129155
CAST(MAX(latency_ms) AS BIGINT) AS p95_latency_ms
130156
FROM paimon_lake.dwd.dwd_ai_llm_request_di
131157
GROUP BY `date`, region, environment, app_name, model_name;
158+
159+
INSERT INTO paimon_lake.dws.dws_ai_llm_session_request_1d
160+
WITH session_metrics AS (
161+
SELECT
162+
`date`,
163+
app_name,
164+
feature_name,
165+
session_id,
166+
COUNT(*) AS turn_cnt,
167+
SUM(total_tokens) AS token_cnt,
168+
TIMESTAMPDIFF(MILLISECOND, MIN(created_at), MAX(created_at)) + MAX(latency_ms) AS session_duration_ms
169+
FROM paimon_lake.dwd.dwd_ai_llm_request_di
170+
GROUP BY `date`, app_name, feature_name, session_id
171+
),
172+
resolved_sessions AS (
173+
SELECT
174+
`date`,
175+
app_name,
176+
feature_name,
177+
session_id,
178+
MAX(
179+
CASE
180+
WHEN feedback_type = 'thumbs_up' OR (feedback_type = 'rating' AND rating_value >= 4) THEN 1
181+
ELSE 0
182+
END
183+
) AS is_resolved
184+
FROM paimon_lake.dwd.dwd_ai_feedback_action_di
185+
GROUP BY `date`, app_name, feature_name, session_id
186+
)
187+
SELECT
188+
sessions.`date`,
189+
sessions.app_name,
190+
sessions.feature_name,
191+
COUNT(*) AS session_cnt_1d,
192+
AVG(sessions.turn_cnt) AS avg_turns_per_session,
193+
AVG(sessions.token_cnt) AS avg_tokens_per_session,
194+
AVG(sessions.session_duration_ms) AS avg_duration_per_session_ms,
195+
SUM(COALESCE(feedback.is_resolved, 0)) AS resolved_session_cnt_1d
196+
FROM session_metrics AS sessions
197+
LEFT JOIN resolved_sessions AS feedback
198+
ON sessions.`date` = feedback.`date`
199+
AND sessions.app_name = feedback.app_name
200+
AND sessions.feature_name = feedback.feature_name
201+
AND sessions.session_id = feedback.session_id
202+
GROUP BY sessions.`date`, sessions.app_name, sessions.feature_name;

‎flink/sql/92_verify_dws_metrics.sql‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,13 @@ SELECT
5050
COUNT(*) AS dws_agent_team_metric_rows,
5151
SUM(run_cnt_1d) AS total_team_run_cnt_1d
5252
FROM paimon_lake.dws.dws_ai_agent_team_run_1d;
53+
54+
SELECT
55+
COUNT(*) AS dws_llm_feature_hourly_metric_rows,
56+
SUM(request_cnt_1h) AS total_hourly_request_cnt_1h
57+
FROM paimon_lake.dws.dws_ai_llm_feature_request_1h;
58+
59+
SELECT
60+
COUNT(*) AS dws_llm_session_metric_rows,
61+
SUM(session_cnt_1d) AS total_session_cnt_1d
62+
FROM paimon_lake.dws.dws_ai_llm_session_request_1d;

‎scripts/check_pipeline_health.sh‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ expected = {
9898
"insert-into_paimon_lake.dws.dws_ai_feedback_feature_action_1d": False,
9999
"insert-into_paimon_lake.dws.dws_ai_guardrail_rule_check_1d": False,
100100
"insert-into_paimon_lake.dws.dws_ai_evaluation_feature_judgment_1d": False,
101+
"insert-into_paimon_lake.dws.dws_ai_llm_feature_request_1h": False,
102+
"insert-into_paimon_lake.dws.dws_ai_llm_session_request_1d": False,
101103
}
102104
for name in running_names:
103105
for expected_name in expected:

0 commit comments

Comments
 (0)