深入认识 Agent - 构建简单的 Agent 框架

目录:

用 Copilot 学 Agent ------ AI 编程的新范式

深入认识 Agent ------ 实现你自己的 Agent

深入认识 Agent ------ 智能体开发框架

深入认识 Agent ------ 构建简单的 Agent 框架


前文介绍了市面上的主流 Agent 开发框架,如 LangGraph、Microsoft Agent Framework 等,这些框架固然好用,但也存在一些问题:

  • 过度抽象的复杂性,为了追求通用型,这些框架都引入了大量抽象层和配置选项。
  • 快速迭代带来的不稳定性,商业化框架为了抢占市场,API 接口变更频繁,不够稳定。
  • 黑盒化的实现逻辑,很多框架的核心逻辑封装得过于严密,开发者难以理解 Agent 的内部工作机制,缺乏深度定制能力。遇到问题只能依赖文档和社区支持。
  • 依赖关系的复杂性,框架中引入了大量依赖包,安装包体积庞大,在需要与别的项目代码配合使用时可能出现依赖冲突问题。

基于以上原因,某些情况下可能需要定制化自己的 Agent 框架,这也是本文将介绍的内容:如何设计一个 Agent 框架。

LLM 客户端

前文介绍三种不同的 Agent 模式时创建过一个 LLM 客户端 HelloAgentsLLM,但只支持 AzureOpenAI,接下来对其进行扩展以便支持更多的 LLM Provider:

python 复制代码
import os
from openai import AzureOpenAI, OpenAI
from dotenv import load_dotenv
from typing import List, Dict

load_dotenv()


class HelloAgentsLLM:
    def __init__(self, provider: str = "azure", model: str = None, api_key: str = None, base_url: str = None, **kwargs):
        if provider == "azure":
            self.model = model or os.getenv("AZURE_MODEL_ID")
            api_key = api_key or os.getenv("AZURE_API_KEY")
            base_url = base_url or os.getenv("AZURE_BASE_URL")
            api_version = kwargs.get('azure_api_version') or os.getenv("AZURE_API_VERSION")
            if not all([self.model, api_key, base_url]):
                raise ValueError("azure llm api parameters not found")
            self.temperature = kwargs.get('temperature', 0.5)
            self.max_tokens = kwargs.get('max_tokens')
            self._client = AzureOpenAI(api_key=api_key, azure_endpoint=base_url, api_version=api_version)
        elif provider == "modelscope":
            self.model = model or os.getenv("MODELSCOPE_MODEL_ID") or "Qwen/Qwen3.5-35B-A3B"
            self.api_key = api_key or os.getenv("MODELSCOPE_API_KEY")
            self.base_url = base_url or "https://api-inference.modelscope.cn/v1/"
            if not self.api_key:
                raise ValueError("ModelScope API key not found.")
            self.temperature = kwargs.get('temperature', 0.7)
            self.max_tokens = kwargs.get('max_tokens')
            self.timeout = kwargs.get('timeout', 60)
            self._client = OpenAI(api_key=self.api_key, base_url=self.base_url, timeout=self.timeout)
        else:
            print("不支持的 provider")

    def invoke(self, messages: List[ChatCompletionMessageParam], temperature: float = 0) -> str:
        print(f"🧠正在调用 {self.model} 模型...")
        collected_content = []
        try:
            with self._client.chat.completions.stream(
                    model=self.model,
                    messages=messages,
                    temperature=temperature
            ) as stream:
                for event in stream:
                    if event.type == "content.delta":
                        content = event.delta
                        print(content, end="", flush=True)  # 实时输出结果
                        collected_content.append(content)
                print()  # 结束后换行
                return "".join(collected_content)
        except Exception as e:
            print(f"❌调用LLM API时发生错误: {e}")
            return "错误: 调用语言模型服务时出错"

这里扩展了对 modelscope 的支持,ModelScope 是阿里推出的大模型平台,有点类似于 HuggingFace Hub。要想使用其模型推理服务,需要注册账号并绑定阿里云,创建 API Token。注册用户每天可使用 2000 次的 API-Inference 调用,每个模型均有额外每日使用限额(不超过 200)。

