{
  "metadata": {
    "id": "ch34",
    "title": "第34章：工具使用模式",
    "volume": "vol9",
    "volume_title": "Agent设计模式",
    "word_count": 1781,
    "difficulty": "advanced",
    "prerequisites": [
      "ch06"
    ],
    "key_concepts": [
      "工具抽象的本质",
      "从函数到工具",
      "Function Calling 标准接口",
      "基础工具模式",
      "单一工具调用",
      "参数提取模式",
      "工具链模式",
      "Sequential Chain（顺序链）",
      "DAG执行模式",
      "条件分支",
      "工具选择策略",
      "基于语义的自动选择",
      "ReAct式动态选择",
      "工具组合与编排",
      "Composite Tools（组合工具）"
    ],
    "learning_objectives": [],
    "estimated_tokens": 1069,
    "source_file": "vol9/ch34_工具使用模式.md"
  },
  "overview": "工具（Tool）是 Agent 与外部世界交互的桥梁。Agent 的能力上限，很大程度上取决于它能使用多少工具、如何选择工具、以及如何组合工具。本章将以设计模式（Design Pattern）的视角，系统讲解 Agent 工具使用的各种模式——从最基础的单工具调用，到复杂的工具链编排、动态选择策略、安全沙箱执行和性能优化。掌握这些模式，将帮助你构建出既强大又可靠的 Agent 工具系统。",
  "sections": [
    {
      "id": "34.1",
      "title": "34.1 工具抽象的本质",
      "level": 2,
      "content": "",
      "subsections": [
        {
          "id": "34.1.1",
          "title": "34.1.1 从函数到工具",
          "content": "在 Agent 编程中，\"工具\"不仅仅是函数——它是一个带有丰富语义描述的结构化抽象。工具不仅要能执行，更要能被 LLM 理解和选择。"
        },
        {
          "id": "34.1.2",
          "title": "34.1.2 Function Calling 标准接口",
          "content": ""
        }
      ]
    },
    {
      "id": "34.2",
      "title": "34.2 基础工具模式",
      "level": 2,
      "content": "",
      "subsections": [
        {
          "id": "34.2.1",
          "title": "34.2.1 单一工具调用",
          "content": "最简单的模式：Agent 识别用户意图，选择并调用单个工具。"
        },
        {
          "id": "34.2.2",
          "title": "34.2.2 参数提取模式",
          "content": ""
        }
      ]
    },
    {
      "id": "34.3",
      "title": "34.3 工具链模式",
      "level": 2,
      "content": "",
      "subsections": [
        {
          "id": "34.3.1",
          "title": "34.3.1 Sequential Chain（顺序链）",
          "content": ""
        },
        {
          "id": "34.3.2",
          "title": "34.3.2 DAG执行模式",
          "content": ""
        },
        {
          "id": "34.3.3",
          "title": "34.3.3 条件分支",
          "content": ""
        }
      ]
    },
    {
      "id": "34.4",
      "title": "34.4 工具选择策略",
      "level": 2,
      "content": "",
      "subsections": [
        {
          "id": "34.4.1",
          "title": "34.4.1 基于语义的自动选择",
          "content": ""
        },
        {
          "id": "34.4.2",
          "title": "34.4.2 ReAct式动态选择",
          "content": ""
        }
      ]
    },
    {
      "id": "34.5",
      "title": "34.5 工具组合与编排",
      "level": 2,
      "content": "",
      "subsections": [
        {
          "id": "34.5.1",
          "title": "34.5.1 Composite Tools（组合工具）",
          "content": ""
        }
      ]
    },
    {
      "id": "34.6",
      "title": "34.6 工具错误处理",
      "level": 2,
      "content": "",
      "subsections": [
        {
          "id": "34.6.1",
          "title": "34.6.1 超时与重试",
          "content": ""
        },
        {
          "id": "34.6.2",
          "title": "34.6.2 Graceful Degradation（优雅降级）",
          "content": ""
        }
      ]
    },
    {
      "id": "34.7",
      "title": "34.7 自定义工具开发",
      "level": 2,
      "content": "",
      "subsections": [
        {
          "id": "34.7.1",
          "title": "34.7.1 工具开发规范",
          "content": "工具命名：`动词_名词`（如 `search_web`、`calculate_expression`）\n\n描述模板：\n- 一句话功能描述\n- 适用场景（2-3个）\n- 不适用场景\n- 输入要求和输出格式\n\n参数设计规则：\n- 必需参数不超过3个\n- 使用具体类型（date 而非 string）\n- 有限选项用 enum 约束\n- 可选参数提供合理默认值"
        },
        {
          "id": "34.7.2",
          "title": "34.7.2 完整工具示例",
          "content": ""
        }
      ]
    },
    {
      "id": "34.8",
      "title": "34.8 工具安全模式",
      "level": 2,
      "content": "",
      "subsections": [
        {
          "id": "34.8.1",
          "title": "34.8.1 输入验证与沙箱",
          "content": ""
        }
      ]
    },
    {
      "id": "34.9",
      "title": "34.9 工具性能模式",
      "level": 2,
      "content": "",
      "subsections": [
        {
          "id": "34.9.1",
          "title": "34.9.1 异步并发",
          "content": ""
        },
        {
          "id": "34.9.2",
          "title": "34.9.2 结果缓存",
          "content": ""
        }
      ]
    },
    {
      "id": "最佳实践",
      "title": "最佳实践",
      "level": 2,
      "content": "1. **工具描述即API文档**：description 是 LLM 选择工具的唯一依据，必须准确详尽\n2. **参数约束要明确**：用 enum 限制选项，用 type 指定格式\n3. **错误信息要友好**：返回给 Agent 的错误信息应包含修复建议\n4. **工具粒度适中**：5-15个工具为最佳范围\n5. **监控工具使用**：记录调用频率、成功率、延迟，持续优化",
      "subsections": []
    },
    {
      "id": "常见陷阱",
      "title": "常见陷阱",
      "level": 2,
      "content": "1. **描述模糊**：\"处理数据\" → 应该写 \"查询SQL数据库并返回JSON结果\"\n2. **参数过多**：超过5个必需参数的工具调用失败率极高\n3. **忽略幂等性**：非幂等工具重试会产生副作用\n4. **缺少降级**：主工具失败时没有备选方案\n5. **无限流和缓存**：高频调用导致成本爆炸",
      "subsections": []
    },
    {
      "id": "小结",
      "title": "小结",
      "level": 2,
      "content": "工具是 Agent 能力的核心载体。本章从设计模式的视角系统讲解了工具抽象、调用、选择、编排、错误处理、安全约束和性能优化等模式。掌握这些模式，能够帮助你构建出既强大又安全的 Agent 工具系统。**好的工具设计 = 清晰的接口 + 健壮的错误处理 + 合理的降级策略**。",
      "subsections": []
    },
    {
      "id": "延伸阅读",
      "title": "延伸阅读",
      "level": 2,
      "content": "1. **MCP协议规范**: https://modelcontextprotocol.io/\n2. **论文**: \"ReAct: Synergizing Reasoning and Acting in Language Models\"\n3. **论文**: \"Toolformer: Language Models Can Teach Themselves to Use Tools\"\n4. **OpenAI Function Calling**: https://platform.openai.com/docs/guides/function-calling\n5. **Anthropic Tool Use**: https://docs.anthropic.com/claude/docs/tool-use",
      "subsections": []
    }
  ],
  "code_blocks": [
    {
      "id": "code-1",
      "language": "python",
      "description": "在 Agent 编程中，\"工具\"不仅仅是函数——它是一个带有丰富语义描述的结构化抽象。工具不仅要能执行，更要能被 LLM 理解和选择。",
      "code": "from pydantic import BaseModel, Field\nfrom typing import Any, Callable\n\n# 普通函数——Agent无法理解\ndef search(query: str) -> str:\n    return api.search(query)\n\n# 工具——Agent可以理解并选择\nclass SearchTool:\n    name = \"web_search\"\n    description = \"在互联网上搜索最新信息，适用于需要查找实时数据、新闻、产品信息等场景\"\n    \n    class Args(BaseModel):\n        query: str = Field(..., description=\"搜索关键词，应简洁明确\")\n        num_results: int = Field(default=5, description=\"返回结果数量，1-20\")\n        language: str = Field(default=\"zh\", description=\"搜索语言：zh/en\")\n    \n    async def execute(self, query: str, num_results: int = 5,\n                      language: str = \"zh\") -> str:\n        results = await search_api(query, num_results, language)\n        return self._format_results(results)",
      "section_ref": "34.1.1",
      "runnable": true,
      "dependencies": [
        "pydantic"
      ]
    },
    {
      "id": "code-2",
      "language": "python",
      "description": "",
      "code": "class StandardTool:\n    \"\"\"标准化的工具接口\"\"\"\n    \n    def __init__(self, name: str, description: str):\n        self.name = name\n        self.description = description\n        self._parameters: list[dict] = []\n    \n    def param(self, name: str, type_: str, description: str,\n              required: bool = True, default: Any = None,\n              enum: list[str] | None = None) -> 'StandardTool':\n        \"\"\"链式定义参数\"\"\"\n        param_def = {\n            \"type\": type_,\n            \"description\": description,\n        }\n        if not required:\n            param_def[\"default\"] = default\n        if enum:\n            param_def[\"enum\"] = enum\n        self._parameters.append({\"name\": name, **param_def})\n        return self\n    \n    def to_openai_schema(self) -> dict:\n        \"\"\"转换为OpenAI Function Calling格式\"\"\"\n        return {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": self.name,\n                \"description\": self.description,\n                \"parameters\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        p[\"name\"]: {\n                            \"type\": p[\"type\"],\n                            \"description\": p[\"description\"],\n                            **({\"enum\": p[\"enum\"]} if p.get(\"enum\") else {})\n                        }\n                        for p in self._parameters\n                    },\n                    \"required\": [\n                        p[\"name\"] for p in self._parameters \n                        if \"default\" not in p\n                    ]\n                }\n            }\n        }\n\n# 使用示例\nsearch = (StandardTool(\"search\", \"搜索互联网获取最新信息\")\n    .param(\"query\", \"string\", \"搜索关键词\")\n    .param(\"num_results\", \"integer\", \"结果数量\", required=False, default=5)\n    .param(\"region\", \"string\", \"搜索区域\", required=False, \n           enum=[\"cn\", \"us\", \"eu\", \"global\"]))",
      "section_ref": "34.1.2",
      "runnable": true,
      "dependencies": []
    },
    {
      "id": "code-3",
      "language": "python",
      "description": "最简单的模式：Agent 识别用户意图，选择并调用单个工具。",
      "code": "class SingleToolAgent:\n    \"\"\"单工具Agent\"\"\"\n    \n    def __init__(self, tool, llm):\n        self.tool = tool\n        self.llm = llm\n    \n    async def run(self, user_input: str) -> str:\n        # 1. 判断是否需要工具\n        decision = await self._should_use_tool(user_input)\n        \n        if not decision[\"use_tool\"]:\n            return await self.llm.generate(user_input)\n        \n        # 2. 提取工具参数\n        args = await self._extract_args(user_input, self.tool)\n        \n        # 3. 执行工具\n        result = await self.tool.execute(**args)\n        \n        # 4. 格式化输出\n        return await self._format_response(user_input, result)",
      "section_ref": "34.2.1",
      "runnable": true,
      "dependencies": []
    },
    {
      "id": "code-4",
      "language": "python",
      "description": "",
      "code": "class ParameterExtractor:\n    \"\"\"工具参数提取器\"\"\"\n    \n    async def extract(self, user_input: str, tool: StandardTool,\n                      llm) -> dict:\n        \"\"\"从自然语言中提取工具参数\"\"\"\n        prompt = f\"\"\"\n        用户输入: {user_input}\n        工具: {tool.name}\n        描述: {tool.description}\n        参数: {json.dumps(tool._parameters, ensure_ascii=False)}\n        \n        请从用户输入中提取工具参数，以JSON格式返回。\n        如果某个参数无法从输入中提取，使用默认值或null。\n        \"\"\"\n        response = await llm.generate(prompt)\n        \n        try:\n            params = json.loads(response)\n            return self._validate_params(params, tool)\n        except json.JSONDecodeError:\n            return self._fallback_extraction(user_input, tool)",
      "section_ref": "34.2.2",
      "runnable": true,
      "dependencies": []
    },
    {
      "id": "code-5",
      "language": "python",
      "description": "",
      "code": "class ToolChain:\n    \"\"\"工具顺序链\"\"\"\n    \n    def __init__(self):\n        self._steps: list[tuple[StandardTool, str]] = []\n    \n    def add_step(self, tool: StandardTool, \n                 input_mapping: str = \"$prev_result\") -> 'ToolChain':\n        self._steps.append((tool, input_mapping))\n        return self\n    \n    async def execute(self, initial_input: dict) -> str:\n        current_result = initial_input\n        \n        for i, (tool, mapping) in enumerate(self._steps):\n            step_input = self._resolve_mapping(mapping, current_result)\n            current_result = await tool.execute(**step_input)\n        \n        return current_result",
      "section_ref": "34.3.1",
      "runnable": true,
      "dependencies": []
    },
    {
      "id": "code-6",
      "language": "python",
      "description": "",
      "code": "import networkx as nx\n\nclass DAGExecutor:\n    \"\"\"有向无环图工具执行器\"\"\"\n    \n    def __init__(self):\n        self.graph = nx.DiGraph()\n        self._tools: dict[str, StandardTool] = {}\n    \n    def add_tool(self, tool: StandardTool, \n                 dependencies: list[str] | None = None):\n        self._tools[tool.name] = tool\n        self.graph.add_node(tool.name)\n        if dependencies:\n            for dep in dependencies:\n                self.graph.add_edge(dep, tool.name)\n    \n    async def execute(self, initial_inputs: dict = None) -> dict:\n        if not nx.is_directed_acyclic_graph(self.graph):\n            raise ValueError(\"工具依赖图包含循环！\")\n        \n        results = initial_inputs or {}\n        execution_order = list(nx.topological_sort(self.graph))\n        \n        for tool_name in execution_order:\n            tool = self._tools[tool_name]\n            args = {}\n            for pred in self.graph.predecessors(tool_name):\n                args[pred] = results.get(pred, \"\")\n            results[tool_name] = await tool.execute(**args)\n        \n        return results",
      "section_ref": "34.3.2",
      "runnable": true,
      "dependencies": [
        "networkx"
      ]
    },
    {
      "id": "code-7",
      "language": "python",
      "description": "",
      "code": "class ConditionalToolChain:\n    \"\"\"条件分支工具链\"\"\"\n    \n    def when(self, condition: str, tools: list[StandardTool]):\n        \"\"\"定义条件分支\"\"\"\n        self._conditions.append({\"condition\": condition, \"tools\": tools})\n        return self\n    \n    async def execute(self, input_data: dict) -> str:\n        for cond in self._conditions:\n            if self._evaluate(cond[\"condition\"], input_data):\n                for tool in cond[\"tools\"]:\n                    result = await tool.execute(**input_data)\n                    input_data[\"last_result\"] = result\n                return result\n        return \"没有匹配的条件分支\"",
      "section_ref": "34.3.3",
      "runnable": true,
      "dependencies": []
    },
    {
      "id": "code-8",
      "language": "python",
      "description": "",
      "code": "class SemanticToolSelector:\n    \"\"\"语义工具选择器\"\"\"\n    \n    def __init__(self, tools: list[StandardTool], embedder):\n        self.tools = tools\n        self.embedder = embedder\n        self._tool_embeddings: dict[str, list[float]] = {}\n    \n    async def build_index(self):\n        for tool in self.tools:\n            index_text = f\"{tool.name}: {tool.description}\"\n            self._tool_embeddings[tool.name] = \\\n                await self.embedder.embed(index_text)\n    \n    async def select(self, query: str, top_k: int = 3) -> list[StandardTool]:\n        query_embedding = await self.embedder.embed(query)\n        \n        scored_tools = []\n        for tool in self.tools:\n            similarity = cosine_similarity(\n                [query_embedding], [self._tool_embeddings[tool.name]]\n            )[0][0]\n            scored_tools.append((tool, similarity))\n        \n        scored_tools.sort(key=lambda x: x[1], reverse=True)\n        return [t for t, s in scored_tools[:top_k]]",
      "section_ref": "34.4.1",
      "runnable": true,
      "dependencies": []
    },
    {
      "id": "code-9",
      "language": "python",
      "description": "",
      "code": "class ReActToolSelector:\n    \"\"\"ReAct模式下的动态工具选择\"\"\"\n    \n    async def think_and_act(self, task: str, tools: list[StandardTool],\n                            llm, max_steps: int = 10) -> str:\n        tools_desc = \"\\n\".join(\n            f\"- {t.name}: {t.description}\" for t in tools\n        )\n        \n        context = f\"任务: {task}\\n可用工具:\\n{tools_desc}\"\n        history = []\n        \n        for step in range(max_steps):\n            thought_prompt = f\"\"\"\n            {context}\n            历史步骤: {self._format_history(history)}\n            \n            请思考下一步。格式：\n            思考: [推理过程]\n            行动: [工具名(参数)] 或 \"最终答案: [回答]\"\n            \"\"\"\n            response = await llm.generate(thought_prompt)\n            action = self._parse_action(response)\n            \n            if action[\"type\"] == \"final_answer\":\n                return action[\"content\"]\n            \n            tool = next(t for t in tools if t.name == action[\"tool_name\"])\n            result = await tool.execute(**action[\"args\"])\n            history.append({\"action\": f\"{action['tool_name']}\", \"observation\": str(result)[:500]})\n            context += f\"\\n\\n观察: {str(result)[:500]}\"\n        \n        return \"未能在限定步骤内完成任务。\"",
      "section_ref": "34.4.2",
      "runnable": true,
      "dependencies": []
    },
    {
      "id": "code-10",
      "language": "python",
      "description": "",
      "code": "class CompositeTool(StandardTool):\n    \"\"\"组合工具：将多个工具封装为一个高级工具\"\"\"\n    \n    def __init__(self, name: str, description: str):\n        super().__init__(name, description)\n        self._sub_tools: list[StandardTool] = []\n    \n    def add_sub_tool(self, tool: StandardTool):\n        self._sub_tools.append(tool)\n        return self\n    \n    async def execute(self, **kwargs) -> str:\n        intermediate = kwargs\n        for tool in self._sub_tools:\n            result = await tool.execute(**intermediate)\n            intermediate[\"prev_result\"] = result\n            intermediate[\"input\"] = str(result)\n        return intermediate.get(\"prev_result\", \"\")\n\n# 智能搜索 = 搜索 + 摘要\nsmart_search = CompositeTool(\"smart_search\", \"智能搜索：搜索并摘要\")\nsmart_search.add_sub_tool(StandardTool(\"search\", \"搜索\").param(\"query\", \"string\", \"查询\"))\nsmart_search.add_sub_tool(StandardTool(\"summarize\", \"摘要\").param(\"text\", \"string\", \"文本\"))",
      "section_ref": "34.5.1",
      "runnable": true,
      "dependencies": []
    },
    {
      "id": "code-11",
      "language": "python",
      "description": "",
      "code": "class ResilientTool:\n    \"\"\"带弹性策略的工具包装器\"\"\"\n    \n    def __init__(self, tool: StandardTool, \n                 max_retries: int = 3, timeout: float = 30.0):\n        self.tool = tool\n        self.max_retries = max_retries\n        self.timeout = timeout\n    \n    async def execute(self, **kwargs) -> dict:\n        try:\n            result = await asyncio.wait_for(\n                self._retry_execute(**kwargs),\n                timeout=self.timeout\n            )\n            return {\"success\": True, \"data\": result}\n        except asyncio.TimeoutError:\n            return {\"success\": False, \"error\": f\"工具 {self.tool.name} 超时\"}\n        except Exception as e:\n            return {\"success\": False, \"error\": str(e)}\n    \n    async def _retry_execute(self, **kwargs):\n        for attempt in range(self.max_retries):\n            try:\n                return await self.tool.execute(**kwargs)\n            except Exception:\n                if attempt == self.max_retries - 1:\n                    raise\n                await asyncio.sleep(2 ** attempt)",
      "section_ref": "34.6.1",
      "runnable": true,
      "dependencies": []
    },
    {
      "id": "code-12",
      "language": "python",
      "description": "",
      "code": "class DegradableTool:\n    \"\"\"可降级的工具\"\"\"\n    \n    def __init__(self, primary: StandardTool,\n                 fallback: StandardTool = None,\n                 fallback_response: str = None):\n        self.primary = primary\n        self.fallback = fallback\n        self.fallback_response = fallback_response\n    \n    async def execute(self, **kwargs) -> Any:\n        try:\n            return await self.primary.execute(**kwargs)\n        except Exception as e:\n            if self.fallback:\n                return await self.fallback.execute(**kwargs)\n            if self.fallback_response:\n                return self.fallback_response\n            raise",
      "section_ref": "34.6.2",
      "runnable": true,
      "dependencies": []
    },
    {
      "id": "code-13",
      "language": "python",
      "description": "- 可选参数提供合理默认值",
      "code": "class FileAnalysisTool(StandardTool):\n    \"\"\"文件分析工具\"\"\"\n    \n    def __init__(self):\n        super().__init__(\"analyze_file\", \"分析上传的文件内容\")\n        self.param(\"file_path\", \"string\", \"文件路径或URL\")\n        self.param(\"analysis_type\", \"string\", \"分析类型\",\n                  enum=[\"summary\", \"statistics\", \"schema\", \"quality\"])\n        self.param(\"max_rows\", \"integer\", \"最大分析行数\",\n                  required=False, default=1000)\n    \n    async def execute(self, file_path: str, analysis_type: str,\n                      max_rows: int = 1000) -> dict:\n        data = await self._load_file(file_path)\n        analyzers = {\n            \"summary\": self._summarize,\n            \"statistics\": self._statistics,\n            \"schema\": self._infer_schema,\n            \"quality\": self._check_quality,\n        }\n        return analyzers[analysis_type](data, max_rows)",
      "section_ref": "34.7.2",
      "runnable": true,
      "dependencies": []
    },
    {
      "id": "code-14",
      "language": "python",
      "description": "",
      "code": "class SecureToolWrapper:\n    \"\"\"安全工具包装器\"\"\"\n    \n    def __init__(self, tool: StandardTool):\n        self.tool = tool\n        self._validators = []\n    \n    def add_validator(self, validator):\n        self._validators.append(validator)\n        return self\n    \n    async def execute(self, **kwargs) -> Any:\n        for validator in self._validators:\n            is_valid, error = validator(kwargs)\n            if not is_valid:\n                raise ValueError(f\"输入验证失败: {error}\")\n        return await self.tool.execute(**kwargs)\n\n# SQL注入检测\ndef no_sql_injection(params: dict) -> tuple[bool, str]:\n    dangerous = [\"DROP\", \"DELETE\", \"INSERT\", \"UPDATE\", \"UNION\", \"--\", \";\"]\n    for key, value in params.items():\n        if isinstance(value, str):\n            if any(kw in value.upper() for kw in dangerous):\n                return False, f\"参数 {key} 包含可疑SQL关键字\"\n    return True, \"\"",
      "section_ref": "34.8.1",
      "runnable": true,
      "dependencies": []
    },
    {
      "id": "code-15",
      "language": "python",
      "description": "",
      "code": "class AsyncToolExecutor:\n    \"\"\"异步工具执行器\"\"\"\n    \n    def __init__(self, max_concurrent: int = 10):\n        self.semaphore = asyncio.Semaphore(max_concurrent)\n    \n    async def execute_many(self, calls: list[tuple[StandardTool, dict]]) -> list[dict]:\n        tasks = [self._execute_one(tool, args) for tool, args in calls]\n        return await asyncio.gather(*tasks, return_exceptions=True)\n    \n    async def _execute_one(self, tool: StandardTool, args: dict) -> dict:\n        async with self.semaphore:\n            start = time.time()\n            try:\n                result = await tool.execute(**args)\n                return {\"tool\": tool.name, \"success\": True, \"result\": result,\n                        \"duration_ms\": (time.time() - start) * 1000}\n            except Exception as e:\n                return {\"tool\": tool.name, \"success\": False, \"error\": str(e)}",
      "section_ref": "34.9.1",
      "runnable": true,
      "dependencies": []
    },
    {
      "id": "code-16",
      "language": "python",
      "description": "",
      "code": "class CachedTool:\n    \"\"\"带缓存的工具\"\"\"\n    \n    def __init__(self, tool: StandardTool, cache_ttl: int = 300):\n        self.tool = tool\n        self._cache: dict[str, tuple[float, Any]] = {}\n        self.ttl = cache_ttl\n    \n    async def execute(self, **kwargs) -> Any:\n        key = hashlib.md5(json.dumps(kwargs, sort_keys=True).encode()).hexdigest()\n        if key in self._cache:\n            ts, val = self._cache[key]\n            if time.time() - ts < self.ttl:\n                return val\n        \n        result = await self.tool.execute(**kwargs)\n        self._cache[key] = (time.time(), result)\n        return result",
      "section_ref": "34.9.2",
      "runnable": true,
      "dependencies": []
    }
  ],
  "tables": [],
  "key_takeaways": [],
  "common_pitfalls": [],
  "related_chapters": [
    "ch06",
    "ch35"
  ]
}