You can give your AI agent different levels of tool-use autonomy through the meta functions:

  • Pattern 1: Pre-planned Tools
    • Provide the specific functions (tools) to be used by an LLM.
  • Pattern 2: Dynamic Tool Discovery and Execution
    • 2.1: Tool List Expansion

      • Use ACI_SEARCH_FUNCTIONS meta function (tool):
      • Search for relevant functions across all apps
      • Add discovered tools directly to the LLM’s tool list
      • Allow the LLM to invoke these discovered tools directly by name
    • 2.2: Tool Definition as Text Context Approach

      • Use ACI_SEARCH_FUNCTIONS and ACI_EXECUTE_FUNCTION meta functions (tools)
      • Use ACI_SEARCH_FUNCTIONS to retrieve tool definitions
      • Present those definitions to the LLM as text content instead of adding them to the LLM’s tool list
      • The LLM uses ACI_EXECUTE_FUNCTION to execute these tools indirectly

Pre-planned

This is the most straright forward use case. You can directly find the functions you want to use on the developer portal, retrieve the function definitions, and append them to your LLM API call. This way your agents will only use the tools you have selected and provided, it would not attempt to find and use other tools.

brave_search_function_definition = aci.functions.get_definition("BRAVE_SEARCH__WEB_SEARCH")

response = openai.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {
            "role": "system",
            "content": "You are a helpful assistant with access to a variety of tools.",
        },
        {
            "role": "user",
            "content": "What is ACI.dev by Aipolabs?",
        },
    ],
    tools=[brave_search_function_definition],
)
tool_call = (
    response.choices[0].message.tool_calls[0]
    if response.choices[0].message.tool_calls
    else None
)

if tool_call:
    result = aci.functions.execute(
        tool_call.function.name,
        json.loads(tool_call.function.arguments),
        linked_account_owner_id=LINKED_ACCOUNT_OWNER_ID,
    )

Dynamic Tool Discovery and Execution With Tool List Expansion

In this use case, the tools list provided to LLM API calls changes according to the function definitions retrieved by the agent from the ACI.dev using the provided meta functions (tools).

The retrieved function definitions are appended to the available tools list for LLMs to decide when and how to use it in subsequent LLM calls. This leverages the ability of many LLMs to enforce adherence of function-call outputs as much as possible to the provided definition, while still offering the flexibility to essentially access as many different tools as needed by the LLM-powered agent.

The trade-off here is that the developer has to manage tool-list and know when to append or remove tools when making the LLM call.

Example starting tools lists provided to the LLM

tools_meta = [
    ACISearchFunctions.to_json_schema(FunctionDefinitionFormat.OPENAI),
]
tools_retrieved = []

Adding retrieved function definitions to the tools_retrieved list

if tool_call.function.name == ACISearchFunctions.get_name():
    tools_retrieved.extend(result)

Subsequent tool-calling

response = openai.chat.completions.create(
    model="gpt-4o",
    messages=[
        {
            "role": "system",
            "content": prompt,
        },
        {
            "role": "user",
            "content": "Can you search online for some information about ACI.dev? Use whichever search tool you find most suitable for the task via the ACI meta functions.",
        },
    ]
    + chat_history,
    tools=tools_meta + tools_retrieved,
    parallel_tool_calls=False,
)
if tool_call:
    print(
        f"{create_headline(f'Function Call: {tool_call.function.name}')} \n arguments: {tool_call.function.arguments}"
    )

    result = aci.handle_function_call(
        tool_call.function.name,
        json.loads(tool_call.function.arguments),
        linked_account_owner_id=LINKED_ACCOUNT_OWNER_ID,
        allowed_apps_only=True,
        format=FunctionDefinitionFormat.OPENAI,
    )

For a full example, see here .

Dynamic Tool Discovery and Execution With Tool Definition as Text Content

In this use case, the tools list provided to the LLM is static, which are just the two meta functions ACI_SEARCH_FUNCTIONS and ACI_EXECUTE_FUNCTION.

The difference between this and the previous pattern is that retrieved function definitions are provided to the LLM directly as text content instead of being added to the tools list. The LLM then has to decide whether to call the ACI_EXECUTE_FUNCTION to actually execute an API call.

By using the meta functions (tools) this way, the developer does not have to manage the tools list, but the accuracy of tool use can decrease.

Example tools list provided to LLM

tools_meta = [
    ACISearchFunctions.to_json_schema(FunctionDefinitionFormat.OPENAI),
    ACIExecuteFunction.to_json_schema(FunctionDefinitionFormat.OPENAI),
]

Tool-calling through LLM

response = openai.chat.completions.create(
    model="gpt-4o",
    messages=[
        {
            "role": "system",
            "content": prompt,
        },
        {
            "role": "user",
            "content": "Can you search online for some information about ACI.dev? Use whichever search tool you find most suitable for the task via the ACI meta functions.",
        },
    ]
    + chat_history,
    tools=tools_meta,
    parallel_tool_calls=False,
)

tool_call = (
    response.choices[0].message.tool_calls[0]
    if response.choices[0].message.tool_calls
    else None
)

if tool_call:
    result = aci.handle_function_call(
        tool_call.function.name,
        json.loads(tool_call.function.arguments),
        linked_account_owner_id=LINKED_ACCOUNT_OWNER_ID,
        allowed_apps_only=True,
        format=FunctionDefinitionFormat.OPENAI,
    )

For a full example, see here .