AI代理环境交互SDK:TypeScript实现标准化观察与动作接口

news2026/5/15 7:20:51
1. 项目概述一个为AI代理构建交互式环境的TypeScript SDK如果你正在尝试构建一个能够与现实世界应用比如浏览器、IDE、甚至操作系统进行交互的AI代理那么你很可能已经遇到了一个核心难题如何让代理“看见”并“操作”这些环境直接让AI模型去理解复杂的DOM树、处理异步事件、或者模拟键盘鼠标操作不仅效率低下而且极易出错。这正是strands-agents/sdk-typescript这个项目要解决的痛点。简单来说这是一个专为AI代理设计的TypeScript SDK它提供了一套标准化的接口和工具让你能够轻松地为你的代理创建、连接和管理各种交互式环境。你可以把它想象成AI代理的“手”和“眼睛”。通过这个SDK你的代理不再是一个只能处理文本的“大脑”而是一个能够操作浏览器点击按钮、在代码编辑器中编写代码、或在文件系统中导航的“智能体”。这个项目源自于strands生态其核心目标是标准化AI代理与环境之间的交互。它抽象了不同环境Web浏览器、终端、桌面应用等的底层复杂性为开发者提供了一致的、类型安全的API。无论你是想构建一个自动化测试机器人、一个智能代码助手还是一个能够自主完成复杂工作流的通用代理这个SDK都能为你提供坚实的地基。2. 核心设计理念与架构拆解2.1 为什么需要专门的环境SDK在深入代码之前我们先理解其设计动机。传统的自动化工具如Puppeteer、Playwright或RPA框架其API设计是面向人类程序员的。它们提供了极其精细的控制但同时也带来了巨大的状态管理负担和复杂性。AI代理尤其是基于LLM的代理其决策过程是概率性的、基于上下文的。让代理直接去调用page.click(‘#submit-btn’)这样的命令存在几个问题状态感知困难代理需要自己解析整个页面的HTML来理解#submit-btn是否存在、是否可点击。这需要消耗大量Token且容易因页面动态变化而失败。动作抽象层级过低点击、输入、滚动这些原子操作对于人类意图如“登录邮箱”来说过于底层。代理需要自己组合多个原子操作来实现一个高级目标容易在中间步骤出错。缺乏标准化不同环境浏览器 vs. VS Code的操作API完全不同为代理编写跨环境的通用逻辑几乎不可能。strands-agents/sdk-typescript的解决思路是引入一个环境抽象层。它定义了两个核心概念观察Observation和动作Action。观察环境以结构化的方式不一定是原始HTML告诉代理“当前你看到了什么”。这可能是一个简化的DOM表示、当前活跃的编辑器标签页列表、或者文件系统的目录树。动作代理发出高级指令如navigateTo(url),fillForm({username: ‘...’})由SDK和环境适配器将其翻译成底层环境的原子操作。这种设计将环境复杂性封装起来让代理可以专注于高级策略和决策。2.2 核心架构组件该SDK的架构清晰地区分了职责主要包含以下几部分环境接口EnvironmentInterface这是最核心的抽象。它定义了任何环境都必须实现的一组标准方法主要是step(action): Promiseobservation。代理通过调用step方法来影响环境并获取新的观察结果。这个接口确保了代理代码可以与任何兼容的环境一起工作。观察与动作空间ObservationActionSpaces这些是TypeScript类型定义规定了观察结果和动作的数据结构。观察空间可能包含screen截图或结构化视图、messages系统消息等字段。动作空间则定义了代理可以发出的所有指令类型如ClickAction、TypeAction、NavigateAction等。强类型保证了开发时的安全性和清晰度。环境运行器EnvironmentRunner负责管理环境实例的生命周期启动、停止、处理代理与环境之间的通信循环并可能集成记录Logging和评估Evaluation功能。它是连接代理逻辑和具体环境的粘合剂。工具集成与适配器Adapters虽然SDK本身提供了抽象接口但其强大之处在于社区或官方提供的各种适配器。例如可能有一个PuppeteerEnvironmentAdapter它实现了Environment接口内部使用Puppeteer来控制Chrome浏览器。另一个VSCodeEnvironmentAdapter则允许代理与VS Code编辑器交互。开发者也可以为自己的内部系统编写适配器。代理基类与工具AgentUtilitiesSDK通常会提供一些构建代理的辅助工具例如将环境动作封装成可供LLM调用的“工具”遵循OpenAI Function Calling或ReAct格式或者提供一个基础的Agent类来处理观察-思考-行动的循环逻辑。这种架构的优势在于关注点分离。环境开发者专注于实现稳定、高效的适配器代理开发者则使用统一的高级API来编写智能逻辑无需关心底层是Chrome还是Terminal。3. 从零开始环境搭建与基础用法3.1 初始化项目与安装假设我们要创建一个能让AI代理操作Web浏览器的项目。首先初始化一个Node.js项目并安装核心SDK。# 创建一个新目录并初始化 mkdir my-ai-agent cd my-ai-agent npm init -y # 安装 strands-agents 环境SDK核心包 # 注意由于项目较新可能需要从GitHub或特定registry安装 npm install strands-agents/sdk-typescript # 安装一个浏览器环境适配器这里以假设的官方puppeteer适配器为例 npm install strands-agents/environment-puppeteer # 安装Puppeteer本身如果适配器未包含 npm install puppeteer # 安装你计划使用的AI模型SDK例如OpenAI npm install openai注意strands-agents/sdk-typescript的主包可能主要提供接口和核心运行时。具体的环境实现如浏览器、IDE很可能以独立的适配器包发布。务必查阅项目官方文档或仓库的README来确定当前可用的适配器。3.2 创建你的第一个可交互环境接下来我们编写代码来启动一个浏览器环境。我们将创建一个simple-browser-env.ts文件。import { createPuppeteerEnvironment } from strands-agents/environment-puppeteer; import { EnvironmentRunner } from strands-agents/sdk-typescript; async function main() { // 1. 创建Puppeteer环境实例 // 这里传入了配置比如无头模式、视口大小等 const env await createPuppeteerEnvironment({ headless: false, // 设置为true则在后台运行false会打开浏览器窗口方便调试 defaultViewport: { width: 1280, height: 720 } }); // 2. 创建环境运行器它负责管理env的生命周期和主循环 const runner new EnvironmentRunner(env); // 3. 启动环境。对于Puppeteer环境这会启动一个Chromium实例。 await runner.start(); console.log(环境启动成功代理现在可以开始交互了。); // 在实际应用中这里会接入你的代理逻辑。 // 例如让代理执行一个简单的动作导航到某个网页。 // 我们先手动模拟一个动作来测试环境。 const testAction { type: navigate, url: https://example.com }; // 4. 环境执行一步step const observation await env.step(testAction); console.log(执行导航后的观察:, observation); // observation可能包含页面标题、URL、以及一个结构化的页面表示而非完整HTML // 例如{ url: ‘https://example.com‘, title: ‘Example Domain‘, domSummary: ‘...‘ } // 5. 保持环境运行一段时间或等待代理任务完成 await new Promise(resolve setTimeout(resolve, 5000)); // 6. 任务完成后清理环境 await runner.stop(); console.log(环境已停止。); } main().catch(console.error);这段代码展示了使用SDK的基本流程创建环境 - 启动运行器 - 执行动作 - 获取观察 - 清理。env.step(action)是核心交互点。3.3 理解观察Observation与动作Action的格式SDK的强大之处在于其结构化的数据交换。我们来看看一个典型的观察和动作可能长什么样。观察示例环境返回给代理的信息。// 这是一个假设的观察对象结构 const observation { // 唯一标识符和时间戳 id: ‘obs_123‘, timestamp: Date.now(), // 环境状态摘要 url: ‘https://github.com/login‘, title: ‘Sign in to GitHub‘, // 核心对当前屏幕的结构化描述专为AI设计 screen: { type: ‘dom_summary‘, // 不是完整DOM而是经过简化和标注的节点树包含关键元素信息 elements: [ { id: ‘input_login‘, tagName: ‘input‘, attributes: { name: ‘login‘, type: ‘text‘, placeholder: ‘Username or email address‘ }, // 关键属性可操作性和描述 actionable: true, description: ‘用户名输入框‘, // 在屏幕中的相对位置或唯一选择器供后续动作使用 selector: ‘#login_field‘ }, { id: ‘input_password‘, tagName: ‘input‘, attributes: { name: ‘password‘, type: ‘password‘ }, actionable: true, description: ‘密码输入框‘, selector: ‘#password‘ }, { id: ‘btn_submit‘, tagName: ‘input‘, attributes: { type: ‘submit‘, value: ‘Sign in‘ }, actionable: true, description: ‘登录按钮‘, selector: ‘[name“commit“]‘ } ] }, // 可选的附加信息如错误消息、系统提示 messages: [], isTerminal: false // 表示任务是否结束 };这种观察格式极大地减少了代理需要处理的信息量并直接突出了可交互的元素。动作示例代理发送给环境的指令。// 1. 导航动作 const navigateAction { type: ‘navigate‘ as const, url: ‘https://example.com‘ }; // 2. 点击动作 - 引用观察中提供的元素ID或选择器 const clickAction { type: ‘click‘ as const, elementId: ‘btn_submit‘ }; // 或 const clickAction2 { type: ‘click‘ as const, selector: ‘[name“commit“]‘ }; // 3. 输入文本动作 const typeAction { type: ‘type‘ as const, elementId: ‘input_login‘, text: ‘my_username‘ }; // 4. 组合动作高级执行一个序列比如填写表单 const compositeAction { type: ‘composite‘ as const, actions: [ { type: ‘type‘, elementId: ‘input_login‘, text: ‘my_username‘ }, { type: ‘type‘, elementId: ‘input_password‘, text: ‘my_password‘ }, { type: ‘click‘, elementId: ‘btn_submit‘ } ] };动作设计成高级指令一个动作可以对应底层环境的多个操作如composite这更符合LLM的思考模式。4. 构建一个完整的AI代理工作流4.1 将环境动作封装为LLM可用的工具大多数现代LLM如GPT-4支持“函数调用”Function Calling或“工具使用”Tool Use。我们需要将环境支持的动作转换成LLM能理解的工具描述。import { Environment } from ‘strands-agents/sdk-typescript‘; import OpenAI from ‘openai‘; // 假设我们有一个简单的环境只支持点击和输入 function getAvailableTools(env: Environment) { // 这里我们根据环境的能力动态定义工具但通常我们会预先知道。 return [ { type: ‘function‘, function: { name: ‘click_element‘, description: ‘点击页面上一个可交互的元素‘, parameters: { type: ‘object‘, properties: { elementId: { type: ‘string‘, description: ‘要点击的元素的唯一标识符从观察中获取‘ } }, required: [‘elementId‘] } } }, { type: ‘function‘, function: { name: ‘type_text‘, description: ‘在指定的输入框中输入文本‘, parameters: { type: ‘object‘, properties: { elementId: { type: ‘string‘, description: ‘输入框的元素ID‘ }, text: { type: ‘string‘, description: ‘要输入的文本‘ } }, required: [‘elementId‘, ‘text‘] } } }, { type: ‘function‘, function: { name: ‘navigate_to‘, description: ‘导航到一个新的URL‘, parameters: { type: ‘object‘, properties: { url: { type: ‘string‘, description: ‘完整的URL地址‘ } }, required: [‘url‘] } } } ]; } // 工具调用执行器将LLM选择的工具调用转换为环境动作 async function executeToolCall(env: Environment, toolCall: any): Promisestring { const { name, arguments: args } toolCall.function; let action; switch (name) { case ‘click_element‘: action { type: ‘click‘ as const, elementId: args.elementId }; break; case ‘type_text‘: action { type: ‘type‘ as const, elementId: args.elementId, text: args.text }; break; case ‘navigate_to‘: action { type: ‘navigate‘ as const, url: args.url }; break; default: return 错误未知工具 ${name}; } const observation await env.step(action); // 返回一个简化的结果给LLM作为上下文 return 执行成功。当前页面标题${observation.title}; }4.2 实现主代理循环现在我们将环境、LLM和工具调用连接起来形成一个完整的“观察-思考-行动”循环。import { EnvironmentRunner } from ‘strands-agents/sdk-typescript‘; class SimpleAIAgent { constructor( private envRunner: EnvironmentRunner, private openai: OpenAI, private model: string ‘gpt-4-turbo‘ ) {} async run(task: string, maxSteps: number 20): Promisevoid { let currentObservation await this.envRunner.getInitialObservation(); let stepCount 0; const systemPrompt 你是一个能够操作网页浏览器的AI助手。你的任务是${task} 你将收到当前页面的结构化描述。请分析页面并选择最合适的工具来逐步完成任务。 一次只执行一个步骤除非是明确的组合动作如填写表单。; const messages: OpenAI.ChatCompletionMessageParam[] [ { role: ‘system‘, content: systemPrompt } ]; while (stepCount maxSteps !currentObservation.isTerminal) { // 1. 将当前观察转换成LLM能理解的文本描述 const userMessage this.formatObservationForLLM(currentObservation); messages.push({ role: ‘user‘, content: userMessage }); // 2. 调用LLM提供可用的工具 const tools getAvailableTools(this.envRunner.environment); const response await this.openai.chat.completions.create({ model: this.model, messages, tools, tool_choice: ‘auto‘ // 让模型决定是否调用工具 }); const message response.choices[0].message; messages.push(message); // 将助手的回复加入历史 // 3. 检查是否调用了工具 const toolCalls message.tool_calls; if (toolCalls toolCalls.length 0) { console.log(步骤 ${stepCount 1}: 模型决定调用工具 ${toolCalls.map(tc tc.function.name).join(‘, ‘)}); // 执行每一个工具调用通常一次一个 for (const toolCall of toolCalls) { const result await executeToolCall(this.envRunner.environment, toolCall); messages.push({ role: ‘tool‘, tool_call_id: toolCall.id, content: result }); console.log(工具执行结果: ${result}); } // 4. 执行工具后获取新的环境观察 currentObservation await this.envRunner.environment.step({ type: ‘get_observation‘ }); } else { // 模型没有调用工具而是给出了文本回复可能是分析、确认或无法完成 console.log(步骤 ${stepCount 1}: 模型回复 - ${message.content}); // 如果模型认为任务完成或无法继续我们可以选择跳出循环 if (message.content?.includes(‘任务完成‘) || message.content?.includes(‘无法‘)) { break; } // 否则可以等待一下或者让模型继续思考这里简单处理为暂停 await new Promise(resolve setTimeout(resolve, 2000)); } stepCount; } if (stepCount maxSteps) { console.log(‘达到最大步数限制任务可能未完成。‘); } else { console.log(‘任务循环结束。‘); } } private formatObservationForLLM(obs: any): string { // 将结构化的观察转换成自然语言描述供LLM理解 let desc 当前页面${obs.title} (${obs.url})\n\n; desc ‘页面上有以下可交互元素\n‘; obs.screen.elements.forEach((el: any, idx: number) { desc ${idx 1}. [ID: ${el.id}] ${el.description} (标签: ${el.tagName}, 属性: ${JSON.stringify(el.attributes)})\n; }); if (obs.messages obs.messages.length 0) { desc \n系统消息${obs.messages.join(‘; ‘)}; } return desc; } } // 使用示例 async function runAgent() { // ... 初始化环境、运行器、OpenAI客户端假设已设置API_KEY // const envRunner ...; // const openai new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); const agent new SimpleAIAgent(envRunner, openai); await agent.run(‘请导航到GitHub并搜索“strands-agents”这个仓库‘, 10); }这个SimpleAIAgent类实现了一个基础的循环获取观察 - 格式化并发送给LLM - LLM决定行动调用工具或回复- 执行行动 - 更新观察。这是构建在strands-agents/sdk之上的典型代理模式。5. 高级特性与自定义环境开发5.1 状态管理与会话持久化对于长时间运行或复杂的任务环境状态的管理至关重要。SDK可能提供了会话Session机制允许你将环境的状态如浏览器Cookie、本地存储、打开的标签页保存下来并在后续恢复。// 假设环境运行器支持保存和加载会话状态 const sessionId ‘my_task_session‘; // 在任务开始时尝试加载现有会话 let envRunner; try { const savedState await loadSessionFromStorage(sessionId); envRunner await EnvironmentRunner.fromState(savedState); console.log(‘从持久化状态恢复环境。‘); } catch (error) { // 如果没有找到会话则创建新环境 console.log(‘创建新环境。‘); const env await createPuppeteerEnvironment({ headless: true }); envRunner new EnvironmentRunner(env); await envRunner.start(); } // ... 执行代理任务 ... // 任务中断或完成后保存当前状态 const currentState await envRunner.saveState(); await saveSessionToStorage(sessionId, currentState); await envRunner.stop(); // 停止环境但状态已保存这对于需要登录、或分多个阶段进行的自动化任务非常有用避免了每次都要重新登录或导航到特定位置。5.2 实现一个自定义环境适配器也许你想让代理操作一个非标准应用比如一个内部的管理后台或者一个桌面游戏。这时你需要实现自己的环境适配器。核心就是实现Environment接口。import { Environment, Action, Observation } from ‘strands-agents/sdk-typescript‘; // 假设我们要为一个简单的命令行计算器应用创建环境 export class CalculatorEnvironment implements Environment { private currentValue: number 0; private display: string ‘0‘; async step(action: Action): PromiseObservation { // 1. 解析动作 if (action.type ‘press_key‘) { const key action.key; // 2. 更新内部状态模拟计算器逻辑 if (!isNaN(parseInt(key))) { // 按下数字键 this.display this.display ‘0‘ ? key : this.display key; } else if (key ‘‘) { // 这里简化处理立即执行加法 this.currentValue parseFloat(this.display); this.display ‘0‘; } else if (key ‘‘) { // 显示结果 this.display this.currentValue.toString(); this.currentValue 0; } else if (key ‘C‘) { // 清零 this.currentValue 0; this.display ‘0‘; } } else if (action.type ‘get_observation‘) { // 纯粹获取状态不改变环境 } else { throw new Error(不支持的Action类型: ${action.type}); } // 3. 返回新的观察 return this.getObservation(); } async reset(): PromiseObservation { this.currentValue 0; this.display ‘0‘; return this.getObservation(); } private getObservation(): Observation { return { id: calc_${Date.now()}, timestamp: Date.now(), screen: { type: ‘text_display‘, content: 显示: ${this.display}, // 提供可用的“按钮”作为可操作元素 elements: [ { id: ‘key_7‘, description: ‘数字键 7‘, value: ‘7‘ }, { id: ‘key_8‘, description: ‘数字键 8‘, value: ‘8‘ }, // ... 其他数字和操作符 { id: ‘key_plus‘, description: ‘加号键‘, value: ‘‘ }, { id: ‘key_equals‘, description: ‘等号键‘, value: ‘‘ }, { id: ‘key_clear‘, description: ‘清除键‘, value: ‘C‘ }, ] }, messages: [], isTerminal: false }; } // 可能还有其他方法如 start(), stop(), 用于资源管理 async start(): Promisevoid { console.log(‘计算器环境启动。‘); } async stop(): Promisevoid { console.log(‘计算器环境停止。‘); } }实现自定义适配器的关键在于两点一是准确地将应用的状态映射到结构化的Observation二是将抽象的Action正确地翻译成对应用的底层操作如模拟按键、调用API等。5.3 性能优化与观察压缩对于复杂的图形界面如整个桌面观察数据可能非常庞大。直接将这些数据发送给LLM会消耗大量Token和带宽。SDK通常会在适配器层进行优化智能截图与OCR对于视觉型代理观察可能是一张屏幕截图加上通过OCR识别出的文本和图标位置信息。DOM过滤与摘要浏览器适配器不会返回整个DOM而是通过预定义的规则或可访问性树过滤掉不可见、不可交互的元素并为剩余元素生成简洁的描述。增量更新不是每一步都返回完整的观察而是只返回自上次观察以来发生变化的部分。在自定义适配器中你也需要考虑这些优化策略以确保代理的响应速度和运行成本在可控范围内。6. 实战构建一个网页自动化测试代理让我们结合以上所有知识构建一个稍微复杂点的例子一个能根据自然语言指令自动进行网页交互测试的代理。目标用户说“测试登录功能用户名test密码123456”代理能自动打开登录页填写并提交表单然后验证登录结果。6.1 定义测试专用的动作和观察我们可以扩展基础动作加入一些测试断言。// 扩展动作类型 type TestAction | { type: ‘navigate‘; url: string } | { type: ‘click‘; selector: string } | { type: ‘type‘; selector: string; text: string } | { type: ‘assert_text‘; selector: string; expectedText: string } // 新增断言文本 | { type: ‘assert_element_present‘; selector: string }; // 新增断言元素存在 // 扩展观察包含上一步动作的执行结果 interface TestObservation extends Observation { lastActionResult?: { success: boolean; message?: string; // 对于断言动作可以包含实际结果 actualText?: string; }; }6.2 实现测试环境适配器我们基于Puppeteer适配器进行包装增加对断言动作的处理。import { createPuppeteerEnvironment, PuppeteerEnvironment } from ‘strands-agents/environment-puppeteer‘; class TestingEnvironment { private baseEnv: PuppeteerEnvironment; private page: any; // Puppeteer Page 对象 constructor(config: any) { this.baseEnv createPuppeteerEnvironment(config); } async step(action: TestAction): PromiseTestObservation { let result { success: true, message: ‘‘ }; // 将高级动作翻译成Puppeteer操作 switch (action.type) { case ‘navigate‘: await this.page.goto(action.url, { waitUntil: ‘networkidle2‘ }); result.message 导航到 ${action.url} 成功; break; case ‘click‘: await this.page.waitForSelector(action.selector); await this.page.click(action.selector); result.message 点击 ${action.selector} 成功; break; case ‘type‘: await this.page.waitForSelector(action.selector); await this.page.type(action.selector, action.text); result.message 在 ${action.selector} 输入成功; break; case ‘assert_text‘: await this.page.waitForSelector(action.selector); const actualText await this.page.$eval(action.selector, el el.textContent?.trim()); if (actualText ! action.expectedText) { result.success false; result.message 断言失败期望文本“${action.expectedText}”实际文本“${actualText}”; } else { result.message 文本断言通过“${action.expectedText}”; } break; case ‘assert_element_present‘: try { await this.page.waitForSelector(action.selector, { timeout: 5000 }); result.message 元素 ${action.selector} 存在; } catch { result.success false; result.message 元素 ${action.selector} 未找到; } break; } // 获取基础观察页面结构 const baseObs await this.baseEnv.step({ type: ‘get_observation‘ }); // 组合成测试观察 const testObs: TestObservation { ...baseObs, lastActionResult: result }; // 如果断言失败可以设置一个标志让代理知道测试用例失败 if (!result.success) { // 可以附加错误信息到messages中 testObs.messages [...(testObs.messages || []), [测试失败] ${result.message}]; } return testObs; } async start() { await this.baseEnv.start(); // 获取内部的page引用具体方式取决于适配器实现 this.page await this.baseEnv.getPage(); // 假设有这个方法 } async stop() { await this.baseEnv.stop(); } }6.3 设计测试代理的工作流这个代理不需要复杂的LLM决策因为它执行的是预定义的测试脚本。但它仍然可以利用SDK的统一接口。async function runLoginTest() { const env new TestingEnvironment({ headless: false }); await env.start(); const testSteps: TestAction[] [ { type: ‘navigate‘, url: ‘https://your-test-site.com/login‘ }, { type: ‘assert_element_present‘, selector: ‘#username‘ }, { type: ‘type‘, selector: ‘#username‘, text: ‘test‘ }, { type: ‘type‘, selector: ‘#password‘, text: ‘123456‘ }, { type: ‘click‘, selector: ‘button[type“submit“]‘ }, { type: ‘assert_text‘, selector: ‘.welcome-message‘, expectedText: ‘Welcome, test!‘ } ]; for (const step of testSteps) { console.log(执行步骤: ${step.type}, step); const obs await env.step(step); console.log(结果: ${obs.lastActionResult?.message}); if (!obs.lastActionResult?.success) { console.error(‘测试步骤失败停止执行。‘); break; } // 可以加入延迟模拟真人操作 await new Promise(resolve setTimeout(resolve, 1000)); } await env.stop(); }这个例子展示了如何利用strands-agents/sdk的架构思想即使在不依赖LLM进行动态决策的场景下也能通过统一的环境接口来组织和管理自动化任务使代码更清晰、更易维护。7. 常见问题、调试技巧与最佳实践7.1 环境连接与稳定性问题问题浏览器环境启动失败或意外崩溃。排查检查Puppeteer/Playwright的本地浏览器安装。在Docker等无头环境中运行时需要安装额外的系统依赖如chromium-browser,libxss1等。技巧在创建环境时增加启动超时和更详细的日志。const env await createPuppeteerEnvironment({ headless: ‘new‘, args: [‘--no-sandbox‘, ‘--disable-setuid-sandbox‘], // Docker常用参数 timeout: 60000 // 60秒超时 });问题动作执行后获取的观察不是预期的新页面状态。排查Web应用通常是异步的。点击按钮后数据可能通过API加载页面会动态更新。技巧在适配器中执行动作后应加入等待条件。例如在click动作后等待某个特定元素出现或消失或者等待网络空闲。// 在自定义step方法中 case ‘click‘: await page.click(selector); // 等待一个代表加载完成的元素出现或者等待一段时间 await page.waitForSelector(‘.success-message‘, { timeout: 10000 }).catch(() {}); break;7.2 观察数据对LLM不友好问题LLM无法从结构化的screen.elements列表中有效理解页面布局和优先级。技巧在formatObservationForLLM函数中做进一步加工。可以按区域页头、主内容区、侧边栏、页脚对元素进行分组或者根据z-index、尺寸、位置信息来推断元素的重要性并排序。甚至可以引入一个轻量级的ML模型或规则引擎为每个元素生成更丰富的自然语言描述。最佳实践观察的质量直接决定代理的性能。投入时间优化适配器生成更精炼、信息密度更高的观察远比增加LLM的上下文长度或调用次数更有效。7.3 代理陷入循环或做出无效动作问题代理反复点击同一个按钮或在登录后仍然尝试填写用户名。排查检查观察是否准确反映了状态变化如登录成功后登录按钮应消失或变为“已登录”状态。检查LLM的系统提示systemPrompt是否清晰是否包含了任务目标和约束如“不要重复已完成的步骤”。技巧在观察中加入历史摘要除了当前状态可以附带最近几步的动作和结果帮助LLM理解上下文。实现动作验证在适配器的step方法中在执行前先检查动作是否有效如元素是否存在、是否可点击。如果无效直接返回一个包含错误信息的观察而不是执行失败的动作。设置步数限制和超时如上面的示例代码所示强制结束可能陷入死循环的任务。7.4 性能与成本优化使用更经济的模型对于简单的导航和表单填写任务gpt-3.5-turbo可能就足够了。将复杂的推理任务留给gpt-4。缓存观察结果如果代理在短时间内多次请求同一状态的观察例如在“思考”阶段可以直接返回缓存的结果避免重复调用可能昂贵的环境渲染或DOM解析过程。批量处理动作设计支持“组合动作”或“脚本动作”让LLM一次规划多个步骤如填写整个表单然后由适配器原子性地执行。这减少了与LLM的往返通信次数。7.5 测试与评估录制与回放利用SDK的会话持久化功能可以录制一次成功的代理运行过程包括所有动作和观察序列。之后可以回放用于回归测试或作为新代理的演示数据。黄金路径测试为你的代理定义几个核心任务“黄金路径”并编写自动化脚本定期运行确保核心功能始终正常。评估指标定义成功标准如任务完成率、平均完成步数、人工干预频率。使用这些指标来衡量不同提示词、观察格式或LLM模型对代理性能的影响。strands-agents/sdk-typescript提供了一个强大而灵活的框架将AI代理与环境交互的复杂性进行了有效的抽象和管理。从简单的浏览器自动化到复杂的多应用工作流它都能提供一致的开发体验。成功的关键在于精心设计适配器产生的观察以及为代理提供清晰、可靠的动作空间。随着你对该SDK的深入使用你会发现它不仅是连接AI与环境的桥梁更是构建可靠、可维护的智能体应用的基础设施。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2614513.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…