现在来调用 modelscope 的模型:

env 在项目根目录,内容如下

# MODELSCOPE_MODEL_ID="deepseek-ai/DeepSeek-V4-Flash"

# MODELSCOPE_API_KEY=ms-f0cfaa08-4247-462b-8823-xxxxxx

python 复制代码
from llm import HelloAgentsLLM

llm = HelloAgentsLLM(provider="modelscope")
messages = [ChatCompletionUserMessageParam(role="user", content="给我讲个笑话吧")]

llm.invoke(messages)

由于 openai sdk 版本的升级,与大模型交互时的消息格式也发生了变更,新版本对 message 类型收紧了,不再像旧版那么宽泛。

结果如下:

复制代码
🧠正在调用 deepseek-ai/DeepSeek-V4-Flash 模型...
当然!这是一个简短的笑话:  

**程序员和酒**  
一个程序员去酒吧,点了一杯酒。  
服务员问:"要加冰吗?"  
程序员想了想,说:"不用了,我喝常温的,免得Bug(冰)又冒出来。"  

------  
笑点在于"冰"和"Bug"的谐音梗,程序员常把程序错误叫"Bug",所以连喝酒都要避讳"冰(Bug)"。😄  

需要更冷或者更热门的笑话吗?我可以继续讲!

使用本地模型

上面的例子均使用的云端模型,如果要使用本地模型,可以通过 VLLM(高性能 LLM 推理库)或者 Ollama 在本地运行大模型

这里以 Ollama 为例,假设已经运行了本地模型 "deepseek-r1:7b",默认情况下其本地访问地址为:http://localhost:11434/v1。增加对 Ollama 本地模型的支持:

python 复制代码
from typing import Optional
from openai import OpenAI
from llm import HelloAgentsLLM

from openai.types.chat import (
    ChatCompletionMessageParam,
    ChatCompletionSystemMessageParam,
    ChatCompletionUserMessageParam,
    ChatCompletionAssistantMessageParam, ChatCompletionDeveloperMessageParam
)

class MyLLM(HelloAgentsLLM):
    def __init__(
            self,
            model: Optional[str] = None,
            api_key: Optional[str] = None,
            base_url: Optional[str] = None,
            provider: Optional[str] = None,
            **kwargs
    ):
        if provider == "ollama":
            self.provider = "ollama"
            self.api_key = "NotNeed"
            self.base_url = "http://localhost:11434/v1"
            self.model = model
            self.temperature = kwargs.get('temperature', 0.7)
            self.max_tokens = kwargs.get('max_tokens')
            self.timeout = kwargs.get('timeout', 60)

            self._client = OpenAI(api_key=self.api_key, base_url=self.base_url, timeout=self.timeout)
        else:
            super().__init__(model=model, api_key=api_key, base_url=base_url, provider=provider, **kwargs)


llm = MyLLM(provider="ollama", model="deepseek-r1:7b")
messages = [ChatCompletionUserMessageParam(role="user", content="给我讲个笑话吧")]
llm.invoke(messages)

由于本地模型受限于本地硬件设备而性能较差,所以后面将不会使用本地模型。这里只是通过子类扩展来演示对本地模型的支持。

核心组件

上文中的例子只是解决了与大语言模型通信的问题,但还远远称不上实现了 Agent。在前文实现你自己的 Agent 中有介绍,Agent 有一些非常重要的组成部分,如工具调用和对话记忆。因此接下来将添加这些核心组件。

Message

首先创建 Message 类来表示对话消息:

python 复制代码
from typing import Optional, Dict, Any, Literal
from datetime import datetime
from pydantic import BaseModel

MessageRole = Literal["user", "assistant", "system", "tool"]


class Message(BaseModel):
    content: str
    role: MessageRole
    timestamp: datetime = None
    metadata: Optional[Dict[str, Any]] = None

    def __init__(self, content: str, role: MessageRole, **kwargs):
        super().__init__(
            content=content,
            role=role,
            timestamp=kwargs.get('timestamp', datetime.now()),
            metadata=kwargs.get('metadata', {})
        )

    def to_dict(self) -> Dict[str, Any]:
        return {
            "role": self.role,
            "content": self.content
        }

    def __str__(self) -> str:
        return f"[{self.role}] {self.content}"

