🔧 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' };
}
}
⚠️ 常见陷阱
- 假设结果格式 - 不同工具返回格式不同,不要假设统一结构
- 忽略错误细节 - stderr 往往包含关键调试信息
- 不处理分页 - 很多API返回分页数据,需要循环获取
- 内存溢出 - 大结果集需要流式处理
重试策略设计
指数退避重试
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,就像一个只会张嘴不会吞咽的人..."