这是我为以下挑战提交的作品:Sanity Challenge,路径一:交付一个能查询真实内容的智能体
我构建了什么
几周前,我在查看我的 DEV.to 仪表盘时,注意到一个奇怪的现象,就在流量来源面板里。浏览量来自一些我从未听说过的网站——随机的博客、内容农场、我从未访问过、更别说发布过内容的网站。
于是我点了进去。果然:我的文章,一字不差地出现在别人的网站上。有些标注了出处。大多数没有。
那一刻的“等等,这是……被盗了吗?”是每个内容创作者最终都会遇到的。但要判断一次转载究竟是真正的抄袭,还是带署名的合法交叉发布,却出奇地困难。你得手动比对文本,检查他们是否提到了你的名字,看看他们是否加了回链。如果没加呢?那就祝你能从零开始起草一份 DMCA 通知吧。
OriginTrace 把这一切都自动化了。
它是一个 AI 驱动的内容溯源智能体,具备三项核心能力:
🔍 扫描器——粘贴任意 DEV.to 文章 URL。OriginTrace 会提取具有辨识度的短语,通过 Google(Serper API)搜索整个网络,抓取最多 15 个候选页面,并运行多信号重叠分析(词重叠、5-gram 切片、LCS 比率)来判断它们是否抄袭了你的内容。
⚖️ 裁决引擎——对于发现的每一处抄袭,OriginTrace 不会只说“匹配上了”。它会检查结构化的署名证据:抄袭内容是否提到了原作者的名字?是否链接回原始文章?是否使用了诸如“via”或“originally published on”之类的署名短语?基于这些布尔信号,它会给出裁决:
credited_syndication(合法,无需处理)或unattributed_repost(该发 DMCA 了)。💬 Sanity AI 智能体——这是路径一提交作品的核心。一个完全由以下驱动的对话式 AI 智能体:Sanity Knowledge Base 和 Context MCP。它让你可以对话你的溯源数据。问它:“哪个抄袭者的重叠度最高,却仍然给我署了名?”或者“为最恶劣的侵权者起草一份 DMCA 下架通知。”该智能体会动态查询 Sanity 中的结构化内容,并给出可操作、有来源支撑的答案。
每篇文章、每次来源检查、每个署名信号以及每个 DMCA 模板,都以结构化 JSON 文档的形式持久化存储在 Sanity 的 Content Lake 中——形成一份不可变、可查询的账本,记录谁抄袭了什么、何时抄袭,以及是否注明了出处。
演示
🔗在线应用:origintrace.onrender.com
📺视频演示:
要测试 Sanity AI 智能体:
(注意:为获得最佳体验,请先在首页扫描一篇 DEV.to 文章,这样 Sanity 知识库中就有你的结构化数据可供查询!)
访问origintrace.onrender.com/chat并尝试提问:
- “哪篇文章的重叠百分比最高?”
- “排名第一的抄袭者是否注明了原作者?”
- “为最严重的侵权者起草一份 DMCA 下架通知。”

AI 智能体通过 Context MCP 查询 Sanity,并以透明的“智能体思考”区块展示其推理过程。

结构化响应:智能体分析了来自 Sanity 的布尔型署名字段,渲染出裁决表格,并得出结论认为无需发出 DMCA,因为抄袭内容已正确注明了作者。

扫描器首页——粘贴一个 DEV.to URL,程序化流水线便会爬取网络寻找抄袭副本。