发送消息的角色被严格限制为四种:userasistantsystemtool,这直接对应 OpenAI API 的规范,保证了类型安全。另外 timestampmetadata 字段是为了后续扩展用的。

Agent

对话记忆的管理放在 Agent 基类中:

python 复制代码
from abc import ABC, abstractmethod
from typing import Optional, List, Dict, Any

from openai.types.chat import (
    ChatCompletionToolParam,
    ChatCompletionFunctionToolParam
)

from .llm import HelloAgentsLLM
from .message import Message
from ..tools import ToolRegistry


class Agent(ABC):

    def __init__(
            self,
            name: str,
            llm: HelloAgentsLLM,
            system_prompt: Optional[str] = None,
            tool_registry: Optional['ToolRegistry'] = None
    ):
        self.name = name
        self.llm = llm
        self.system_prompt = system_prompt
        self.tool_registry = tool_registry
        self._history: list[Message] = []

    def __str__(self) -> str:
        return f"Agent(name={self.name}, provider={self.llm.provider})"

    @abstractmethod
    def run(self, input_text: str, **kwargs) -> str:
        pass

    def add_message(self, message: Message):
        self._history.append(message)

    def clear_history(self):
        self._history.clear()

    def get_history(self) -> list[Message]:
        return self._history.copy()

Agent 基类通过继承 ABC 被定义为一个不能直接实例化的抽象类,run 方法保证所有子类智能体都有统一的执行入口。对话记忆通过 _history 属性进行管理。

Tool

接下来需要定义工具接口 Tool

python 复制代码
from abc import ABC, abstractmethod
from typing import Any, Dict, List
from pydantic import BaseModel


class ToolParameter(BaseModel):
    name: str
    type: str
    description: str
    required: bool = True
    default: Any = None


class Tool(ABC):
    def __init__(self, name: str, description: str):
        self.name = name
        self.description = description

    @abstractmethod
    def run(self, parameters: Dict[str, Any]) -> str:
        """执行工具,返回 str"""
        pass

    @abstractmethod
    def get_parameters(self) -> List[ToolParameter]:
        """获取工具参数定义"""
        pass

工具接口是一个抽象类,以 run 方法作为工具调用的统一入口,接受字典参数并返回字符串结果。

ToolRegistry

再通过工具注册表来对工具进行管理:

python 复制代码
from typing import Any, Callable
from .base import Tool


class ToolRegistry:
    """HelloAgents 工具注册表"""

    def __init__(self):
        self._tools: dict[str, Tool] = {}
        self._functions: dict[str, dict[str, Any]] = {}

    def register_tool(self, tool: Tool):
        if tool.name in self._tools:
            print(f"⚠️ 工具 '{tool.name}' 已存在,将被替换")
        self._tools[tool.name] = tool

    def register_function(self, name: str, description: str, func: Callable[[str], str]):
        """
        直接注册函数作为工具
        Args:
            name: 工具名称
            description: 工具描述
            func: 工具函数,接受字符串参数,返回字符串结果
        """
        if name in self._functions:
            print(f"⚠️ 工具 '{name}' 已存在,将被覆盖")

        self._functions[name] = {
            "description": description,
            "func": func
        }

    def get_tools_description(self) -> str:
        """获取所有可用工具的格式化描述字符串"""
        descriptions = []

        for tool in self._tools.values():
            descriptions.append(f"- {tool.name}: {tool.description}")

        for name, info in self._functions.items():
            descriptions.append(f"- {name}: {info['description']}")

        return "\n".join(descriptions) if descriptions else "暂无可用工具"

    def get_all_tools(self) -> list[Tool]:
        return list(self._tools.values())
        
    def get_tool(self, name: str) -> Optional[Tool]:
        return self._tools.get(name)

ToolRegistry支持两种注册方式:Tool 对象注册和函数直接注册。

SimpleAgent

上文虽然定义了 ToolToolRegistry,但可以先不使用自定义工具,直接实现一个最简单的自定义 Agent:

