别再当黑匣子用了!手把手教你用FastMCP的tool()、resource()和prompt()装饰器,从源码理解到实战避坑
FastMCP装饰器深度解析从tool()到prompt()的实战进阶指南1. 为什么需要理解装饰器内部机制在Python开发领域装饰器(Decorator)是一种强大的元编程工具而FastMCP框架中的tool()、resource()和prompt()装饰器更是将这一理念发挥到了极致。许多开发者虽然能够使用这些装饰器完成基础功能但当遇到以下场景时就会陷入困境参数类型注解与实际传入类型不符时的处理逻辑动态URI模板与函数参数的匹配规则返回值的自动转换机制边界装饰器嵌套时的执行顺序问题理解这些装饰器的工作原理不仅能帮助开发者规避常见陷阱更能释放FastMCP框架的全部潜力。下面我们通过一个典型问题场景切入mcp.tool() def process_data(input: dict[str, int]) - list[float]: # 当input包含非int值时会发生什么 return [float(v) for v in input.values()]2. tool()装饰器的核心机制剖析2.1 元数据提取流程mcp.tool()装饰器的核心任务是将普通函数转化为MCP工具其处理流程可分为四个关键阶段函数签名解析通过inspect.signature获取参数类型注解参数模型生成基于Pydantic创建参数验证模型返回类型处理根据返回注解决定结构化输出方式工具注册将处理后的函数注册到工具管理器# 参数模型生成伪代码 def create_arg_model(func): sig inspect.signature(func) fields {} for name, param in sig.parameters.items(): field_type param.annotation if param.annotation ! inspect.Parameter.empty else Any fields[name] (field_type, Field(...)) return create_model(f{func.__name__}Arguments, **fields)2.2 类型系统的边界处理FastMCP对类型注解的处理遵循以下规则注解类型处理方式典型场景基础类型直接验证str,int,bool等复合类型递归验证list[int],dict[str, float]等Pydantic模型直接使用自定义的BaseModel子类未注解参数视为Any无类型提示的参数Optional类型允许NoneOptional[str]或str注意当实际参数与声明类型不符时FastMCP会尝试强制类型转换无法转换时将抛出ValidationError2.3 结构化输出的三种模式根据函数返回类型注解的不同tool()会产生不同的输出结构原始输出模式无注解或structured_outputFalsemcp.tool(structured_outputFalse) def greet(name: str) - str: return fHello, {name}自动包装模式基础类型注解mcp.tool() # 自动包装为{result: value} def calculate(x: int, y: int) - float: return x / y直接模型输出返回Pydantic模型class ResultModel(BaseModel): value: float unit: str mcp.tool() def measure() - ResultModel: return ResultModel(value3.14, unitcm)3. resource()装饰器的URI魔法3.1 静态资源与动态模板mcp.resource()的核心价值在于URI处理它支持两种资源定义方式静态资源mcp.resource(resource://config) def get_config(): return {version: 1.0.0}动态模板mcp.resource(resource://user/{user_id}/profile) def get_profile(user_id: str): return fetch_user_profile(user_id)关键区别在于特性静态资源动态模板URI格式固定字符串含{}占位符参数来源无参数函数URI匹配函数参数注册方式直接注册生成模板后注册访问方式直接GET请求需提供参数值3.2 参数绑定机制动态URI模板与函数参数的绑定遵循严格规则URI中的占位符名称必须与函数参数名完全匹配所有函数参数都必须在URI中有对应占位符类型注解用于请求参数的预处理常见错误示例# 错误URI缺少region参数 mcp.resource(resource://weather/{city}) def get_weather(city: str, region: str): ... # 错误参数名不匹配 mcp.resource(resource://product/{prod_id}) def get_product(product_id: int): ...4. prompt()装饰器的消息转换艺术4.1 自动消息转换规则mcp.prompt()最强大的特性是灵活的返回值处理其转换逻辑如下graph TD A[函数返回值] -- B{是否为list/tuple?} B --|是| C[遍历处理每个元素] B --|否| D[包装为单元素列表] C -- E{元素类型判断} E --|Message实例| F[直接使用] E --|字典类型| G[验证为User/AssistantMessage] E --|字符串| H[转换为UserMessage] E --|其他类型| I[JSON序列化后转换]4.2 实战模式对比模式1直接返回消息字典mcp.prompt() def system_prompt() - list: return [ {role: system, content: 你是有帮助的AI助手}, {role: user, content: 请介绍FastMCP} ]模式2返回字符串自动转换mcp.prompt() def welcome_prompt(username: str) - str: return f欢迎{username}有什么可以帮您模式3混合返回类型mcp.prompt() def complex_prompt() - list: return [ 第一部分文本, {role: assistant, content: 预置回复}, CustomMessage(content自定义内容) ]5. 调试技巧与性能优化5.1 使用func_metadata调试FastMCP提供的调试工具可清晰展示装饰器内部处理结果from mcp.server.fastmcp.utilities import func_metadata def example(a: int, b: str test) - float: pass meta func_metadata(example) print(meta.arg_model.model_json_schema())输出示例{ title: exampleArguments, type: object, properties: { a: {title: A, type: integer}, b: {title: B, type: string, default: test} }, required: [a] }5.2 性能优化建议避免频繁装饰在循环外部装饰函数合理使用缓存对纯函数使用functools.lru_cache类型注解优化尽量使用基础类型而非复杂嵌套批量注册工具使用mcp.add_tools()替代多个装饰器# 不推荐 for i in range(100): mcp.tool() def temp_func(x): return x * i # 推荐 def create_tool(i): functools.lru_cache mcp.tool() def optimized_func(x: int) - int: return x * i return optimized_func tools [create_tool(i) for i in range(100)] mcp.add_tools(tools)6. 高级模式与自定义扩展6.1 自定义装饰器链可以组合多个装饰器实现更复杂的功能def validate_input(*validators): def decorator(func): functools.wraps(func) def wrapper(*args, **kwargs): for v in validators: v(*args, **kwargs) return func(*args, **kwargs) return wrapper return decorator mcp.tool() validate_input(lambda x: x 0, lambda y: y ! ) def process(x: int, y: str) - dict: return {x: x, y: y}6.2 修改默认行为通过继承Tool类可以覆盖默认处理逻辑class CustomTool(mcp.Tool): classmethod def from_function(cls, fn, **kwargs): tool super().from_function(fn, **kwargs) tool.extra_info Custom processing return tool mcp.tool_manager.tool_class CustomTool7. 常见问题解决方案7.1 参数类型不匹配问题现象mcp.tool() def add(a: int, b: int) - int: return a b # 调用时传递字符串参数解决方案前端增加类型校验使用Union类型注解添加自定义转换逻辑mcp.tool() def safe_add(a: Union[int, str], b: Union[int, str]) - int: return int(a) int(b)7.2 URI参数冲突问题现象mcp.resource(resource://{name}/{id}) def get_item(name: str, id: str, version: int): ...解决方案将额外参数改为查询参数修改URI模板包含所有参数使用POST方法传递参数mcp.resource(resource://{name}/{id}?version{version}) def get_item(name: str, id: str, version: int): ...8. 最佳实践总结经过对FastMCP装饰器的深度分析我们总结出以下黄金法则类型注解即文档始终为重要函数添加完整类型提示保持URI简洁动态资源参数不超过3个为佳消息结构一致prompt返回保持统一消息格式早发现早处理在装饰阶段暴露问题而非运行时合理分层将核心逻辑与MCP装饰器分离最后分享一个综合应用所有知识点的示例class DataModel(BaseModel): values: list[float] stats: dict[str, float] mcp.tool() def analyze_dataset( dataset: list[dict], algorithm: Literal[svm, tree, nn] svm ) - DataModel: 数据集分析工具 :param dataset: 待分析数据集每行代表一个样本 :param algorithm: 使用的分析算法 :return: 包含分析结果的数据模型 # 实现细节省略... return DataModel(...) mcp.resource(resource://analysis/{job_id}) def get_analysis_result(job_id: UUID) - DataModel: 获取异步分析结果 return query_async_result(job_id) mcp.prompt() def analysis_instructions(dataset_desc: str) - list: return [ {role: system, content: 你是有经验的数据分析师}, {role: user, content: f请分析此数据集{dataset_desc}} ]
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2461902.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!