@dudko.dev/代理网络
无头, 普遍可配置 浏览器 LLM 代理基于 维塞尔 AI SDK一个软件包驱动器 任意 模型从 浏览器 :
- ☁️ 云提供商,带上你的钥匙 — OpenAI, Anthropic, Google, xAI, (英语), DeepSeek,任何OpenAI相容的服务器,以及Vercel AI Gateway.
- 🖥️ 本地 WebGPU 模型 通过 网络LLM
(单位:千美元)
@browser-ai/web-llm;没有服务器,没有密钥,数据从不离开设备. - 🔌 任何AI SDK
LanguageModel例如:Chrome/Edge内置AI.
它给你 简单的文本生成、流出、本地工具调用以及 结构化产出,加上一个高级 规划 执行 重新规划 综合 代理 - 有主机定义 工具选项 MCP 磁共振, (中文(简体) ), 系统提示, (中文(简体) ), 索引数据库 存储,和 a 加密令牌金库. UI 不可知:它流出 键入事件; 随你喜欢.
代理循环也有 思维 (便携式推理水平或精确度)
预算, 符号限制 (投入/产出/思考/每跑总),
上下文收缩 (自动和 agent.compact(), (中文(简体) ), 快速缓存, (中文(简体) ),
技能 (SKILLL.MD捆绑), 带有自动驾驶开关的工具同意模式, (中文(简体) ),
工具搜索 数百种多氯乙烯工具目录, 子剂 内
网络工人, 图像 / PDF / 文件 / URL 输入 (模型有明显错误)
无法带走他们,
(单位:千美元) 虚拟文件系统 在索引数据库中 fs_* 工具, 使用量 数活
整个届会agent.usage, (中文(简体) ), 引导 跑去的时候RunControl, (中文(简体) ),
恢复 运行中断, 环球 打开并绑定(模型的)
web_fetch),以及 学习 ——经纪人将自己的课写成技能
看到了吗 文件/能力。 。 。 .
节点包的浏览器比对
@dudko.dev/agent。 。 。 .
使用反应?
@dudko.dev/agent-web-react将此核心包成一个useAgent钩子,一个<AgentProvider>上下文和下进 UI 组件——尝试 现场演示 (单位:千美元)来源) (中文(简体) ).
安装
npm install @dudko.dev/agent-web ai zod
然后添加 只有您使用的提供者 (所有可选的同伴,动态) 进口:
# cloud, pick what you need
npm install @ai-sdk/openai # or @ai-sdk/anthropic, @ai-sdk/google, @ai-sdk/xai, @ai-sdk/deepseek, @ai-sdk/openai-compatible
# local WebGPU models
npm install @browser-ai/web-llm @mlc-ai/web-llm
# optional: HTTP MCP
npm install @modelcontextprotocol/sdk
CDN / 独立捆绑( 无构建步骤)
dist/agent-web.js 是一个 自成一体的无害环境管理捆绑 (b) 核心资源 ai, (中文(简体) ),
zod 财务报告和审定财务报表 idb 被烤进。 它在npm的包装(所以它在每个
CDN),并附着于各条
GitHub 发布 作为
agent-web-<version>.min.js数字 :
<script type="module">
import { createAgent, defineTool } from 'https://cdn.jsdelivr.net/npm/@dudko.dev/agent-web/dist/agent-web.js'
// or: https://unpkg.com/@dudko.dev/agent-web/dist/agent-web.js
</script>
Bundler 用户也可以通过 @dudko.dev/agent-web/bundle
子路径。 可选提供者同伴即使在捆绑中也保持外部; 在 a
无捆绑页, 用导入地图映射您使用的页面( 运行时间)
import() 尊重)——直接 LanguageModels 和 gateway 规格不需要
导入映射 :
<script type="importmap">
{
"imports": {
"@ai-sdk/openai": "https://cdn.jsdelivr.net/npm/@ai-sdk/openai/+esm"
}
}
</script>
快速启动——云型,带上自有钥匙
密钥已存储 休息时加密 (WebCrypto,索引DB)和引用者为: id——从未被内入到配置中. 见 警卫。 。 。 .
import {
createAgent,
defineTool,
VaultCredentialStore,
} from '@dudko.dev/agent-web'
import { z } from 'zod'
// 1. Store the user's key once (e.g. from a settings form). Encrypted at rest.
const credentials = new VaultCredentialStore()
await credentials.setApiKey('openai', userProvidedKey)
// 2. Tools = your app's actions.
const tools = {
add_text: defineTool({
description: 'Add a text block to the page.',
inputSchema: z.object({ text: z.string(), x: z.number().optional() }),
execute: async ({ text, x }) => addTextBlock(text, x), // your code
}),
}
// 3. Create the agent. The key is fetched from the vault at build time.
const agent = await createAgent({
model: { providerType: 'openai', model: 'gpt-4o-mini', credentialRef: 'openai' },
credentials,
tools,
describeState: () => serializeMyCanvas(), // optional grounding
})
// 4. Run. Every step is a typed event.
const result = await agent.run('Add a centered title and a totals line', {
onEvent: (e) => {
if (e.type === 'plan.created') console.log('plan:', e.plan.steps)
if (e.type === 'step.tool-call') console.log('tool:', e.name, e.input)
if (e.type === 'final') console.log('done:', e.text)
},
})
console.log(result.final, `— ${result.applied} changes`)
直接浏览器调用 & CORS : 截至2026年10月,谷歌、OpenAI、xAI和 DeepSeek发送 CORS 头,所以用户自己的密钥从页面直接工作; Athropic 作品也 (为您注入所需的头条) 。 基米 格罗克, Mistral 和 OpenRouter 也这样做(自己建造他们的 AI SDK 模型并通过) 他们作为
model) (中文(简体) ). 见 文件/提供者.md。 。 。 .
快速启动——本地WebGPU模型(无密钥,下行)
import { createAgent, createWebLLMModel, isWebGPUAvailable } from '@dudko.dev/agent-web'
if (!isWebGPUAvailable()) throw new Error('WebGPU required for local models')
const model = await createWebLLMModel('Llama-3.2-3B-Instruct-q4f16_1-MLC', {
initProgressCallback: (r) => console.log('loading', Math.round(r.progress * 100), '%'),
contextWindowTokens: 8192, // WebLLM loads with 4096 unless told otherwise
})
// Local models default to the robust "prompted" tool-mode automatically, and
// the agent sizes compaction and tool lists from the model's own window.
const agent = await createAgent({ model, tools /* ...same as above */ })
await agent.run('Summarize the current page and add a heading')
当地人 愿景 型号(Phi-3.5-vision-instruct-q4f16_1-MLC) 获取图像
如来一一云. 见 文件/能力
对于窗口:WebLLM的默认值为4096个令牌,每个模型在负载时提高.
您也可以通过 spec: { providerType: 'web-llm', model: '…' }。 。 。 .
简单生成工具调用( 无代理循环) (T)
import { generate, stream, generateStructured, runToolLoop } from '@dudko.dev/agent-web'
import { z } from 'zod'
const { text } = await generate(model, { prompt: 'Write a haiku about WebGPU' })
const { textStream } = stream(model, { prompt: 'Explain IndexedDB' })
for await (const chunk of textStream) process.stdout.write(chunk)
const { object } = await generateStructured(
model,
z.object({ title: z.string(), tags: z.array(z.string()) }),
{ prompt: 'Suggest a title and tags for this article: …' },
)
// Tool-calling with feedback (native or prompted), normalized result. In both
// modes the model sees its tool results and can keep going, up to `maxSteps`
// rounds (default 4; prompted models get the results as a TOOL RESULTS turn):
const { text: reply, toolCalls } = await runToolLoop(model, {
mode: 'native',
prompt: 'Add a title',
tools,
})
可选: HTTP 上的 MCP 工具
import { connectMcpHttp } from '@dudko.dev/agent-web/mcp'
const mcp = await connectMcpHttp({
docs: { url: 'https://my-mcp-server.example/mcp', headers: { Authorization: `Bearer ${t}` } },
})
const agent = await createAgent({ model, tools: { ...tools, ...mcp.tools } })
// ... later: await mcp.close()
浏览器只能说 HTTP (Streamable HTTP) (缩写为"硬") 运输——Stdio MCP是
唯节. 连接者住在 ./mcp 子路径让 MCP SDK 永远不要
进入你的核心捆绑.
一次通过数个服务器——它们同时连接,每个服务器都有自己的
截止日期(connectTimeoutMs,默认为 30 s),每页
tools/list 读取,工具为服务器标记 readOnlyHint 被视为
仅由同意门读取。 一个失败的服务器在 results; 数据
其他人还在上山 用数百个工具 代理交换工具搜索
自动( 见 文件/能力) (中文(简体) ).
connectMcpHttp 返回时 { tools, catalog, results, refreshServer, close }。 。 。 .
tools 财务报告和审定财务报表 catalog 已变异 到位 以 refreshServer(name),所以一个
由它们构建的代理取取取服务器的新工具列表(通过
onToolsChanged 选项)而不重建。 过滤是普通对象的工作 :
合并前删除您不想要的密钥.
OAuth 2.1 + 动态客户注册
OAuth 背后的服务器需要的不止一个信头:访问符已到期中跑.
通过一个 authProvider mCP SDK 发现了授权服务器
(RFC 9728),动态注册此应用程序(RFC 7591-没有客户端 id to
运行PKCE 刷新任何 401 上的访问令牌
重复请求。 。 。 .
import { BrowserOAuthProvider, connectMcpHttp, finishMcpOAuth, readOAuthCallback }
from '@dudko.dev/agent-web/mcp'
const provider = new BrowserOAuthProvider({
serverUrl: 'https://my-mcp-server.example/mcp',
redirectUrl: `${location.origin}/callback`, // must match on the way back
})
// 1. On the callback page: finish the flow before anything else.
const callback = readOAuthCallback()
if (callback) await finishMcpOAuth(provider, callback)
// 2. Connect. `needsAuthorization` means "send the user to authorize".
const mcp = await connectMcpHttp({ docs: { url: provider.serverUrl, authProvider: provider } })
if (mcp.results[0]?.needsAuthorization) {
location.href = String(provider.authorizationUrl) // a button, a popup — your call
}
托肯斯,动态注册和PCE验证器被存储在同一处
加密索引化DB金库 作为提供者 API 密钥( E)VaultOAuthStorage; 互换为
MemoryOAuthStorage 或者你自己通过 storage) (中文(简体) ). redirectToAuthorization 永远
自行导航——SDK可以要求运行内部的授权
工具调用,然后驱逐用户就会丢弃运行。 URL 被记录
打开 provider.authorizationUrl 而应用程序决定何时使用.
CORS: 服务器必须发送的内容
一个浏览器不告诉 JavaScript 为何会屏蔽一个请求—— 一个 CORS
拒绝和下方的服务器均以 TypeError: Failed to fetch, (中文(简体) ),
真实原因只打印到devtools控制台. 所以这些是值得的
先检查而不是最后检查 :
Access-Control-Allow-Origin: <your origin>
Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, mcp-session-id,
MCP-Protocol-Version, Last-Event-ID
Access-Control-Expose-Headers: mcp-session-id, WWW-Authenticate
每一行都得到它的位置:
MCP-Protocol-Version在允许页眉中 是最难受的 这个 spec 要求每个请求都使用该页眉 之后initialize自此以来 2025-06-18,CORS在该日期前所写的列表省略. 第一项请求 不携带,所以发现, 整个OAuth舞蹈initialize全部 成功——然后一切就被封锁。 读起来就像 同意步骤失败,并派您去调试客户端 .WWW-Authenticate在 Expose 标题中,或浏览器隐藏 401 jS和OAuth的质疑从未开始.Last-Event-ID用于恢复已丢弃的SSE流.- 授权服务器的
/register,元数据和符号端点需要CORS 我也一样,OPTIONS包含 —— 一个浏览器客户端直接调用它们. - 如果服务器使用 MCP SDK 的 DNS 绑定保护,则其
allowedOrigins必须包含您的来源。 空的,它回答 403 (韩语)Origin not allowed对载有下列内容的任何请求:Origin标题——每个请求 a 浏览器制造.
当连接失败时, connectMcpHttp 不再只是重复
浏览器的沉默:它用一个简单的请求重新检测端点,如果是的话
说哪个标题被拒绝了. diagnoseMcpCors(url) 实值
导出一个 UI 可以显示相同的句子 .
可选:WebMCP——一个页面的工具,双向
网络MCP 允许一个已命名、已输入工具的页面寄存器
(单位:千美元)document.modelContext.registerTool(tool, { signal })),浏览器的代理——克罗姆的出处
审判,Edge,一个扩展,一个页面内代理——可以发现和调用. 这个 ./webmcp 亚道桥
和经纪人的 ToolSet,没有额外的依赖性,并作为浏览器没有WebMCP的禁用:
import { connectWebMcp, registerWebMcpTools } from '@dudko.dev/agent-web/webmcp'
// 1. Publish the tools your in-page agent uses, so any agent of the browser can drive them too.
// One definition, two consumers; withdrawn when the signal aborts or unregister() is called.
const reg = await registerWebMcpTools(tools, {
prefix: 'myapp_', // WebMCP tool names are shared with everything else on the page
except: ['debug_dump'],
consequential: ['clear_page'], // annotated consequentialHint: true
signal: controller.signal,
})
// readOnly: true tools (defineTool) are annotated readOnlyHint: true
// 2. Use the tools a page has registered as the agent's own.
const web = await connectWebMcp({ onToolsChanged: () => rebuildAgent() })
const agent = await createAgent({ model, tools: { ...tools, ...web?.tools } })
两代的浏览器 API 都被处理: 当前的一个打开 document.modelContext 联合国
工具通过中止其信号而被撤回)和 Chrome 的早期预览 navigator.modelContext
(单位:千美元)unregisterTool(name)) (中文(简体) ). 结果作为文本内容返回(结构化结果为JSON),作为
WebMCP客户端阅读. 严格在您体内验证 execute,松散在计划: a
拒绝调用应该是调用代理能够读取的结果,而不是一个被停顿的工具.
思考、限制、同意、技能、潜质
import { createAgent, createSubagentTool, defineSkill } from '@dudko.dev/agent-web'
const agent = await createAgent({
model,
tools: { ...tools, ...mcp.tools },
thinking: 'high', // or { level, budgetTokens }; per stage via stageThinking
limits: { maxTotalTokens: 200_000, maxReasoningTokens: 20_000 },
maxToolCalls: 40,
compaction: { contextWindowTokens: 128_000 }, // auto-compacts history and long runs
skills: [defineSkill({ name: 'triage', description: 'Triage a bug report', content: '…' })],
toolApproval: {
mode: 'ask-writes', // autopilot | ask-writes | ask-all | read-only
onRequest: async (req) => window.confirm(`Allow ${req.toolName}?`),
},
})
agent.setToolApprovalMode('autopilot') // flip the switch at any time
await agent.compact() // summarise the stored transcript now
使用超过40个工具(多个MCP服务器)的执行器切换到 工具
搜索: 它从每个小步骤开始并呼叫 find_tools 来激活它
需要帮助。 副剂也是工具—— createSubagentTool({ config }) 运行一个孩子
正在处理的代理, createSubagentTool({ worker, workerConfig }) 把它隔开
网络工作者 serveSubagentWorker()添加 profiles 和父模式
每个调用时,在同一副剂的命名变体之间进行选择——另一种型号,
思维级别、限制或系统提示。 给 persistence 仓库
(单位:千美元)new IndexedDBRunStore())和被重新装入或崩溃恢复中断的运行
最后一个检查点为 run('', { resumeFromRunId }) 计划员不会再跑了
没有重复已完成步骤. createRunControl() 引导运行中: send()
在下一个边界的模型前放置一条信息( 重新规划者重新检查计划)
反对, stopSubagent(id) 停止一个副剂. egress (默认关闭)给模型一个安全 web_fetch 带有允许/拒绝列表的工具. learning (默认关闭)让代理记下自己发现的技巧——在记忆中,或在整个会话中的文件系统中. 细节、 默认和事件 :
文件/能力。 。 。 .
配置( 亮点)
| 选项 | 默认 | 目的 |
|---|---|---|
model | —— 说 | (单位:千美元) LanguageModel 或者说 ProviderModelSpec (执行者/ 默认) |
planner 编号 : synthesizer | =: model | 每个阶段的模型都超过(如果是部分,则继承基供应商/红外线) |
credentials | —— 说 | (单位:千美元) CredentialStore 页:1 credentialRef 密钥 |
tools | {} | 主机工具( r)defineTool(中文(简体) ) |
availableTools 编号 : excludedTools | —— 说 | 挂起的工具名称的白名单/黑名单 tools |
toolMode | 'auto' | native | prompted | auto (云) 本地、当地(即时) |
toolSelectionStrategy | 'auto' | all | plan-narrowed | search | auto (上文查询) toolSearchThreshold,40,或者一旦定义需要1⁄4的窗口) |
toolApproval | 自动驾驶 | 同意政策: { mode, rules, onRequest, timeoutMs }· ; agent.setToolApprovalMode() |
skills | —— 说 | SKILL.MD 捆绑defineSkill 编号 : parseSkillMarkdown 编号 : loadSkillFromUrl(中文(简体) ) |
egress | 关 | 这是什么 模式 可能获取( e)web_fetch: true = https上的任何公共主机,或 { allow, deny, protocols, allowPrivateNetwork, fetch, ... };浏览器无法对 DNS 进行编码,所以倾向于 allow 列表 - 见 侵略 |
learning | 关 | 代理人将课程写作技能(save_skill 编号 : forget_skill: true = 本届会议, { store: new VfsSkillStore(vfs) } = 各届会议——见 学习 |
thinking 编号 : stageThinking | 提供者默认 | true,一个级别('low'.'xhigh', (中文(简体) ), 'none'),或 { level, budgetTokens, includeThoughts } |
limits | —— 说 | 运行盖 maxInputTokens 编号 : maxOutputTokens 编号 : maxReasoningTokens 编号 : maxTotalTokens + 键 perCall 输出封顶 |
maxToolCalls 编号 : maxPlanSteps | 页:1 | 每个运行/每个计划的步骤 |
compaction | 自动, 占 128k 窗口的 85% (本地型号: 自己的) | { auto, contextWindowTokens, thresholdRatio, thresholdTokens, keepRecentTurns, keepRecentSteps, maxToolOutputChars } ——根据整个上下文(固定的快取+笔录+步骤日志)判断,从未超过模型的窗口 |
promptCaching | true | 稳定的系统前缀、 Anthropic 断点、 OpenAI 缓存密钥 |
vision 编号 : inputs | 推断 | 模型将什么作为附件(run(goal, { images, files }); 见 agent.capabilities |
systemPrompt 编号 : prompts | —— 说 | 准备到每个阶段 / 覆盖任何阶段的快速构建器 |
describeState | —— 说 | 将世界状态序列化为快速上下文 |
memory | —— 说 | ContextStore (单位:千美元)IndexedDBStore 编号 : MemoryStore); 最近的转折被读回规划器提示 |
sessionId | —— 说 | 名称 memory还有水桶 agent.sessionUsage() 计数为 |
persistence | —— 说 | 检查站运行IndexedDBRunStore 编号 : MemoryRunStore这样 run(goal, { resumeFromRunId }) 重新装入后继续——见 续会 |
maxIterations 编号 : maxStepsPerTask 编号 : maxRevisions | 8,4,2 (中文(简体) ) | 循环盖 |
chatTimeoutMs | 120 000 个 | 每个呼叫监视器 |
temperature | 提供者默认 | 每个阶段的取样温度 |
budgets | —— 说 | 遗留的每个阶段产出上限; limits.perCall 胜出,默认为模型窗口的股份 |
clientName | —— 说 | 以 Openai 兼容提供者名称和 MCP 客户端名称发送 |
logLevel 编号 : logger | 'warn' 编号 : console | 动词( r)'silent' | 'error' | 'warn' | 'info' | 'debug'和控制台一样沉入木头 |
replan 编号 : synthesize | true | 切换相机 |
replanAfter | 'failure' | 重新规划触发器 : 'failure' | 'always' | (stepResult) => boolean | Promise<boolean> |
compressAfterChars | 12000 (韩语) | 遗留: 总结过去的历史( 当 compaction 未设置) |
文档
- 文件/设计 ——建筑与原理.
- 文件/能力 思维、限制、收缩, 缓存、技能、工具同意/自动驾驶、大型MCP目录、副剂、, 引导运行的代理,学习,进取,使用,恢复,自主,事件引用.
- 文件/提供者.md ——每个供应商,CORS & direct-vs-proxy.
- 文档/安保 ——道具地库,出行地和学习:威胁模型.
- 文件/任务 ——状态和行进图.
@dudko.dev/agent-web-react—— 说 反应绑定( R)useAgent, (中文(简体) ),<AgentProvider>,预型组件)- 现场演示 • 妇女 来源。 。 。 .@dudko.dev/agent- 那个 节点/服务器兄弟会(stdio MCP,持久性/再生,OpenTeleometry, CLI).
许可证
麻省理工学院