赛道 A · 任务 A4
Agent 链路 分析工具
AI Agent 输出的 JSON 结构可能随每次发布而变化。将其存入 VARIANT,像查询普通列一样访问。
- 60–120 分钟
- 进阶任务
- 任意语言 · CLI、Notebook 或网页
- 欢迎使用 AI 工具
遇到问题? 可在 #dev 提问,频道位于 Apache Doris Slack。维护者和其他参与者会提供帮助。
准备环境
了解构建目标、Doris 如何简化实现,并启动集群。
构建目标
Agent 的 JSON 结构复杂,也能直接查询。
LLM 调用、工具调用、错误和最终回答各有不同字段,新版本还会加入新字段。Doris 的 VARIANT 会自动将这些 JSON 路径转为有类型的列,让您通过普通 SQL 过滤、搜索和聚合,无需迁移 Schema。
构建链路分析工具,数据包含 1,000 个 AI Agent 会话,约 20,000 个事件:
- ,提供 会话列表 ,显示事件数、错误数、Token 数和延迟;
- 一个 会话时间线,点击会话即可逐步查看 Agent 行为;
- 工具与模型统计,包括 p95 延迟、错误率和 Token 用量;
- JSON 内部检索,找出所有提到 timeout 的工具错误。
随后回答: 13:00 部署 Agent v2 后,效果变好还是变差?
草图位于代码面板顶部
Doris 为何适合这个项目
无需预定义 Schema 的写入时建模。 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 :每个 Agent 事件一行,事件正文存于 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");示例 Payload,注意它们的结构差异:
{"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 或之后开始的会话运行 Agent v2,Payload 中标明 "agent_version": "v2",v2 的 LLM 调用还新增 cache_hit 标记。里程碑 5 会进一步导入表中从未出现过的新字段。
代码面板 → 建表:完整表定义
借助 AI 构建
推荐与 AI 编程助手协作完成任务。 Doris 4.x 的搜索与 AI 能力较新,可能晚于多数模型的训练数据。在让助手编写 SQL 前,请先提供正确上下文:
将下方 上下文提示词 粘贴给编程助手。
让助手阅读本页列出的 参考文档 以及入门包中的
AGENTS.md文件。许多编程助手会自动读取该文件。将 SQL 接入代码前,要求助手先在您的实际集群中运行每条语句。
还可以连接 Doris MCP Server ,参见 任务 A2,让助手自行检查 Schema 和测试查询。
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 不能直接用作 Key 列或关联列 |
期望通过 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 Shell 中执行:
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;围绕这些查询构建界面:会话列表 → 时间线 → 统计。
检查点会话
s-0042包含 25 个事件:提问、LLM 与工具调用,其中一次sql_query调用失败后重试,最后生成回答。观察 Schema 演进
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,说明项目功能、运行方式、使用的 Doris 能力,并附一张截图或 GIF。
提交 Pull Request ,将成果添加到 morningman/demo-env ,目录为
doris-hackathon-glasgow-2026/<your-github-id>/。可以先 Fork 后推送,或使用 GitHub 的 Add file → Upload files(添加文件 → 上传文件)。无需填写其他内容。分享成果 到
#dev( Apache Doris Slack):附上 Pull Request 或仓库链接,以及截图或简短演示。问题交流、成果提交和徽章领取都在该频道处理。
每位参与任务的开发者都可获得 Apache Doris 贡献者徽章,无需先有 PR 合入 Doris。任务没有截止日期,可按自己的节奏完成。可以组队,也可独立参与;请将每位成员的 GitHub ID 写入 README.
进阶目标
使用自己的链路。 导出您使用的 AI 编程助手本地会话日志(JSONL),导入 Doris,每行一个
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 Key:
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 | 9030、8030 或 8040 已被其他程序占用,常见原因是另一个 Doris 实例。先查看运行中的容器(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 的 Settings → Resources 中分配至少 6 GB。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 黑客松
