ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

CAI 工具系统深度指南:从 function_tool 装饰器到 FunctionTool 的完整实现解析

CAI 工具系统深度指南:从 function_tool 装饰器到 FunctionTool 的完整实现解析 CAI 工具系统深度指南从 function_tool 装饰器到 FunctionTool 的完整实现解析【免费下载链接】caiCybersecurity AI (CAI), the framework for AI Security项目地址: https://gitcode.com/GitHub_Trending/cai3/caiCAICybersecurity AI的 Agent 工具系统允许 LLM 通过调用工具来执行实际动作——读取文件、执行命令、调用 API甚至操作计算机。本文以cai.sdk.agents.tool模块为骨架结合docs/tools.md使用指南、tests/tools/test_function_tool.py测试与多个实战示例系统讲解 CAI 中四类工具的模型、function_tool装饰器的完整工作流程、严格 JSON Schema 的生成机制以及错误处理与安全相关的设计细节。读完本文你将掌握在 CAI 中注册自定义工具、控制工具调用行为并理解其底层原理的完整能力。工具系统总览三类工具与安全杀伤链CAI 的 Agent 工具系统由 Tool 联合类型 统一定义实际包含四类可注册进 Agent 的实体工具类型类名运行位置典型用途函数调用工具FunctionTool本地 Python 进程将任意 Python 函数包装为工具覆盖大部分自定义需求文件搜索工具FileSearchToolLLM 服务端托管在向量存储vector store中检索文件目前仅 OpenAI Responses API 支持网页搜索工具WebSearchToolLLM 服务端托管让模型搜索网页可配置用户位置与上下文大小计算机控制工具ComputerToolLLM 服务端托管让模型控制一台计算机点击、截图等对应computer_use_preview三类托管工具的共同特点是它们运行在模型服务端由 LLM 提供商托管因此对模型与 API 有硬性要求——FileSearchTool、WebSearchTool、ComputerTool目前仅在使用OpenAIResponsesModelResponses API时受支持。从源码可以看出它们的name属性均为固定值file_search、web_search_preview、computer_use_preview见 tool.py。CAI 自带的托管工具在 src/cai/tools 目录下按安全杀伤链kill chain分为六大类对应侦察与武器化、利用、权限提升、横向移动、数据外泄、命令与控制六个阶段。这些内置工具同样通过function_tool装饰器实现例如generic_linux_command工具位于 generic_linux_command.py即使用from cai.sdk.agents import function_tool包装了 Linux 命令执行函数并在其中内置了 Unicode 同形字homograph检测与 NFKD 归一化等防护逻辑——这体现了 CAI 在安全场景下的特有考量。FunctionTool 数据结构一个可执行的工具单元FunctionTool是一个 dataclass见 tool.py其字段完整描述了一个工具对 LLM 可见的元信息与对运行时可见的执行逻辑字段类型说明namestr工具名称即展示给 LLM 的名字默认取自函数名descriptionstr工具描述展示给 LLM默认取自函数 docstringparams_json_schemadict[str, Any]工具参数的 JSON Schema由函数签名自动推导on_invoke_toolCallable[[RunContextWrapper[Any], str], Awaitable[Any]]工具调用执行函数接收运行上下文与 LLM 传入的参数JSON 字符串必须返回可用str()表示的输出strict_json_schemabool是否启用严格 JSON Schema默认True强烈建议保持开启以提升 LLM 输出合法 JSON 的概率on_invoke_tool是工具执行的核心契约它接收两个参数——RunContextWrapper工具运行上下文和来自 LLM 的 JSON 字符串参数。出错时有两种处理方式抛出异常会导致整个 run 失败或返回字符串形式的错误信息会被回传给 LLM 让其自行纠错。FunctionToolResulttool.py则记录了工具的一次执行结果被调用的工具tool、原始输出output、以及产生的RunItemrun_item后者被 Agent 运行时用于追踪工具调用轨迹。RunContextWrapper工具的依赖注入通道工具函数接收的第一个参数是RunContextWrapper[TContext]run_context.py它包装了通过Runner.run()传入的上下文对象。要点是上下文不会传给 LLM它只是把依赖、数据如数据库连接、用户会话、API 客户端传递给工具函数、回调与钩子函数的通道。RunContextWrapper还携带usage字段记录运行至今的用量信息流式响应下该数据在流结束前可能滞后。function_tool 装饰器从普通函数到工具的自动化流水线function_tool是创建FunctionTool的首选方式。它支持两种调用形态见 tool.py 的 overload 声明# 形态一function_tool 不带括号推荐 function_tool def get_weather(city: str) - str: Get the weather for a city. return sunny # 形态二function_tool(...) 带括号配置 function_tool(name_overrideread_log, description_overrideRead a log file) def read_log_file(path: str) - str: return log contentfunction_tool的完整参数如下tool.py参数默认值说明name_overrideNone覆盖工具名不使用函数__name__description_overrideNone覆盖工具描述不使用 docstring 首段docstring_styleNone指定 docstring 风格google/numpy/sphinx缺省自动检测use_docstring_infoTrue是否用 docstring 填充工具与参数描述failure_error_functiondefault_tool_error_function工具调用失败时生成回传给 LLM 的错误信息传None则直接抛出异常strict_modeTrue是否启用严格 JSON Schema创建FunctionTool的过程分为四步_create_function_tooltool.py提取函数 Schema调用function_schema()解析签名与 docstring生成FuncSchema构造执行闭包_on_invoke_tool_impl把 LLM 传入的 JSON 字符串解析为 dict再经 Pydantic 模型校验后转换成原函数可接受的(args, kwargs)最后调用原函数包裹错误处理层_on_invoke_tool捕获所有异常按failure_error_function配置决定是回传错误消息并同时把非致命错误附加到当前 tracing span还是原样抛出组装FunctionTool并回传。该设计还体现在一个细节上同步函数会被放入线程池执行loop.run_in_executor避免阻塞事件循环tool.py因此同步与异步函数都可以安全地用于异步 Agent 运行。参数解析与调用链在_on_invoke_tool_impl中工具执行的完整调用链是json.loads(input)解析 LLM 传来的参数 JSON解析失败会抛出ModelBehaviorError模型行为错误——这是 CAI 区分模型产生非法输入与业务代码出错的关键异常类型用schema.params_pydantic_model(**json_data)通过 Pydantic 做类型校验ValidationError同样转为ModelBehaviorErrorschema.to_call_args(parsed)将 Pydantic 模型转换为(args, kwargs)按inspect.iscoroutinefunction(the_func)判断函数是否为协程决定直接await还是走线程池用truncate_for_logging截断输出默认 1000 字符后记录 debug 日志。当设置了_debug.DONT_LOG_TOOL_DATA时日志不会记录工具输入输出数据这对处理敏感信息的 Agent 是重要的隐私开关。Schema 生成机制inspect griffe Pydantic 三件套函数 Schema 的提取实现在 function_schema.py 中核心函数是function_schema()function_schema.py产物是FuncSchemadataclass包含name、description、params_pydantic_model动态 Pydantic 模型、params_json_schema、signature、takes_context是否接收RunContextWrapper首参与strict_json_schema。签名解析与类型映射function_schema()的工作流程用inspect.signature()取函数签名get_type_hints()取类型注解检测上下文参数若第一个参数的类型注解是RunContextWrapper则标记takes_contextTrue并从参数列表剔除若RunContextWrapper出现在非首位直接抛出UserError动态建 Pydantic 模型create_model(f{func_name}_args, __base__BaseModel, **fields)按每个参数生成字段——无默认值的为必填字段Field(...)有默认值的为Field(default...)docstring 中提取的参数描述会作为description注入通过model_json_schema()生成 JSON Schema若开启严格模式则调用ensure_strict_json_schema()变换。各参数类型按如下规则映射FuncSchema.to_call_args与建模型时的处理*args: int→ 转为List[int]*args: tuple[int, ...]→ 转为List[int]缺省为list[Any]默认值为空列表**kwargs: int→ 转为Dict[str, int]缺省dict[str, Any]默认值为空字典POSITIONAL_ONLY/POSITIONAL_OR_KEYWORD参数在*args之前按位置传参之后转为关键字传参KEYWORD_ONLY参数始终按关键字传参ctf参数名会被特殊处理并跳过大小写不敏感——这是 CAI 为安全对抗场景保留的特有约定无类型注解的参数按Any处理。docstring 解析与风格自动检测docstring 解析依赖griffe库支持google、numpy、sphinx三种风格。由于 griffe 的自动检测在开源版中不可用CAI 在 function_schema.py 中自行实现了近似检测通过正则分别识别:param:/:type:sphinx、Parameters/Returns加虚线numpy、Args:/Returns:/Raises:google等特征按得分取最高者平局时优先级为 sphinx numpy google无法识别时回退到 google 风格。提取结果FuncDocumentation包含函数描述docstring 首个文本段落与各参数描述。你也可以用use_docstring_infoFalse完全关闭 docstring 解析或通过docstring_style显式指定风格。严格 JSON Schema 变换ensure_strict_json_schema()strict_schema.py递归地把 JSON Schema 变换为 OpenAI 严格模式structured outputs要求的标准。以test_func_schema_is_strict测试为证test_function_tool.py默认生成的 schema 一定包含additionalProperties: false。该变换的规则包括空 schema 替换为固定的空对象模板additionalProperties: false、空properties、空required所有object类型强制additionalProperties false若用户显式允许额外属性则抛出UserError并提示改用非严格模式对象的required被设为全部properties的键集合递归处理$defs、definitions、items、anyOf、allOf单元素的allOf直接内联展开值为None的default被移除对同时含$ref与其他键的节点做$ref内联展开再递归修正内联后的 schema。当strict_modeFalse时跳过该变换允许带默认值的参数成为可选、允许额外属性等见test_manual_function_tool_creation_works中对非严格工具传入多余字段bar: baz仍能成功的验证test_function_tool.py。实战创建、注册与调用你的第一个工具以下示例改编自 examples/basic/tools.py展示了完整的工具创建与运行流程import asyncio from pydantic import BaseModel from cai.sdk.agents import Agent, Runner, function_tool class Weather(BaseModel): city: str temperature_range: str conditions: str function_tool def get_weather(city: str) - Weather: print([debug] get_weather called) return Weather(citycity, temperature_range14-20C, conditionsSunny with wind.) agent Agent( nameHello world, instructionsYou are a helpful agent., tools[get_weather], ) async def main(): result await Runner.run(agent, inputWhats the weather in Tokyo?) print(result.final_output) if __name__ __main__: asyncio.run(main())注意get_weather的返回类型是 Pydantic 模型Weather——CAI 的 FunctionTool 支持任意 Python 类型作为参数与返回值包括 Pydantic 模型与 TypedDict。测试 test_complex_args_function 验证了嵌套 Pydantic 模型与 TypedDict 参数会被自动生成正确的嵌套 schema并能正确调用。带上下文与覆盖参数的工具参考docs/tools.md中的安全场景示例可以定义带RunContextWrapper上下文、并显式覆盖名称的工具from typing import Any from typing_extensions import TypedDict from cai.sdk.agents import Agent, RunContextWrapper, function_tool class IPAddress(TypedDict): ip: str function_tool async def check_ip_reputation(ip_data: IPAddress) - str: Check if an IP address has a bad reputation. Args: ip_data: A dictionary with the IP address to check. # In a real system, this would query an IP reputation API return malicious if ip_data[ip].startswith(192.168) else clean function_tool(name_overrideread_log_file) def read_log_file(ctx: RunContextWrapper[Any], path: str, directory: str | None None) - str: Read the contents of a log file. Args: path: The path to the log file. directory: The optional directory to search in. return log file contents: suspicious activity found agent Agent( nameCyberSecBot, tools[check_ip_reputation, read_log_file], )上下文参数ctx: RunContextWrapper[Any]必须是函数的第一个参数且其泛型类型需与使用该工具的 Agent 的上下文类型一致。工具注册进 Agent 后可以通过遍历agent.tools并检查isinstance(tool, FunctionTool)来打印每个工具的name、description与params_json_schema见 docs/tools.md 中的展示示例——这正是调试工具元数据是否正确的便捷手段。手动构造 FunctionTool如果不想用 Python 函数作工具也可以直接实例化FunctionTool见 docs/tools.md 的 Custom function tools 章节需要自行提供name、description、params_json_schema与on_invoke_tool异步执行函数from typing import Any from pydantic import BaseModel from cai.sdk.agents import RunContextWrapper, FunctionTool def do_some_work(data: str) - str: return done class FunctionArgs(BaseModel): username: str age: int async def run_function(ctx: RunContextWrapper[Any], args: str) - str: parsed FunctionArgs.model_validate_json(args) return do_some_work(dataf{parsed.username} is {parsed.age} years old) tool FunctionTool( nameprocess_user, descriptionProcesses extracted user data, params_json_schemaFunctionArgs.model_json_schema(), on_invoke_toolrun_function, )这种方式下错误处理必须由你自己在on_invoke_tool内部完成框架不会介入。对应的测试用例是test_manual_function_tool_creation_workstest_function_tool.py。工具调用行为控制tool_choice 与 tool_use_behavior除了工具本身CAI 还允许你控制 Agent 何时、以何种方式调用工具。examples/agent_patterns/forcing_tool_use.py 展示了三种模式模式tool_choicetool_use_behavior行为default不设置run_llm_again工具输出回传 LLM 继续推理可多次调用工具first_toolrequiredstop_on_first_tool强制调用工具且第一个工具结果直接作为最终输出customrequired自定义函数传入list[FunctionToolResult]自行决定最终输出其中自定义行为函数签名如下对应ToolsToFinalOutputFunctionasync def custom_tool_use_behavior( context: RunContextWrapper[Any], results: list[FunctionToolResult] ) - ToolsToFinalOutputResult: weather: Weather results[0].output return ToolsToFinalOutputResult( is_final_outputTrue, final_outputf{weather.city} is {weather.conditions}. )注意默认模式下不要设置tool_choicerequired否则 LLM 会被强制每轮都调用工具形成无限循环。错误处理机制failure_error_function 与异常类型工具调用错误处理遵循三条明确规则docs/tools.md 的 Handling errors in function tools 章节不传任何参数使用default_tool_error_function向 LLM 返回通用错误消息An error occurred while running the tool. Please try again. Error: ...tool.py传入自定义错误函数调用它生成错误消息回传 LLM同时把错误作为非致命错误附加到当前 tracing spanSpanErrordata 中包含tool_name与error字段便于观测显式传None异常被重新抛出交给上层处理——可能是模型产生非法 JSON 时的ModelBehaviorError也可能是业务代码崩溃时的其他异常。自定义错误函数可以是同步或异步的ToolErrorFunction类型MaybeAwaitable[str]测试test_sync_custom_error_function_works与test_async_custom_error_function_workstest_function_tool.py验证了两种形态返回形如error_ModelBehaviorError、error_ValueError的消息表明工具内部异常的类型信息可透传给 LLM 作为纠错依据。完整的错误处理优先级在_on_invoke_tool中体现tool.py先捕获执行异常若failure_error_function is None则直接raise否则调用错误函数await 其结果后附加 span 错误并返回给 LLM。补充Agent 即工具与其他集成除函数工具外CAI 还支持将整个 Agent 作为工具注册给另一个 AgentAgents as tools见 docs/tools.md通过agent.as_tool(tool_name..., tool_description...)实现编排型架构例如让一个编排 Agent 路由扫描 IP与分析日志两个子 Agent。这与 Tool 系统的核心FunctionTool机制相互补充共同构成 CAI 的多工具协作能力。总结CAI 的工具系统围绕 tool.py 中的FunctionTool与function_tool展开形成了装饰器自动建 Schema → Pydantic 校验 → 线程池/协程执行 → 统一错误处理的完整链路。开发者只需写一个带类型注解的 Python 函数CAI 就会自动完成工具名、描述、参数 Schema 的生成与 LLM 参数的解析校验而strict_mode、failure_error_function、RunContextWrapper上下文注入等机制则为生产级与安全对抗场景提供了必要的控制力。可继续深入 tests/tools/test_function_tool.py 查看全部边界用例或在 examples/basic/tools.py 与 examples/agent_patterns/forcing_tool_use.py 中运行端到端示例。【免费下载链接】caiCybersecurity AI (CAI), the framework for AI Security项目地址: https://gitcode.com/GitHub_Trending/cai3/cai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表