代理与应用之间的缺失环节
大多数AI代理工具运行在服务器上,限制了浏览器API、设备功能和前端状态的访问。了解LangChain的无头工具如何为现代代理应用启用安全的客户端工具执行。
开源
代理与应用之间的缺失链接
2026年6月10日
6
分钟
返回博客
创建代理
关键要点
大多数代理工具只看到后端。浏览器、应用和设备包含服务器端工具无法直接访问的有价值状态和能力。
无头工具将客户端能力引入代理循环。代理可以调用浏览器API、本地内存和应用特定操作作为第一类工具,同时保留结构化输入和输出。
在客户端执行既改善用户体验又提升隐私。代理可以直接与用户环境交互,减少往返,并允许敏感数据默认保留在本地。
TL;DR:大多数代理工具在服务器上运行,这意味着代理可以调用API,但无法与用户实际工作的浏览器、应用状态或设备能力交互。通过LangChain中的无头工具,我们通过让代理调用客户端能力(如地理位置、剪贴板访问、本地内存和应用内操作)作为第一类工具来弥补这一差距。这使代理更有用、更私密,并更好地与实际应用行为对齐。
如今的代理能力越来越强,但用户关心的许多能力存在于客户端运行时而非服务器上。浏览器和应用拥有本地状态、用户选择、设备API和应用特定操作,这些通常无法通过后端系统访问。结果,代理可以推理下一步该做什么,但难以在用户实际工作的环境中行动。
造成这一差距的一个原因是大多数代理工具在服务器上执行。当模型决定使用工具时,代理在进程中运行它或将其委托给外部服务(如MCP服务器),然后将结果反馈给推理循环。这对API、数据库和后端系统有效,但存在明显限制:
无法直接访问仅浏览器或仅设备的API。
无法对从未同步到服务器的前端状态采取行动。
通常迫使隐私敏感数据离开设备。
为本质上是本地的操作引入不必要的往返。
浏览器是许多高价值代理操作实际发生的地方:读取本地应用状态、对当前UI采取行动、使用设备能力而不将数据发送到后端。桌面应用通过本地文件、原生集成和会话特定状态展现相同模式。如果您的代理无法访问该运行时,它仍然擅长后端工作流,但弱于用户实际体验的交互。
想象您正在为Figma、Google Slides或富文本编辑器构建副驾驶代理。代理可以在服务器上推理用户的请求,但文档模型、选择状态和编辑命令都在客户端。服务器端工具无法在光标处插入文本、重新格式化选定对象或跳转到活动幻灯片,因为这些操作属于应用运行时,而非后端API。如今,团队通常通过临时UI桥接来弥合这一差距:将一些客户端状态序列化到服务器,获取响应,然后命令式地修补客户端。这可行,但脆弱、难以组合,且对模型的推理循环不可见。
让您的代理直接从用户浏览器访问内存或地理位置API。
这就是LangChain中无头工具解决的问题。
无头工具改变了什么
对模型而言,无头工具看起来与其他工具无异:它有名称、描述和一组预期输入。模型决定何时调用它,就像其他工具一样。不同之处在于接下来发生的事情。
服务器不是自己运行工具,而是将工具调用发送到客户端:用户的浏览器、桌面应用或实际拥有该能力的任何环境。客户端在本地运行工具并将结果发送回来,代理从中断处继续。
虽然这初听起来像一个小实现细节,但它实际上改变了代理可以可靠控制的系统类型。
模型从不需要知道工具在哪里运行。它看到一个工具,决定使用它,并获得结果。但在幕后,服务器和客户端正在协调:服务器知道代理想做什么,客户端知道如何做。这种分离是核心思想。
您可以手动连接,从React应用调用navigator.geolocation.getCurrentPosition()并将结果发送给代理。但这样一来,模型无法发现或决定何时调用该能力。它作为临时侧通道存在于推理循环之外。无头工具将客户端操作置于代理的推理循环之内,而非旁边。
为什么这很重要
好处不仅仅是“浏览器访问”。想象一个帮助您处理幻灯片堆的代理:它应该能够跳转到活动幻灯片、读取本地上下文并原地更新演示文稿,而无需将会话发送到后端。无头工具通过将客户端能力作为代理循环中的真实工具暴露出来,使这种交互成为可能。
某些操作无法在后端正确模拟。地理位置是明显的例子——浏览器拥有权限提示和设备信号。剪贴板访问、画布渲染、文件选择器和实时UI导航都依赖于活动客户端环境。标准工具可以通过后端服务近似这些操作。无头工具可以调用真实事物。
但无头工具不仅限于浏览器API。它们是给代理安全访问应用原生操作的通用机制。例如,slidev-agent(Popular Slidev演示框架的插件)使用无头工具在用户活动演示中导航到特定幻灯片。这不是数据检索问题或服务器自动化问题。
这种模式也改变了隐私权衡。代理内存并不总属于集中式后端。通过由浏览器存储(如IndexedDB)支持的无头工具,内存可以默认保留在本地——持久、低延迟、自然限定于该用户和浏览器——而无需将回忆转化为服务器端数据管理问题。
它在代码中如何工作
在TypeScript中,定义和实现之间的分离特别清晰。您定义一次工具,使用.implement(...)附加实现,并将实现传递给前端流式钩子。服务器和客户端共享相同的模式,但只有客户端加载特定于浏览器的执行逻辑。
// tools.ts import { tool } from "langchain";
export const geolocationGet = tool({ name: "geolocation_get", description: "从浏览器获取用户的当前位置。", schema: z.object({}), }); // App.tsx import { useStream } from '@langchain/react';
// 共享工具定义 import { geolocationGet as geolocationGetDefinition } from './tools';
export function App() { const stream = useStream({ // ... tools: [ // 客户端侧的实际工具实现 geolocationGetDefinition.implement(async () => { const position = await new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject), );
return { latitude: position.coords.latitude, longitude: position.coords.longitude, accuracy: position.coords.accuracy, }; }), ], });
return
...
; }
在我们的LangChain文档中查看实时演示,结合浏览器本地内存、地理位置和可选的人工审批。
总结
标准工具让代理访问后端系统。无头工具让它们访问用户实际工作的地方。
用户并不生活在您的后端。他们生活在浏览器、应用和设备中,许多最有价值的代理交互都在那里发生。无头工具使这些交互可用,同时保留类型化模式、显式能力、结构化输出和可审查性,允许代理使用对用户原生、而非仅对服务器方便的工具。
开始使用LangChain Python或LangChain JS中的无头工具。
感谢@huntlovell、@colifran_和@sydneyrunkle的 thoughtful 审阅和反馈。
查看您的代理真正在做什么
LangSmith,我们的代理工程平台,帮助开发者调试每个代理决策、评估更改并一键部署。
尝试LangSmith
获取演示