欢迎光临
我们一直在努力

AG-19_工具系统架构:工具注册、调用与结果处理

工具系统架构:工具注册、调用与结果处理

Agent 的能力上限不是模型的智商,而是它能调用的工具。工具系统是 Agent 的手和脚。

前言

一个纯文本 LLM 只能"说",不能"做"。它能告诉你如何修改代码,但不能真的帮你修改;它能分析数据的趋势,但不能真的去数据库查询。工具系统(Tool System) 是将 LLM 从"语言模型"升级为"行动模型"的关键桥梁。

Claude Code 的工具系统是其核心架构中最精妙的部分之一。它定义了 10+ 种工具(read、write、edit、exec、search 等),每种工具都有严格的 Schema 定义、执行约束和错误处理策略。本文将深入拆解工具系统的三层架构:注册层、调用层和结果处理层,并展示如何构建一个可扩展的工具框架。

工具系统的战略地位

工具系统的战略地位

从 Function Calling 到 Tool Use

工具系统的演进可以追溯到 OpenAI 在 2023 年推出的 Function Calling 功能。在此之前,开发者需要通过精心设计的提示词来让 LLM 输出结构化的函数调用意图(如 JSON 格式),然后在应用层解析并执行。

Function Calling 将这个过程标准化了:开发者在 API 请求中定义可用的函数(工具),LLM 在需要时输出结构化的调用请求,应用层执行后将结果返回给 LLM。这个看似简单的流程,背后需要一整套工程架构来支撑。

工具系统的三大支柱

