Skip to content

Commit a0389b1

Browse files
authored
Parlant conversational agent
Parlant conversational agent
2 parents d1a5a2e + 960e86b commit a0389b1

File tree

8 files changed

+3737
-0
lines changed

8 files changed

+3737
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
OPENAI_API_KEY=<YOUR_OPENAI_API_KEY>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
__pycache__/
2+
*.py[oc]
3+
build/
4+
dist/
5+
wheels/
6+
*.egg-info
7+
8+
.env
9+
.venv
10+
11+
parlant-data/
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.12
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Loan Approval Conversational Agent with Parlant
2+
3+
A compliance-driven conversational AI agent built with [Parlant](https://github.com/emcie-co/parlant) that guides customers through a structured loan approval process.
4+
5+
## Overview
6+
7+
This project demonstrates a financial services chatbot that helps customers navigate the loan application process. The agent uses a state-based journey to guide users through eligibility checks, document collection, and approval workflows while maintaining compliance with financial service standards using deterministic and rule-based behavioral patterns.
8+
9+
## Installation
10+
11+
1. **Prerequisites**:
12+
- Python 3.12 +
13+
14+
2. **Install dependencies:**
15+
First, install `uv` and set up the environment:
16+
```bash
17+
# MacOS/Linux
18+
curl -LsSf https://astral.sh/uv/install.sh | sh
19+
20+
# Windows
21+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
22+
```
23+
24+
Install dependencies:
25+
```bash
26+
# Create a new directory for our project
27+
uv init research-assistant
28+
cd research-assistant
29+
30+
# Create virtual environment and activate it
31+
uv venv
32+
source .venv/bin/activate # MacOS/Linux
33+
34+
.venv\Scripts\activate # Windows
35+
36+
# Install dependencies
37+
uv sync
38+
```
39+
40+
3. Set up environment variables:
41+
```bash
42+
# Create a .env file with your configuration
43+
cp .env.example .env
44+
```
45+
46+
## Usage
47+
48+
Run the main application:
49+
```bash
50+
uv run loan_approval.py
51+
```
52+
53+
This will start the Parlant server locally on port 8800 with the loan approval agent configured and ready to handle customer interactions.
54+
55+
![](parlant-chat.png)
56+
57+
## Loan Approval Flow
58+
59+
The agent follows a structured conversational journey for processing loan applications:
60+
61+
```mermaid
62+
stateDiagram-v2
63+
N0: Determine the type of loan user is interested in
64+
N1: Ask them to provide income and loan related details
65+
N2: Use the tool check_eligibility
66+
N3: Inform them that they are not qualified for the loan and ask them if they are interested in other types of loans
67+
N4: Ask them to provide their tax returns and recent pay stubs
68+
N5: Use the tool process_documents
69+
N6: Ask them to use our Online Portal to submit their documents, or contact a Loan Specialist at our Customer Care Phone Number for assistance
70+
N7: Inform them that their application has been approved and a Loan Specialist will review their information and contact them shortly
71+
[*] --> N0
72+
N0 --> N1: The customer specified the type of loan
73+
N1 --> N2
74+
N2 --> N3: The customer is not eligible for the loan
75+
N2 --> N4: The customer is eligible for the loan
76+
N4 --> N5
77+
N5 --> N6: The documents are either invalid, missing or not uploaded correctly
78+
N5 --> N7: Documents are successfully uploaded
79+
N7 --> [*]
80+
N6 --> [*]
81+
N3 --> [*]
82+
style N0 fill:#006e53,stroke:#ffffff,stroke-width:2px,color:#ffffff
83+
style N1 fill:#006e53,stroke:#ffffff,stroke-width:2px,color:#ffffff
84+
style N2 fill:#ffeeaa,stroke:#ffeeaa,stroke-width:2px,color:#dd6600
85+
style N3 fill:#006e53,stroke:#ffffff,stroke-width:2px,color:#ffffff
86+
style N4 fill:#006e53,stroke:#ffffff,stroke-width:2px,color:#ffffff
87+
style N5 fill:#ffeeaa,stroke:#ffeeaa,stroke-width:2px,color:#dd6600
88+
style N6 fill:#006e53,stroke:#ffffff,stroke-width:2px,color:#ffffff
89+
style N7 fill:#006e53,stroke:#ffffff,stroke-width:2px,color:#ffffff
90+
```
91+
92+
## Key Components
93+
94+
### Tools
95+
- **`check_eligibility`**: Validates customer creditworthiness based on credit score, income, and loan amount
96+
- **`process_documents`**: Simulates document validation for tax returns and pay stubs
97+
- **`get_current_rates`**: Fetches current interest rates by location
98+
- **`get_loan_types`**: Returns available loan products
99+
100+
### Agent Capabilities
101+
- Domain-specific terminology understanding
102+
- Compliance guidelines for financial advice limitations
103+
- Structured conversation flow management
104+
- Human handoff protocols
105+
106+
## 📬 Stay Updated with Our Newsletter!
107+
**Get a FREE Data Science eBook** 📖 with 150+ essential lessons in Data Science when you subscribe to our newsletter! Stay in the loop with the latest tutorials, insights, and exclusive resources. [Subscribe now!](https://join.dailydoseofds.com)
108+
109+
[![Daily Dose of Data Science Newsletter](https://github.com/patchy631/ai-engineering/blob/main/resources/join_ddods.png)](https://join.dailydoseofds.com)
110+
111+
---
112+
113+
## Contribution
114+
115+
Contributions are welcome! Please fork the repository and submit a pull request with your improvements.
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import asyncio
2+
import parlant.sdk as p
3+
from dotenv import load_dotenv
4+
5+
load_dotenv()
6+
7+
8+
@p.tool
9+
async def check_eligibility(context: p.ToolContext, credit_score: int, income: float, loan_amount: float) -> p.ToolResult:
10+
"""
11+
Checks if the customer meets the basic qualification criteria for a loan.
12+
"""
13+
# Simulate a business logic check for eligibility
14+
if credit_score >= 680 and income >= 50000 and loan_amount <= 500000:
15+
return p.ToolResult(data={"is_eligible": True})
16+
else:
17+
# Provide reason for ineligibility
18+
reason = "insufficient credit score" if credit_score < 680 else "income criteria does not meet the requirements"
19+
return p.ToolResult(data={"is_eligible": False, "reason": reason})
20+
21+
@p.tool
22+
async def process_documents(context: p.ToolContext, document_list: list) -> p.ToolResult:
23+
"""
24+
Simulates a service that processes and validates uploaded documents.
25+
"""
26+
# Simulate document processing and validation logic
27+
# We add a condition to simulate a document failing the validation
28+
if "tax_returns.pdf" in document_list and "pay_stubs.pdf" in document_list:
29+
# Assume one document is found to be inaccurate, a common real-world scenario
30+
if "inaccurate_info" in document_list:
31+
return p.ToolResult(data={"documents_processed": False, "reason": "inaccurate information"})
32+
else:
33+
return p.ToolResult(data={"documents_processed": True})
34+
else:
35+
# Case for when the expected documents are not provided
36+
return p.ToolResult(data={"documents_processed": False, "reason": "missing documents"})
37+
38+
@p.tool
39+
async def get_current_rates(context: p.ToolContext, zip_code: str) -> p.ToolResult:
40+
"""
41+
Fetches the current loan interest rates based on the customer's location.
42+
"""
43+
# Simulate an API call to get dynamic rates
44+
return p.ToolResult(data={"rates": {"30-year-fixed": "6.2%", "15-year-fixed": "5.8%"}})
45+
46+
@p.tool
47+
async def get_loan_types(context: p.ToolContext) -> p.ToolResult:
48+
"""
49+
Provides a list of available loan types.
50+
"""
51+
return p.ToolResult(data=["Home Loan", "Personal Loan", "Auto Loan", "Mortgage", "Refinancing"])
52+
53+
54+
async def add_domain_glossary(agent: p.Agent) -> None:
55+
"""
56+
Adds domain-specific terminology to align the agent's understanding with
57+
financial services concepts and brand voice.
58+
"""
59+
await agent.create_term(
60+
name="Customer Care Phone Number",
61+
description="The direct line for human assistance, at +1-234-567-8900",
62+
)
63+
await agent.create_term(
64+
name="Loan Specialist",
65+
description="A specific term to use when referring to human experts who handle loan applications.",
66+
)
67+
await agent.create_term(
68+
name="Loan Operations",
69+
description="The official department name to refer to in legal disclaimers.",
70+
)
71+
await agent.create_term(
72+
name="Online Portal",
73+
description="The online platform where customers can manage their application and upload documents manually."
74+
)
75+
# Define financial concepts to prevent misinformation
76+
await agent.create_term(name="APR", description="Annual Percentage Rate")
77+
await agent.create_term(name="LTV", description="Loan-to-Value ratio")
78+
await agent.create_term(name="Loan Qualification", description="A preliminary estimate, not a guaranteed loan")
79+
80+
81+
async def create_loan_journey(agent: p.Agent) -> p.Journey:
82+
"""
83+
Defines the structured, multi-step journey for loan approval.
84+
"""
85+
journey = await agent.create_journey(
86+
title="Loan Approval",
87+
description="Guides a potential borrower through a two-stage loan approval process.",
88+
conditions=["The customer asks about loans or related financial services"],
89+
)
90+
91+
# Ask the customer what type of loan they are interested in
92+
t0 = await journey.initial_state.transition_to(
93+
chat_state="Determine the type of loan user is interested in"
94+
)
95+
96+
# Collect initial details from the user
97+
t1 = await t0.target.transition_to(
98+
chat_state="Ask them to provide their credit score, annual income, and the desired loan amount",
99+
condition="The customer specified the type of loan",
100+
)
101+
102+
# Use a tool to check basic credit eligibility
103+
t2 = await t1.target.transition_to(tool_state=check_eligibility)
104+
105+
# Handle the path for initial credit ineligibility
106+
t3_credit_ineligible = await t2.target.transition_to(
107+
chat_state="Inform them that they are not qualified for the loan and ask them if they are interested in other types of loans",
108+
condition="The customer is not eligible for the loan",
109+
)
110+
await t3_credit_ineligible.target.transition_to(state=p.END_JOURNEY)
111+
112+
# Else continue this path: request and process documents
113+
t3_request_docs = await t2.target.transition_to(
114+
chat_state="Inform them that they meet the initial criteria and ask them to provide their tax returns and recent pay stubs",
115+
condition="The customer is eligible for the loan",
116+
)
117+
118+
# Process the documents using a tool
119+
t4_process_docs = await t3_request_docs.target.transition_to(tool_state=process_documents)
120+
121+
# Handle the path for document ineligibility
122+
t5_docs_ineligible = await t4_process_docs.target.transition_to(
123+
chat_state="Ask them to use our Online Portal to submit their documents, or contact a Loan Specialist at our Customer Care Phone Number for assistance",
124+
condition="The documents are either invalid, missing or not uploaded correctly",
125+
)
126+
await t5_docs_ineligible.target.transition_to(state=p.END_JOURNEY)
127+
128+
# Else continue this path: success and hand-off to human
129+
t5_final_eligible = await t4_process_docs.target.transition_to(
130+
chat_state="Inform them that their application has been approved and a Loan Specialist will review their information and contact them shortly",
131+
condition="Documents are successfully uploaded",
132+
)
133+
134+
# End the journey
135+
await t5_final_eligible.target.transition_to(state=p.END_JOURNEY)
136+
137+
# Create additional guidelines for the journey
138+
await journey.create_guideline(
139+
condition="The customer asks about the types of loans we offer.",
140+
action="Call the get_loan_types tool and provide the list of loan types we offer.",
141+
tools=[get_loan_types]
142+
)
143+
144+
return journey
145+
146+
147+
async def main() -> None:
148+
"""
149+
The main function to initialize and configure the Parlant agent.
150+
"""
151+
async with p.Server(session_store="local") as server:
152+
agent = await server.create_agent(
153+
name="Financial Services Agent",
154+
description="A compliance-driven agent that helps customers with loan approval.",
155+
)
156+
157+
# Add foundational components
158+
await add_domain_glossary(agent)
159+
await agent.create_canned_response(
160+
template="Hello! My name is {{generative.agent_name}}. I am here to assist you with the loan approval process."
161+
)
162+
163+
loan_approval_journey = await create_loan_journey(agent)
164+
165+
# Implement guidelines for behavioral control
166+
await agent.create_guideline(
167+
condition="The customer asks about current loan interest rates.",
168+
action="Call the get_current_rates tool and provide the current rates for the customer's zip code.",
169+
tools=[get_current_rates],
170+
)
171+
172+
await agent.create_guideline(
173+
condition="The customer asks for legal or financial advice",
174+
action="State that you cannot provide financial or legal advice and recommend a licensed professional.",
175+
)
176+
177+
await agent.create_guideline(
178+
condition="The customer asks about something that has nothing to do with financial services.",
179+
action="Kindly tell them you cannot assist with off-topic inquiries - do not engage with their request.",
180+
)
181+
182+
await agent.create_guideline(
183+
condition="The customer asks for contact information for human support.",
184+
action="Provide the Customer Care Phone Number and tell them a Loan Specialist can assist them.",
185+
)
186+
187+
188+
if __name__ == "__main__":
189+
asyncio.run(main())
89.5 KB
Loading
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[project]
2+
name = "parlant-demo"
3+
version = "0.1.0"
4+
description = "Add your description here"
5+
readme = "README.md"
6+
requires-python = ">=3.12"
7+
dependencies = [
8+
"parlant>=3.0.2",
9+
"parlant-client>=3.0.1",
10+
]

0 commit comments

Comments
 (0)