Chatbot Workflow Cookbook¶
Split bot into multiple smaller bots¶
For complex bots it may be the case that a single LLM node with a large prompt does not perform well. For example, a bot that is expected to perform multiple different functions such as Role Play, Quiz, Q&A.
In such cases, it can be better to create smaller, narrowly focused prompts and use a router to select which 'mode' the bot is currently in.
Here is a more complex example that uses a LLM Router to route the input to one of three linked nodes.
graph TB
A@{ shape: stadium, label: "Input" } --> Router("`**LLM Router**
Route to one of the linked nodes using an LLM`");
Router -->|GENERAL| Gen(LLM);
Router -->|ROLEPLAY| Rp(LLM);
Router -->|QUIZ| Qz(LLM);
Gen --> C@{ shape: stadium, label: "Output" };
Qz --> C
Rp --> C;
Safety check in parallel¶
In this example, we are using a Router to determine if the user input complies with the usage policy of the bot. The router has two outputs, safe and unsafe. The safe output is not connected to any other nodes but the unsafe output is connected to a Python Node which will abort the pipeline with an error message.
flowchart TD
start["start"] --> Safety["SafetyRouter"] & LLM
Safety -. safe .-> Dangle:::hidden
Safety -. unsafe .-> PythonNode["PythonNode
*abort_with_message('...')*"]
LLM --> __end__(["<p>end</p>"])
start:::first
__end__:::last
If the Safety Router routes to the Python Node, the user will not see the output generated by the LLM node but will instead see a message generated by an LLM based on the message passed to the abort_with_message function.
Router for classification¶
Router nodes can have unconnected outputs as seen above, enabling more flexible routing patterns where not all paths need to be explicitly handled. It is also OK to connect multiple router outputs to the same input of another node. This can be useful if you want to the router node to categorize the input but not actually affect the execution flow.
flowchart TD
start["start"] --> Router[RouterA]
Router -. categoryA .-> PythonNode
Router -. categoryB .-> PythonNode
PythonNode --> LLM
LLM --> __end__(["<p>end</p>"])
start:::first
__end__:::last
You might use this to perform some logic in the PythonNode:
def main(input, **kwargs):
route = get_selected_route("RouterA")
if route == "categoryA":
set_temp_state_key("question", "A")
elif route == "categoryB":
set_temp_state_key("question", "B")
return input
Then in the LLM node prompt you could use the temp_state to inject the category:
Reading user uploaded files¶
This workflow allows users (participants) to upload files that your chatbot can process and analyze. Supported file types are listed here.
Setup Steps¶
- Enable file uploads: In your chatbot settings, enable the "File uploads enabled" option
- Create a Python Node: Use a Python node to read and process the uploaded file contents from the temporary state - specifically from the attachments key.
- Pass to LLM: Either return the user input along with the file contents directly to the LLM node, or save the file contents to the temporary state and inject them into your LLM prompt
Workflow Structure¶
flowchart TD
start["start"] --> PythonNode
PythonNode --> LLM
LLM --> __end__(["<p>end</p>"])
start:::first
__end__:::last
Python Node Implementation:
Option 1: Single File Processing Process only the first uploaded file:
def main(input: str, **kwargs) -> str:
# Get uploaded files from temp state
attachments = get_temp_state_key("attachments")
if not attachments:
return input
# Read the first file's content
file_content = attachments[0].read_text()
set_temp_state_key("file_contents", file_content)
return input
Option 2: Multiple Files Processing Process all uploaded files:
def main(input: str, **kwargs) -> str:
# Get uploaded files from temp state
attachments = get_temp_state_key("attachments")
if not attachments:
return input
# Read all files and combine their contents
all_file_contents = []
for i, attachment in enumerate(attachments):
file_content = attachment.read_text()
filename = attachment.name if hasattr(attachment, 'name') else f"File {i+1}"
all_file_contents.append(f"## {filename}\n{file_content}")
# Save combined contents to temp state
combined_contents = "\n\n".join(all_file_contents)
set_temp_state_key("file_contents", combined_contents)
return input
In these examples, the Python node reads the uploaded file(s) and saves their contents to the temp state under the key "file_contents". The user's original input is passed through unchanged to the LLM node.
LLM Node Configuration:
Configure your LLM node to utilize the uploaded file contents by injecting them into the prompt using temp state variables.
Basic Prompt Template:
You are a helpful assistant. Answer the user's query as best you can.
Here are some file contents that you should consider when generating your answer:
## File Contents
{temp_state.file_contents}
User Query: {input}
Instructions:
- If the file contents are empty or not provided, inform the user that no files were uploaded
- Base your response on both the file contents and the user's query
- Be specific about what you found in the uploaded files
- If you cannot find relevant information in the files, clearly state this
Making external API calls¶
This workflow demonstrates how to integrate external APIs into your chatbot using the HTTP Client. The HTTP client allows your bot to fetch data from external services, submit information, or interact with third-party APIs securely.
Prerequisites¶
- Enable network access: Network access must be enabled for your pipeline (contact your team administrator if needed)
- Set up Authentication Provider (if required): If the API requires authentication, configure an Authentication Provider in your team settings
Workflow Structure¶
flowchart TD
start["start"] --> PythonNode["Python Node
*Make API call using http_client*"]
PythonNode --> LLM["LLM Node
*Process API response*"]
LLM --> __end__(["<p>end</p>"])
start:::first
__end__:::last
Example 1: Fetch External Data¶
This example fetches weather information from an external API and passes it to the LLM for processing:
def main(input, **kwargs) -> str:
"""Fetch weather data and prepare it for the LLM"""
try:
# Make GET request to weather API
response = http.get(
"https://api.weather.gov/gridpoints/TOP/31,80/forecast",
timeout=10
)
if response["status_code"] == 200:
data = response["json"]
forecast = data["properties"]["periods"][0]
# Format the data for the LLM
weather_info = f"""
Weather Forecast:
- Condition: {forecast['shortForecast']}
- Temperature: {forecast['temperature']}°F
- Wind: {forecast['windSpeed']} {forecast['windDirection']}
- Detailed Forecast: {forecast['detailedForecast']}
"""
# Store in temp state for LLM prompt access
set_temp_state_key("weather_data", weather_info)
return input
else:
set_temp_state_key("weather_data", "Weather data unavailable")
return input
except Exception as e:
set_temp_state_key("weather_data", f"Error: {str(e)}")
return input
LLM Prompt Configuration:
You are a helpful weather assistant. Use the weather data provided to answer the user's question.
{temp_state.weather_data}
User Question: {input}
Instructions:
- Provide a clear, conversational response based on the weather data
- If weather data is unavailable, inform the user politely
Example 2: Submit Data to External API¶
This example takes user input, submits it to an external API, and returns the result:
def main(input, **kwargs) -> str:
"""Submit user feedback to external service"""
try:
# Prepare data from user input
feedback_data = {
"message": input,
"timestamp": "2024-01-01T12:00:00Z",
"user_id": get_participant_data().get("identifier", "unknown")
}
# POST to API with authentication
response = http.post(
"https://api.example.com/feedback",
json=feedback_data,
auth="feedback-api-key", # Reference to Auth Provider
timeout=15
)
if response["status_code"] == 201:
result = response["json"]
return f"Thank you for your feedback! Reference ID: {result['id']}"
else:
return "We encountered an issue submitting your feedback. Please try again later."
except Exception as e:
return f"Sorry, we couldn't process your feedback at this time: {str(e)}"
Example 3: Enriching User Data¶
This workflow fetches additional information based on user input and enriches the conversation context:
def main(input, **kwargs) -> str:
"""Look up product information from external catalog"""
try:
# Extract product ID from user input (simplified example)
product_id = input.strip()
# Query external product API
response = http.get(
f"https://api.example.com/products/{product_id}",
auth="product-api",
timeout=10
)
if response["status_code"] == 200:
product = response["json"]
# Store product details in temp state
set_temp_state_key("product_name", product["name"])
set_temp_state_key("product_price", product["price"])
set_temp_state_key("product_description", product["description"])
set_temp_state_key("product_available", product["in_stock"])
return input
elif response["status_code"] == 404:
set_temp_state_key("product_error", "Product not found")
return input
else:
set_temp_state_key("product_error", "Unable to fetch product details")
return input
except Exception as e:
set_temp_state_key("product_error", str(e))
return input
LLM Prompt Configuration:
You are a product support assistant.
{% if temp_state.product_error %}
Product Information: Not available ({{ temp_state.product_error }})
{% else %}
Product Information:
- Name: {{ temp_state.product_name }}
- Price: ${{ temp_state.product_price }}
- Description: {{ temp_state.product_description }}
- Availability: {{ "In Stock" if temp_state.product_available else "Out of Stock" }}
{% endif %}
User Query: {input}
Instructions:
- Help the user with their product inquiry
- Use the product information provided above
- If product information is not available, help the user find what they need
Best Practices for API Integration¶
- Always handle errors: Wrap API calls in try-except blocks to gracefully handle failures
- Set appropriate timeouts: Prevent requests from hanging indefinitely
- Use Authentication Providers: Never hardcode API keys in your Python code
- Validate user input: Sanitize any user input used in API calls
- Store results in temp state: Use
set_temp_state_key()to make API data available to LLM prompts - Check status codes: Always verify the response status before processing data
- Provide user feedback: Return meaningful messages when API calls fail
See the HTTP Client documentation for more details on available methods, security features, and advanced usage.