Conversation
Allows users to register Python functions as tools using @fast.tool (bare or parameterized with name/description). Global tools are available to all agents by default; agents with explicit function_tools only see those tools. Made-with: Cursor
The demonstration script was committed to the repo root; the proper unit tests live in tests/unit/core/test_tool_decorator.py. Made-with: Cursor
This update includes examples of registering Python functions as tools using the @fast.tool decorator, highlighting both synchronous and asynchronous support. It also explains the global availability of tools and how to restrict them to specific agents using the function_tools parameter.
|
Thanks very much for this PR; it's a great idea; before merge though I do want to consider the global-by-default. I'm in complete agreement that for quick single agent programs this is the right thing to do - but think it might be too surprising behaviour for any multi-agent setup. I'm trying to think of options that give us the best of both worlds. |
…les. Introduce @agent.tool decorator for scoping tools to individual agents, clarifying the distinction between global and agent-specific tools. Update examples to reflect new functionality and improve clarity on tool registration and usage.
|
Hi, thanks for the comment — that's a fair concern and I completely agree. In order to have the best of both worlds, I've updated the PR to add an agent-specific tool decorator ( Per-agent scoping with
|
Context
Currently, attaching Python functions as tools to agents requires either external MCP servers or passing
function_tools=["file.py:func"]string specs. There is no way to define tools inline alongside agent code using a decorator.This PR adds a
@fast.tooldecorator that lets users register Python functions as agent tools directly in code, matching the declarative style of@fast.agent,@fast.chain, etc.Changes
src/fast_agent/core/direct_decorators.pytool()method toDecoratorMixinwith@overloadfor type safety@fast.tool) and parameterized (@fast.tool(name=..., description=...)) usagebuild_default_function_toolfrom the existing function tool loadersrc/fast_agent/core/fastagent.py_registered_tools: list[FunctionTool]toFastAgent.__init__src/fast_agent/core/direct_factory.py_resolve_function_tools_with_globals()implementing the scoping logic_load_configured_function_toolscalls at all 3 injection points (basic agent, smart agent, agents-as-tools)AGENTS.md@fast.toolarchitectureREADME.mdexamples/function-tools/basic.py-- bare and parameterized@fast.toolwith a single agentscoping.py-- global tools vs explicitfunction_toolsopt-outtests/unit/core/test_tool_decorator.pyHow to use it
Both sync and async functions are supported. Name and description default to the function name and docstring.
Scoping behavior
@fast.toolregisters tools globally -- all agents receive them by defaultfunction_tools=[...], only those explicit tools apply; globals are skipped@fast.tooltools with the same name (existinglist_toolsmerge behavior)Design decisions
Why on
DecoratorMixin? Keeps the API consistent with@fast.agent,@fast.chain, etc. -- all decorators live in one place.Why global by default? Single-agent apps (the common case) should just work with
@fast.tooland no extra wiring. Multi-agent apps can opt out per-agent withfunction_tools=.Why
getattrfor_registered_tools? The build context receivesCore(notFastAgent), so the registry is attached dynamically.getattrwith a default keeps the factory code safe for non-FastAgent callers (e.g.create_basic_agents_in_dependency_order).Test plan
uv run scripts/lint.pypassesuv run scripts/typecheck.pypassestests/unit/core/test_tool_decorator.py)