dsh-tinyfish
Đã xác minhdsh-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 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
Phát hành lên npm mà không có repository công khai. Hãy kiểm tra nội dung package trước khi cài.
Thẻ
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