dsh-tinyfish
Đã xác minh@viztor/dsh-tinyfish · v0.12.1 · MIT · Giao diện web
TinyFish-backed search and fetch providers for the DeepSeek Harness web capability seam (ctx.web) — $0 SERP and page extraction, direct or via Monid.
Cài đặt
dsh plugin add @viztor/dsh-tinyfish Xác nhận layer đã áp bằng dsh --profile default --dump-config — xem hướng dẫn cài plugin.
Mã nguồn
Thẻ
Tác giả
Readme
你的 agent 早就学会了推理,现在给它一点可供推理的“材料”:实时搜索结果与干净的页面正文,直连 harness 自带的 web_search 与 web_fetch 工具。由 TinyFish 驱动——两个端点都免费,你主机上的网络链路从此不再按次计费。
| 之前 | 之后 |
|---|---|
| 每次搜索调用都要计费 | $0,永久免费 |
| 抓回来的页面是 HTML,转换得磕磕绊绊 | 干净的 Markdown,来自浏览器级提取器,开箱即用 |
| 换提供方就要重装 | 改配置文件的两个单词即可,不用重装 |
| 每个提供方要么接受你的原样查询,要么什么都不接受 | 常驻默认值——域名、语言、时效、日期上下界、缓存 TTL、选择器 |
📊 方案对比
| 适用场景 | 解决方案 | 费用 |
|---|---|---|
| 在原生工具里免费搜索与抓取 | dsh-tinyfish,任一通道 | $0——直连通道用 TinyFish 密钥,Monid 通道用平台密钥;两种载荷完全一致 |
| 官方默认 | deepseek-official + http |
搜索按次计费;抓回的 HTML 还要再付一道 turndown 转换 |
| 需要提供方特有的 SERP 细节(地理、流量、排名)或批量查询 | 经 monid_run 调用的 Monid SERP 镜像 |
每次约 $0.002–$0.18,随提供方而定——只在任务真需要这些细节时用 |
fetch 读不了的页面(重 JS、需登录、要交互) |
CLI 里的 tinyfish agent / browser |
按量计费($0.016/step,$0.002/min)——只在抓取落空后果断升级 |
🚀 快速上手
方法一:从 Web 界面直接安装(推荐)
DeepSeek Harness 支持直接在 Web 界面安装插件,全程不用碰终端:
- 打开 DSH Web,在侧边栏选择 Plugins(插件)。
- 点击 Add plugin(添加插件)。
- 输入包名
dsh-tinyfish(或@viztor/dsh-tinyfish)——这是自由输入框,不是仓库搜索。 - 点击 Install(安装)——DSH 会从 npm 拉取软件包,并加载它声明的 bundle 补丁。live profile(官方 Web profile 挂载了 HMR)会立即生效;否则 DSH 会提示下次启动时生效。
- 在该 Plugins 页面打开 Tinyfish 卡片:选择通道(
direct或monid),填入密钥——两个都填也行——然后点击 Save(保存)! - 安装到此结束——bundle 已把两条网络链路都指向 TinyFish。若想另行选择,参见选择提供方。
- 问 agent 一个时效性问题;会返回实时来源。结果本身不会标明提供方——失败信息会,并会写出
tinyfish。
方法二:终端 / Profile 的 package.json
适用于无头环境、服务器或纳入版本管理的 dotfiles:
cd ~/.dsh/profiles/web
npm install dsh-tinyfish # or: npm install @viztor/dsh-tinyfish — same thing
认准一个名字装一次。两个 tarball 打包的是同一份构建产物、读取同一份设置(提供方注册为 tinyfish,配置挂在行 id dsh-tinyfish 下,凭证共用同一对引用)——所以之后改名不丢任何东西,但两个都挂会把 bundle 加载两遍。带 scope 的 tarball 其 manifest 与补丁的 name 不同,好让 loader 在 scope 下解析它。dsh-tinyfish 是 DSH 约定用的名字,本文档也用它。
📦 改从 GitHub Packages 安装
每次发版都会把 @viztor/dsh-tinyfish 镜像到 GitHub Packages——npmjs.org 连不上时的第二来源,也是仓库侧边栏的数据来源。只有带作用域的名字会镜像:GitHub 按所有者作用域把软件包关联到仓库。与 npmjs 不同,GitHub Packages 连公开包也要认证:未认证的请求会 404,且不告诉你这个包是否存在。用带有 read:packages 权限的 token:
# project-local .npmrc is better than global for a token
@viztor:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=ghp_xxx
之后 npm install @viztor/dsh-tinyfish 就会从镜像解析。除非 npmjs 宕机,一律优先用它:不用 token,也不用额外配置。
挂载它——加进该 profile 的 package.json,然后重启 DSH:
{
"dependencies": { "dsh-tinyfish": "^0.11.0" },
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-tinyfish",
],
},
},
}
经
package.json安装的 bundle 在启动时解析,所以这里要重启 DSH——只重载补丁加载不了它们。
配一把密钥——从下面的通道里选一个存好,然后问 agent 一个时效性问题(who won the last Formula 1 race?)。能返回实时来源,就证明该行已校验通过、凭证已解析;结果本身不会标明提供方,所以请到 profile 补丁里确认指向(searchProvider / fetchProvider)——或者故意触发一次失败,错误信息里会写出 tinyfish。
选择提供方
默认 bundle 会把两条网络链路都指向 TinyFish,所以全新安装不用改任何补丁就能跑。它通过设置网络 seam 的 searchProvider / fetchProvider 实现,按行 id 匹配。插件只有这一条路:dsh-web 在构造时一次性解析这两个字段,没有给提供方可“自荐”的 API。
若想另行选择,在 profile 的 cordis.patch.yml 里覆盖,它在每个 bundle 补丁之后生效,因此优先级最高:
- id: web
config:
searchProvider: deepseek-official
fetchProvider: http
搜索和抓取是两个独立字段,所以搜索走 TinyFish、抓取留在官方 http 提供方可以,反过来也行。插件保持挂载、静默待命。主机也可以改设 DSH_WEB_SEARCH_PROVIDER / DSH_WEB_FETCH_PROVIDER——它们喂给同一对字段,两边都设时以配置文件为准,因此在官方 profile 上始终是基础补丁胜出。
🔑 双通道,一个插件
| 特性 | 直连 (默认) | 经 Monid |
|---|---|---|
| 背后是什么 | TinyFish 官方 API | 同样的 TinyFish 端点,经你的 Monid 钱包转发 |
| 需要什么 | tinyfish.ai 的免费密钥 | app.monid.ai 的平台密钥 |
| 最快配法 | tinyfish auth login |
monid keys add |
| 花费 | $0 | $0 |
默认是 direct,因为包名就叫 TinyFish——全新安装索取的正是名字里那个凭证。更想用 Monid(你的 Monid MCP 挂载可能已经有一把平台密钥)?在 profile 补丁里钉死:
- id: dsh-tinyfish
config:
channel: monid
两把密钥可以并存——存一个永远不会覆盖另一个,切换通道也不丢任何东西。
⚙️ 插件卡片
Plugins → Tinyfish(插件 → Tinyfish)。GUI 里能改的东西都在这里:TinyFish 是否回答搜索与抓取、通道选择、密钥、搜索排序依据和尝试次数。改动先暂存、一起保存;你输入的密钥由 harness 保管,绝不写入 profile。
两个密钥框同时摆在页面上,各自标明所鉴权的服务;Monid 框的提示里写着它的引用,Direct 框的引用则印在它下方那行说明里——于是可以在 Direct 选中时配 Monid 密钥,不用来回切换也能看清两个密钥各是否存在。
这一行只管 TinyFish 的行为,不管选中它。bundle 已经把
searchProvider/fetchProvider指向tinyfish,所以全新安装无需改补丁就在 Web 路径上——想用别的,就在你自己的补丁层里覆盖这两个字段。参见选择提供方。
📖 完整配置参考
全都住在同一行 dsh-tinyfish 里。该行带校验,越界值会被带着报错信息拒绝,而不是悄悄钳制。
| 键 | 默认值 | 含义 |
|---|---|---|
channel |
direct |
monid 或 direct |
apiKey |
(未设置) | 两个通道通用的字面凭证;优先用引用 |
apiKeyEnv |
TINYFISH_API_KEY |
direct 的凭证引用或环境变量 |
monidKeyEnv |
MONID_API_KEY |
monid 的凭证引用或环境变量 |
purpose |
(未设置) | 随每次搜索与抓取发送的目标说明;TinyFish 据此排序;最多 2000 个字符 |
attempts |
3 |
瞬时失败或搜索落空时的总尝试次数(1–5);3 即最多重试 2 次 |
filters.domainType |
(未设置) | web | news | research_paper——仅补丁文件 |
filters.language / .location |
(未设置) | 地域定向——仅补丁文件 |
filters.includeDomains / .excludeDomains |
(未设置) | 逗号分隔——仅补丁文件 |
filters.recencyMinutes |
(未设置) | 按分钟计的时效窗口(1–5256000);上游与 .afterDate 互斥——仅补丁文件 |
filters.afterDate |
(未设置) | 日期下界,YYYY-MM-DD;不适用于 research_paper——仅补丁文件 |
filters.pubYearMin |
(未设置) | 发表年份下界(0–9999),仅 research_paper——仅补丁文件 |
fetchOptions.ttl |
(未设置) | 缓存容忍秒数;0 强制实时抓取,不设则接受任何缓存——仅补丁文件 |
fetchOptions.perUrlTimeoutMs |
(未设置) | 单 URL 耗时上限,毫秒(1–110000)——仅补丁文件 |
fetchOptions.excludeSelectors |
(未设置) | 提取前剔除的 CSS 选择器,逗号分隔(1–20 个 × 每个 ≤1000 字符);直接下载 PDF/CSV 时会被拒绝——仅补丁文件 |
monidBase / searchBase / fetchBase |
上游默认 | 端点覆盖,给测试环境用 |
search / fetch |
true |
是否提供该种类;false 不注销注册,只报告不可用 |
关掉一个报的是“不可用”而非“缺失”——harness 分得清这两者,只有后者意味着“安装坏了”。但“不可用”也不会悄悄 fallback:如果 searchProvider/fetchProvider 还写着 TinyFish,调用会失败。想用另一个提供方,就把对应工具指向它。
- id: dsh-tinyfish
config:
search: true
fetch: false # TinyFish stays registered but unavailable for fetch; point fetchProvider elsewhere to use another fetch
Manifest 元数据,而非配置
manifest 里还带着 dsh.compatibility:显式声明的 Node 与 DSH 范围,外加每个发版的实测结论——compatible、incompatible 或 unknown——供目录核验各 DSH 版本。
**DSH 本体从不读它。**harness 里没有任何代码读 dsh.compatibility,dshReleases 映射也全无引用,所以这些字段改变不了插件的加载、注册与行为。DSH 自己的兼容机制是另一回事:loader 检查的是 peerDependencies,豁免记录在 profile 的 compatibility.json 里。这些字段的存在,是让列表页能如实写清“哪些已被验证”,这里的结论也的确是实测而非期望:0.2.0-rc.2 是本仓库每次构建与测试所跑的版本,0.2.0-rc.1 虽在 peer 范围内但从未实际跑过,0.1.7-rc.2 则低于下限。0.2.0-rc.1 以同样方式验证过(把五个 harness devDependencies 全部钉到该版本并跑门禁);0.2.1-alpha.1 记为 incompatible,因为 peer 范围并不接受它 —— semver 只在范围本身带有同一 major.minor.patch 的预发布版本时才放行预发布版,因此 loader 在该 alpha 主机上会拒绝加载本插件。
过滤器与抓取选项写在补丁文件里
filters 与 fetchOptions 是嵌套对象,而设置表单一个字段只对应一个扁平键——所以搜索与抓取调优留在运维层面:
- id: dsh-tinyfish
config:
channel: monid
filters:
domainType: research_paper
language: zh
includeDomains: arxiv.org,openreview.net
pubYearMin: 2023
fetchOptions:
ttl: 0 # force a live fetch instead of accepting a cached page
excludeSelectors: nav, .cookie-banner
两个分组永远有解析结果:没设就是空分组,不给请求加任何东西;不可用的成员退化为未设置,绝不发往上游。三条上游注意事项按文档行为原样透出,不做强制:
recencyMinutes与afterDate在 TinyFish API 里互斥;一行里两个都写就两个都发。excludeSelectors对直接下载的 PDF/CSV 无效,带上它会答selector_unsupported。- 上游会按
domainType交叉核验每个日期边界:recencyMinutes与afterDate不接受research_paper,pubYearMin只属于它,配错任一一对都会让整个搜索被拒绝。
凭证从哪里来
每次调用现解析——轮换密钥下次搜索即生效,不用重启。先命中先生效:
- 行里的
apiKey字面量(写在配置里的秘密;优先用 2–3) - 凭证服务——
apiKeyEnv(直连)或monidKeyEnv(Monid),从设置界面保存 - 启动环境(DSH 启动前 export 的)
- 实时环境——先读配置的引用名(
apiKeyEnv/monidKeyEnv,可以是你自己的变量名),再读MONID_API_KEY/MONID_MCP_TOKEN/TINYFISH_API_KEY(任一 Monid 变量都覆盖monid通道) - 各通道的 CLI 存储(
monid keys add/tinyfish auth login)
凭证服务挂了就落到下一个来源,而不是让搜索失败。
端点从哪里来
行配置优先,其次环境变量,最后内置默认——测试环境不用改补丁就能换向:
| 设置项 | 环境变量 |
|---|---|
monidBase |
TINYFISH_MONID_BASE_URL |
searchBase |
TINYFISH_SEARCH_BASE_URL |
fetchBase |
TINYFISH_FETCH_BASE_URL |
🔍 值得了解的行为
- **404 是结果,不是错误。**单个 URL 抓取失败会带着状态码回来,因为那是模型需要的资源状态。
- **
publishedAt诚实。**TinyFish 报的是人类日期("Apr 30, 2026"、"1 year ago")。能解析的转成 ISO-8601,解析不了的直接丢掉,绝不编造。只有不含时刻、也不带时区的日期按 UTC 读,所以同一页面在全球报的是同一天;带时刻的值按原样解析。 - **空搜索会重试。**上游大约三跑空一——对合法查询也可能什么都不回;在认定之前,会一直重试到用尽尝试预算(
attempts,总尝试次数)。 - **被拦的 run 是终态。**若 Monid 工作区控制拦停一次运行,错误里会写原因并附充值链接。永不重试。
- **关闭意味着“不可用”,不是“没了”。**关掉的种类保持注册、只是谢绝。若 profile 还把对应工具钉在 Tinyfish 上,调用会大声失败,而不会悄悄改道——想用别的提供方就把工具指向它。什么都没钉时,退出的 Tinyfish 只管让路:自动选择用剩下的人选,关掉其中一种正是把“多提供方歧义”收敛到唯一候选的办法。
- **“不可用”三种成因、一种面孔。**seam 只看到一个布尔值,所以“开关关了”“没凭证”“base URL 写错”在下游读起来一模一样。卡片分得清——去看卡片上的开关和密钥徽标,端点覆盖则在行里或环境里看。
- **
purpose是每次请求的一句话。**seam 的请求没有目标槽位——搜索只有{query},抓取只有{url}——不动 harness 就不可能有按调用的目标。配置里的那句话会逐字跟随每次搜索与抓取:常驻偏置,而非按任务指令。
TinyFish 的 agent 与 browser 界面没有暴露:按量计费、走钱包,既不是搜索也不是抓取。页面真需要开浏览器时,直接用 tinyfish CLI。
🛠 开发
工具链是 Vite+:vp pack 用 tsdown 构建,vp test 跑 Vitest,vp lint / vp fmt 即 Oxlint 与 Oxfmt,且是类型感知的。Lint 与格式配置住在 vite.config.ts 里——Vite+ 会忽略独立配置文件。
pnpm install
pnpm test # hermetic — no network, no credential
pnpm run check # format + lint + types
pnpm run release:gate # build, then the full gate incl. the package checks
pnpm run test:live # the real APIs, still $0, needs credentials
需要 DSH ^0.2.0-rc.1(0.2.0-rc.1 及之后、0.3.0 之前)与 Node 24+。完整流程与不变量:AGENTS.md。参与贡献:CONTRIBUTING.md。
许可证
MIT