trustgraph/docs/tech-specs/tool-services.zh-cn.md
Alex Jenkins 8954fa3ad7 Feat: TrustGraph i18n & Documentation Translation Updates (#781)
Native CLI i18n: The TrustGraph CLI has built-in translation support
that dynamically loads language strings. You can test and use
different languages by simply passing the --lang flag (e.g., --lang
es for Spanish, --lang ru for Russian) or by configuring your
environment's LANG variable.

Automated Docs Translations: This PR introduces autonomously
translated Markdown documentation into several target languages,
including Spanish, Swahili, Portuguese, Turkish, Hindi, Hebrew,
Arabic, Simplified Chinese, and Russian.
2026-04-14 12:08:32 +01:00

14 KiB
Raw Blame History

layout title parent
default 工具服务:动态可插拔的代理工具 Chinese (Beta)

工具服务:动态可插拔的代理工具

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.

状态

已实现

概述

本规范定义了一种称为“工具服务”的动态可插拔代理工具的机制。 与现有的内置工具类型(KnowledgeQueryImplMcpToolImpl等)不同,工具服务允许通过以下方式引入新的工具:

  1. 部署一个新的基于Pulsar的服务
  2. 添加一个配置描述符,该描述符告诉代理如何调用它

这实现了可扩展性,而无需修改核心代理响应框架。

术语

术语 定义
内置工具 具有硬编码实现的现有工具类型,位于tools.py
工具服务 可以作为代理工具调用的Pulsar服务由服务描述符定义
工具 引用工具服务的已配置实例,暴露给代理/LLM

这是一个两层模型类似于MCP工具 MCPMCP服务器定义工具接口 → 工具配置引用它 工具服务工具服务定义Pulsar接口 → 工具配置引用它

背景:现有工具

内置工具实现

当前,工具在trustgraph-flow/trustgraph/agent/react/tools.py中定义,并具有类型化的实现:

class KnowledgeQueryImpl:
    async def invoke(self, question):
        client = self.context("graph-rag-request")
        return await client.rag(question, self.collection)

每种工具类型: 具有一个硬编码的 Pulsar 服务,它会调用该服务(例如:graph-rag-request 知道要调用的客户端上的确切方法(例如:client.rag() 在实现中定义了类型化的参数

工具注册 (service.py:105-214)

工具从配置文件中加载,其中有一个 type 字段,它映射到实现:

if impl_id == "knowledge-query":
    impl = functools.partial(KnowledgeQueryImpl, collection=data.get("collection"))
elif impl_id == "text-completion":
    impl = TextCompletionImpl
# ... etc

架构

两层模型

第一层:工具服务描述器

一个工具服务定义了 Pulsar 服务接口。它声明: 用于请求/响应的 Pulsar 队列 它需要的来自使用它的工具的配置参数

{
  "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}
  ]
}

一种不需要任何配置参数的工具服务:

{
  "id": "calculator",
  "request-queue": "non-persistent://tg/request/calc",
  "response-queue": "non-persistent://tg/response/calc",
  "config-params": []
}

第二层:工具描述

一个工具引用一个工具服务,并提供: 配置参数值(满足服务的要求) 代理的工具元数据(名称、描述) LLM 的参数定义

{
  "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"
    }
  ]
}

多个工具可以使用不同的配置来引用相同的服务:

{
  "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"
    }
  ]
}

请求格式

当一个工具被调用时,发送给工具服务的请求包括: user: 来自代理的请求(多租户) config: 来自工具描述的 JSON 编码的配置值 arguments: 来自 LLM 的 JSON 编码的参数

{
  "user": "alice",
  "config": "{\"collection\": \"customers\"}",
  "arguments": "{\"question\": \"What are the top customer complaints?\"}"
}

工具服务接收这些数据,并将其解析为字典,在 invoke 方法中进行处理。

通用工具服务实现

一个 ToolServiceImpl 类根据配置调用工具服务:

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)

设计决策

双层配置模型

工具服务遵循类似于 MCP 工具的双层模型:

  1. 工具服务 (Tool Service):定义 Pulsar 服务接口(主题、所需的配置参数)。
  2. 工具 (Tool):引用工具服务,提供配置值,定义 LLM 参数。

这种分离方式允许: 一个工具服务可以被多个具有不同配置的工具使用。 清晰区分服务接口和工具配置。 重用服务定义。

请求映射:带外壳的透传

发送到工具服务的请求是一个结构化的外壳,包含: user:从代理请求中传播,用于多租户。 配置值:来自工具描述符(例如,collection)。 argumentsLLM 提供的参数,作为字典传递。

代理管理器将 LLM 的响应解析为 act.arguments(作为字典,agent_manager.py:117-154)。此字典包含在请求外壳中。

模式处理:无类型

请求和响应使用无类型的字典。代理级别不进行任何模式验证 - 工具服务负责验证其输入。这提供了定义新服务的最大灵活性。

客户端接口:直接 Pulsar 主题

工具服务使用直接的 Pulsar 主题,无需配置流。工具服务描述符指定完整的队列名称:

