🔧 Tool Composition(工具组合)

"单个工具是螺丝刀,组合起来就是瑞士军刀" - 让Agent从只会一招到能打出combo

Agent工具 工具编排 Tool Calling OpenClaw

📌 定义

Tool Composition(工具组合)是指AI Agent将多个独立工具按照特定逻辑链式组合使用,使前一个工具的输出成为后一个工具的输入,从而完成单个工具无法实现的复杂任务。它是Agent从"能用工具"到"善用工具"的关键能力跃迁。

🎭 为什么工具组合如此重要?

你能想象一个只会用锤子的木匠吗?给你一把锤子,你能把钉子敲进去。再给你一把锯子,你能截断木头。但给你锤子+锯子+尺子+墨斗的组合——你就能盖一栋房子。Tool Composition就是教Agent学会"盖房子"而不是"只会敲钉子"。

单工具 vs 组合工具

维度单工具调用工具组合
能力边界单一功能跨功能协作
输入输出固定格式动态适配
错误处理重试即可需考虑链路回滚
可维护性简单直接需要编排逻辑
典型场景查天气查天气→规划行程→订票

📐 五大工具组合模式

1. 串行管道(Sequential Pipeline)

最基础的组合模式,工具A的输出直接作为工具B的输入,像流水线一样。
// 串行管道:搜索 → 提取 → 翻译
async function sequentialPipeline(query) {
  // Step 1: 搜索
  const searchResults = await web_search({ query });
  
  // Step 2: 提取内容
  const content = await web_fetch({ 
    url: searchResults[0].url 
  });
  
  // Step 3: 翻译
  const translated = await translate({
    text: content.text,
    target: 'zh-CN'
  });
  
  return translated;
}

2. 并行扇出(Parallel Fan-out)

同时调用多个工具,聚合结果。适合需要从多个源获取信息的场景。
// 并行扇出:同时搜索多个源
async function parallelFanout(topic) {
  const [github, twitter, news] = await Promise.all([
    github_search({ q: topic }),
    twitter_search({ q: topic }),
    news_search({ q: topic })
  ]);
  
  // 聚合去重
  return deduplicate([...github, ...twitter, ...news]);
}

3. 条件分支(Conditional Branching)

根据中间结果决定下一步调用哪个工具,实现动态决策。
// 条件分支:根据内容类型选择不同处理
async function conditionalBranch(url) {
  const content = await web_fetch({ url });
  
  if (content.type === 'pdf') {
    return await pdf_extract({ content });
  } else if (content.type === 'video') {
    return await video_transcribe({ content });
  } else {
    return await text_analyze({ content });
  }
}

4. 循环迭代(Iterative Loop)

工具组合结果不满足条件时循环调用,直到达到目标。
// 循环迭代:搜索直到找到满意结果
async function iterativeSearch(query, minResults = 5) {
  let results = [];
  let page = 1;
  
  while (results.length < minResults && page <= 10) {
    const batch = await web_search({ 
      query, 
      page 
    });
    results.push(...batch);
    page++;
  }
  
  return results.slice(0, minResults);
}

5. Map-Reduce模式

先并行处理多个数据项(Map),再汇总结果(Reduce),适合批量处理场景。
// Map-Reduce:批量分析多个URL
async function mapReduce(urls) {
  // Map: 并行提取所有页面
  const contents = await Promise.all(
    urls.map(url => web_fetch({ url }))
  );
  
  // Reduce: 汇总分析
  const summary = await llm.complete(`
综合分析以下${contents.length}个页面的核心观点:
${contents.map(c => c.text).join('\n---\n')}
`);
  
  return summary;
}

🔧 OpenClaw实战:工具组合编排

使用OpenClaw Skills实现工具组合

# SKILL.md - 定义一个内容研究Skill
# 触发条件:用户需要研究某个话题

## 研究工作流
1. 使用 web_search 搜索话题关键词
2. 使用 web_fetch 获取前3个结果页面
3. 使用 write 生成研究报告
4. 使用 message 通知用户完成

## 参数
- topic: 研究话题
- depth: 研究深度 (quick | standard | deep)
- format: 输出格式 (md | html | pdf)

OpenClaw Agent工具组合示例

// 竞品分析工具组合链
async function competitorAnalysis(competitor) {
  // 1. 搜索竞品信息
  const info = await web_search({ 
    query: `${competitor} product features pricing` 
  });
  
  // 2. 获取官网数据
  const siteData = await Promise.all([
    web_fetch({ url: `https://${competitor}.com` }),
    web_fetch({ url: `https://${competitor}.com/pricing` })
  ]);
  
  // 3. 生成对比报告
  const report = await llm.complete(`
基于以下信息生成竞品分析报告:
官网内容:${siteData[0].text}
定价信息:${siteData[1].text}
搜索结果:${JSON.stringify(info)}
`);
  
  // 4. 保存报告
  await write({ 
    path: `/reports/${competitor}-analysis.md`, 
    content: report 
  });
}

⚠️ 常见踩坑与最佳实践

踩坑一:输出格式不匹配
工具A输出JSON,工具B期望CSV——格式不匹配是工具组合最常见的问题。建议在每个工具之间添加数据转换层,使用TypeScript接口定义明确的数据契约。
踩坑二:错误传播与链路断裂
工具链中任何一个环节失败,整条链路就断了。必须实现优雅降级:搜索失败→尝试缓存→返回部分结果,而不是直接报错。
踩坑三:无限循环
迭代模式下如果不设退出条件,Agent可能在工具间无限循环。必须设置最大迭代次数(建议3-5次)和超时机制。
最佳实践:
  • 每个工具的输入输出格式预先定义并测试
  • 实现断路器模式(Circuit Breaker)防止错误扩散
  • 添加日志记录每个工具的输入输出用于调试
  • 组合链不宜过长,3-5个工具为宜
  • 关键节点添加人工确认(HITL)

🔗 相关术语