Uh oh!
There was an error while loading. Please reload this page.
') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })();
There was an error while loading. Please reload this page.
参赛项目名称
Attention:面向个人 Agent 的收藏与发现基础设施
团队 / 作者
Attention Team / Github:EthanSMC、pryceyang
我们做了什么
Attention 解决的是 AI 时代的信息质量与知识沉淀问题。传统内容平台依赖用户交互和分发算法组织信息;当低成本生成内容大量涌入后,重复、空洞却“看似言之有物”的 AI Slop 不断消耗用户的注意力。与此同时,平台中仍然存在真正有思考、有经验、有长期价值的内容,只是它们越来越难被识别和保留下来。
对普通用户(Consumer)而言,Attention 利用真实的人类收藏作为质量信号,帮助用户减少筛选低质量内容的时间,在公开瀑布流中发现经 Filter 判断“值得保留”的信息,并始终回到原作者和原文阅读,从而达到更好的学习效果。
对收藏者(Filter) 而言,Attention 解决的是优质信息散落和无法复用的问题。过去收藏通常分散在微信文件传输助手、浏览器书签以及不同平台的收藏夹中,之后很难再次找到。Attention 将这些跨平台收藏统一整理为可检索、可管理、可被个人 Agent 调用的知识资产;Filter 也可以通过公开收藏完成一次轻量背书,让自己筛选过的好内容获得再次被看见的机会。
Attention 不试图用 AI 生产更多内容,而是让人的判断成为过滤 AI Slop 的信号,让真正有价值的信息能够被保存、发现和持续利用。
Ver 0.1.0
Attention 第一版提供了一套统一的收藏基础设施:
用户可以提交普通链接或平台分享文案。
系统识别其中的候选链接,并完成链接标准化、来源识别和重复内容合并。
收藏可以设为公开或私密。
公开收藏进入发现瀑布流,成为 Filter 对内容的人工背书。
收藏始终保留作者、来源和“查看原文”入口。
用户可以在网页中管理收藏,也可以让自己的 Agent 通过 Attention Skill 和 Hosted MCP 操作同一份数据。
OAuth 与 API Key 均绑定真实 Attention 账号,Agent 获得的能力不会超过用户本人在网页中的权限。
Tradeoffs
使用的工具
Qwen模型:使用Qwen3.8-max作为开发模型使用;第一期产品不依赖百炼进行解析
Skill 名称:Attention Skill(自研)
其他:Attention Hosted MCP
OAuth 2.0 Authorization Code + PKCE
API Key
Next.js
PostgreSQL
Docker Compose
效果展示
Attention 当前已经提供可访问的 Staging 环境,评委可以直接体验:
项目链接(可选)
在线 Demo:https://attention-staging.noveltystudio.cn/
GitHub:https://github.com/EthanSMC/Attention
评委专用 Filter 账号:
邮箱:filter_dev@attention.com
密码:请私下联系
该账号拥有公开收藏权限,可用于体验完整的收藏与发现流程。
踩坑记录
最初的架构曾考虑由 Attention 自己运行 Agent,接收网页或微信消息,再调用收藏服务。
继续拆解后发现,第一期没有必要托管 Agent。不同用户已经在使用不同的 Agent,Attention 更应该提供一套稳定、开放的业务能力。
因此第一期最终收敛为:
用户自己的 Agent → Attention Skill → Hosted MCP → Attention Core
Attention 托管的是 MCP 服务,不是用户的 Agent,也不托管用户的模型凭据。
Skill 和 MCP 不能互相替代
Skill 适合描述工作流、调用时机、失败处理和交互规则;MCP 负责提供真实工具、鉴权、权限控制与错误协议。
如果只有 Skill,Agent 知道应该做什么,却没有统一的业务接口;如果只有 MCP,Agent 虽然看得到工具,却容易错误组合工具。
最终我们将 Skill 与 MCP 作为版本配套的集成包维护。
网页与 Agent 的能力容易出现分叉
早期设计中,网页接口和 MCP 工具分别增长,可能造成“网页能做、Agent 做不了”的问题。
后来统一了业务核心:网页和 MCP 都进入 Attention Core。收藏、查询、公开与私密、账号权限和处理状态等规则只有一份业务真相。
MCP 的实际能力也会随当前账号权限变化,而不是给 API Key 设置一套脱离用户账号的固定权限。
分享文案不等于一个干净的 URL
小红书、抖音和微信公众号的分享内容通常包含标题、提示语、短链接和多个候选地址。
第一版如果只用正则取第一个 URL,很容易收藏错误目标。因此系统会先提取候选链接;无法唯一判断时要求用户确认,而不是静默收藏一个可能错误的地址。
链接重复比预想中复杂
同一内容可能存在短链接、重定向链接、带追踪参数的链接和不同分享入口。
如果只按用户提交的原始字符串去重,同一内容会重复出现。Attention 因此区分:用户提交的原始输入;解析出的候选链接;跟随重定向后的最终链接;用于平台内去重的规范化链接。
收藏操作还需要具备幂等性,避免 Agent 重试时制造重复数据。
外部内容读取失败不能等于收藏失败
平台反爬、登录墙和链接暂时不可达都可能导致元数据获取失败。
最后采用的原则是:先保存收藏,再异步处理。即使暂时无法获得摘要或元数据,也保留原始链接,并明确显示处理状态,不伪造内容,也不因为一次抓取失败删除收藏。
OAuth 成功不代表 MCP 已经可用
仅仅完成浏览器授权或写入配置,并不能证明 Agent 真正完成接入。
因此安装验收不是“配置文件存在”,而是要求 Agent 实际调用:
attention_get_my_account
只有该工具返回当前 Attention 账号,才认为 OAuth、MCP 和账号权限已经完整连通。
多个 Agent 宿主的安装方式并不统一
Codex、Claude Code、OpenClaw、Hermes 和 WorkBuddy 对 Skill、MCP 与 OAuth 的加载方式不同。
我们没有假设所有客户端共用一套安装命令,而是为不同宿主维护独立接入文档,同时让它们加载同一份 Attention Skill、调用同一个 Hosted MCP,并采用相同的最终验收标准。
不应把Agent绑定进业务核心
Attention 的价值是收藏、权限、公开背书、检索和跨 Agent 调用,而不是绑定某个Agent去生成内容。
最终用户可以选择自己的 Agent 和模型,Attention 只提供稳定、可授权的业务基础设施。