Multi-Tool Task Agent – Manus / OpenAI Agents SDK Inspired¶
Demonstrates building a versatile task agent with multiple tools, safety guardrails, and dependency injection – inspired by Manus AI’s tool-using agent and the OpenAI Agents SDK patterns.
Pipeline topology: task_agent [tools: search, calc, read_file] [guardrail] [inject: api_key] >> verifier [C.from_state(“task_result”)]
Uses: .tool(), .guard(), .inject(), .sub_agent(), .context()
Tip
What you’ll learn How to attach tools to an agent using the fluent API.
Source: 58_multi_tool_agent.py
from adk_fluent import Agent, C
def search_web(query: str) -> str:
"""Search the web for information."""
return f"Results for: {query}"
def calculate(expression: str) -> str:
"""Evaluate a mathematical expression."""
return f"Result: {expression}"
def read_file(path: str, api_key: str) -> str:
"""Read a file from cloud storage."""
return f"Contents of {path} (via {api_key})"
def safety_guardrail(callback_context, llm_request):
"""Screen requests for unsafe operations.
Blocks attempts to access system files, execute arbitrary code,
or exfiltrate data through tool calls.
"""
return None
# The fluent builder provides:
# .tool() -- add tools one at a time (appends, not replaces)
# .guard() -- registers both before_model and after_model
# .inject() -- hides infra params from LLM schema
task_agent = (
Agent("task_agent")
.model("gemini-2.5-flash")
.instruct(
"You are a versatile task agent. Use your tools to research, "
"calculate, and read files to complete the user's request. "
"Always explain your reasoning before using a tool."
)
.tool(search_web)
.tool(calculate)
.tool(read_file)
.inject(api_key="prod_key") # Hidden from LLM -- only visible to read_file
.guard(safety_guardrail)
)
# Verifier agent checks the task agent's work
verifier = (
Agent("verifier")
.model("gemini-2.5-flash")
.instruct("Verify the task agent's output for accuracy and completeness.")
.context(C.none() + C.from_state("task_result"))
)
# Compose: task agent -> verifier pipeline
verified_agent = task_agent.writes("task_result") >> verifier
import functools
from google.adk.agents.llm_agent import LlmAgent
def search_web_native(query: str) -> str:
"""Search the web for information."""
return f"Results for: {query}"
def calculate_native(expression: str) -> str:
"""Evaluate a mathematical expression."""
return f"Result: {expression}"
def read_file_native(path: str, api_key: str = "") -> str:
"""Read a file from storage."""
return f"Contents of {path}"
# Native: tools with infra params leak into LLM schema
agent_native = LlmAgent(
name="task_agent",
model="gemini-2.5-flash",
instruction=(
"You are a versatile task agent. Use your tools to research, "
"calculate, and read files to complete the user's request."
),
tools=[
search_web_native,
calculate_native,
functools.partial(read_file_native, api_key="prod_key"),
],
)
graph TD
n1[["task_agent_then_verifier (sequence)"]]
n2["task_agent"]
n3["verifier"]
n2 --> n3
Equivalence¶
# Task agent has 3 tools (stored in _lists, not _config)
assert len(task_agent._lists["tools"]) == 3
# Guardrail registered on both before and after model callbacks
assert safety_guardrail in task_agent._callbacks["before_model_callback"]
assert safety_guardrail in task_agent._callbacks["after_model_callback"]
# DI resources stored
assert task_agent._config["_resources"] == {"api_key": "prod_key"}
# inject_resources hides infra params from LLM
import inspect
from adk_fluent.di import inject_resources
wrapped = inject_resources(read_file, {"api_key": "test"})
sig = inspect.signature(wrapped)
assert "path" in sig.parameters
assert "api_key" not in sig.parameters
# Pipeline builds correctly
from adk_fluent import Pipeline
assert isinstance(verified_agent, Pipeline)
built = verified_agent.build()
assert len(built.sub_agents) == 2
assert built.sub_agents[0].name == "task_agent"
assert built.sub_agents[1].name == "verifier"
# --- T Module equivalent ---
# T module composes all tools in a single fluent expression
from adk_fluent._tools import T
multi_t = (
Agent("multi_tool_t")
.model("gemini-2.5-flash")
.instruct("Help with various tasks.")
.tools(T.fn(search_web) | T.fn(calculate) | T.fn(read_file))
)
ir_t = multi_t.to_ir()
assert len(ir_t.tools) == 3
See also
API reference: FunctionTool