🔧 Tool Result Processing(工具结果处理)详解

"凌晨3点27分,Agent 收到了一个工具返回的结果。它不是简单地把结果塞进上下文,而是要理解、验证、提取、应用——这中间有太多可以踩的坑..."

Tool Result Processing 是 Agent 接收工具返回结果后的处理流程。这听起来简单,实际包含:结果解析、错误检测、数据提取、状态更新、重试决策等多个环节。一个健壮的 Agent,其核心竞争力往往在于 如何优雅地处理工具结果

📊 处理流程概览
工具返回 → 解析格式 → 检查状态 → 提取数据 → 更新状态 → 决定下一步
    ↓           ↓           ↓           ↓           ↓
 错误处理   格式验证   错误检测   数据转换   上下文更新   重试/继续
      

为什么 Tool Result Processing 重要?

看一个真实案例:

// 工具返回了一个复杂的结果
{
  "status": "success",
  "data": {
    "users": [
      { "id": 1, "name": "Alice", "email": "alice@example.com" },
      { "id": 2, "name": "Bob", "email": "bob@example.com" }
    ],
    "pagination": {
      "page": 1,
      "total": 100,
      "hasMore": true
    }
  },
  "metadata": {
    "timestamp": "2026-04-21T04:00:00Z",
    "server": "api-prod-01"
  }
}

// Agent 需要:
// 1. 理解这是成功还是失败?
// 2. 真正的数据在哪里?
// 3. 还需要继续获取下一页吗?
// 4. 如何把这个结果告诉用户?

处理流程详解

1. 结果解析(Parsing)

async function parseToolResult(result) {
  // 处理不同格式
  if (typeof result === 'string') {
    try {
      return JSON.parse(result);
    } catch {
      return { raw: result };
    }
  }
  
  if (result instanceof Buffer) {
    return { buffer: result, text: result.toString() };
  }
  
  if (result instanceof Error) {
    return { 
      error: true, 
      message: result.message,
      stack: result.stack 
    };
  }
  
  return result;
}

2. 状态检测(Status Detection)

function detectStatus(result) {
  // 常见的状态字段名
  const statusFields = ['status', 'code', 'statusCode', 'error', 'success'];
  
  for (const field of statusFields) {
    if (result[field] !== undefined) {
      // 判断是否成功
      const value = result[field];
      if (typeof value === 'boolean') return value ? 'success' : 'error';
      if (typeof value === 'number') return value >= 200 && value < 300 ? 'success' : 'error';
      if (typeof value === 'string') {
        const lower = value.toLowerCase();
        if (['success', 'ok', 'completed'].includes(lower)) return 'success';
        if (['error', 'fail', 'failed'].includes(lower)) return 'error';
      }
    }
  }
  
  // 检查是否有 error 字段
  if (result.error || result.err || result.errorMessage) {
    return 'error';
  }
  
  // 默认成功
  return 'success';
}

3. 错误处理(Error Handling)

async function handleToolError(error, context) {
  const errorStrategy = {
    // 网络错误 - 重试
    'ECONNREFUSED': { action: 'retry', maxRetries: 3, delay: 1000 },
    'ETIMEDOUT': { action: 'retry', maxRetries: 3, delay: 2000 },
    
    // 认证错误 - 报告用户
    'UNAUTHORIZED': { action: 'report', message: '需要重新授权' },
    'FORBIDDEN': { action: 'report', message: '权限不足' },
    
    // 资源错误 - 尝试替代方案
    'NOT_FOUND': { action: 'fallback', fallback: 'useAlternativeSource' },
    
    // 服务错误 - 等待后重试
    'RATE_LIMIT': { action: 'retry', maxRetries: 5, delay: 60000 },
    'SERVICE_UNAVAILABLE': { action: 'retry', maxRetries: 3, delay: 30000 }
  };
  
  const strategy = errorStrategy[error.code] || { action: 'report' };
  
  switch (strategy.action) {
    case 'retry':
      if (context.retryCount < strategy.maxRetries) {
        await sleep(strategy.delay);
        return { shouldRetry: true, delay: strategy.delay };
      }
      break;
    case 'fallback':
      return { shouldFallback: true, fallbackMethod: strategy.fallback };
    case 'report':
    default:
      return { shouldReport: true, message: strategy.message || error.message };
  }
}

4. 数据提取(Data Extraction)

