126 lines
3.8 KiB
TypeScript
126 lines
3.8 KiB
TypeScript
/**
|
|
* Example 16: Advanced Tool Chaining
|
|
*
|
|
* This example demonstrates sophisticated tool chaining and data flow in worker mode.
|
|
* It shows how to:
|
|
* - Chain multiple tools with complex data dependencies
|
|
* - Pass data between tools with type-safe schemas
|
|
* - Perform data transformations and filtering between tool calls
|
|
* - Execute complex multi-step workflows in a single turn
|
|
* - Handle nested object structures and array operations
|
|
*
|
|
* Key concepts:
|
|
* - Multi-tool orchestration in single execution
|
|
* - Complex data flow between tools
|
|
* - Type-safe data extraction and transformation
|
|
* - Single-turn complex workflows
|
|
* - Demonstration of LLMz's superiority over traditional tool calling
|
|
*/
|
|
|
|
import { Client } from '@botpress/client'
|
|
import { z } from '@bpinternal/zui'
|
|
import chalk from 'chalk'
|
|
import { execute, Exit, Tool } from 'llmz'
|
|
import { box } from '../utils/box'
|
|
|
|
// Initialize Botpress client
|
|
const client = new Client({
|
|
botId: process.env.BOTPRESS_BOT_ID!,
|
|
token: process.env.BOTPRESS_TOKEN!,
|
|
})
|
|
|
|
// This example demonstrates the power of LLMz's code generation approach:
|
|
// Tool C requires:
|
|
// 1. A deep nested number from Tool A
|
|
// 2. Filtered array data from Tool B (only numbers > 50)
|
|
// All of this complex orchestration happens in a SINGLE LLM turn
|
|
// Traditional JSON tool calling would require multiple expensive roundtrips
|
|
|
|
// Tool A: Generates a random number in a deeply nested structure
|
|
// This demonstrates how LLMz handles complex object schemas
|
|
const ToolA = new Tool({
|
|
name: 'tool_a',
|
|
output: z.object({
|
|
pick: z.object({
|
|
deep: z.object({
|
|
deep_number: z.number(),
|
|
}),
|
|
}),
|
|
}),
|
|
async handler() {
|
|
const deep_number = Math.floor(Math.random() * 100)
|
|
console.log('Tool A executed, returning number:', deep_number)
|
|
return {
|
|
pick: {
|
|
deep: {
|
|
deep_number,
|
|
},
|
|
},
|
|
}
|
|
},
|
|
})
|
|
|
|
// Tool B: Generates an array of random numbers
|
|
// The LLM will need to filter this array for Tool C
|
|
const ToolB = new Tool({
|
|
name: 'tool_b',
|
|
output: z.number().array(),
|
|
async handler() {
|
|
const array = Array.from({ length: 10 }, () => Math.floor(Math.random() * 100))
|
|
console.log('Tool B executed, returning array:', array)
|
|
return array
|
|
},
|
|
})
|
|
|
|
// Tool C: Consumes processed data from both Tool A and Tool B
|
|
// This demonstrates complex data dependencies and processing
|
|
const ToolC = new Tool({
|
|
name: 'tool_c',
|
|
input: z.object({
|
|
first_task: z.number().describe('Number from tool A'),
|
|
second_task: z.number().array().describe('Numbers from tool B that are greater than 50'),
|
|
}),
|
|
output: z.number().describe("The 'secret' number"),
|
|
async handler({ first_task, second_task }) {
|
|
console.log('Tool C executed with input:', { first_task, second_task })
|
|
|
|
// Compute the final "secret" number by combining the inputs
|
|
return first_task + second_task.reduce((acc, num) => acc + num, 0)
|
|
},
|
|
})
|
|
|
|
// Exit condition to capture the final result
|
|
const exit = new Exit({
|
|
name: 'exit',
|
|
description: 'Exit the program',
|
|
schema: z.object({
|
|
result: z.number(),
|
|
}),
|
|
})
|
|
|
|
// Execute the complex workflow
|
|
// The LLM will generate code that:
|
|
// 1. Calls Tool A and extracts the deep nested number
|
|
// 2. Calls Tool B and filters the array for numbers > 50
|
|
// 3. Calls Tool C with the processed data
|
|
// 4. Returns the final result through the exit
|
|
const result = await execute({
|
|
instructions: "I need the 'secret' number please. Do not think, try to do it in one step.",
|
|
tools: [ToolA, ToolB, ToolC],
|
|
exits: [exit],
|
|
client,
|
|
})
|
|
|
|
// Display the results showing both the generated code and final output
|
|
if (result.is(exit)) {
|
|
console.log(
|
|
box([
|
|
'The LLM wrote the code to solve the problem:',
|
|
...result.iteration.code!.split('\n'),
|
|
'',
|
|
'It then executed it and returned the result:',
|
|
chalk.cyan.bold(result.output.result.toString()),
|
|
])
|
|
)
|
|
}
|