Skip to main content

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.

FieldTypeRequiredDescription
idintegerAutoPrimary key
codestringYesUnique identifier (max 32 chars, lowercase alphanumeric + dashes)
namestringYesDisplay name (max 200 chars)
created_atdatetimeAutoCreation timestamp
updated_atdatetimeAutoLast 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:

  1. Adds the retrieve_decision_tree tool to the Assistant's available tools
  2. Injects decision tree instructions into the system prompt
  3. 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"
}
note

The code field cannot be updated after creation.

Delete Decision Tree

DELETE /api/v1/knowledge-assist/decision-trees/{code}/
warning

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

ComponentLocation
DecisionTree modelknowledge_assist/models.py:135-144
RetrieveDecisionTreeToolassistants/tools/retrieve_decision_tree.py
Decision tree instructionsbackend/domains/assistants/models/instructions.py:48-91
DecisionTreeViewSetknowledge_assist/views.py:210-224
DecisionTreeSerializerknowledge_assist/serializers.py:127-131
Tool registryassistants/tools/tool_registry.py
Admin interfaceknowledge_assist/admin.py:116-126