python 复制代码
from typing import Optional

from openai.types.chat import ChatCompletionMessageParam

from ..core.agent import Agent
from ..core.llm import HelloAgentsLLM
from ..core.message import Message


class SimpleAgentNonTool(Agent):

    def __init__(
            self,
            name: str,
            llm: HelloAgentsLLM,
            system_prompt: Optional[str] = None
    ):
        super().__init__(name, llm, system_prompt)

    def run(self, input_text: str, **kwargs) -> str:
        messages = self._build_messages(input_text)
        llm_response = self.llm.invoke(messages, **kwargs)
        response_text = llm_response.content if hasattr(llm_response, 'content') else str(llm_response)
        self.add_message(Message(input_text, "user"))
        self.add_message(Message(response_text, "assistant"))
        return response_text

    def _build_messages(self, input_text: str) -> list[ChatCompletionMessageParam]:
        messages = []
        # 添加系统提示词
        if self.system_prompt:
            messages.append({
                "role": "system",
                "content": self.system_prompt
            })
        # 添加历史消息
        for msg in self._history:
            messages.append({
                "role": msg.role,
                "content": msg.content
            })
        # 添加用户消息
        messages.append({
            "role": "user",
            "content": input_text
        })
        return messages

写一个测试用例:

python 复制代码
from pathlib import Path
from .core import HelloAgentsLLM
from .agents import SimpleAgentNonTool
from dotenv import load_dotenv

# 相同目录下的 .env 文件中配置:
load_dotenv(Path(__file__).with_name(".env"))

llm = HelloAgentsLLM(provider="modelscope")
simpleAgent = SimpleAgentNonTool(
	"AI 助手",
	llm,
	"你是一个友好的 AI 助手,请用简洁明了的方式回答问题"
)
simpleAgent.run("请介绍一下你自己")
simpleAgent.run("复述我的上一个问题")

输出结果如下:

复制代码
🧠正在调用 deepseek-ai/DeepSeek-V4-Flash 模型...
你好!我是你友好的 AI 助手,擅长用简洁明了的方式回答问题。无论是查询知识、解决问题,还是聊天陪伴,我都能快速给你准确、有用的信息。我的知识不限时,可以为你解释概念、提供建议、写点东西,或者帮你理清思路。有什么想了解的,尽管问我吧!我随时在这儿,乐意为你提供帮助。
🧠正在调用 deepseek-ai/DeepSeek-V4-Flash 模型...
你的上一个问题是:"请介绍一下你自己"。

Agent 框架扩展

可以看到加上对话历史后,Agent 就有记忆,可以连续交互了,但是还缺少工具调用。下面先创建一个计算器工具:

python 复制代码
import ast
import operator
import math
from typing import Dict, Any
from ..base import Tool


class CalculatorTool(Tool):

    OPERATORS = {
        ast.Add: operator.add,
        ast.Sub: operator.sub,
        ast.Mult: operator.mul,
        ast.Div: operator.truediv,
        ast.Pow: operator.pow,
        ast.BitXor: operator.xor,
        ast.USub: operator.neg,
    }

    FUNCTIONS = {
        'abs': abs,
        'round': round,
        'max': max,
        'min': min,
        'sum': sum,
        'sqrt': math.sqrt,
        'sin': math.sin,
        'cos': math.cos,
        'tan': math.tan,
        'log': math.log,
        'exp': math.exp,
        'pi': math.pi,
        'e': math.e,
    }

    def __init__(self):
        super().__init__(
            name="python_calculator",
            description="执行数学计算。支持基本运算、数学函数等。例如:2+3*4, sqrt(16), sin(pi/2)等。"
        )

    def run(self, parameters: Dict[str, Any]) -> str:
        expression = parameters.get("input", "") or parameters.get("expression", "")
        if not expression:
            return "expression should not be empty"
        try:
            node = ast.parse(expression, mode='eval')
            result = self._eval_node(node.body)
            return str(result)
        except SyntaxError as e:
            print(f"❌ 表达式语法错误: {str(e)}")
            return "无效的表达式"
        except Exception as e:
            print(f"❌ 计算失败: {str(e)}")
            return "执行失败"

    def _eval_node(self, node):
        if isinstance(node, ast.Constant):
            return node.value
        elif isinstance(node, ast.BinOp):
            return self.OPERATORS[type(node.op)](
                self._eval_node(node.left),
                self._eval_node(node.right)
            )
        elif isinstance(node, ast.UnaryOp):
            return self.OPERATORS[type(node.op)](self._eval_node(node.operand))
        elif isinstance(node, ast.Call):
            func_name = node.func.id
            if func_name in self.FUNCTIONS:
                args = [self._eval_node(arg) for arg in node.args]
                return self.FUNCTIONS[func_name](*args)
            else:
                raise ValueError(f"Not supported function: {func_name}")
        elif isinstance(node, ast.Name):
            if node.id in self.FUNCTIONS:
                return self.FUNCTIONS[node.id]
            else:
                raise ValueError(f"Undefined: {node.id}")
        else:
            raise ValueError(f"Not supported expression: {type(node)}")

    def get_parameters(self):
        from ..base import ToolParameter
        return [
            ToolParameter(
                name="input",
                type="string",
                description="the expression of Math",
                required=True
            )
        ]

