trustgraph/docs/tech-specs/tool-services.pt.md

480 lines
17 KiB
Markdown
Raw Normal View History

---
layout: default
title: "Serviços de Ferramentas: Ferramentas de Agente Dinamicamente Plugáveis"
parent: "Portuguese (Beta)"
---
# Serviços de Ferramentas: Ferramentas de Agente Dinamicamente Plugáveis
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Status
Implementado
## Visão Geral
Esta especificação define um mecanismo para ferramentas de agente dinamicamente plugáveis, chamadas "serviços de ferramentas". Ao contrário dos tipos de ferramentas integradas existentes (`KnowledgeQueryImpl`, `McpToolImpl`, etc.), os serviços de ferramentas permitem que novas ferramentas sejam introduzidas por:
1. Implantando um novo serviço baseado em Pulsar
2. Adicionando um descritor de configuração que informa ao agente como invocá-lo
Isso permite a extensibilidade sem modificar o framework de resposta do agente principal.
## Terminologia
| Termo | Definição |
|------|------------|
| **Ferramenta Integrada** | Tipos de ferramentas existentes com implementações codificadas em `tools.py` |
| **Serviço de Ferramenta** | Um serviço Pulsar que pode ser invocado como uma ferramenta de agente, definido por um descritor de serviço |
| **Ferramenta** | Uma instância configurada que referencia um serviço de ferramenta, exposta ao agente/LLM |
Este é um modelo de duas camadas, análogo às ferramentas MCP:
MCP: O servidor MCP define a interface da ferramenta → A configuração da ferramenta a referencia
Serviços de Ferramenta: O serviço de ferramenta define a interface Pulsar → A configuração da ferramenta a referencia
## Contexto: Ferramentas Existentes
### Implementação de Ferramenta Integrada
As ferramentas são atualmente definidas em `trustgraph-flow/trustgraph/agent/react/tools.py` com implementações tipadas:
```python
class KnowledgeQueryImpl:
async def invoke(self, question):
client = self.context("graph-rag-request")
return await client.rag(question, self.collection)
```
Cada tipo de ferramenta:
Possui um serviço Pulsar pré-definido que ele chama (por exemplo, `graph-rag-request`)
Conhece o método exato a ser chamado no cliente (por exemplo, `client.rag()`)
Possui argumentos tipados definidos na implementação
### Registro de Ferramentas (service.py:105-214)
As ferramentas são carregadas da configuração com um campo `type` que mapeia para uma implementação:
```python
if impl_id == "knowledge-query":
impl = functools.partial(KnowledgeQueryImpl, collection=data.get("collection"))
elif impl_id == "text-completion":
impl = TextCompletionImpl
# ... etc
```
## Arquitetura
### Modelo de Duas Camadas
#### Camada 1: Descritor do Serviço de Ferramenta
Um serviço de ferramenta define uma interface de serviço Pulsar. Ele declara:
As filas Pulsar para solicitação/resposta
Os parâmetros de configuração que ele requer das ferramentas que o utilizam
```json
{
"id": "custom-rag",
"request-queue": "non-persistent://tg/request/custom-rag",
"response-queue": "non-persistent://tg/response/custom-rag",
"config-params": [
{"name": "collection", "required": true}
]
}
```
Um serviço de ferramenta que não requer parâmetros de configuração:
```json
{
"id": "calculator",
"request-queue": "non-persistent://tg/request/calc",
"response-queue": "non-persistent://tg/response/calc",
"config-params": []
}
```
#### Nível 2: Descritor de Ferramenta
Uma ferramenta referencia um serviço de ferramenta e fornece:
Valores de parâmetros de configuração (que satisfazem os requisitos do serviço)
Metadados da ferramenta para o agente (nome, descrição)
Definições de argumentos para o LLM
```json
{
"type": "tool-service",
"name": "query-customers",
"description": "Query the customer knowledge base",
"service": "custom-rag",
"collection": "customers",
"arguments": [
{
"name": "question",
"type": "string",
"description": "The question to ask about customers"
}
]
}
```
Múltiplas ferramentas podem referenciar o mesmo serviço com diferentes configurações:
```json
{
"type": "tool-service",
"name": "query-products",
"description": "Query the product knowledge base",
"service": "custom-rag",
"collection": "products",
"arguments": [
{
"name": "question",
"type": "string",
"description": "The question to ask about products"
}
]
}
```
### Formato da Requisição
Quando uma ferramenta é invocada, a requisição ao serviço da ferramenta inclui:
`user`: Do pedido do agente (multi-tenência)
`config`: Valores de configuração codificados em JSON, provenientes da descrição da ferramenta
`arguments`: Argumentos codificados em JSON, provenientes do LLM
```json
{
"user": "alice",
"config": "{\"collection\": \"customers\"}",
"arguments": "{\"question\": \"What are the top customer complaints?\"}"
}
```
O serviço de ferramenta recebe estes como dicionários analisados no método `invoke`.
### Implementação Genérica do Serviço de Ferramenta
Uma classe `ToolServiceImpl` invoca serviços de ferramenta com base na configuração:
```python
class ToolServiceImpl:
def __init__(self, context, request_queue, response_queue, config_values, arguments, processor):
self.request_queue = request_queue
self.response_queue = response_queue
self.config_values = config_values # e.g., {"collection": "customers"}
# ...
async def invoke(self, **arguments):
client = await self._get_or_create_client()
response = await client.call(user, self.config_values, arguments)
if isinstance(response, str):
return response
else:
return json.dumps(response)
```
## Decisões de Design
### Modelo de Configuração de Duas Camadas
Os serviços de ferramentas seguem um modelo de duas camadas semelhante às ferramentas MCP:
1. **Serviço de Ferramenta**: Define a interface do serviço Pulsar (tópico, parâmetros de configuração necessários)
2. **Ferramenta**: Referencia um serviço de ferramenta, fornece valores de configuração, define argumentos do LLM
Essa separação permite:
Que um único serviço de ferramenta seja usado por várias ferramentas com diferentes configurações
Uma distinção clara entre a interface do serviço e a configuração da ferramenta
A reutilização das definições de serviço
### Mapeamento de Solicitações: Transmissão com Envelope
A solicitação a um serviço de ferramenta é um envelope estruturado contendo:
`user`: Propagado do pedido do agente para multi-inquilinato
Valores de configuração: Do descritor da ferramenta (por exemplo, `collection`)
`arguments`: Argumentos fornecidos pelo LLM, transmitidos como um dicionário
O gerenciador de agente analisa a resposta do LLM em `act.arguments` como um dicionário (`agent_manager.py:117-154`). Este dicionário é incluído no envelope da solicitação.
### Tratamento de Esquemas: Não Tipados
As solicitações e respostas usam dicionários não tipados. Não há validação de esquema no nível do agente - o serviço de ferramenta é responsável por validar suas entradas. Isso fornece o máximo de flexibilidade para definir novos serviços.
### Interface do Cliente: Tópicos Pulsar Diretos
Os serviços de ferramentas usam tópicos Pulsar diretos, sem a necessidade de configuração de fluxo. O descritor do serviço de ferramenta especifica os nomes completos das filas:
```json
{
"id": "joke-service",
"request-queue": "non-persistent://tg/request/joke",
"response-queue": "non-persistent://tg/response/joke",
"config-params": [...]
}
```
Isso permite que os serviços sejam hospedados em qualquer namespace.
### Tratamento de Erros: Convenção de Erro Padrão
As respostas do serviço de ferramenta seguem a convenção de esquema existente com um campo `error`:
```python
@dataclass
class Error:
type: str = ""
message: str = ""
```
Estrutura da resposta:
Sucesso: `error` é `None`, a resposta contém o resultado
Erro: `error` é preenchido com `type` e `message`
Isso corresponde ao padrão usado em todo o serviço existente (por exemplo, `PromptResponse`, `QueryResponse`, `AgentResponse`).
### Correlação de Requisições/Respostas
As requisições e respostas são correlacionadas usando um `id` nas propriedades da mensagem Pulsar:
A requisição inclui `id` nas propriedades: `properties={"id": id}`
A(s) resposta(s) incluem o mesmo `id`: `properties={"id": id}`
Isso segue o padrão existente usado em todo o código-fonte (por exemplo, `agent_service.py`, `llm_service.py`).
### Suporte a Streaming
Os serviços de ferramenta podem retornar respostas em streaming:
Múltiplas mensagens de resposta com o mesmo `id` nas propriedades
Cada resposta inclui o campo `end_of_stream: bool`
A resposta final tem `end_of_stream: True`
Isso corresponde ao padrão usado em `AgentResponse` e outros serviços de streaming.
### Tratamento da Resposta: Retorno de String
Todas as ferramentas existentes seguem o mesmo padrão: **receber argumentos como um dicionário, retornar a observação como uma string**.
| Ferramenta | Tratamento da Resposta |
|------|------------------|
| `KnowledgeQueryImpl` | Retorna `client.rag()` diretamente (string) |
| `TextCompletionImpl` | Retorna `client.question()` diretamente (string) |
| `McpToolImpl` | Retorna uma string, ou `json.dumps(output)` se não for uma string |
| `StructuredQueryImpl` | Formata o resultado para uma string |
| `PromptImpl` | Retorna `client.prompt()` diretamente (string) |
Os serviços de ferramenta seguem o mesmo contrato:
O serviço retorna uma resposta em string (a observação)
Se a resposta não for uma string, ela é convertida via `json.dumps()`
Nenhuma configuração de extração é necessária no descritor
Isso mantém o descritor simples e coloca a responsabilidade de retornar uma resposta de texto apropriada para o agente no serviço.
## Guia de Configuração
Para adicionar um novo serviço de ferramenta, são necessários dois itens de configuração:
### 1. Configuração do Serviço de Ferramenta
Armazenado sob a chave de configuração `tool-service`. Define as filas Pulsar e os parâmetros de configuração disponíveis.
| Campo | Obrigatório | Descrição |
|-------|----------|-------------|
| `id` | Sim | Identificador único para o serviço de ferramenta |
| `request-queue` | Sim | Tópico Pulsar completo para requisições (por exemplo, `non-persistent://tg/request/joke`) |
| `response-queue` | Sim | Tópico Pulsar completo para respostas (por exemplo, `non-persistent://tg/response/joke`) |
| `config-params` | Não | Array de parâmetros de configuração que o serviço aceita |
Cada parâmetro de configuração pode especificar:
`name`: Nome do parâmetro (obrigatório)
`required`: Se o parâmetro deve ser fornecido pelas ferramentas (padrão: falso)
Exemplo:
```json
{
"id": "joke-service",
"request-queue": "non-persistent://tg/request/joke",
"response-queue": "non-persistent://tg/response/joke",
"config-params": [
{"name": "style", "required": false}
]
}
```
### 2. Configuração da Ferramenta
Armazenado sob a chave de configuração `tool`. Define uma ferramenta que o agente pode usar.
| Campo | Obrigatório | Descrição |
|-------|----------|-------------|
| `type` | Sim | Deve ser `"tool-service"` |
| `name` | Sim | Nome da ferramenta exposto para o LLM |
| `description` | Sim | Descrição do que a ferramenta faz (mostrada para o LLM) |
| `service` | Sim | ID do serviço de ferramenta a ser invocado |
| `arguments` | Não | Array de definições de argumentos para o LLM |
| *(parâmetros de configuração)* | Varia | Quaisquer parâmetros de configuração definidos pelo serviço |
Cada argumento pode especificar:
`name`: Nome do argumento (obrigatório)
`type`: Tipo de dados, por exemplo, `"string"` (obrigatório)
`description`: Descrição mostrada para o LLM (obrigatório)
Exemplo:
```json
{
"type": "tool-service",
"name": "tell-joke",
"description": "Tell a joke on a given topic",
"service": "joke-service",
"style": "pun",
"arguments": [
{
"name": "topic",
"type": "string",
"description": "The topic for the joke (e.g., programming, animals, food)"
}
]
}
```
### Carregando a Configuração
Use `tg-put-config-item` para carregar as configurações:
```bash
# Load tool-service config
tg-put-config-item tool-service/joke-service < joke-service.json
# Load tool config
tg-put-config-item tool/tell-joke < tell-joke.json
```
O agente-gerenciador deve ser reiniciado para aplicar novas configurações.
## Detalhes da Implementação
### Esquema
Tipos de requisição e resposta em `trustgraph-base/trustgraph/schema/services/tool_service.py`:
```python
@dataclass
class ToolServiceRequest:
user: str = "" # User context for multi-tenancy
config: str = "" # JSON-encoded config values from tool descriptor
arguments: str = "" # JSON-encoded arguments from LLM
@dataclass
class ToolServiceResponse:
error: Error | None = None
response: str = "" # String response (the observation)
end_of_stream: bool = False
```
### Servidor: DynamicToolService
Classe base em `trustgraph-base/trustgraph/base/dynamic_tool_service.py`:
```python
class DynamicToolService(AsyncProcessor):
"""Base class for implementing tool services."""
def __init__(self, **params):
topic = params.get("topic", default_topic)
# Constructs topics: non-persistent://tg/request/{topic}, non-persistent://tg/response/{topic}
# Sets up Consumer and Producer
async def invoke(self, user, config, arguments):
"""Override this method to implement the tool's logic."""
raise NotImplementedError()
```
### Cliente: ToolServiceImpl
Implementação em `trustgraph-flow/trustgraph/agent/react/tools.py`:
```python
class ToolServiceImpl:
def __init__(self, context, request_queue, response_queue, config_values, arguments, processor):
# Uses the provided queue paths directly
# Creates ToolServiceClient on first use
async def invoke(self, **arguments):
client = await self._get_or_create_client()
response = await client.call(user, config_values, arguments)
return response if isinstance(response, str) else json.dumps(response)
```
### Arquivos
| Arquivo | Propósito |
|------|---------|
| `trustgraph-base/trustgraph/schema/services/tool_service.py` | Esquemas de requisição/resposta |
| `trustgraph-base/trustgraph/base/tool_service_client.py` | Cliente para invocar serviços |
| `trustgraph-base/trustgraph/base/dynamic_tool_service.py` | Classe base para implementação de serviço |
| `trustgraph-flow/trustgraph/agent/react/tools.py` | Classe `ToolServiceImpl` |
| `trustgraph-flow/trustgraph/agent/react/service.py` | Carregamento de configuração |
### Exemplo: Serviço de Piadas
Um exemplo de serviço em `trustgraph-flow/trustgraph/tool_service/joke/`:
```python
class Processor(DynamicToolService):
async def invoke(self, user, config, arguments):
style = config.get("style", "pun")
topic = arguments.get("topic", "")
joke = pick_joke(topic, style)
return f"Hey {user}! Here's a {style} for you:\n\n{joke}"
```
Configuração do serviço de ferramenta:
```json
{
"id": "joke-service",
"request-queue": "non-persistent://tg/request/joke",
"response-queue": "non-persistent://tg/response/joke",
"config-params": [{"name": "style", "required": false}]
}
```
Configuração da ferramenta:
```json
{
"type": "tool-service",
"name": "tell-joke",
"description": "Tell a joke on a given topic",
"service": "joke-service",
"style": "pun",
"arguments": [
{"name": "topic", "type": "string", "description": "The topic for the joke"}
]
}
```
### Compatibilidade com versões anteriores
Os tipos de ferramentas integradas existentes continuam a funcionar sem alterações.
`tool-service` é um novo tipo de ferramenta, juntamente com os tipos existentes (`knowledge-query`, `mcp-tool`, etc.).
## Considerações futuras
### Serviços de autoanúncio
Uma melhoria futura poderia permitir que os serviços publicassem seus próprios descritores:
Os serviços publicam em um tópico `tool-descriptors` conhecido na inicialização.
O agente se inscreve e registra dinamicamente as ferramentas.
Permite um verdadeiro sistema de plug-and-play sem alterações de configuração.
Isso está fora do escopo da implementação inicial.
## Referências
Implementação atual da ferramenta: `trustgraph-flow/trustgraph/agent/react/tools.py`
Registro de ferramentas: `trustgraph-flow/trustgraph/agent/react/service.py:105-214`
Esquemas do agente: `trustgraph-base/trustgraph/schema/services/agent.py`