ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

[FastMCP设计、原理与应用-08]FastMCP核心组件:工具和提示词

[FastMCP设计、原理与应用-08]FastMCP核心组件:工具和提示词 原语Primitive是MCP规范中最为核心的概念我们在“Primitive——MCP最核心的概念”的四大核心原语工具、静态资源、动态资源模板和提示词进行了系统介绍。FastMCP实现了这个四个原语并将它们统称为“组件”对应的类型以FastMCPComponent为基类“上篇”介绍了两个与资源相关的组件这篇介绍余下的两个即工具和提示词。1. 工具工具组件通过类型Tool表示。它的KEY_PREFIX被设置成tools如下所示的是定义在Tool类型中的字段定义。classTool(FastMCPComponent):KEY_PREFIX:ClassVar[str]toolparameters:dict[str,Any]output_schema:dict[str,Any]|Noneannotations:ToolAnnotations|Noneexecution:ToolExecution|Noneserializer:ToolResultSerializerType|Noneauth:AuthCheck|list[AuthCheck]|Nonetimeout:float|NoneToolResultSerializerType:TypeAliasCallable[[Any],str]Tool的核心字段说明如下parameters输入Schema通常是解析工具函数的输入参数生成output_schema输出Schema通常是解析工具函数的返回类型生成如果没有返回值此字段返回Noneannotations利用ToolAnnotations对象提供工具在只读性、破坏性、幂等性和开发世界方面的提示我们在介绍工具原语的时候介绍过这个类型execution用于控制工具调用以后台任务指定的行为serializer工具使用的序列化器timeout: 工具执行的时间限制(以秒为单位)。None表示没有时间限制Tool定义了to_mcp_tool实现了工具组件向mcp.types.Tool原语的转换工具的执行通过run方法完成方法返回代表工具执行结果的ToolResult对象。这两个方法都需要子类通过重写来实现。classTool(FastMCPComponent):defto_mcp_tool(self,**overrides:Any,)-mcp.types.Toolasyncdefrun(self,arguments:dict[str,Any])-ToolResultdefconvert_result(self,raw_value:Any)-ToolResultTool的convert_result方法负责将工具函数返回的原始值转换成ToolResult该类型定义如下classToolResult(BaseModel):content:list[ContentBlock]structured_content:dict[str,Any]|Nonemeta:dict[str,Any]|Nonedef__init__(self,content:list[ContentBlock]|Any|NoneNone,structured_content:dict[str,Any]|Any|NoneNone,meta:dict[str,Any]|NoneNone,)defto_mcp_result(self,)-(list[ContentBlock]|tuple[list[ContentBlock],dict[str,Any]]|CallToolResult)ContentBlockTextContent|ImageContent|AudioContent|ResourceLink|EmbeddedResource相关成员说明如下content表示荷载内容的一组ContentBlock对象我们在介绍MCP原语时详细介绍过这个类型structured_content工具的结构化输出它是根据输出Schema构建而成to_mcp_result将ToolResult转换成客户端执行工具最终得到的结果1.1 FunctionTool以函数形式定义的工具通过FunctionTool类型表示。FunctionTool是fn字段表示的一个函数的封装这个函数并不是我们定义的原始工具函数而是通过剔除注入参数转换而成具体转换规在“为什么可以在MCP工具函数中以参数形式注入上下文”有详细介绍。也就说这个函数具有与输入Schema完全一致的参数定义所以run方法只需传入指定的参数对应arguments参数便可直接调用它便可得到执行的原始结果将其作为参数调用convert_result方法便可得到ToolResult对象。classFunctionTool(Tool):fn:Callable[...,Any]return_type:AnyNoneclassmethoddeffrom_function(cls,fn:Callable[...,Any],*,metadata:ToolMeta|NoneNone,name:str|NoneNone,version:str|int|NoneNone,title:str|NoneNone,description:str|NoneNone,icons:list[Icon]|NoneNone,tags:set[str]|NoneNone,annotations:ToolAnnotations|NoneNone,exclude_args:list[str]|NoneNone,output_schema:dict[str,Any]|NotSetT|NoneNotSet,serializer:ToolResultSerializerType|NoneNone,meta:dict[str,Any]|NoneNone,task:bool|TaskConfig|NoneNone,timeout:float|NoneNone,auth:AuthCheck|list[AuthCheck]|NoneNone,)-FunctionToolasyncdefrun(self,arguments:dict[str,Any])-ToolResult函数的转换实现在类方法from_function上它会根据指定的工具函数和其他关键字参数创建一个FunctionTool对象此类函数是对定义在基类Tool上的同名函数的重写。1.2 TransformedTool工具组件不一定非得通过函数来定义也可以由另一个工具转换而成这种类型的工具组件通过如下这个TransformedTool表示。这个TransformedTool类是典型的代理模式的实现专门用于对现有工具进行手术级的定制。它的核心逻辑是不改变原工具的代码但在其外层套一个转换壳。这种转换包含两个层面Schema转换外表参数重命名/删除通过transform_args改变工具对外暴露的参数名或隐藏某些参数;元数据重写修改name、description或tags让同一个功能在不同场景下看起来不一样。逻辑逻辑转换灵魂自定义函数注入通过transform_fn你可以在调用原工具前后加入自己的代码逻辑;结构化输出控制继承或覆盖原工具的输出格式。classTransformedTool(Tool):model_configConfigDict(extraallow,arbitrary_types_allowedTrue)parent_tool:Tool fn:Callable[...,Any]forwarding_fn:Callable[...,Any]transform_args:dict[str,ArgTransform]asyncdefrun(self,arguments:dict[str,Any])-ToolResultclassmethoddeffrom_tool(cls,tool:Tool|Callable[...,Any],name:str|NoneNone,version:str|NotSetT|NoneNotSet,title:str|NotSetT|NoneNotSet,description:str|NotSetT|NoneNotSet,tags:set[str]|NoneNone,transform_fn:Callable[...,Any]|NoneNone,transform_args:dict[str,ArgTransform]|NoneNone,annotations:ToolAnnotations|NotSetT|NoneNotSet,output_schema:dict[str,Any]|NotSetT|NoneNotSet,serializer:Callable[[Any],str]|NotSetT|NoneNotSet,# Deprecatedmeta:dict[str,Any]|NotSetT|NoneNotSet,)-TransformedToolTransformedTool定义了如下的字段parent_tool指向被包装的原始工具fn是新工具真正暴露出来的执行入口forwarding_fn是内部的转发器它负责处理参数的校验和转换逻辑确保当你调用forward()时参数能正确对齐到parent_tooltransform_args存放具体的参数转换规则.transform_args字段返回一个字典其Value是一个用于对参数实施转换的ArgTransform对象其类型定义如下dataclass(kw_onlyTrue)classArgTransform:name:str|NotSetTNotSet description:str|NotSetTNotSet default:Any|NotSetTNotSet default_factory:Callable[[],Any]|NotSetTNotSettype:Any|NotSetTNotSet hide:boolFalserequired:Literal[True]|NotSetTNotSet examples:Any|NotSetTNotSet如下为字段说明name重命名。比如把原参数名q改成更易读的search_querydescription重写描述。为了让LLM更好地理解这个参数可以提供比原工具更详细或更针对特定场景的解释default/default_factory注入默认值。如果原参数是必填的你可以通过这里给它一个固定值从而在对外接口中隐藏它或使其变为选填type修改类型。比如把原有的str限制为特定的Enumhide隐藏参数。如果设为True这个参数将不再出现在工具的输入Schema中。通常配合default使用实现“硬编码”某些参数的效果required显式标记该参数是否为必填examples为 LLM 提供参数示例提高工具调用的准确率TransformedTool的run方法被调用时会先根据transform_args处理传入的参数再执行执行fn。如果fn内部调用了forward函数则通过forwarding_fn把处理后的参数交给parent_tool去运行。类方法from_tool是对基类同名方法的重写它根据指定的参数对给定的原始工具进行转换生成一个TransformedTool对象。如下的实例演示了针对这个类方法的使用fromfastmcp.toolsimportToolfromfastmcp.tools.tool_transformimportArgTransform,TransformedToolimportjson,asynciodefdivide(a:int,b:int)-float:Divide a by breturna/bdefsafe_divide(x:float,y:float)-float:Divide x by y, but raise an error if y is zeroify0:raiseValueError(Cannot divide by zero)returnx/y transform_args{a:ArgTransform(namex,description1st operand to divide,typefloat),b:ArgTransform(namey,description2nd operand to divide,typefloat),}toolTransformedTool.from_tool(toolTool.from_function(fndivide,nameDivide),transform_argstransform_args,transform_fnsafe_divide,nameSafeDivide,descriptionDivide two numbers, but raise an error if the second number is zero)asyncdefmain():print(f name:{tool.name}description:{tool.description}parameters:{json.dumps(tool.parameters,indent2)})try:resultawaittool.run(arguments{x:10,y:0})print(fResult of safe_divide(10, 2):{result})exceptValueErrorase:print(fError:{e})asyncio.run(main())输出name: SafeDivide description: Divide two numbers, but raise an error if the second number is zero parameters: { type: object, properties: { x: { type: number, description: 1st operand to divide }, y: { type: number, description: 2nd operand to divide } }, required: [ y, x ], additionalProperties: false } Error: Cannot divide by zero2. 提示词提示词模板组件通过如下的Prompt类型表示。它的KEY_PREFIX被设置成prompt。classPrompt(FastMCPComponent):KEY_PREFIX:ClassVar[str]promptarguments:list[PromptArgument]|Noneauth:AuthCheck|list[AuthCheck]|Nonedefto_mcp_prompt(self,**overrides:Any,)-mcp.types.Promptasyncdefrender(self,arguments:dict[str,Any]|NoneNone,)-str|list[Message|str]|PromptResultdefconvert_result(self,raw_value:Any)-PromptResultclassPromptArgument(FastMCPBaseModel):name:strdescription:str|Nonerequired:bool几个核心成员说明如下arguments该字段返回一个PromptArgument列表。每个PromptArgument是对提示词函数参数的描述每个参数对应着提示词模板的一个占位符to_mcp_prompt将当前组件转换成mcp.types.Prompt原语render使用参数填充模板得到完整提示词这个操作被成员渲染Render。对于实现MCP协议的mcp库来说提示词渲染的结果通过GetPromptResult对象来表示而一个GetPromptResult对象本质就是一组PromptMessage的集合。这组携带角色的PromptMessage表示会话历史正是Chat模型所谓的提示词。render方法可以返回一个或多个字符串也可以是一个或多个Message对象还可以是一个PromptResult对象。PromptResult是提示词组件对提示词渲染结果的表达所以Prompt提供了convert_result方法将提示词函数的返回的原始结果转换成PromptResult。至于包含角色和内容的Message对象正是PropmtMessage在FastMCP中的对等类型to_mcp_prompt_message方法实现了针对PromptMessage的类型转换。classMessage(pydantic.BaseModel):role:Literal[user,assistant]content:TextContent|ImageContent|AudioContent|EmbeddedResourcedef__init__(self,content:Any,role:Literal[user,assistant]user,)defto_mcp_prompt_message(self)-PromptMessage:returnPromptMessage(roleself.role,contentself.content)我们利用函数定义的提示词模板最终会转换成一个FunctionPrompt。FunctionPrompt和前面介绍的FunctionResource和FunctionTool的实现别无二致。FunctionPrompt定义的类方法from_function是对定义在基类Prompt中的同名方法的重写它帮助我们将自定义的提示词函数转换成FunctionPrompt对象。classFunctionPrompt(Prompt):fn:Callable[...,Any]classmethoddeffrom_function(cls,fn:Callable[...,Any],*,metadata:PromptMeta|NoneNone,# Keep individual params for backwards compatname:str|NoneNone,version:str|int|NoneNone,title:str|NoneNone,description:str|NoneNone,icons:list[Icon]|NoneNone,tags:set[str]|NoneNone,meta:dict[str,Any]|NoneNone,task:bool|TaskConfig|NoneNone,auth:AuthCheck|list[AuthCheck]|NoneNone,)-FunctionPromptasyncdefrender(self,arguments:dict[str,Any]|NoneNone,)-PromptResult
返回列表