尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
在 Sentry 中开发 Seer Embed 组件:从 Zod Schema 到后端代码生成的完整实践指南
在 Sentry 中开发 Seer Embed 组件从 Zod Schema 到后端代码生成的完整实践指南【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentrySeer 是 Sentry 中由大模型驱动的智能代理agent它在回答用户问题时会产生 Markdown 输出。Seer Embed 是渲染在 Seer Markdown 输出中的富交互组件采用 Markdoc 风格的标签语法{% name %}{ ... }{% /name %}嵌入文本流每个 embed 由 Zod Schema、React 组件与注册表条目三部分组成。本文基于 .agents/skills/seer-embed/SKILL.md 展开结合仓库内真实源码与测试完整讲解从 schema 定义、组件编写、注册、代码生成到验证、特性开关feature flag门控的全流程读完即可为 Seer 新增一个可被 LLM 生成、可被前端渲染的 embed 组件。一、Seer Embed 的工作原理在深入操作步骤前先理解 embed 在整条链路中的位置。Seer 的 markdown 输出会先被前端做流式逐块重新词法分析与重新渲染因此标签语法LLM 在输出中写{% myEmbed %}{someField:hello}{% /myEmbed %}标签体内的内容是 JSON 格式的数据载荷数据校验前端用该 embed 对应的 Zod schema 对 JSON 载荷做解析校验失败时丢弃该组件并告警生产环境通过 Sentry 上报注册表分发渲染器从注册表按标签名查组件命中则渲染未命中则上报 no renderer for tag 后返回 null渲染器实现契约双端共享同一个 Zod schema 通过代码生成脚本导出为 JSON Schema写入后端文件后端再将其注入发给 Seer agent 的系统提示词让 LLM 知道何时该输出哪个标签、标签体该长什么样。所以一个 embed 实际跨越三层前端 schemaTS/Zod→ 生成产物JSON Schema→ 后端过滤与注入。这也是为什么新增 embed 需要前端组件、注册表、代码生成三处配合。二、开始前的准备动手前先完成三项确认对应 SKILL.md 的 Before You Start阅读 schemas.ts了解现有 embed 的 schema 写法与命名习惯——当前仓库已内置 20 个 embed覆盖timestamp、docs、dashboard、dsn、user、issue/issues、replay、release、chart、autofix/autofixRef、alert、monitor、savedIssueView、savedQuery、trace、profile、各类查询 embedissuesQuery、errorsQuery、spansQuery、logsQuery、replaysQuery、metricsQuery以及结构化 embedagentWriteApproval阅读 index.ts查看已注册的 embed 列表确认要新增的 embed 名称不存在避免与既有注册表条目冲突。SEER_EMBED_SCHEMAS与STRUCTURED_SEER_EMBED_SCHEMAS两个对象通过ALL_SEER_EMBED_SCHEMAS合并SeerEmbedName类型由keyof typeof ALL_SEER_EMBED_SCHEMAS推导因此 schema 中每新增一个 key全链路的 TypeScript 类型都会随之收紧schemas.ts。三、Step 1定义 Zod Schema在 schemas.ts 的SEER_EMBED_SCHEMAS对象中追加条目export const SEER_EMBED_SCHEMAS { // ...existing entries myEmbed: { description: One sentence describing what this embed does—this passes through directly to the LLMs system prompt., level: [inline], // inline, block, or both schema: z.object({ // Define the data shape the LLM will produce someField: z.string(), optionalField: z.number().optional(), }), examples: [{label: Basic, data: {someField: hello}}], // featureFlag: organizations:seer-explorer-my-embed, // optional }, } as const satisfies Recordstring, SeerEmbedSchema;SeerEmbedSchema接口schemas.ts由五个字段组成description、level、schema、可选的examples与可选的featureFlag。关键决策点description写给 LLM 看。它会被原样注入 LLM 的系统提示词LLM 依据它决定何时输出该 embed。描述要具体说明使用场景并明确唯一方式类约束。例如timestamp的 description 明确要求所有 datetime 值都必须用本 embed绝不允许输出裸时间文本issue则声明引用 Sentry issue 的唯一方式禁止出现在 Markdown 表格或列表中。这类约束直接决定了 LLM 行为的正确性。levelinline 与 block 两种形态。[inline]流式内嵌在文字中的小部件时间戳、徽标、紧凑链接[block]独占一行的富卡片图表、表格、数据预览两者都列embed 具备自适应的双形态组件render会收到第二参数level来分支渲染。注意 examples 里可用level覆盖单个示例的形态只有与 schema 默认level数组首项不同时才需要显式设置。schema用 Zod保持扁平简单。LLM 必须产出合法 JSON因此字段越简单、约束越明确LLM 越不容易出错。仓库中的成熟实践包括用.default()为可选字段提供合理默认值例如format: z.enum([absolute, relative]).default(absolute)、mode: z.enum([samples, aggregate]).default(samples)用.enum()约束字符串取值例如chart的visualization: z.enum([line, area, bar])、x_axis: z.enum([time, category])、y_axis_unit: z.enum([number, percentage, duration, bytes])用.describe()给关键字段补充给 LLM 的说明例如yAxes字段的Aggregate functions to chart, e.g. count() or p95(span.duration)需要跨字段约束时用superRefine如chart中category 轴仅支持 bar 图time 轴数值必须是带偏移的 ISO 8601 时间戳schemas.ts查询类 embed 通过对象展开复用公共字段pageFilterFieldsprojects/environments/statsPeriod/start/end被所有查询 embed 共享exploreQueryFields进一步叠加 query/mode/groupBy/yAxes 等schemas.ts。statsPeriod用正则^\d[smhdw]$约束相对时间写法如24h、7d。一个容易踩的坑agents 经常输出裸数字作为 ID所以idString保留为z.union([z.string(), z.number()])不要加.transform()否则代码生成脚本无法导出 JSON Schemaschemas.ts。该设计有测试覆盖schemas.spec.ts断言spansQuery接受数字型 project ID且导出的 JSON Schema 中projects的items为anyOf: [string, number]schemas.spec.ts。examplesfew-shot 示例。每个data必须能通过 schema 校验。它们会被放入发送给 LLM 的生成 JSON 中作为少样本示例在 stories 页面中同一 embed 的所有 examples 会被组合进一个 Markdown 块通过单个SeerMarkdown渲染——inline 示例包裹在散文文本中block 示例追加在末尾。用多个 examples 展示不同的属性组合或 block/inline 形态差异。featureFlag可选门控。设置后后端在该 flag 关闭时会把这个 embed 从发给 LLM 的 schema 中过滤掉详见第七节。四、Step 2创建组件在 embeds/components/ 下新建name.tsximport {defineSeerEmbed} from sentry/components/seer/markdown/embeds/utils; export const MyEmbed defineSeerEmbed({ name: myEmbed, // must match the key in SEER_EMBED_SCHEMAS render({someField, optionalField}) { // Props are typed from the Zod schema — already validated return span{someField}/span; }, });defineSeerEmbed为你做了什么查看 utils.tsx 的源码实现按名称查找 Zod schema从ALL_SEER_EMBED_SCHEMAS[name]取到 schemasafeParse校验dataprop解析失败返回null绝不渲染脏数据开发环境警告 / 生产环境 Sentry 上报由于 markdown 在每个流式 chunk 都会重解析重渲染非法 props 理论上每 chunk 报一次——reportInvalidEmbed用reportedInvalidEmbedsSet 按name:codepath去重保证每个不同失败每页只上报一次开发环境console.warn生产环境以seer_embed.name为 tag、seer-embed-invalid-props为 fingerprint 上报captureExceptionutils.tsx设置displayNameEmbed.displayName name注册表以它为 key。编写规则name必须与SEER_EMBED_SCHEMAS中的 key 完全一致render第一参数是Zod 输出类型EmbedOutputN即已经过解析与校验、带默认值的 props天然获得完整类型推导若 schema 的level同时含inline与blockrender收到第二参数levelinline | block用于分支保持组件简单优先复用现成 Sentry 组件DateTime、TimeSince、Link等而不是从零手写组件拿不到任何上下文它不知道自己在页面哪里出现只拿到标签体内的数据。这也是 embeds/README.md 强调的约束来源——embed 的交互必须自包含不要把 host 路由的location/navigate传入 widgetlegend 选择、排序、列宽等 UI 状态要存在 embed 内部widget 动作要么本地处理要么禁用。常见组件形态参考仓库中已注册的组件覆盖了几种典型形态可作为参考实现纯内联轻组件timestamp.tsx、docs.tsx、user.tsx、dsn.tsx等单文件组件inline/block 双形态issue、replay、release、dashboard、alert、monitor等含 block 预览的查询类errorsQuery、spansQuery、logsQuery、issuesQuery、replaysQuery、metricsQuery会拉取数据并渲染图表/表格如 errorsQuery 的 block 形态在聚合图上叠加最多前五行匹配记录结构化 embedagentWriteApproval.tsx用于浏览器会话中请求 Sentry API 写权限授权schema 中requiredScopes直接取用API_ACCESS_SCOPES常量枚举schemas.ts。五、Step 2b组件长成一个目录时如何拆分链接型 embed 保持单文件即可。一旦 embed 要渲染 block 预览——需要拉数据、懒加载重型视图或按子类型分支——就把它升级为目录让 reviewer 一次只读一个关注点。仓库中monitor、alert、dashboard均已采用该模式。以monitor为例monitor/components/monitor/ monitor.tsx # defineSeerEmbed only: inline link vs lazily imported block monitorLink.tsx # the inline level monitorBlock.tsx # default export: fetch, card chrome, dispatch monitorTypes/ # one file per subtype, when the embed has subtypes cron.tsx uptime.tsx monitor.spec.tsx # colocated, not in resourceEmbeds.spec.tsx入口name.tsx只负责按 level 挑选渲染哪个形态const LazyMonitorBlock lazy(() import(./monitorBlock)); export const Monitor defineSeerEmbed({ name: monitor, render(props, level) { if (level block) { return LazyLoad LazyComponent{LazyMonitorBlock} {...props} /; } return MonitorLink {...props} /; }, });该模式的关键约定目录没有index.tsx入口文件按 embed 命名monitor/monitor.tsx并在 embeds/index.ts 中显式导入入口只放defineSeerEmbed level 分发block 需要的一切都放在lazy(() import(./nameBlock))后面、以default导出lazy()的要求。这样文本中内联提到资源时不会把沉重的 block 拉进 bundle——dashboard与monitor都遵循此约定子类型按变化轴建目录当 block 按子类型分支detector 类型、widget 类型时每个分支一个文件放在以变化轴命名的兄弟目录monitorTypes/而不是容易读成 TypeScript 类型的types/block 里用单个switch分发。新增子类型 新文件 一个 case而不是在两个散落的长 switch 里改来改去公共条件在 block 里推导一次、以 props 下传不要在每个变体文件里重复推导旧单文件里的两处 switch 难以同步正是该约定要解决的问题测试就近放置规格测试写成name.spec.tsx并与组件同目录使用embeds/testUtils.tsx里共享的renderEmbed/hrefFor辅助函数。resourceEmbeds.spec.tsx只放链接级 embed——它被所有 embed 共享block embed 往里加 case 会造成持续冲突。六、Step 3注册组件在 embeds/index.ts 中导入组件并加入embeds数组import {MyEmbed} from ./components/myEmbed; import {Timestamp} from ./components/timestamp; import {SeerEmbedRegistry} from ./registry; const embeds [Timestamp, MyEmbed]; for (const embed of embeds) { SeerEmbedRegistry.register(embed.displayName, embed); }注册表本身是一个模块级Mapstring, RegisteredEmbed提供register/get/list三个方法key 即defineSeerEmbed设置的displayNameregistry.tsx。渲染端在 SeerMarkdown 中通过SeerEmbedRegistry.get(name)查组件命中则渲染block 形态包一层带间距的Container未命中则调用reportUnhandledTag上报后返回 null而不会像普通 Markdown 那样把未知标签当纯文本回显——未知标签的告警同样按名去重、每页只报一次index.tsx。七、Step 4重新生成后端 Schema运行代码生成脚本把前端 Zod schema 同步为后端发给 Seer agent 的 JSON Schemapnpm gen:embed-widgets该脚本实现在 scripts/genEmbedWidgets.ts核心逻辑是调用seerEmbedsToJsonSchemas()——遍历SEER_EMBED_SCHEMAS把每个条目的description、level、examples、featureFlag与z.toJSONSchema(def.schema)导出的body组装为 widget 定义schemas.ts——写入src/sentry/seer/agent/embed_widgets.generated.json并用pnpm oxfmt格式化保证字节级稳定让 CI 用git diff就能校验新鲜度。必须提交这个生成文件它被版本管理不在 gitignore 中。生成样例timestamp条目可见 embed_widgets.generated.jsonbody是标准的 JSON Schema含$schema、type、properties、requiredLLM 靠它学会输出合法载荷。后端侧embed_widgets.py 在进程启动时加载该生成文件get_embed_widgets(organization, actor)负责按 feature flag 过滤entry 带featureFlag且组织未开启该 flag 时会被剔除无 flag 的 widget 恒包含过滤逻辑内部通过features.has(w[featureFlag], organization, actoractor)判定embed_widgets.py。八、Step 5验证新增 embed 后按顺序验证Lint对新文件运行pnpm run lint:js类型运行pnpm run typecheck确认 schema 类型在组件、注册、渲染链路上正确流转——render参数由 Zod 输出类型推导schema 改动会即时暴露类型错误手动测试在 Seer Explorer 中触发会使用该 embed 的响应或直接用SeerMarkdown渲染原始标签做本地验证SeerMarkdown raw{{% myEmbed %}{someField:hello}{% /myEmbed %}} /此外仓库还提供两层自动化保障可参考schema 契约测试schemas.spec.ts 断言seerEmbedsToJsonSchemas()的输出如 replay 的时间戳偏移要求在 agent 契约中可见、数字 project ID 在 JSON Schema 中以anyOf导出——新增 embed 时可仿照补充此类断言组件级测试目录化 embed 使用共享的renderEmbed/hrefFor辅助函数写*.spec.tsx例如 monitor.spec.tsx、alert.spec.tsx并可通过stories下的 story 页面直观检查渲染效果。九、可选用 Feature Flag 门控新 embed如果需要逐步灰度而非直接全量上线在 schema 条目上加featureFlag: organizations:seer-explorer-name在 src/sentry/features/temporary.py 注册该 flag——仓库中同族 flag 均以OrganizationFeatureFeatureHandlerStrategy.FLAGPOLE注册如organizations:seer-explorer、organizations:seer-explorer-embeds、organizations:seer-agent-autofix等后端 embed_widgets.py 会自动通过features.has()过滤带 flag 的 widgetflag 关闭时该 embed 不会出现在发给 LLM 的 schema 中——前端组件可以保留注册因为 LLM 根本不会生成对应标签。注意区分autofix与autofixRef两个 embed 共用organizations:seer-agent-autofixflag其余 embed 的 flag 命名遵循organizations:seer-explorer-name惯例。十、文件清单速查文件职责static/app/components/seer/markdown/embeds/schemas.ts新增 Zod schema 条目static/app/components/seer/markdown/embeds/components/创建defineSeerEmbed组件渲染 block 时改用目录结构static/app/components/seer/markdown/embeds/index.ts导入并注册src/sentry/seer/agent/embed_widgets.generated.json由pnpm gen:embed-widgets重新生成并提交scripts/genEmbedWidgets.ts前端 → 后端的代码生成脚本src/sentry/seer/agent/embed_widgets.py后端按 feature flag 过滤 widget 定义结语Seer Embed 的整套机制可以概括为一条单点定义、双端生效的契约链在 schemas.ts 里用 Zod 定义一次数据形状前端组件据此获得强类型 props 与运行时校验后端通过代码生成拿到 JSON Schema 注入 LLM 提示词feature flag 再为上线节奏提供灰度能力。新增 embed 时只需遵循本文的五个步骤加 schema、写组件、注册、重生成、验证必要时辅以目录化拆分与 flag 门控即可让 Seer 的答案从一段文字升级为一个可交互的富组件。【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentry创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

OI-wiki 归并排序全解析:稳定分治排序、合并过程与逆序对计数

OI-wiki 归并排序全解析:稳定分治排序、合并过程与逆序对计数

OI-wiki 归并排序全解析:稳定分治排序、合并过程与逆序对计数 【免费下载链接】OI-wiki :star2: Wiki of OI / ICPC for everyone. (某大型游戏线上攻略,内含炫酷算术魔法) 项目地址: https://gitcode.com/GitHub_Trending/oi/O…

📅 2026/9/11 9:28:26
Mac上跑本地大模型:Ollama安装、模型选型与性能优化实战

Mac上跑本地大模型:Ollama安装、模型选型与性能优化实战

前两天有个做剪辑的朋友问我:他手里是一台 16GB 内存的 M2 版 MacBook Pro,想试试最近被到处安利的 Ollama 本地大模型,又担心机器带不动、装完吃灰。我跟他说,在 Mac 上跑本地大模型,真正卡你的不是硬件,而…

📅 2026/9/11 9:28:26
Java音视频处理实战:Spring Boot与FFmpeg集成指南

Java音视频处理实战:Spring Boot与FFmpeg集成指南

1. 音视频场景在Java技术栈中的核心地位 音视频处理能力已成为现代互联网应用的标配功能。从抖音、快手这类短视频平台,到在线教育、视频会议系统,再到智能家居的实时监控,音视频技术渗透到了互联网产品的各个角落。作为Java开发者&#xff0…

📅 2026/9/11 9:28:26
MORE NEWS

更多资讯

📰

零门槛上手Maestro录制:4步生成第一个完整的测试视频

零门槛上手Maestro录制:4步生成第一个完整的测试视频 【免费下载链接】Maestro Painless E2E Automation for Mobile and Web 项目地址: https://gitcode.com/GitHub_Trending/ma/Maestro 你给同事演示功能、给开发提交bug,大概率都需要一段"操作过程"的视频。…

📰

免费管好全部 IT 资产:开源资产管理系统 Snipe-IT 落地实操

免费管好全部 IT 资产:开源资产管理系统 Snipe-IT 落地实操 【免费下载链接】snipe-it A free open source IT asset/license management system 项目地址: https://gitcode.com/GitHub_Trending/sn/snipe-it 审计前一周,你还没查清 50 台笔记本分…

📰

GHelper 免费开源:5 分钟配好华硕笔记本的性能控制与风扇曲线

GHelper 免费开源:5 分钟配好华硕笔记本的性能控制与风扇曲线 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenb…

📰

基于STM32F4的机房巡检机器人环境监测系统设计与实现

简介:这套基于STM32F4微控制器的机房巡检机器人环境监测系统源码包,面向嵌入式开发者和物联网项目学习者,适用于机房环境数据采集与报警场景。系统通过RS485协议连接WT2000TUG温湿度大气压一体传感器与毕达斯火灾烟雾传感器,可实时…

📰

如何 30 分钟本地部署 Duix-Avatar:离线 AI 数字人完整教程

如何 30 分钟本地部署 Duix-Avatar:离线 AI 数字人完整教程 【免费下载链接】Duix-Avatar 🚀 Truly open-source AI avatar(digital human) toolkit for offline video generation and digital human cloning. 项目地址: https://gitcode.com/GitHub_T…

📰

Golang-Gin 框架写的免杀平台,内置分离、捆绑等多种BypassAV方式

Golang-Gin 框架写的免杀平台,内置分离、捆绑等多种BypassAV方式 Golang-Gin 框架写的免杀平台,内置分离、捆绑等多种BypassAV方式。 cool 时间线: Golang Gin 框架写的免杀平台- (2021.11.12)Golang Gin 框架写的免杀平台,更…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