트랙 A · 과제 A4
에이전트 트레이스 탐색기
AI 에이전트의 JSON은 릴리스마다 구조가 바뀝니다. VARIANT에 저장하고 일반 열처럼 쿼리하세요.
- 60~120분
- 중급
- 언어 자유 · CLI, 노트북 또는 웹
- AI 도구 사용 환영
막히는 부분이 있나요? 질문할 곳: #dev 참여할 곳: Apache Doris Slack. 유지 관리자와 다른 참가자가 도와드립니다.
환경 준비
만들 결과물과 Doris로 간단히 구현하는 이유를 알아보고 클러스터를 실행합니다.
만들 결과물
제각각인 에이전트 JSON도 그대로 쿼리하세요.
LLM 호출, 도구 호출, 오류, 최종 답변은 각각 다른 필드를 가지며 릴리스마다 새 필드가 추가됩니다. Doris의 VARIANT 는 JSON을 타입이 있는 열로 자동 변환하므로 일반 SQL로 필터링, 검색, 집계할 수 있습니다. 스키마 마이그레이션이 필요 없습니다.
다음을 위한 트레이스 탐색기: AI 에이전트 세션 1,000개(약 20,000개 이벤트):
- 검색용 세션 목록 (이벤트 수, 오류, 토큰, 지연 시간 표시),
- 검색용 세션 타임라인: 세션을 클릭하면 에이전트의 동작을 단계별로 재생,
- 도구 및 모델 통계: p95 지연 시간, 오류율, 토큰 사용량,
- JSON 내부 검색: ‘timeout’이 포함된 모든 도구 오류를 찾습니다.
그리고 다음에 답하세요: 13:00에 배포된 에이전트 v2로 상황이 좋아졌나요, 나빠졌나요?
개략도는 코드 패널 상단에 있습니다
Doris로 구현할 때의 장점
스키마 정의 없이 쓰기 시점에 타입 결정. VARIANT는 각 JSON 경로의 타입을 추론하고 자주 쓰는 경로를 실제 컬럼형 하위 열로 저장합니다. 따라서
payload['latency_ms']는 문서 전체를 파싱하지 않고 열 하나만 읽습니다.새 필드는 자동으로 나타납니다. v2 이벤트에 다음이 추가되면
cost_usd, 다음 적재 시 새 경로가 하위 열로 나타납니다. 다음 작업은 필요 없습니다:ALTER TABLE.JSON 내부 검색. VARIANT 열의 역색인으로 모든 텍스트 경로를 다음과 같이 검색할 수 있습니다:
MATCH_*.
패널: 테이블 정의의 인덱스 부분
시작하기 전에
docker pull apache/doris:all-in-one-4.1.3docker run -d --name doris -p 9030:9030 -p 8030:8030 -p 8040:8040 apache/doris:all-in-one-4.1.3docker exec -it doris mysql -uroot -h127.0.0.1 -P9030curl -LO https://github.com/morningman/demo-env/releases/download/for-hackathon/doris-hackathon-glasgow-2026.zip && unzip doris-hackathon-glasgow-2026.zip && cd doris-hackathon-glasgow-2026
- 호스트
- 127.0.0.1
- 포트
- 9030
- 사용자
- root비밀번호 없음
- 데이터베이스
- hackathon
데이터
hackathon.agent_events : 에이전트 이벤트마다 한 행이며 이벤트 본문은 VARIANT 열에 저장합니다:
CREATE TABLE agent_events ( session_id VARCHAR(40) NOT NULL, ts DATETIME(3) NOT NULL, event_id BIGINT NOT NULL, event_type VARCHAR(32) NOT NULL, -- user_message / llm_call / tool_call / error / final_answer payload VARIANT NOT NULL, INDEX idx_payload (payload) USING INVERTED PROPERTIES ("parser" = "english"))DUPLICATE KEY(session_id, ts)DISTRIBUTED BY HASH(session_id) BUCKETS 1PROPERTIES ("replication_num" = "1", "storage_format" = "V3");페이로드 예시: 구조가 어떻게 다른지 살펴보세요:
{"text": "Why did checkout fail at 11:40?", "lang": "en", "agent_version": "v1"}{"model": "model-large", "usage": {"input_tokens": 812, "output_tokens": 96}, "latency_ms": 1430, "agent_version": "v1"}{"tool": "sql_query", "args": {"sql": "SELECT ..."}, "latency_ms": 37, "status": "ok", "agent_version": "v1"}{"tool": "http_get", "args": {"url": "..."}, "latency_ms": 3012, "status": "error", "agent_version": "v1", "error": {"type": "Timeout", "message": "read timeout after 3000 ms calling https://api.internal/orders"}}13:00 이후에 시작하는 세션은 다음을 실행합니다: 에이전트 v2: 페이로드에 다음이 표시되며 "agent_version": "v2", v2의 LLM 호출에는 다음 플래그가 추가됩니다: cache_hit . 마일스톤 5에서는 테이블이 한 번도 본 적 없는 필드가 있는 이벤트를 적재합니다.
패널 → 테이블: 전체 테이블 정의
AI로 개발하기
AI 코딩 에이전트와 함께 개발하는 것을 권장합니다. Doris 4.x의 검색 및 AI 기능은 대부분의 모델 학습 데이터보다 최신이므로 SQL을 작성하기 전에 올바른 맥락을 제공하세요:
다음 컨텍스트 프롬프트 를 에이전트에 붙여 넣으세요.
참조할 곳을 안내하세요: 이 페이지에 나열된 참고 문서 및
AGENTS.md(스타터 키트 내). 많은 코딩 에이전트가 이 파일을 자동으로 읽습니다.코드에 연결하기 전에 모든 SQL 문을 실행 중인 클러스터에서 직접 실행하게 하세요.
더 나아가 다음을 연결하세요: Doris MCP Server (참고: 과제 A2)를 연결하면 에이전트가 직접 스키마를 확인하고 쿼리를 테스트할 수 있습니다.
I'm building an agent trace explorer on Apache Doris 4.1 for a hackathon.Connection: MySQL protocol, 127.0.0.1:9030, user root, empty password, database `hackathon`.Use a plain MySQL driver and raw SQL.Table `agent_events`: session_id, ts DATETIME(3), event_id, event_type(user_message / llm_call / tool_call / error / final_answer), payload VARIANT(inverted index, english parser). Payload shapes differ by event_type. Every payload hasagent_version: "v1" before 13:00, "v2" from 13:00; v2 llm_calls add cache_hit.Doris SQL rules:1. Read JSON paths as payload['a']['b']. CAST to a concrete type before comparing, sorting or aggregating: CAST(payload['latency_ms'] AS DOUBLE).2. VARIANT cannot be a key, a sort key or a join key.3. Text search inside JSON: payload['error']['message'] MATCH_ANY 'timeout'. Never LIKE.4. Percentiles: percentile_approx(expr, 0.95).5. `SET describe_extend_variant_column = true; DESC agent_events;` lists inferred subcolumns.6. To insert JSON yourself, CAST the text: CAST('{"a": 1}' AS JSON).Build: sessions list, session timeline, tool/model stats, and JSON search.Run every SQL statement against the live cluster first.AI 에이전트가 빠지기 쉬운 Doris 함정
| Trap | What to do instead |
|---|---|
| 형 변환 없이 VARIANT 경로 비교 | CAST(payload['latency_ms'] AS INT). VARIANT는 키 열이나 조인 열로 사용할 수 없습니다 |
다음에 기대하는 동작: DESC 로 JSON 경로 목록 표시 | 실행: SET describe_extend_variant_column = true; 를 먼저 실행하세요. 그러면 DESC 에 추론된 모든 하위 열과 타입이 표시됩니다 |
다음 사용: LIKE '%term%' 로 텍스트 검색 | col MATCH_ANY 'a b' (OR), MATCH_ALL (AND), MATCH_PHRASE 'a b' . 이들은 역색인을 사용합니다 |
| ORM 사용 | 일반 MySQL 드라이버와 직접 작성한 SQL을 권장합니다. ORM은 Doris가 지원하지 않는 문을 생성할 수 있습니다 |
구현하기
6 마일스톤, M0 → M5. 패널은 현재 읽는 단계를 따라갑니다.
적재 및 확인
스타터 키트 폴더 안에서 실행하세요:
M0 · 적재bashdocker exec -i doris mysql -uroot -h127.0.0.1 -P9030 < seed/a4_agent_events.sql다음으로 SQL 셸에서 실행:
M0 · 확인sqlUSE hackathon;SET describe_extend_variant_column = true;DESC agent_events; -- see the subcolumns Doris inferred: payload.tool, payload.usage.input_tokens, ...확인 사항적재 결과: 세션 1,000개, 이벤트 20,401개;
DESC에는 약 20개의payload.*경로와 각각의 추론된 타입이 표시됩니다.JSON 경로 쿼리
M1 · JSON 경로 쿼리sqlSELECT CAST(payload['tool'] AS STRING) AS tool, COUNT(*) AS callsFROM agent_eventsWHERE event_type = 'tool_call'GROUP BY toolORDER BY calls DESC;확인 사항도구는 다섯 개이며
sql_query가 가장 많이 호출됩니다(3,506회).추론된 하위 열 집계
M2 · 추론된 하위 열 집계sqlSELECT CAST(payload['tool'] AS STRING) AS tool, COUNT(*) AS calls, percentile_approx(CAST(payload['latency_ms'] AS DOUBLE), 0.95) AS p95_ms, SUM(CASE WHEN CAST(payload['status'] AS STRING) = 'error' THEN 1 ELSE 0 END) AS errorsFROM agent_eventsWHERE event_type = 'tool_call'GROUP BY toolORDER BY p95_ms DESC;확인 사항python_exec의 p95가 가장 높고(약 2.1초),http_get가 가장 자주 실패합니다(오류 425건).JSON 내부 검색
M3 · JSON 내부 검색sqlSELECT session_id, ts, CAST(payload['error']['message'] AS STRING) AS error_messageFROM agent_eventsWHERE payload['error']['message'] MATCH_ANY 'timeout'ORDER BY tsLIMIT 20;확인 사항총 398개 이벤트에 timeout이 포함되어 있습니다. 시간별로 집계하면 v2에 대한 첫 단서를 얻습니다.
세션 타임라인
M4 · 세션 타임라인sqlSELECT ts, event_type, CAST(payload['tool'] AS STRING) AS tool, CAST(payload['status'] AS STRING) AS status, CAST(payload['latency_ms'] AS INT) AS latency_ms, CAST(payload['text'] AS STRING) AS textFROM agent_eventsWHERE session_id = 's-0042'ORDER BY ts;이 쿼리로 세션 목록 → 타임라인 → 통계 UI를 만드세요.
확인 사항세션
s-0042에는 이벤트가 25개 있습니다. 질문, LLM 및 도구 호출(실패 후 재시도한sql_query, 재시도), 최종 답변으로 구성됩니다.스키마 변화 관찰
M5 · 변경 배치 적재bashdocker exec -i doris mysql -uroot -h127.0.0.1 -P9030 < a4/drift_batch.sql # events with new fieldsM5 · 새 필드 쿼리sqlSELECT CAST(payload['agent_version'] AS STRING) AS version, COUNT(*) AS llm_calls, ROUND(SUM(CAST(payload['cost_usd'] AS DOUBLE)), 2) AS cost_usdFROM agent_eventsWHERE event_type = 'llm_call'GROUP BY versionORDER BY version;실행:
DESC agent_events를 다시 실행하면 새 경로가 표시됩니다. 다음 작업은ALTER TABLE수행하지 않았습니다.확인 사항세 번째 버전이 나타납니다:
v2.1: 해당 버전의 LLM 호출에만 다음이 포함됩니다:cost_usd, 그리고DESC에 다음이 표시됩니다:payload.cost_usd및payload.retries.
완성하기
체크리스트를 확인하고 결과물을 공유한 뒤 배지를 받으세요.
완료 기준
체크리스트제출하고 배지 받기
참여 → 제출 → 배지 받기.
다음 이름의 폴더에 결과물을 넣으세요: 본인의 GitHub ID. 코드와 README를 포함하고, README에 기능, 실행 방법, 스크린샷 또는 GIF 한 개, 사용한 Doris 기능을 적어주세요.
풀 리퀘스트 만들기 다음 위치에 추가하세요: morningman/demo-env 저장 경로:
doris-hackathon-glasgow-2026/<your-github-id>/. 포크 후 push하거나 GitHub의 다음 기능을 사용하세요: Add file → Upload files. 그 외에 작성할 항목은 없습니다.공유하기 공유할 곳:
#dev참여할 곳: Apache Doris Slack: 풀 리퀘스트 또는 저장소 링크와 함께 스크린샷이나 짧은 데모를 공유하세요. 질문, 제출, 배지 관련 안내는 모두 이곳에서 진행됩니다.
과제에 참여하는 모든 분께 다음 배지를 드립니다: Apache Doris Contributor 배지. Doris 프로젝트에 병합된 풀 리퀘스트가 없어도 됩니다. 마감일이 없으니 필요한 만큼 시간을 쓰세요. 팀 참여도 가능하지만 필수는 아닙니다. 모든 팀원의 GitHub ID를 다음에 적어주세요: README.
추가 도전 과제
본인의 트레이스 사용하기. 본인의 AI 코딩 에이전트 로컬 세션 로그(JSONL)를 내보내 한 줄에 하나의
CAST('…' AS JSON)를 적재합니다. 데이터는 로컬 Doris 인스턴스에 저장됩니다.Schema Template. 자주 쓰는 경로를 다음으로 고정하세요:
VARIANT<'latency_ms': INT, 'tool': STRING>(테이블 복사본에서 실행). 다음 결과와DESC쿼리 동작을 비교하세요. 데이터 적재 방법:INSERT … SELECT …, CAST(payload AS JSON): VARIANT를 템플릿이 있는 VARIANT로 바로 형 변환할 수 없습니다.어시스턴트가 탐색하게 하기. Doris MCP Server를 연결하고(과제 A2), 가장 문제가 많은 세션을 찾도록 어시스턴트에 요청하세요.
SQL로 오류 분류 (본인 API 키 사용):
AI_CLASSIFY(CAST(payload['error']['message'] AS STRING), ['timeout', 'auth', 'bad input', 'other']). 참고: LLM SQL 함수.
문제 해결 및 참고 자료
| Symptom | Fix |
|---|---|
docker: command not found (macOS) | Docker Desktop을 시작하세요. 이미 실행 중이면 CLI 링크를 연결하세요: sudo ln -s /Applications/Docker.app/Contents/Resources/bin/docker /usr/local/bin/docker |
error getting credentials 이미지를 내려받을 때 | 삭제할 항목: credsStore 필드가 있는 파일: ~/.docker/config.json (로컬 개발 환경에서만) |
port is already allocated | 다른 프로세스(주로 다른 Doris)가 9030, 8030 또는 8040 포트를 사용 중입니다. 실행 중인 컨테이너를 확인하세요(docker ps), 또는 다른 호스트 포트를 노출하세요. 예: -p 19030:9030, 이 경우 19030에 연결하세요 |
The container name "/doris" is already in use | 이전에 실행한 적이 있습니다: docker start doris. 처음부터 다시 시작하려면 docker rm -f doris (데이터가 삭제됩니다) |
컨테이너가 종료되거나 다음 상태로 바뀌지 않음: (healthy) | 확인: docker logs doris. 대개 메모리 문제입니다. Docker Desktop에 6 GB 이상을 할당하세요(Settings → Resources). Apple Silicon에서는 다음을 추가하지 마세요: --platform linux/amd64 |
permission denied: ./doris.sh | 압축 해제 도구가 실행 권한을 제거했습니다: chmod +x doris.sh, 또는 다음 실행: bash doris.sh start |
Current database is not set | 실행: USE hackathon; 를 먼저 실행하거나 다음으로 연결하세요: -Dhackathon |
score() function requires WHERE clause with MATCH function, ORDER BY and LIMIT | 다음 추가: MATCH_* 조건자, ORDER BY score() DESC 및 LIMIT그리고 다음을 유지하세요: score() 를 다른 식으로 감싸지 마세요(위의 함정 참고) |
| 10분 이상 해결하지 못한 경우 | 게시할 곳: #dev 참여할 곳: Apache Doris Slack |
참고 자료
Apache Doris 해커톤