接下来需要改造 AgentSimpleAgent 以支持工具调用。

Agent 中初始化方法中添加 tool_registry: Optional['ToolRegistry'] = None 参数并赋值给 self.tool_registry。还需要添加工具调用模式构建方法,用于从工具注册表生成工具的调用 schema,以及工具执行方法:

python 复制代码
    def _build_tool_schemas(self) -> list[ChatCompletionToolParam]:
        """
        Generate Tools Schema
        returns:
            Tool Schema list
        """
        if not self.tool_registry:
            return []

        schemas: list[ChatCompletionToolParam] = []

        for tool in self.tool_registry.get_all_tools():
            properties: Dict[str, Any] = {}
            required: List[str] = []
            try:
                parameters = tool.get_parameters()
            except Exception:
                parameters = []

            for param in parameters:
                properties[param.name] = {
                    "type": self._map_parameter_type(param.type),
                    "description": param.description or ""
                }
                if param.default is not None:
                    properties[param.name]["default"] = param.default
                if getattr(param, "required", True):
                    required.append(param.name)

            schema: ChatCompletionFunctionToolParam = {
                "type": "function",
                "function": {
                    "name": tool.name,
                    "description": tool.description or "",
                    "parameters": {
                        "type": "object",
                        "properties": properties
                    }
                }
            }
            if required:
                schema["function"]["parameters"]["required"] = required
            schemas.append(schema)

        function_map = getattr(self.tool_registry, "_functions", {})
        for name, info in function_map.items():
            schemas.append({
                "type": "function",
                "function": {
                    "name": name,
                    "description": info.get("description", ""),
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "input": {
                                "type": "string",
                                "description": "input text"
                            }
                        },
                        "required": ["input"]
                    }
                }
            })

        return schemas

    @staticmethod
    def _map_parameter_type(param_type: str) -> str:
        normalized = (param_type or "").lower()
        if normalized in {"string", "number", "integer", "boolean", "array", "object"}:
            return normalized
        return "string"

    def _execute_tool_call(self, tool_name: str, arguments: Dict[str, Any]) -> str:
        """执行工具调用并返回字符串结果"""
        if not self.tool_registry:
            return "❌ 错误:未配置工具注册表"

        tool = self.tool_registry.get_tool(tool_name)
        if tool:
            try:
                response = tool.run(arguments)
                return response
            except Exception as exec:
                return f"❌ 工具调用失败:{exec}"
        return f"❌ 错误:未找到工具 '{tool_name}'"

SimpleAgent 中加上工具调用相关的内容:

复制代码
import json
from typing import Optional

from openai.types.chat import (
    ChatCompletionMessageParam,
    ChatCompletionAssistantMessageParam, ChatCompletionToolMessageParam
)

from ..core.agent import Agent
from ..core.llm import HelloAgentsLLM
from ..core.message import Message
from ..tools import ToolRegistry


