forked from i-am-bee/bee-agent-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: initial commit of developer information about tools (i-am-bee#7)
- Loading branch information
1 parent
0eca2c8
commit 2ad1a60
Showing
5 changed files
with
293 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
# Tools | ||
|
||
## Introduction | ||
|
||
Tools in the context of an agent refer to additional functionalities or capabilities integrated with the agent to perform specific tasks beyond text processing. | ||
|
||
These tools extend the agent's abilities, allowing it to interact with external systems, access information, and execute actions. | ||
|
||
## Development | ||
|
||
### Writing a new tool | ||
|
||
To create a tool, the `Tool` class must be extended/ implemented or `DynamicTool` created from [tools base](../src/tools/base.ts). When starting to write tools, it is recommended to implement the `Tool` class. The `DynamicTool` class is an extension of the `Tool` class that allows for dynamic forms of input. Refer to the list of [examples](#examples) for some simple examples or any of the built-in tools in order to guide the creation of new tools. | ||
|
||
Tools MUST do the following: | ||
|
||
- Implement the `Tool` class: | ||
|
||
`MyNewToolOutput` is required, must be an implementation of `ToolOutput` such as `StringToolOutput` or `JSONToolOutput` | ||
|
||
`ToolOptions` is optional (default BaseToolOptions), constructor parameters that are passed during tool creation | ||
|
||
`ToolRunOptions` is optional (default BaseToolRunOptions), optional parameters that are passed to the run method | ||
|
||
```typescript | ||
import { BaseToolOptions, BaseToolRunOptions } from "bee-agent-framework/tools/base"; | ||
|
||
type ToolOptions = BaseToolOptions; | ||
type ToolRunOptions = BaseToolRunOptions; | ||
|
||
export class MyNewTool extends Tool<MyNewToolOutput, ToolOptions, ToolRunOptions> { | ||
// tool implementation | ||
} | ||
``` | ||
|
||
- Be given a unique name: | ||
|
||
Note: Convention and best practice is to set the tool's name to the name of its class | ||
|
||
```typescript | ||
name = "MyNewTool"; | ||
``` | ||
|
||
- Provide a natural language description of what the tool does: | ||
|
||
❗Important: this description is used by the agent to determine when the tool should be used. It's probably the most important aspect of your tool and you should experiment with different natural language descriptions to ensure the tool is used in the correct circumstances. | ||
|
||
```typescript | ||
description = "Takes X action when given Y input resulting in Z output"; | ||
``` | ||
|
||
- Declare an input schema: | ||
|
||
This is used to define the format of the input to your tool. The agent will formalise the natural language input(s) it has received and structure them into the fields described in the tool's input. The input schema can be specified using [Zod](https://github.com/colinhacks/zod) (recommended) or JSONSchema. It must be a function (either sync or async). Zod effects (e.g. `z.object().transform(...)`) are not supported. The return value of `inputSchema` must always be an object and pass validation by the `validateSchema()` function defined in [schema.ts](../src/internals/helpers/schema.ts). | ||
|
||
<!-- eslint-skip --> | ||
|
||
```typescript | ||
inputSchema() { | ||
// any Zod definition is good here, this is typical simple example | ||
return z.object({ | ||
// list of key-value pairs | ||
}); | ||
} | ||
``` | ||
|
||
- Implement initialisation: | ||
|
||
The unnamed static block is executed when your tool is called for the first time. It is used for registering your tool as `serializable` to the agent and any other custom initialisation that may be required. | ||
|
||
<!-- eslint-skip --> | ||
|
||
```typescript | ||
static { | ||
this.register(); | ||
} | ||
``` | ||
|
||
- Implement the `_run()` method: | ||
|
||
<!-- eslint-skip --> | ||
|
||
```typescript | ||
protected async _run(input: ToolInput<this>, options?: BaseToolRunOptions) { | ||
// insert custom code here | ||
// MUST: return an instance of the output type specified in the tool class definition | ||
// MAY: throw an instance of ToolError upon unrecoverable error conditions encountered by the tool | ||
} | ||
``` | ||
|
||
### Using tools with agents | ||
|
||
In order for a tool to be of some utility within an agent, you must enable the agent with knowledge of the tool. To do this, the tool code module must be imported into the agent and passed to the tools array during creation of a `BeeAgent`. An example can be found in the [bee agent](../examples/agents/bee.ts) or you can use a code snippet such as the one below that creates an agent with the built-in [ArXiv tool](../src/tools/arxiv.ts): | ||
|
||
```typescript | ||
import { ArXivTool } from "bee-agent-framework/tools/arxiv"; | ||
|
||
const llm = new OllamaChatLLM({ | ||
modelId: "insert-model-id-here", | ||
}); | ||
|
||
const agent = new BeeAgent({ | ||
llm, | ||
memory: new TokenMemory({ llm }), | ||
tools: [new ArXivTool()], | ||
}); | ||
``` | ||
|
||
## Examples | ||
|
||
### Hello World | ||
|
||
The simplest form of an example tool is the [helloworld](../examples/tools/helloWorld.ts) tool. This example implements the Tool class and will simply prepend the word "Hello" to a given input such as "Please give a special greeting to Bee!". It is not intended for usage with any agents since the functionality provided is highly trivial. However, it may serve as a starter template for writing new tools. | ||
|
||
### Open Library | ||
|
||
The [openlibrary](../examples/tools/openLibrary.ts) tool allows an agent to query the [Open Library](https://openlibrary.org/) via its [book search API](https://openlibrary.org/dev/docs/api/search). This functionality injects knowledge about book metadata (not book content) into an agent. It serves as an example for several key aspects of tool creation: | ||
|
||
- Implementing the `Tool` class | ||
- Specifying the input schema to a tool | ||
- Calling out to a third-party service and handling responses and errors | ||
- Using `JSONToolOutput` to return JSON formatted data to the agent |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { | ||
BaseToolOptions, | ||
BaseToolRunOptions, | ||
StringToolOutput, | ||
Tool, | ||
ToolInput, | ||
} from "@/tools/base.js"; | ||
import { z } from "zod"; | ||
|
||
type ToolOptions = BaseToolOptions; | ||
type ToolRunOptions = BaseToolRunOptions; | ||
|
||
export class HelloWorldTool extends Tool<StringToolOutput, ToolOptions, ToolRunOptions> { | ||
name = "HelloWorld"; | ||
description = "Says hello when asked for a special greeting."; | ||
|
||
inputSchema() { | ||
return z.object({ | ||
identifier: z | ||
.string() | ||
.describe("The identifier (person, object, animal, etc.) used to when saying Hello"), | ||
}); | ||
} | ||
|
||
static { | ||
this.register(); | ||
} | ||
|
||
protected async _run(input: ToolInput<this>): Promise<StringToolOutput> { | ||
return new StringToolOutput(`Hello, ${input.identifier}`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { | ||
BaseToolOptions, | ||
BaseToolRunOptions, | ||
Tool, | ||
ToolInput, | ||
JSONToolOutput, | ||
ToolError, | ||
} from "@/tools/base.js"; | ||
import { z } from "zod"; | ||
import { createURLParams } from "@/internals/fetcher.js"; | ||
|
||
type ToolOptions = BaseToolOptions; | ||
type ToolRunOptions = BaseToolRunOptions; | ||
|
||
export interface OpenLibraryResponse { | ||
numFound: number; | ||
start: number; | ||
numFoundExact: boolean; | ||
num_found: number; | ||
q: string; | ||
offset: number; | ||
docs: { | ||
_version_: number; | ||
key: string; | ||
title: string; | ||
subtitle: string; | ||
alternative_title: string; | ||
alternative_subtitle: string; | ||
cover_i: number; | ||
ebook_access: string; | ||
ebook_count_i: number; | ||
edition_count: number; | ||
edition_key: string[]; | ||
format: string[]; | ||
publish_date: string[]; | ||
lccn: string[]; | ||
ia: string[]; | ||
oclc: string[]; | ||
public_scan_b: boolean; | ||
isbn: string[]; | ||
contributor: string[]; | ||
publish_place: string[]; | ||
publisher: string[]; | ||
seed: string[]; | ||
first_sentence: string[]; | ||
author_key: string[]; | ||
author_name: string[]; | ||
author_alternative_name: string[]; | ||
subject: string[]; | ||
person: string[]; | ||
place: string[]; | ||
time: string[]; | ||
has_fulltext: boolean; | ||
title_suggest: string; | ||
title_sort: string; | ||
type: string; | ||
publish_year: number[]; | ||
language: string[]; | ||
last_modified_i: number; | ||
number_of_pages_median: number; | ||
place_facet: string[]; | ||
publisher_facet: string[]; | ||
author_facet: string[]; | ||
first_publish_year: number; | ||
ratings_count_1: number; | ||
ratings_count_2: number; | ||
ratings_count_3: number; | ||
ratings_count_4: number; | ||
ratings_count_5: number; | ||
ratings_average: number; | ||
ratings_sortable: number; | ||
ratings_count: number; | ||
readinglog_count: number; | ||
want_to_read_count: number; | ||
currently_reading_count: number; | ||
already_read_count: number; | ||
subject_key: string[]; | ||
person_key: string[]; | ||
place_key: string[]; | ||
subject_facet: string[]; | ||
time_key: string[]; | ||
lcc: string[]; | ||
ddc: string[]; | ||
lcc_sort: string; | ||
ddc_sort: string; | ||
}[]; | ||
} | ||
|
||
export class OpenLibraryToolOutput extends JSONToolOutput<OpenLibraryResponse> { | ||
isEmpty(): boolean { | ||
return !this.result || this.result.numFound === 0 || this.result.docs.length === 0; | ||
} | ||
} | ||
|
||
export class OpenLibraryTool extends Tool<OpenLibraryToolOutput, ToolOptions, ToolRunOptions> { | ||
name = "OpenLibrary"; | ||
description = | ||
"Provides access to a library of books with information about book titles, authors, contributors, publication dates, publisher and isbn."; | ||
|
||
inputSchema() { | ||
return z | ||
.object({ | ||
title: z.string(), | ||
author: z.string(), | ||
isbn: z.string(), | ||
subject: z.string(), | ||
place: z.string(), | ||
person: z.string(), | ||
publisher: z.string(), | ||
}) | ||
.partial(); | ||
} | ||
|
||
static { | ||
this.register(); | ||
} | ||
|
||
protected async _run(input: ToolInput<this>, options?: ToolRunOptions) { | ||
const params = createURLParams(input); | ||
const url = `https://openlibrary.org/search.json?${decodeURIComponent(params.toString())}`; | ||
const response = await fetch(url, { | ||
signal: options?.signal, | ||
}); | ||
if (!response.ok) { | ||
throw new ToolError("Request to Open Library API has failed!", [ | ||
new Error(await response.text()), | ||
]); | ||
} | ||
try { | ||
const json = await response.json(); | ||
return new OpenLibraryToolOutput(json); | ||
} catch (e) { | ||
throw new ToolError("Request to Open Library has failed to parse!", [e]); | ||
} | ||
} | ||
} |