Developer Guide
This guide covers the technical implementation of Decision Trees, including data models, the retrieval tool, and API reference.
Architecture Overview
Decision Trees use semantic search to retrieve structured guidance based on agent questions and conversation context:
Data Model
DecisionTree
Stores decision tree configuration and references to indexed content.
| Field | Type | Required | Description |
|---|---|---|---|
id | integer | Auto | Primary key |
code | string | Yes | Unique identifier (max 32 chars, lowercase alphanumeric + dashes) |
name | string | Yes | Display name (max 200 chars) |
created_at | datetime | Auto | Creation timestamp |
updated_at | datetime | Auto | Last update timestamp |
Django Model Definition:
class DecisionTree(TimestampedModel):
code = models.CharField(max_length=32, unique=True, validators=[KnowledgeBaseCodeValidator()])
name = models.CharField(max_length=200)
def __str__(self) -> str:
return cast(str, self.code)
class Meta:
verbose_name = "Decision Tree"
verbose_name_plural = "Decision Trees"
Assistant Integration
Assistants can be connected to a decision tree through a foreign key relationship:
class Assistant(TimestampedModel):
# ... other fields ...
decision_tree = models.ForeignKey(
"knowledge_assist.DecisionTree",
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name="assistants",
help_text="When configured, the assistant will use this decision tree for structured guidance.",
)
RetrieveDecisionTreeTool
The RetrieveDecisionTreeTool is an LLM tool that retrieves structured guidance from a decision tree.
Tool Schema
{
"name": "retrieve_decision_tree",
"description": "Retrieve decision tree guidance to help the agent through structured decision-making processes. This tool searches for relevant decision trees and provides step-by-step guidance. Use this when you need structured guidance for specific situations or procedures.",
"parameters": {
"type": "object",
"properties": {
"decision_tree_code": {
"type": "string",
"description": "The decision tree code to call"
},
"user_input": {
"type": "string",
"description": "The user's question or topic"
},
"headers": {
"type": "object",
"description": "Optional additional headers"
},
"skip_evaluation": {
"type": "boolean",
"description": "Whether to skip evaluation after the tool is executed. Default is False. Do not set this to True unless explicitly instructed."
}
},
"required": [
"decision_tree_code",
"user_input"
]
}
}
Implementation
class RetrieveDecisionTreeTool(LLMTool[TextToolResponse]):
tool_name = "retrieve_decision_tree"
def __init__(self, assistant: Assistant, conversation: Conversation | None = None):
self._assistant = assistant
self._conversation = conversation
async def __call__(
self,
decision_tree_code: str,
user_input: str,
headers: dict | None = None,
skip_evaluation: bool = False,
) -> TextToolResponse:
"""
Call decision tree knowledge search.
:param decision_tree_code: The decision tree code to call
:param user_input: The user's question or topic
:param headers: Optional additional headers
:param skip_evaluation: Whether to skip evaluation after execution
"""
# Verify decision tree exists
if not await DecisionTree.objects.filter(code=decision_tree_code).aexists():
return TextToolResponse(
text=f"Decision tree with code '{decision_tree_code}' does not exist",
skip_evaluation=skip_evaluation,
)
# Extract conversation history
conversation_history: list[tuple[str, str]] | None = None
if self._conversation and self._conversation.messages:
conversation_history = [
(msg.source or msg.type or "unknown", " ".join(msg.text.split()))
for msg in self._conversation.messages
]
# Build payload
payload: dict[str, Any] = {
"user_input": user_input,
}
if conversation_history:
payload["conversation_history"] = conversation_history
if self._conversation:
payload["conversation_id"] = self._conversation.external_id
# Call decision tree API
response = await CallURLTool(skip_url_check=True)(
url=f"{settings.KNOWLEDGE_ASSIST_BASE_URL}/api/decision-tree-assistants/{decision_tree_code}/search",
method="POST",
payload=payload,
headers={"Content-Type": "application/json", **(headers or {})},
skip_evaluation=skip_evaluation,
)
return TextToolResponse(
text=response.text,
skip_evaluation=skip_evaluation,
)
Conversation History Format
The tool converts conversation messages to a list of tuples:
conversation_history = [
("visitor", "Hello, I need help"),
("agent", "Sure, what can I help with?"),
("visitor", "I have a question about my order"),
]
This format provides:
- Source/Role: Who sent the message (visitor, agent, assistant, etc.)
- Content: The message text, normalized (whitespace collapsed)
Response Format
The tool returns a TextToolResponse:
class TextToolResponse(ToolResponse):
text: str
skip_evaluation: bool
The text contains structured guidance from the decision tree system, including:
- Current position in the decision tree
- Next steps to take
- Questions to ask
- Reasoning and explanation
Automatic Tool Injection
When an Assistant has a decision_tree configured, the system automatically:
- Adds the
retrieve_decision_treetool to the Assistant's available tools - Injects decision tree instructions into the system prompt
- Enables conversation-aware guidance based on the decision tree
Injected Instructions:
def get_decision_tree_instructions(decision_tree_code: str, include_conversation: bool = False) -> str:
base_message = (
DECISION_TREE_SYSTEM_MESSAGE_WITH_CONVERSATION if include_conversation
else DECISION_TREE_SYSTEM_MESSAGE
)
retrieval_instructions = (
f"\n\nTo get guidance from the decision trees, use the retrieve_decision_tree tool. "
f"The tool requires the knowledge_base_code parameter (use '{decision_tree_code}') "
f"and a user_input parameter based on the agent's question or topic. "
f"The decision tree system will retrieve relevant decision trees and guide you through them step by step."
)
return base_message + retrieval_instructions
Base System Message (without conversation):
You are a knowledge assistant helping a helpdesk agent. The helpdesk agent is in conversation with a customer.
You are given decision trees and a question or topic.
Try to find the right decision tree for the question or topic.
Try to follow it and see what the agent needs to do.
If you need information to follow the decision tree(s), ask for it.
Do not make up information yourself. Only follow steps from the decision trees if you are certain of the answer,
otherwise ask the agent a question. Think out loud. Show which steps you are taking and why.
Ask one question at a time. Ask questions as simple, numbered multiple-choice questions as much as possible.
For example:
Is the customer verified?
1. Yes
2. No
3. Partially
Base System Message (with conversation):
You are a knowledge assistant helping a helpdesk agent. The helpdesk agent is in conversation with a customer.
You are given decision trees and a question or topic.
You also receive the conversation history between the agent and the customer.
Try to find the right decision tree for the question or topic.
Try to follow it and see what the agent needs to do.
If there is information in the conversation history that can be used to follow the steps in the decision tree,
use it.
If you need information to follow the decision tree(s), ask for it.
Only ask for confirmation if you are not sure.
Do not make up information yourself. Only follow steps from the decision trees if you are certain of the answer,
otherwise ask the agent a question. Think out loud. Show which steps you are taking and why.
Ask one question at a time. Ask questions as simple, numbered multiple-choice questions as much as possible.
For example:
Is the customer verified?
1. Yes
2. No
3. Partially
Elasticsearch Index Structure
Decision tree content is stored in Elasticsearch with the following index naming convention:
{ACCOUNT_CODE}-{decision_tree_code}-decision-tree-vectors
Each entry in the index contains decision tree content vectorized for semantic search.
API Reference
Decision Trees
List Decision Trees
GET /api/v1/knowledge-assist/decision-trees/
Response:
[
{
"id": 1,
"code": "customer-verification",
"name": "Customer Verification Procedures"
},
{
"id": 2,
"code": "troubleshooting",
"name": "Technical Troubleshooting"
}
]
Create Decision Tree
POST /api/v1/knowledge-assist/decision-trees/
Request Body:
{
"code": "returns-refunds",
"name": "Returns and Refunds Procedures"
}
Response:
{
"id": 3,
"code": "returns-refunds",
"name": "Returns and Refunds Procedures"
}
Get Decision Tree
GET /api/v1/knowledge-assist/decision-trees/{code}/
Response:
{
"id": 1,
"code": "customer-verification",
"name": "Customer Verification Procedures"
}
Update Decision Tree
PUT /api/v1/knowledge-assist/decision-trees/{code}/
Request Body:
{
"name": "Updated Customer Verification Procedures"
}
The code field cannot be updated after creation.
Delete Decision Tree
DELETE /api/v1/knowledge-assist/decision-trees/{code}/
Deleting a decision tree also deletes the associated Elasticsearch index.
Decision Tree Search (External API)
The decision tree search endpoint is called by the RetrieveDecisionTreeTool and is hosted in the Knowledge Assist service:
POST {KNOWLEDGE_ASSIST_BASE_URL}/api/decision-tree-assistants/{code}/search
Request Body:
{
"user_input": "How do I verify a customer?",
"conversation_history": [
["visitor", "I need help with my order"],
["agent", "I can help you with that"]
],
"conversation_id": "conv-123"
}
Response:
{
"text": "I'm following the Customer Verification decision tree.\n\nStep 1: Ask for the customer's account number or email address.\n\nWhat information did the customer provide?\n1. Account number\n2. Email address\n3. Neither"
}
Tool Registry Integration
The tool is registered in the ToolRegistry and automatically included when an Assistant has a decision tree configured:
class ToolRegistry:
def __init__(self, ...):
self._tools = {
# ... other tools ...
RetrieveDecisionTreeTool.name(): lambda: self._create_retrieve_decision_tree_tool(
assistant=assistant, conversation=conversation
),
}
def _create_retrieve_decision_tree_tool(
self, assistant: Assistant, conversation: Conversation | None
) -> RetrieveDecisionTreeTool:
return RetrieveDecisionTreeTool(
assistant=assistant,
conversation=conversation,
)
Background Tasks
delete_elastic_index
When a decision tree is deleted, the associated Elasticsearch index is cleaned up:
@app.task()
def delete_elastic_index(index_name: str) -> None:
elastic = Elasticsearch(settings.ELASTIC_URL, ...)
elastic.indices.delete(index=index_name, ignore_unavailable=True)
The index name for decision trees follows the pattern:
index_name = f"{settings.ACCOUNT_CODE}-{decision_tree.code}-decision-tree-vectors"
Error Handling
Non-existent Decision Tree
If a decision tree code is used that doesn't exist:
return TextToolResponse(
text=f"Decision tree with code '{decision_tree_code}' does not exist",
skip_evaluation=skip_evaluation,
)
API Errors
The CallURLTool handles HTTP errors and connection issues. Errors are propagated back to the LLM as tool execution failures.
Testing
Unit Tests
Test the tool with various scenarios:
class ToolRetrieveDecisionTreeTests(TestCase):
async def test_retrieve_decision_tree_success(self):
# Given a conversation with messages
conversation = Conversation(
external_id="conv-123",
messages=[
ConversationMessage(source="visitor", text="Hello, I need help"),
ConversationMessage(source="agent", text="Sure, what can I help with?"),
],
)
# Given a DecisionTree database record
decision_tree = await DecisionTree.objects.acreate(
code="customer-verification",
name="Customer Verification Trees",
)
# Given a tool instance
retrieve_decision_tree = RetrieveDecisionTreeTool(
assistant=assistant,
conversation=conversation,
)
# When function is called
result = await retrieve_decision_tree(
decision_tree_code=decision_tree.code,
user_input="How do I verify a customer?",
)
# Then response contains guidance
self.assertIn("verify", str(result).lower())
Source Code References
| Component | Location |
|---|---|
| DecisionTree model | knowledge_assist/models.py:135-144 |
| RetrieveDecisionTreeTool | assistants/tools/retrieve_decision_tree.py |
| Decision tree instructions | backend/domains/assistants/models/instructions.py:48-91 |
| DecisionTreeViewSet | knowledge_assist/views.py:210-224 |
| DecisionTreeSerializer | knowledge_assist/serializers.py:127-131 |
| Tool registry | assistants/tools/tool_registry.py |
| Admin interface | knowledge_assist/admin.py:116-126 |
Related Documentation
- Overview: Conceptual overview
- User Guide: Configuration instructions
- Knowledge Assist Developer Guide: Similar retrieval system
- Assistants Developer Guide: Assistant evaluation flow