扫描结果包含重叠百分比、署名裁决和 DMCA 生成——全部由 Sanity 提供支持。
代码
JaniDhruv/OriginTrace
OriginTrace 使用 Sanity 知识库协调作为来源溯源引擎。你发布的文章是权威来源;被检查的 URL 会成为知识库来源;Context 的协调功能通过 MCP 呈现带有引用的冲突。
OriginTrace
AI 驱动的文章抄袭侦探与 DMCA 智能体
为以下而构建路径一:交付一个可查询真实内容的智能体—— Sanity Challenge 2026
🧠 太长不看
粘贴一个 DEV.to 文章 URL → OriginTrace 的程序化流水线爬取网络寻找被盗副本 → 结构化证据被持久化到Sanity Content Lake→ 然后与AI 智能体(由 NVIDIA NIM +Sanity Context MCP驱动)对话,以分析数据、判定署名裁决、生成 DMCA 下架通知,甚至直接从聊天中触发新的扫描。
为什么这只有配合结构化内容才能实现:该智能体不会用关键词搜索答案——它会查询布尔归因标志(hasAuthorName,hasOriginalLink)、精确overlapPercent整数,以及来自 Sanity 的verdict枚举。关键词搜索永远无法回答"哪个抄袭者的重合度最高,却仍然标注了……
我如何使用 Sanity
核心论点:这个智能体之所以能工作,只是因为内容是有结构的
OriginTrace 的 AI 智能体不做关键词搜索。它查询类型化字段在 Sanity 中——布尔值、整数、枚举和字符串数组——来回答那些用非结构化文本不可能回答的问题。
这里有一个具体的例子。当用户问:
"哪个抄袭者的重合度最高,却仍然标注了原作者?"
该智能体连接到Sanity Context MCP并查询 Content Lake。它需要:
- 筛选
provenanceCheck满足以下条件的文档verdict != "no_match" - 按
overlapPercent(一个数字字段)降序排序 - 检查
attribution.hasAuthorName(一个布尔值)和attribution.hasOriginalLink(一个布尔值) - 读取
attribution.signals(一个字符串数组),以获取找到的确切署名条目
世界上没有任何关键词搜索能回答这个问题。答案需要将一个数字与布尔值跨多个文档进行比较。这就是结构化内容重要的原因。
Sanity Schema 设计
四种文档类型支撑起一切:
article—— 权威的事实来源。当用户扫描一个 DEV.to URL 时,流水线会抓取该文章,提取其内容,并将其持久化到 Sanity 中,同时引用其author。这确立了来源声明。
provenanceCheck—— 证据记录。在网络上发现的每个候选副本都会有自己的检查文档,其中包含:
-
overlapPercent(数字)—— 来自多信号 NLP 引擎的相似度分数 -
verdict(枚举)——credited_syndication,unattributed_repost,或no_match -
attribution(对象)—— 结构化布尔值:hasAuthorName,hasOriginalLink,hasAttributionPhrase,isProperlyAttributed,外加signals[]和missing[]数组 -
dmcaTemplate(文本)—— 预生成的法律下架通知 -
reconciledEntries(数组)—— 原文与副本之间匹配的段落
author—— 被文章引用,包含name和handle用于归因匹配。
user—— 账户级文档,包含email,authId,以及avatarUrl用于未来的按用户门控。
Sanity Context MCP 集成
AI 智能体通过AI SDK(@ai-sdk/mcp使用 HTTP 传输)连接到 Sanity Context MCP。在运行时,该智能体:
- 调用
initial_context以获取知识库大纲 - 使用
groq_query动态编写并执行针对 Content Lake 的 GROQ 查询 - 解释结构化的 JSON 结果(布尔值、数字、数组),以形成人类可读的答案
- 在其回答中引用源文档
// 1. 连接到 Sanity Context MCP
const mcpClient = await createMCPClient({
transport: {
type: 'http',
url: SANITY_CONTEXT_ENDPOINT,
headers: { Authorization: `Bearer${mcpToken}` },
},
});
// 2. 获取 MCP 工具并与我们自定义的 scan_article 动作工具合并
const mcpTools = await mcpClient。tools();
const tools = { ...mcpTools, scan_article: scanArticleTool };
// 3. 使用 NVIDIA NIM 流式输出响应,并注入 Sanity 工具
const result = streamText({
model: nim。chatModel('nvidia/nemotron-3.5-lightning-30b-a3b'),
tools,
messages,
system: agentSystemPrompt,
});
预取的结构化上下文(不只是 MCP)
在智能体开始推理之前,后端会运行一个专用fetchSanityContext()函数,直接对 Sanity 执行 GROQ 查询,构建所有已索引文章及其来源校验的结构化快照——包括每篇文章的可操作数与计入数的细分、归属布尔值、重叠百分比以及平台聚合统计。这个预取的上下文会被注入到智能体的系统提示中,使其能够即时回答摘要类问题("有多少篇文章已被扫描?"),而无需消耗一次 MCP 往返。
智能体实际如何处理检索到的内容
| Sanity 字段 | 类型 | 智能体行为 |
|---|---|---|
overlapPercent |
number |
对抄袭内容进行排名:"最高重叠率为 98%" |
verdict |
string(枚举) |
筛选结果:"发现 3 篇未署名的转载" |
attribution.hasAuthorName |
boolean |
评估署名:"该抄袭内容确实提到了你的名字" |
attribution.hasOriginalLink |
boolean |
评估反向链接:"没有指向原帖的链接" |
attribution.signals |
string[] |
解释存在何种署名:"使用了 'via' 并链接到原文" |
attribution.missing |
string[] |
解释缺失项:"缺少作者姓名和原始 URL" |
dmcaTemplate |
text |
检索并定制预生成的 DMCA 通知 |
Sanity 项目详情
Sanity 项目 ID:iossngh3
数据集:production
该 schema 以 TypeScript 定义于sanity/schemas/——核心文档是provenanceCheck.ts及其嵌套的attribution对象,其中包含布尔证据字段。
智能体会话
该项目在挑战期内通过多次智能体辅助的 IDE 会话迭代构建。README包含智能体架构、schema 设计决策以及完整流水线流程的全面技术解析——包括展示每个组件如何连接的 ASCII 架构图。
技术亮点
⚡ 借助 NVIDIA NIM 实现闪电般快速的智能体推理
使用工具(如 Sanity Context MCP)的 Action Agents 在最终回复用户之前需要多轮“思考”循环。如果底层 LLM 速度慢,聊天体验就会感觉像坏了一样。
为了解决这个问题,OriginTrace Agent 由NVIDIA NIM驱动,具体运行的是Nemotron 3.5 Lightning模型。这提供了摄取复杂 Sanity JSON 负载所需的大规模上下文窗口,同时提供实时工具调用所需的超低延迟。
🧠 自定义多信号重叠引擎
我们不想仅仅依赖基础的关键词匹配。OriginTrace 在 TypeScript 中实现了一个强大的 NLP 引擎,能够抵御常见的抄袭混淆手段(如删除句子或替换同义词)。它通过加权三重算法计算总体overlapPercent得分:
- 词重叠(20%):共享词汇的 Jaccard 相似度。
- 5-Gram 切片(50%):通过匹配重叠的 5 词片段来检测结构相同的段落。
- 最长公共子序列 / LCS(30%):通过查找最长的连续相同字符串来处理重排序和插入词。
未来增强(超越黑客松)
OriginTrace 目前是在黑客松范围内构建的功能完整的原型。AI Agent 和 Sanity Ledger 目前处于Beta阶段,作为一个全球公共账本(类似于区块链浏览器)运行,任何人都可以查询所有已扫描的 DEV.to URL。
以下是未来路线图:
- 🔐 用户认证:添加 NextAuth/JWT,使每位作者拥有私有仪表板和个人 AI Agent 会话,仅查询其特定的来源数据。
- 🌐 多平台支持:将扫描器扩展到索引 Hashnode、Medium 和个人博客。
- 📬 自动监控:基于 Cron 的定时扫描,在出现新的盗用副本时主动提醒作者。
- 🤖 改写检测:用 LLM 嵌入升级 NLP 引擎,以捕获规避传统词重叠算法的“改写”内容。
技术栈
| 组件 | 技术 |
|---|---|
| 前端 | Next.js 16(App Router) |
| AI Agent LLM | NVIDIA NIM(Nemotron 3.5 Lightning) |
| Agent 框架 | AI SDK(ai+@ai-sdk/mcp+@ai-sdk/react) |
| MCP 桥接 | Sanity Context MCP(HTTP 传输) |
| 知识库 | Sanity Content Lake(GROQ 查询) |
| 网络搜索 | Serper.dev(Google Search API) |
| NLP 引擎 | 自定义 TypeScript(词重叠、5-gram 分片、LCS) |
| 内容提取 | Mozilla Readability + Cheerio + JSDOM |
| 部署 | Render |
感谢阅读!如果你曾在自己没有发布过的网站上看到自己的内容,OriginTrace 就是为你而生的。🛡️

