{"record":{"id":"ee14005f921b620a","repo":"datawhalechina/hello-agents","slug":"tool-name","errorCode":null,"errorMessage":"工具 '{tool_name}' 不存在","messagePattern":"工具 '(.+?)' 不存在","errorType":"exception","errorClass":"AgentException","httpStatus":500,"severity":"error","filePath":"Co-creation-projects/Apricity-InnocoreAI/agents/base.py","lineNumber":61,"sourceCode":"            \"function\": tool_func,\n            \"description\": description\n        }\n    \n    def get_tools_description(self) -> str:\n        \"\"\"获取工具描述\"\"\"\n        if not self.tools:\n            return \"暂无可用工具\"\n        \n        descriptions = []\n        for name, tool_info in self.tools.items():\n            descriptions.append(f\"- {name}: {tool_info['description']}\")\n        \n        return \"\\n\".join(descriptions)\n    \n    async def call_tool(self, tool_name: str, tool_input: Any) -> Any:\n        \"\"\"调用工具\"\"\"\n        if tool_name not in self.tools:\n            raise AgentException(f\"工具 '{tool_name}' 不存在\")\n        \n        try:\n            tool_func = self.tools[tool_name][\"function\"]\n            if asyncio.iscoroutinefunction(tool_func):\n                result = await asyncio.wait_for(\n                    tool_func(tool_input), \n                    timeout=self.timeout\n                )\n            else:\n                result = await asyncio.wait_for(\n                    asyncio.to_thread(tool_func, tool_input),\n                    timeout=self.timeout\n                )\n            \n            self._add_to_history(f\"Tool {tool_name} called with input: {tool_input}\")\n            self._add_to_history(f\"Tool {tool_name} result: {result}\")\n            \n            return result","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/Apricity-InnocoreAI/agents/base.py#L43-L79","documentation":"Raised by BaseAgent.call_tool in InnocoreAI when the requested tool_name is not a key in self.tools, the registry populated at agent construction. It is a lookup precondition failure: the LLM (or caller) hallucinated a tool name, or the agent was built without registering the tool. Note the f-string in the source means the message interpolates the actual name (e.g. \"工具 'search_papers' 不存在\").","triggerScenarios":"An LLM tool-calling loop emitting a function name that differs from the registered key (snake_case vs camelCase, suffix drift like 'search_arxiv' vs 'arxiv_search'); constructing an agent subclass whose __init__ never registers tools; JSON-parsing a tool call where the name field picks up whitespace/quotes; removing a tool from the registry while prompts still advertise it.","commonSituations":"Prompt template lists tool names that no longer match the registry after refactoring; model outputs localized tool names; registration happens in a subclass the DI container skips; typos in tool names inside hand-written orchestration code.","solutions":["Log/print self.tools.keys() next to the failing name to spot exact spelling/case mismatch.","Make the system prompt's tool list generated from the same registry (get_tools_description already does this) instead of hardcoded names.","Register the missing tool in the agent's __init__ (self.register_tool(name, {'function':..., 'description':...})).","Normalize LLM output (strip, lower, alias map) before call_tool.","Optionally return a corrective message instead of raising so the LLM loop can retry with the valid tool list."],"exampleFix":"# before\nasync def call_tool(self, tool_name, tool_input):\n    if tool_name not in self.tools:\n        raise AgentException(f\"工具 '{tool_name}' 不存在\")\n\n# after — fuzzy-correct before failing\nasync def call_tool(self, tool_name, tool_input):\n    name = tool_name.strip()\n    if name not in self.tools:\n        candidates = [k for k in self.tools if k.lower().replace('_','') == name.lower().replace('_','')]\n        if not candidates:\n            raise AgentException(\n                f\"工具 '{tool_name}' 不存在; 可用工具: {sorted(self.tools)}\"\n            )\n        name = candidates[0]\n    ...","handlingStrategy":"validation","validationCode":"# Pre-check before dispatching an LLM tool call\nif tool_name not in agent.tools:\n    valid = sorted(agent.tools)\n    # return corrective feedback to the model instead of crashing the loop\n    return f\"Unknown tool '{tool_name}'. Available tools: {valid}\"","typeGuard":"def is_registered_tool(agent, name: str) -> bool:\n    return isinstance(name, str) and name in agent.tools","tryCatchPattern":"try:\n    result = await agent.call_tool(name, tool_input)\nexcept AgentException as e:\n    if \"不存在\" in str(e):\n        log.warning(str(e)); reply_correction_to_model(e)  # let LLM retry\n    else:\n        raise","preventionTips":["Generate the tool list in prompts from the live registry, never hardcode tool names.","Strip/normalize names from LLM output before lookup.","Include the valid tool list in the error message so failures self-correct."],"tags":["python","agent","tool-registry","llm","validation"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}