function extractData(result, schema) {
  // 从复杂结果中提取关键数据
  const extracted = {};
  
  for (const [key, path] of Object.entries(schema)) {
    // 支持路径表达式: "data.users.0.name"
    const value = getNestedValue(result, path);
    if (value !== undefined) {
      extracted[key] = value;
    }
  }
  
  return extracted;
}

// 示例:从API响应中提取关键信息
const userSchema = {
  userId: 'data.id',
  userName: 'data.name',
  userEmail: 'data.email',
  hasMore: 'pagination.hasMore',
  nextPage: 'pagination.page + 1'
};

const extracted = extractData(apiResponse, userSchema);
// { userId: 1, userName: 'Alice', hasMore: true, nextPage: 2 }

OpenClaw 工具结果处理实战

1. 工具返回格式

OpenClaw 工具统一返回结构:

// 成功返回
{
  "status": "success",
  "data": { /* 工具特定数据 */ },
  "message": "操作成功"
}

// 错误返回
{
  "status": "error",
  "error": {
    "code": "TOOL_EXECUTION_FAILED",
    "message": "具体错误信息",
    "details": { /* 错误详情 */ }
  }
}

2. 处理 exec 工具结果

// exec 工具返回特殊结构
const execResult = await exec({ command: "npm test" });

// 结果结构
{
  "status": "error",  // 或 "success"
  "stdout": "...",
  "stderr": "...",
  "exitCode": 1,
  "signal": null
}

// 处理逻辑
if (execResult.status === 'error') {
  // 检查是否需要重试
  if (execResult.stderr.includes('ECONNREFUSED')) {
    // 网络问题,可能需要重试
    return { shouldRetry: true };
  }
  
  // 检查是否是测试失败
  if (execResult.stdout.includes('FAIL')) {
    // 分析失败原因
    const failures = parseTestFailures(execResult.stdout);
    return { testFailures: failures };
  }
}

3. 处理 web_fetch 结果

const fetchResult = await web_fetch({ url: "https://example.com" });

// 可能的结果
// 1. 成功
{
  "content": "网页文本内容...",
  "title": "页面标题",
  "links": ["https://..."]
}

// 2. 错误
{
  "error": "Failed to fetch",
  "code": "ENOTFOUND"
}

// 处理逻辑
if (fetchResult.error) {
  if (fetchResult.code === 'ENOTFOUND') {
    // 域名不存在,可能拼写错误
    return { shouldReport: true, message: '网址不存在,请检查拼写' };
  }
  if (fetchResult.code === '403') {
    // 访问被拒绝
    return { shouldFallback: true, fallback: 'tryAlternative' };
  }
}
⚠️ 常见陷阱

重试策略设计

指数退避重试

async function retryWithBackoff(toolCall, options = {}) {
  const { maxRetries = 3, baseDelay = 1000, maxDelay = 60000 } = options;
  let attempt = 0;
  
  while (attempt < maxRetries) {
    try {
      const result = await toolCall();
      if (result.status === 'success') return result;
      
      // 检查是否可重试错误
      if (!isRetryableError(result)) {
        return result; // 不可重试,直接返回
      }
    } catch (error) {
      // 网络层错误
    }
    
    // 指数退避
    const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
    await sleep(delay);
    attempt++;
  }
  
  return { status: 'error', message: '重试次数已用完' };
}

条件重试

const retryConditions = {
  // 网络问题 - 总是重试
  network: ['ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND'],
  
  // 服务问题 - 有条件重试
  service: {
    'RATE_LIMIT': { retry: true, delay: 60000 },
    'SERVICE_UNAVAILABLE': { retry: true, delay: 30000 },
    'INTERNAL_ERROR': { retry: true, delay: 5000 }
  },
  
  // 用户问题 - 不重试
  user: ['UNAUTHORIZED', 'FORBIDDEN', 'NOT_FOUND', 'BAD_REQUEST']
};

结果缓存策略

const cacheStrategy = {
  // 幂等操作 - 可缓存
  cacheable: ['read', 'list', 'search', 'get'],
  
  // 非幂等操作 - 不缓存
  nonCacheable: ['create', 'update', 'delete', 'execute'],
  
  // 缓存配置
  config: {
    ttl: 300000,        // 5分钟
    maxSize: 1000,      // 最多1000条
    keyGenerator: (tool, params) => `${tool}:${JSON.stringify(params)}`
  }
};
"工具结果处理是 Agent 的「消化系统」——不仅要吃进去,还要消化、吸收、排出。一个只会调用工具不会处理结果的 Agent,就像一个只会张嘴不会吞咽的人..."