class SimpleAgent(Agent):

    def __init__(
            self,
            name: str,
            llm: HelloAgentsLLM,
            system_prompt: Optional[str] = None,
            tool_registry: Optional['ToolRegistry'] = None,
            enable_tool_calling: bool = True,
            max_tool_iterations: int = 3
    ):
        super().__init__(name, llm, system_prompt, tool_registry=tool_registry)
        self.enable_tool_calling = enable_tool_calling and tool_registry is not None
        self.max_tool_iterations = max_tool_iterations

    def run(self, input_text: str, **kwargs) -> str:
        messages = self._build_messages(input_text)
        if not self.enable_tool_calling or not self.tool_registry:
            llm_response = self.llm.invoke(messages, **kwargs)
            response_text = llm_response.content if hasattr(llm_response, 'content') else str(llm_response)
            self.add_message(Message(input_text, "user"))
            self.add_message(Message(response_text, "assistant"))
            return response_text
        tool_schemas = self._build_tool_schemas()
        current_iteration = 0
        final_response = ""
        while current_iteration < self.max_tool_iterations:
            current_iteration += 1
            try:
                response = self.llm.invoke_with_tools(
                    messages=messages,
                    tools=tool_schemas,
                    tool_choice="auto",
                    **kwargs
                )
            except Exception as e:
                print(f"❌ LLM 调用失败:{e}")
                break
            # 处理工具调用
            tool_calls = response['tool_calls']
            if not tool_calls:
                final_response = response['content'] or "sorry,i can't answer the question"
                break

            # 将助手消息添加到历史
            messages.append(
                ChatCompletionAssistantMessageParam(
                    role="assistant",
                    content=response['content'],
                    tool_calls=response['tool_calls']
                )
            )

            # 执行所有工具调用
            for tool_call in tool_calls:
                tool_name = tool_call.function.name
                tool_call_id = tool_call.id

                try:
                    arguments = json.loads(tool_call.function.arguments)
                except json.JSONDecodeError as e:
                    print(f"❌ 工具调用参数解析失败: {e}")
                    messages.append(ChatCompletionToolMessageParam(
                        role="tool",
                        tool_call_id=tool_call_id,
                        content=f"错误:参数格式不正确 - {str(e)}"
                    ))
                    continue

                # 执行工具调用
                result = self._execute_tool_call(tool_name, arguments)
                # 添加工具执行结果到消息
                messages.append(ChatCompletionToolMessageParam(
                    role="tool",
                    tool_call_id=tool_call_id,
                    content=result
                ))

        # 如果超过最大迭代次数,获取最后一次回答
        if current_iteration >= self.max_tool_iterations and not final_response:
            llm_response = self.llm.invoke(messages, **kwargs)
            final_response = llm_response.content if hasattr(llm_response, 'content') else str(llm_response)

        # 保存到历史记录
        self.add_message(Message(input_text, "user"))
        self.add_message(Message(final_response, "assistant"))

        return final_response



    def _build_messages(self, input_text: str) -> list[ChatCompletionMessageParam]:
        messages = []
        # 添加系统提示词
        if self.system_prompt:
            messages.append({
                "role": "system",
                "content": self.system_prompt
            })
        # 添加历史消息
        for msg in self._history:
            messages.append({
                "role": msg.role,
                "content": msg.content
            })
        # 添加用户消息
        messages.append({
            "role": "user",
            "content": input_text
        })
        return messages

当未开启工具调用或者工具注册表为空时,仍然按照原来的方式直接调用 llm 并返回结果,否则通过工具注册表来生成工具调用 schema,并将其传给 llm 供大模型调用。这里用到了一个新的大模型调用方法 invoke_with_tools,需要在 llm 中添加:

