Shortcuts

Source code for agentlego.search

from typing import List

import numpy as np

from .apis.tool import list_tools
from .utils import load_or_build_object


def _cosine_similarity(a: np.ndarray, b: np.ndarray) -> np.ndarray:
    """Calculate the cosine similarity of a and b."""
    dot_product = np.dot(b, a)
    norm_a = np.linalg.norm(a)
    norm_b = np.linalg.norm(b, axis=1)
    res = dot_product / (norm_a * norm_b)
    return res


def _search_with_openai(query, choices, model='text-embedding-ada-002', topk=5):
    """Search tools with openai API.

    Note:
        You need to install openai first. And you need to set the
        OPENAI_API_KEY.

    Args:
        query (str): User input.
        choices (list): List of tool descriptions.
        topk (int): Max number of tools to be returned.
        model (str): OpenAI API model name.
            Defaults to 'text-embedding-ada-002'.

    Returns:
        list: List of tool descriptions.
    """
    try:
        from openai import OpenAI
    except ModuleNotFoundError:
        raise ModuleNotFoundError(
            'please install openai to enable searching tools powered by '
            'openai')

    client = OpenAI()
    embeddings = client.embeddings.create(input=[query] + choices, model=model).data
    similarity = _cosine_similarity(np.array(embeddings[0]), np.array(embeddings[1:]))

    indices = np.argsort(-similarity)[:topk]
    return [choices[i] for i in indices]


def _serach_with_sentence_transformers(query,
                                       choices,
                                       model='sentence-transformers/all-mpnet-base-v2',
                                       topk=5):
    """Search tools with sentence-transformers.

    Args:
        query (str): User input.
        choices (list): List of tool descriptions.
        model (str): Sentence-transformers model name. Defaults to
            'sentence-transformers/all-mpnet-base-v2'.

    Returns:
        list: List of tool descriptions.
    """
    from sentence_transformers import SentenceTransformer

    model = load_or_build_object(SentenceTransformer, model)
    embeddings = model.encode([query] + choices)
    similarity = _cosine_similarity(embeddings[0], embeddings[1:])

    indices = np.argsort(-similarity)[:topk]
    return [choices[i] for i in indices]


def _search_with_thefuzz(query, choices, topk=5):
    from thefuzz import process
    result = process.extract(query, choices=choices, limit=topk)
    return [res for res, _ in result]


[docs]def search_tool(query: str, kind: str = 'thefuzz', topk=5) -> List[str]: """Search several proper tools according to the query. Args: query (str): User input. kind (str): Different third-party libraries are used to assist in searching the appropriate tools. Optional values are "thefuzz", "openai", and "st". Defaults to "thefuzz". topk (int): Return the top-k results. Defaults to 5. Examples: >>> from agentlego import search_tool >>> # use the thefuzz to search tools >>> search_tool('human pose') >>> # use the openai API to search tools >>> search_tool('human pose', kind='openai') >>> # use the sentence-transformers to search tools >>> search_tool('human pose', kind='st') """ choice2names = dict() for name, description in list_tools(with_description=True): choice2names[description] = name choices = list(choice2names.keys()) if kind == 'thefuzz': result = _search_with_thefuzz(query, choices, topk=topk) elif kind == 'openai': result = _search_with_openai(query, choices, topk=topk) elif kind == 'st': result = _serach_with_sentence_transformers(query, choices, topk=topk) else: raise ValueError('The supported kinds are "thefuzz", "openai" or "st",' f' but got {kind}.') names = [] for description in result: names.append(choice2names[description]) return names