一个成熟的工具系统需要解决三个核心问题:

  • 发现(Discovery):Agent 如何知道有哪些工具可用?
  • 调用(Invocation):Agent 如何正确地调用工具?
  • 反馈(Feedback):工具执行结果如何影响 Agent 的后续决策?
  • 这三个问题分别对应工具系统的三个子系统:注册中心、执行引擎和结果处理器。

    工具注册机制

    工具注册机制

    工具描述的重要性

    工具注册的核心不是代码,而是描述。LLM 通过阅读工具的自然语言描述来决定何时、如何使用该工具。一个描述不清的工具,即使功能再强大,LLM 也可能永远不会调用它。

    一个好的工具描述应该包含:

    • 功能说明:这个工具做什么?
    • 参数说明:每个参数的含义、类型、约束
    • 使用场景:什么时候应该使用这个工具?
    • 注意事项:使用限制、副作用、权限要求

    工具注册表设计

    from typing import Any, Callable, Dict, List, Optional, Type
    from dataclasses import dataclass, field
    from enum import Enum
    import json
    from pydantic import BaseModel, Field

    class ToolCategory(Enum):
    """工具分类 – 用于权限控制和UI展示"""
    FILE_READ = "file_read" # 文件读取类
    FILE_WRITE = "file_write" # 文件写入类
    EXECUTION = "execution" # 命令执行类
    SEARCH = "search" # 搜索类
    COMMUNICATION = "communication" # 通信类
    SYSTEM = "system" # 系统类

    @dataclass
    class ToolDefinition:
    """工具定义 – 描述一个可用工具的完整元信息"""
    name: str # 工具名称(唯一标识)
    description: str # 自然语言描述
    category: ToolCategory # 工具分类
    input_schema: Dict[str, Any] # JSON Schema 格式的输入参数定义
    handler: Callable # 实际执行函数
    requires_approval: bool = False # 是否需要用户审批
    dangerous: bool = False # 是否为危险操作
    timeout_seconds: int = 30 # 默认超时时间
    retry_on_failure: bool = False # 是否允许重试
    max_retries: int = 3 # 最大重试次数
    side_effects: List[str] = field(default_factory=list) # 副作用说明

    def to_tool_use_schema(self) > Dict[str, Any]:
    """转换为 Claude API 的 tool_use 格式"""
    return {
    "name": self.name,
    "description": self.description,
    "input_schema": self.input_schema
    }

    class ToolRegistry:
    """工具注册中心 – 管理所有可用工具"""

    def __init__(self):
    self._tools: Dict[str, ToolDefinition] = {}
    self._categories: Dict[ToolCategory, List[str]] = {
    cat: [] for cat in ToolCategory
    }

    def register(self, tool_def: ToolDefinition) > None:
    """注册一个新工具"""
    if tool_def.name in self._tools:
    raise ValueError(f"Tool '{tool_def.name}' already registered")

    # 验证 Schema 格式
    self._validate_schema(tool_def.input_schema)

    self._tools[tool_def.name] = tool_def
    self._categories[tool_def.category].append(tool_def.name)

    def get(self, name: str) > Optional[ToolDefinition]:
    """获取工具定义"""
    return self._tools.get(name)

    def list_tools(self, category: Optional[ToolCategory] = None,
    include_dangerous: bool = True) > List[ToolDefinition]:
    """列出可用工具(可按分类过滤)"""
    tools = list(self._tools.values())
    if category:
    tools = [t for t in tools if t.category == category]
    if not include_dangerous:
    tools = [t for t in tools if not t.dangerous]
    return tools

    def get_tool_use_schemas(self, category: Optional[ToolCategory] = None) > List[Dict]:
    """获取所有工具的 API 格式 Schema(用于传入 Claude API)"""
    tools = self.list_tools(category=category, include_dangerous=False)
    return [t.to_tool_use_schema() for t in tools]

    def _validate_schema(self, schema: Dict[str, Any]) > None:
    """验证 JSON Schema 格式的正确性"""
    if "type" not in schema:
    raise ValueError("Schema must have a 'type' field")
    if schema["type"] != "object":
    raise ValueError("Tool input schema type must be 'object'")
    if "properties" not in schema:
    raise ValueError("Schema must have a 'properties' field")

    这段代码展示了工具注册中心的核心设计。几个关键点:

  • ToolDefinition 是一等公民:每个工具的完整元信息(包括 Schema、执行函数、权限要求)都封装在一个数据结构中
  • 分类管理:通过 ToolCategory 枚举实现工具的分组,便于权限控制和 UI 展示
  • Schema 验证:注册时自动验证 JSON Schema 格式的正确性,避免运行时错误
  • API 格式转换:to_tool_use_schema 方法将内部表示转换为 Claude API 要求的格式
  • 工具注册的装饰器模式

    为了让工具注册更优雅,可以使用装饰器模式:

    # 全局工具注册表实例
    _global_registry = ToolRegistry()

    def tool(name: str, description: str, category: ToolCategory,
    input_schema: Dict[str, Any], **kwargs):
    """工具注册装饰器 – 将函数注册为可用工具"""
    def decorator(func: Callable) > Callable:
    tool_def = ToolDefinition(
    name=name,
    description=description,
    category=category,
    input_schema=input_schema,
    handler=func,
    **kwargs
    )
    _global_registry.register(tool_def)
    return func # 返回原始函数,不影响直接调用
    return decorator

    # 使用示例:注册文件读取工具
    @tool(
    name="read_file",
    description="读取指定路径的文件内容。适用于查看代码、配置文件等文本文件。",
    category=ToolCategory.FILE_READ,
    input_schema={
    "type": "object",
    "properties": {
    "path": {
    "type": "string",
    "description": "文件路径(绝对路径或相对于工作目录的路径)"
    },
    "offset": {
    "type": "integer",
    "description": "从第几行开始读取(1-indexed)",
    "default": 1
    },
    "limit": {
    "type": "integer",
    "description": "最大读取行数",
    "default": 2000
    }
    },
    "required": ["path"]
    }
    )
    async def read_file_handler(path: str, offset: int = 1,
    limit: int = 2000) > str:
    """实际的文件读取逻辑"""
    with open(path, 'r', encoding='utf-8') as f:
    lines = f.readlines()
    selected = lines[offset1:offset1+limit]
    return ''.join(selected)

    工具调用流程

    工具调用流程

    调用的完整生命周期

    一次工具调用从 LLM 输出 tool_use 块开始,到结果返回给 LLM 结束。完整的生命周期包括:

  • 解析:从 LLM 响应中提取 tool_use 块
  • 验证:检查参数是否符合 Schema 定义
  • 授权:检查是否需要用户审批
  • 执行:调用实际的工具处理函数
  • 后处理:格式化执行结果
  • 返回:将结果封装为 tool_result 消息
  • 用户执行沙箱注册中心执行引擎LLM用户执行沙箱注册中心执行引擎LLM#mermaid-svg-mxUnSOZ5NuYbXN2Q{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mxUnSOZ5NuYbXN2Q .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .error-icon{fill:#552222;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .marker.cross{stroke:#333333;}#mermaid-svg-mxUnSOZ5NuYbXN2Q svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mxUnSOZ5NuYbXN2Q p{margin:0;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-mxUnSOZ5NuYbXN2Q text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-mxUnSOZ5NuYbXN2Q .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-mxUnSOZ5NuYbXN2Q #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .sequenceNumber{fill:white;}#mermaid-svg-mxUnSOZ5NuYbXN2Q #sequencenumber{fill:#333;}#mermaid-svg-mxUnSOZ5NuYbXN2Q #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .messageText{fill:#333;stroke:none;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .labelText,#mermaid-svg-mxUnSOZ5NuYbXN2Q .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .loopText,#mermaid-svg-mxUnSOZ5NuYbXN2Q .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-mxUnSOZ5NuYbXN2Q .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .noteText,#mermaid-svg-mxUnSOZ5NuYbXN2Q .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .actorPopupMenu{position:absolute;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-mxUnSOZ5NuYbXN2Q .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-mxUnSOZ5NuYbXN2Q .actor-man circle,#mermaid-svg-mxUnSOZ5NuYbXN2Q line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-mxUnSOZ5NuYbXN2Q :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}alt[需要审批]tool_use 块查找工具定义ToolDefinition参数验证请求确认批准/拒绝执行工具原始结果结果格式化tool_result 消息
    import json
    import time
    import traceback
    from typing import Any, Dict, Optional
    from dataclasses import dataclass

    @dataclass
    class ToolCallRequest:
    """工具调用请求"""
    id: str # 调用ID(用于匹配请求和响应)
    name: str # 工具名称
    input: Dict[str, Any] # 输入参数

    @dataclass
    class ToolCallResult:
    """工具调用结果"""
    tool_use_id: str # 对应的调用ID
    content: str # 结果内容
    is_error: bool # 是否为错误结果
    duration_ms: int # 执行耗时
    metadata: Dict[str, Any] = None # 元数据(如文件行数、token数等)

    class ToolExecutionEngine:
    """工具执行引擎 – 处理从调用请求到结果返回的完整流程"""

    def __init__(self, registry: ToolRegistry,
    approval_handler: Optional[Callable] = None):
    self.registry = registry
    self.approval_handler = approval_handler # 用户审批回调
    self.execution_history: List[Dict] = [] # 执行历史记录

    async def execute(self, request: ToolCallRequest) > ToolCallResult:
    """执行工具调用的完整流程"""
    start_time = time.time()

    # 第一步:查找工具定义
    tool_def = self.registry.get(request.name)
    if not tool_def:
    return ToolCallResult(
    tool_use_id=request.id,
    content=f"Error: Unknown tool '{request.name}'",
    is_error=True,
    duration_ms=0
    )

    # 第二步:参数验证
    validation_error = self._validate_input(tool_def, request.input)
    if validation_error:
    return ToolCallResult(
    tool_use_id=request.id,
    content=f"Validation error: {validation_error}",
    is_error=True,
    duration_ms=int((time.time() start_time) * 1000)
    )

    # 第三步:权限检查和用户审批
    if tool_def.requires_approval:
    approved = await self._request_approval(tool_def, request.input)
    if not approved:
    return ToolCallResult(
    tool_use_id=request.id,
    content="User denied approval for this operation.",
    is_error=True,
    duration_ms=int((time.time() start_time) * 1000)
    )

    # 第四步:执行工具(带重试逻辑)
    result = await self._execute_with_retry(tool_def, request)

    # 第五步:记录执行历史
    duration_ms = int((time.time() start_time) * 1000)
    result.duration_ms = duration_ms
    self._record_execution(request, result)

    return result

    def _validate_input(self, tool_def: ToolDefinition,
    input_data: Dict[str, Any]) > Optional[str]:
    """验证输入参数是否符合 Schema"""
    schema = tool_def.input_schema
    required = schema.get("required", [])
    properties = schema.get("properties", {})

    # 检查必需参数
    for param in required:
    if param not in input_data:
    return f"Missing required parameter: '{param}'"

    # 检查参数类型
    for param, value in input_data.items():
    if param in properties:
    expected_type = properties[param].get("type")
    if expected_type == "string" and not isinstance(value, str):
    return f"Parameter '{param}' must be a string"
    if expected_type == "integer" and not isinstance(value, int):
    return f"Parameter '{param}' must be an integer"

    return None # 验证通过

    async def _execute_with_retry(self, tool_def: ToolDefinition,
    request: ToolCallRequest) > ToolCallResult:
    """带重试逻辑的工具执行"""
    max_retries = tool_def.max_retries if tool_def.retry_on_failure else 0
    last_error = None

    for attempt in range(max_retries + 1):
    try:
    # 调用工具的实际处理函数
    result = await tool_def.handler(**request.input)

    # 将结果序列化为字符串
    if isinstance(result, dict):
    content = json.dumps(result, ensure_ascii=False, indent=2)
    else:
    content = str(result)

    return ToolCallResult(
    tool_use_id=request.id,
    content=content,
    is_error=False,
    duration_ms=0 # 会在外层被覆盖
    )

    except Exception as e:
    last_error = e
    if attempt < max_retries:
    # 等待后重试(指数退避)
    wait_time = 2 ** attempt
    time.sleep(wait_time)
    continue

    # 所有重试都失败了
    error_detail = traceback.format_exception(type(last_error),
    last_error,
    last_error.__traceback__)
    return ToolCallResult(
    tool_use_id=request.id,
    content=f"Tool execution failed after {max_retries + 1} attempts:\\n"
    f"{type(last_error).__name__}: {str(last_error)}",
    is_error=True,
    duration_ms=0
    )

    async def _request_approval(self, tool_def: ToolDefinition,
    input_data: Dict[str, Any]) > bool:
    """请求用户审批"""
    if self.approval_handler:
    return await self.approval_handler(tool_def, input_data)
    # 默认拒绝危险操作
    return not tool_def.dangerous

    def _record_execution(self, request: ToolCallRequest,
    result: ToolCallResult) > None:
    """记录执行历史"""
    self.execution_history.append({
    'tool_name': request.name,
    'input': request.input,
    'is_error': result.is_error,
    'duration_ms': result.duration_ms,
    'timestamp': time.time()
    })

    这段执行引擎的代码覆盖了工具调用的完整生命周期。几个关键设计点:

  • 五步流程:查找 → 验证 → 授权 → 执行 → 记录,每一步都有明确的错误处理
  • 重试机制:支持指数退避的重试策略,适用于网络请求等临时性失败
  • 审批集成:通过 approval_handler 回调支持用户审批,不硬耦合审批逻辑
  • 执行历史:记录每次调用的输入、输出和耗时,用于调试和分析
  • 结果处理与错误恢复

    结果格式化

    工具的原始输出通常不适合直接发送给 LLM。需要一个结果格式化层来:

  • 截断过长的输出:避免占用过多上下文空间
  • 添加元数据:如文件行数、执行时间、token 计数
  • 结构化错误信息:让 LLM 能理解错误原因并采取修正行动
  • 去除敏感信息:过滤输出中的密码、token 等敏感内容
  • class ResultFormatter:
    """结果格式化器 – 将工具原始输出转换为 LLM 友好的格式"""

    def __init__(self, max_content_length: int = 50000,
    sensitive_patterns: List[str] = None):
    self.max_content_length = max_content_length
    self.sensitive_patterns = sensitive_patterns or [
    r'(?i)(password|secret|token|api[_-]?key)\\s*[:=]\\s*\\S+',
    r'(?i)Bearer\\s+\\S+',
    r'—–BEGIN.*PRIVATE KEY—–',
    ]

    def format_result(self, result: ToolCallResult,
    tool_def: ToolDefinition) > Dict[str, Any]:
    """将工具结果格式化为 API 响应格式"""
    content = result.content

    # 1. 过滤敏感信息
    content = self._redact_sensitive(content)

    # 2. 截断过长内容
    truncated = False
    if len(content) > self.max_content_length:
    content = content[:self.max_content_length]
    truncated = True

    # 3. 构建结构化响应
    response_parts = []
    if result.is_error:
    response_parts.append(f"❌ 执行失败")

    response_parts.append(content)

    if truncated:
    response_parts.append(
    f"\\n[注意] 输出已截断,原始长度 {len(result.content)} 字符"
    )

    if result.duration_ms > 0:
    response_parts.append(f"⏱ 耗时: {result.duration_ms}ms")

    final_content = "\\n".join(response_parts)

    # 4. 返回 API 格式
    return {
    "type": "tool_result",
    "tool_use_id": result.tool_use_id,
    "content": final_content,
    "is_error": result.is_error
    }

    def _redact_sensitive(self, content: str) > str:
    """脱敏处理 – 替换敏感信息为占位符"""
    import re
    for pattern in self.sensitive_patterns:
    content = re.sub(pattern, '[REDACTED]', content)
    return content

    错误恢复策略

    工具执行失败时,Agent 需要决定如何恢复。常见的恢复策略包括:

    错误类型恢复策略示例
    参数错误 修正参数后重试 文件路径拼写错误
    权限不足 请求用户授权或换用其他工具 文件写入权限不足
    资源不存在 通知用户或搜索替代资源 文件不存在
    网络超时 指数退避重试 API 调用超时
    逻辑错误 换用不同策略 搜索无结果,尝试不同关键词

    工具系统的设计模式

    管道模式(Pipeline)

    多个工具可以串联成管道,前一个工具的输出作为后一个工具的输入。这在代码分析场景中很常见:

    read_file → search_pattern → edit_file → exec_command

    适配器模式(Adapter)

    对于外部 API 或系统命令,可以通过适配器模式统一接口:

    class CommandAdapter:
    """系统命令适配器 – 将 shell 命令封装为工具"""

    def __init__(self, command: str, parser: Callable = None):
    self.command = command
    self.parser = parser or str

    async def execute(self, **kwargs) > str:
    """执行命令并解析输出"""
    import subprocess
    formatted_cmd = self.command.format(**kwargs)
    result = subprocess.run(
    formatted_cmd, shell=True, capture_output=True, text=True, timeout=30
    )
    output = result.stdout if result.returncode == 0 else result.stderr
    return self.parser(output)

    观察者模式(Observer)

    工具执行的事件可以广播给多个观察者,用于日志记录、监控和审计:

    • 日志观察者:记录每次工具调用的详细信息
    • 监控观察者:统计工具调用的成功率和延迟
    • 审计观察者:记录敏感操作用于安全审计

    总结

    工具系统是 AI Agent 的能力基础设施。一个设计良好的工具系统应该具备:

  • 声明式注册:通过 Schema 和描述定义工具,而非硬编码
  • 完整生命周期:覆盖从调用请求到结果返回的每个环节
  • 安全第一:内置参数验证、权限检查和敏感信息过滤
  • 可扩展性:通过装饰器、适配器等模式支持灵活扩展
  • 可观测性:完整的执行历史和错误追踪
  • 在构建自己的 Agent 工具系统时,建议从最核心的三个工具(读文件、写文件、执行命令)开始,逐步扩展。每个新工具都应该经过严格的 Schema 定义和描述优化,因为工具描述的质量直接决定了 LLM 使用工具的准确率。

    参考资料

  • OpenAI, “Function Calling and Other API Updates” (2023) – Function Calling 的官方文档和设计哲学
  • Claude Code 源码 – 工具注册、调用和结果处理的完整实现
  • Anthropic, “Tool Use with Claude” (2024) – Anthropic 官方的工具使用文档和最佳实践
  • Gao et al., “PAL: Program-aided Language Models” (2023) – 工具增强语言模型的研究论文
  • Schick et al., “Toolformer: Language Models Can Teach Themselves to Use Tools” (2023) – LLM 自主学习使用工具的开创性研究

  • 本系列覆盖 AI 大模型基础、Agent 开发、MCP 协议、Skill 开发、RAG、模型微调、部署推理 七大方向,从入门到实战的全栈内容持续更新中。

    所有文章的 Markdown 源文件、可运行代码、高清配图已整理成完整资料包。

    👍 点赞 + ⭐ 关注,评论区扣「1」,挨个发你领取方式 👇

    赞(0)
    未经允许不得转载:171主机测评 » AG-19_工具系统架构:工具注册、调用与结果处理
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址