python 复制代码
    def invoke_with_tools(
            self,
            messages: list[ChatCompletionMessageParam],
            tools: list[ChatCompletionToolParam],
            tool_choice: ChatCompletionToolChoiceOptionParam = "auto",
            **kwargs):

        call_kwargs = {
            "temperature": kwargs.pop("temperature", self.temperature),
            "tool_choice": tool_choice,
        }
        if self.max_tokens:
            call_kwargs["max_tokens"] = kwargs.pop("max_tokens", self.max_tokens)
        call_kwargs.update(kwargs)

        try:
            response = self._client.chat.completions.create(
                model=self.model,
                messages=messages,
                tools=tools,
                **call_kwargs)
            """
            message:
            {
              "role": "assistant",
              "content": "好的,我来帮你计算这个表达式",
              "tool_calls": [
                {
                  "id": "call_xxx",
                  "type": "function",
                  "function": {
                    "name": "python_calculator",
                    "arguments": '{"input": "15 * 8 + 32"}'
                  }
                }
              ]
            }
            """
            message = response.choices[0].message

            tool_calls = message.tool_calls or []

            return {"content": message.content, "tool_calls": tool_calls, "model": response.model}

        except Exception as e:
            print(f"OpenAI Function Calling调用失败: {str(e)}")

现在写一个测试用例:

python 复制代码
tool_registry = ToolRegistry()
calculator = CalculatorTool()
tool_registry.register_tool(calculator)

enhanced_agent = SimpleAgent(
	name="增强助手",
	llm=llm,
	system_prompt="你是一个智能助手,可以使用工具来帮助用户。",
	tool_registry=tool_registry,
	enable_tool_calling=True
)

enhanced_agent.run("帮我计算 15 * 8 + 32")

由于只注册了一个计算器工具,所以获取工具 schemas 时只有该工具的 scehma:

json 复制代码
[{
  "function": {
    "description": "执行数学计算。支持基本运算、数学函数等。例如:2+3*4, sqrt(16), sin(pi/2)等。",
    "name": "python_calculator",
    "parameters": {
      "properties": {
        "input": {
          "description": "the expression of Math",
          "type": "string"
        }
      },
      "required": [
        "input"
      ],
      "type": "object"
    }
  },
  "type": "function"
}]

将该 schemas 传给大模型进行调用时,大模型返回的结果示例所示:

json 复制代码
{
  "role": "assistant",
  "content": "好的,我来帮你计算这个表达式",
  "tool_calls": [
    {
      "id": "call_xxx",
      "type": "function",
      "function": {
        "name": "python_calculator",
        "arguments": '{"input": "15 * 8 + 32"}'
      }
    }
  ]
}

也就是说,当存在可用工具时,与大模型交互的第一步是让大模型来判断该使用哪一个工具,返回该工具的调用方式。然后将大模型的返回结果加入到对话记忆中,再次发起调用。如果还需要继续调用其他工具,那么就循环此过程,否则就返回大模型的响应的 content。

上文例子最终输出结果如下:

复制代码
工具增强响应: 15 × 8 + 32 = **152** ✅

计算步骤:
1. 先算乘法:15 × 8 = **120**
2. 再加 32:120 + 32 = **152**

免费的大模型 API 调用不太稳定,偶尔会出现调用失败的情况。

参考资料

1. https://github.com/datawhalechina/hello-agents

相关推荐
kiros_wang3 小时前
DeepSeek 智能效果全景展示
ai
码农阿强3 小时前
GPT-5.6 Sol/Terra/Luna 三模型全量上架StartAPI|分层选型+可运行调用代码实战
大数据·人工智能·gpt·ai·aigc
星释4 小时前
鸿蒙智能体开发实战:19.使用变量
华为·ai·harmonyos·鸿蒙·智能体
山塘小鱼儿4 小时前
Agent多轮对话调用MCP Tools
java·ai·ai编程
孟郎郎5 小时前
AI 辅助开发编程敏感信息保护安全规范
人工智能·安全·ai·风险·隐患
Z-D-K5 小时前
AI对Anthropic的J-space的分析(1)
ai·aigc·人机交互·agent·agi
沉默王二6 小时前
Hy3 + WorkBuddy = 国产顶级 Agent(附完整提示词)。
agent·ai编程·腾讯
doiito6 小时前
【Agent Harness】Gliding Horse v0.1.4.preview 发布:时间感知、闭环审计与智能增强
ai·rust·架构设计·系统设计·ai agent
不好听6136 小时前
MCP Resources:让 AI Agent 拥有"知识库"
agent