dsh-ai-news
DeepSeek Harness AI 新闻插件:多源聚合(HN/ArXiv/官方博客/中文媒体)+ 侧边栏大弹窗新闻流 + 面板内 LLM 总结
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 21, 2026
- Updated
- Aug 21, 2026
Introduction
dsh-ai-news
DeepSeek Harness AI 新闻插件:多源聚合最新 AI 动态(国际媒体 / 中文媒体 / 论文),侧边栏一键打开大弹窗新闻流(2-3 列自适应),每条新闻支持卡片内一键 LLM 总结。
能力
| 能力 | 说明 |
|---|---|
| 多源聚合 | Hacker News(AI 过滤)、ArXiv(cs.AI 论文)、OpenAI Blog、Google AI Blog、The Verge AI、量子位、InfoQ 中文,共 7 个默认源(全部可在 Config 增删) |
| 分类筛选 | 全部 / 国际 / 中文 / 论文 四个 tab,切换即按来源类型过滤(maxDays 窗口内) |
| 时间窗口 | 只显示最近 N 天新闻(Config.maxDays,默认 3;缓存保留全量,仅显示层过滤) |
| 排序规则 | 发布时间降序(最新在前);时间戳缺失的条目沉底(源未提供时间时用抓取时刻兜底并标记 timeFallback) |
| 侧边栏入口 | sidebar.footer.action 按钮(与设置按钮并列、位于其上方),点击打开大弹窗(全屏 mask + 1280px 面板,接近对话区尺寸) |
| 自适应网格 | 新闻流 2-3 列自适应(auto-fill minmax(320px, 1fr)):窄窗 2 列、宽窗 3 列 |
| 面板内总结 | 每条新闻的「总结 ✦」→ 后台抓正文 + 调默认模型生成中文要点总结(3-5 条 + 原文链接),卡片内展开显示;不进对话、不唤醒 agent,结果按条目缓存(重复查看零成本) |
| 文本清洗 | 所有摘要/总结展示前经解码→去 HTML 标签→压缩空白,杜绝 <div> 等源码泄漏 |
| ai_news 工具 | 模型可直接调用 ai_news(refresh / limit / kind 参数)拉取新闻,Markdown 渲染 |
| 事件 | 抓取完成 ctx.emit('ai-news/updated')(当前为手动刷新模式,事件为后续自动推送预留) |
架构
┌─────────── node half(Host 侧,strip-types 直载)───────────┐
│ src/index.ts 插件入口:Config + apply + ai_news 工具 │
│ src/engine.ts NewsEngine:内存缓存 + id 去重 + 并发刷新 │
│ src/fetchers.ts 多源抓取:RSS(轻量 XML 解析)/HN/ArXiv │
│ src/extract.ts 文章正文提取(抓 HTML → 去噪 → 段落文本) │
│ lib/gateway.js NewsGateway(预编译,@Remote 装饰器) │
│ aiNews.list / refresh / summarize → /api/aiNews/* │
│ summarize:正文 → ctx.llm.stream(默认模型)→ 要点总结 │
└──────────────────────────────────────────────────────────────┘
┌─────────── browser half(client 插件,esbuild bundle)───────┐
│ src/client/index.tsx 侧边栏入口按钮 + 大弹窗 + 2-3 列网格 │
│ (lib/client.js 由 scripts/build-client.mjs 构建) │
└──────────────────────────────────────────────────────────────┘
为什么 Gateway 要预编译?
@Remote 是标准装饰器(addInitializer),Node 的 strip-types 不支持装饰器语法。
沿用 dsh-omp-tools 同款方案:scripts/build-gateway.mjs 用 esbuild 把 src/gateway.ts
转译为 lib/gateway.js(@deepseek-ai/* external),src/index.ts 运行时 import 构建产物。
模块实例一致性(关键!)
Host 的 dsh-api-gateway 通过同一模块实例的私有 markers WeakMap 发现 @Remote 方法。
因此本插件的 node_modules/@deepseek-ai 必须解析到 dsh 安装树的 @deepseek-ai
(/…/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai),与 api-gateway 保持一致;
否则装饰器标记互相不可见,remote 端点 404。@deepseek-ai/dsh-client-ui-slots 仅 tsc 需要,
通过 tsconfig paths 指向 omp-tools 的副本(dsh 树没有此包)。
总结链路
点击「总结 ✦」 → /api/aiNews/summarize {itemId}
→ engine.get(itemId) → 命中缓存? 直接返回
→ fetchArticleText(url) 抓正文(≤8000 字符)
→ ctx.agentDefaultModel.currentSelection() 取默认模型(provider/model)
→ ctx.llm.stream({provider, model, messages, temperature:0.3, maxTokens:800})
→ 聚合 text-delta → 中文要点总结 → 写入缓存 → 返回
client 面板内展开显示;按钮变「总结 ✓」;再点直接读缓存
开发
# 0) 依赖(symlink 复用 dsh 安装树 + omp-tools 工具链)
# node_modules/@deepseek-ai -> dsh 安装树(模块实例一致性,见上文)
# esbuild/typescript/react/zod 等 -> dsh-omp-tools/node_modules(工具链)
# 1) 构建(Gateway 装饰器转译 + client bundle)
npm run build # = build:gateway + build:client
# 2) 类型检查
npm run typecheck
# 3) 起隔离验证实例(插件已在 profile cordis.patch.yml,无需 --patch)
dsh web --port 3100 --no-open
# 4) 浏览器验证(headless Chrome + CDP)
# 起 headless Chrome: Google Chrome --headless=new --window-size=1600,1000 \
# --remote-debugging-port=9222 about:blank
# 用 CDP 脚本断言:侧边栏按钮 → 大弹窗 → 分类 tab → 列表 → 面板内总结
# (验证脚本为开发期工具,不入库;本机保留在 scratch 目录即可)
使用(正式接入)
~/.dsh/profiles/web/cordis.patch.yml:
- insert:
- id: dsh-ai-news
name: dsh-ai-news
config:
fetchTimeoutMs: 15000
maxItems: 50
maxDays: 3 # 只显示最近 N 天(0 = 不限)
可选配置(覆盖默认源 / 抓取参数):
config:
sources:
- id: my-rss
name: 我的源
kind: international
type: rss
url: https://example.com/feed.xml
已知限制
- 缓存为进程内内存:新闻与总结缓存随插件卸载 / dsh web 重启清空(重启后首次打开会自动重新抓取)。
- 手动刷新按钮:页面内保留「刷新」按钮可随时重新抓取;尚未做后台定时自动拉取(
ai-news/updated事件已预留,后续可加 timer)。 - 总结走默认模型:用
ctx.agentDefaultModel的当前选择(默认 deepseek-v4-flash),每篇首次总结消耗一次模型调用。 - 正文提取为启发式:抓 HTML 提取段落,部分站点(JS 渲染、反爬)可能拿不到正文,总结会报"无法获取文章内容"。