{
  "id": "joke-service",
  "request-queue": "non-persistent://tg/request/joke",
  "response-queue": "non-persistent://tg/response/joke",
  "config-params": [...]
}

这允许服务在任何命名空间中被托管。

错误处理:标准错误约定

工具服务响应遵循现有的模式约定,包含一个 error 字段:

@dataclass
class Error:
    type: str = ""
    message: str = ""

响应结构: 成功: errorNone,响应包含结果 错误: error 被填充为 typemessage

这符合现有服务模式(例如:PromptResponseQueryResponseAgentResponse)。

请求/响应关联

请求和响应使用 id 在 Pulsar 消息属性中进行关联:

请求包含 id 在属性中:properties={"id": id} 响应包含相同的 idproperties={"id": id}

这遵循代码库中使用的现有模式(例如:agent_service.pyllm_service.py)。

流式支持

工具服务可以返回流式响应:

具有相同 id 在属性中的多个响应消息 每个响应包含 end_of_stream: bool 字段 最终响应具有 end_of_stream: True

这符合 AgentResponse 和其他流式服务中使用的模式。

响应处理:字符串返回

所有现有工具都遵循相同的模式:接收参数作为字典,将观察结果作为字符串返回

工具 响应处理
KnowledgeQueryImpl 直接返回 client.rag() (字符串)
TextCompletionImpl 直接返回 client.question() (字符串)
McpToolImpl 返回字符串,或如果不是字符串则返回 json.dumps(output)
StructuredQueryImpl 将结果格式化为字符串
PromptImpl 直接返回 client.prompt() (字符串)

工具服务遵循相同的约定: 服务返回一个字符串响应(观察结果) 如果响应不是字符串,则通过 json.dumps() 进行转换 描述符中不需要提取配置

这使描述符保持简单,并将责任放在服务上,使其为代理返回适当的文本响应。

配置指南

要添加一个新的工具服务,需要两个配置项:

1. 工具服务配置

存储在 tool-service 配置键下。定义了 Pulsar 队列和可用的配置参数。

字段 必需 描述
id 工具服务的唯一标识符
request-queue 用于请求的完整 Pulsar 主题(例如:non-persistent://tg/request/joke
response-queue 用于响应的完整 Pulsar 主题(例如:non-persistent://tg/response/joke
config-params 服务接受的配置参数数组

每个配置参数可以指定: name: 参数名称 (必需) required: 是否需要工具提供该参数 (默认: 否)

示例:

{
  "id": "joke-service",
  "request-queue": "non-persistent://tg/request/joke",
  "response-queue": "non-persistent://tg/response/joke",
  "config-params": [
    {"name": "style", "required": false}
  ]
}

2. 工具配置

存储在 tool 配置键下。定义了代理可以使用的工具。

字段 必需 描述
type 必须是 "tool-service"
name 暴露给 LLM 的工具名称
description 描述工具的功能(显示给 LLM
service 调用工具服务的 ID
arguments LLM 的参数定义数组
(配置参数) 变化 服务定义的任何配置参数

每个参数可以指定: name:参数名称(必需) type:数据类型,例如 "string"(必需) description:显示给 LLM 的描述(必需)

示例:

{
  "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)"
    }
  ]
}

加载配置

使用 tg-put-config-item 加载配置:

# 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

必须重启代理服务器才能加载新的配置。

实现细节

模式

trustgraph-base/trustgraph/schema/services/tool_service.py 中的请求和响应类型:

@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

服务器端DynamicToolService

基类位于 trustgraph-base/trustgraph/base/dynamic_tool_service.py

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()

客户端ToolServiceImpl

trustgraph-flow/trustgraph/agent/react/tools.py 中的实现:

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)

文件

文件 目的
trustgraph-base/trustgraph/schema/services/tool_service.py 请求/响应模式
trustgraph-base/trustgraph/base/tool_service_client.py 调用服务的客户端
trustgraph-base/trustgraph/base/dynamic_tool_service.py 服务实现的基类
trustgraph-flow/trustgraph/agent/react/tools.py ToolServiceImpl
trustgraph-flow/trustgraph/agent/react/service.py 配置加载

示例:笑话服务

一个用 trustgraph-flow/trustgraph/tool_service/joke/ 编写的示例服务:

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}"

工具服务配置:

{
  "id": "joke-service",
  "request-queue": "non-persistent://tg/request/joke",
  "response-queue": "non-persistent://tg/response/joke",
  "config-params": [{"name": "style", "required": false}]
}

工具配置:

{
  "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"}
  ]
}

向后兼容性

现有的内置工具类型继续以不变的方式工作。 tool-service 是一种新的工具类型,与现有类型(knowledge-querymcp-tool等)并存。

未来考虑事项

自定义服务

未来的增强功能可能允许服务发布自己的描述:

服务在启动时发布到已知的 tool-descriptors 主题。 代理订阅并动态注册工具。 实现了真正的即插即用,无需配置更改。

这超出了初始实现的范围。

参考文献

当前工具实现:trustgraph-flow/trustgraph/agent/react/tools.py 工具注册:trustgraph-flow/trustgraph/agent/react/service.py:105-214 代理模式:trustgraph-base/trustgraph/schema/services/agent.py