-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathllms.txt
More file actions
459 lines (360 loc) · 13.3 KB
/
Copy pathllms.txt
File metadata and controls
459 lines (360 loc) · 13.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
# Parlant
> Open-source AI agent framework for building customer-facing conversational agents with ensured rule compliance and enterprise-grade behavior control.
Parlant is a Python framework for building **predictable, business-aligned AI agents**. Unlike prompt-based approaches, Parlant ensures agents follow behavioral rules through structured guideline matching and contextual application.
Install: `pip install parlant`
## Quick Start Example
```python
import parlant.sdk as p
import asyncio
@p.tool
async def get_account_balance(context: p.ToolContext, account_id: str) -> p.ToolResult:
# Your business logic here
balance = 1234.56
return p.ToolResult(data={"balance": balance, "currency": "USD"})
async def main() -> None:
async with p.Server() as server:
agent = await server.create_agent(
name="Banking Assistant",
description="Helpful and professional banking support agent",
)
# Add behavioral guidelines
await agent.create_guideline(
condition="The customer asks about their balance",
action="Retrieve and clearly present their account balance",
tools=[get_account_balance],
)
await agent.create_guideline(
condition="The customer asks about topics unrelated to banking",
action="Politely decline and redirect to banking topics",
)
# Server runs until shutdown - no additional code needed here.
# When the process exits, the context manager handles cleanup automatically.
if __name__ == "__main__":
asyncio.run(main())
```
Run with: `python your_agent.py` then open http://localhost:8800
---
## Core Concepts
### 1. Agents
AI personalities that interact with customers. Created via `server.create_agent()`.
Learn more: [Agents Documentation](https://parlant.io/docs/concepts/agents)
### 2. Guidelines
Natural language if-then rules that control agent behavior contextually:
```python
await agent.create_guideline(
condition="When this situation occurs", # The trigger
action="Do this specific thing", # The response behavior
tools=[optional_tool], # Tools available for this guideline
)
```
Learn more: [Guidelines Documentation](https://parlant.io/docs/concepts/customization/guidelines)
### 3. Journeys
Structured multi-step interaction flows (state machines):
```python
journey = await agent.create_journey(
title="Order Support",
description="Helps customers with order issues",
conditions=["The customer has an order-related question"],
)
# Chain states with transitions
t0 = await journey.initial_state.transition_to(chat_state="Ask for order number")
t1 = await t0.target.transition_to(tool_state=lookup_order)
t2 = await t1.target.transition_to(
chat_state="Present order status",
condition="Order was found",
)
await t2.target.transition_to(state=p.END_JOURNEY)
```
Learn more: [Journeys Documentation](https://parlant.io/docs/concepts/customization/journeys)
### 4. Tools
Functions the agent can call. Always async, always return `ToolResult`:
```python
@p.tool
async def my_tool(
context: p.ToolContext, # Always first param
required_param: str, # Required parameters
optional_param: int = 10, # Optional with defaults
) -> p.ToolResult:
# Business logic here
return p.ToolResult(data={"key": "value"})
```
Learn more: [Tools Documentation](https://parlant.io/docs/concepts/customization/tools)
### 5. Glossary Terms
Teach agents domain-specific terminology:
```python
await agent.create_term(
name="SKU",
description="Stock Keeping Unit - unique product identifier",
synonyms=["product code", "item number"],
)
```
Learn more: [Glossary Documentation](https://parlant.io/docs/concepts/customization/glossary)
### 6. Canned Responses
Template responses to eliminate hallucination and control language style:
```python
await agent.add_canned_response(
key="greeting",
content="Hello! I'm here to help with your order. How can I assist you today?",
)
```
Learn more: [Canned Responses Documentation](https://parlant.io/docs/concepts/customization/canned-responses)
### 7. Streaming Mode
Agents can deliver responses in real-time chunks for a more interactive experience:
```python
from parlant.sdk import MessageOutputMode
agent = await server.create_agent(
name="Support Agent",
description="Helpful support agent",
message_output_mode=MessageOutputMode.STREAMING, # Enable streaming
)
```
Output modes:
- `MessageOutputMode.BLOCK` (default): Complete response delivered at once
- `MessageOutputMode.STREAMING`: Response delivered in real-time chunks with token-by-token animation
Streaming mode provides actual token usage information (input/output tokens) in generation metadata.
---
## Common Patterns
### Pattern: Tool with Customer Context
```python
@p.tool
async def get_customer_orders(context: p.ToolContext) -> p.ToolResult:
# context.customer_id is automatically available
orders = await db.get_orders(context.customer_id)
return p.ToolResult(data=orders)
```
### Pattern: Conditional Transitions
```python
# Branch based on conditions
t0 = await journey.initial_state.transition_to(tool_state=check_eligibility)
# Multiple outgoing transitions from same state
await t0.target.transition_to(
chat_state="Approve the request",
condition="Customer is eligible",
)
await t0.target.transition_to(
chat_state="Explain why they're not eligible",
condition="Customer is not eligible",
)
```
### Pattern: Disambiguation
Handle ambiguous user intents:
```python
observation = await agent.create_observation(
"The customer mentions a problem but doesn't specify what kind",
)
await observation.disambiguate([billing_journey, technical_support_journey])
```
### Pattern: Journey-Scoped Guidelines
Guidelines that only apply within a specific journey:
```python
await journey.create_guideline(
condition="Customer seems frustrated",
action="Acknowledge their frustration and offer to escalate",
)
```
---
## Environment Variables
Set your LLM provider credentials before running. Examples:
- `OPENAI_API_KEY` - For OpenAI
- `ANTHROPIC_API_KEY` - For Anthropic
- `AZURE_OPENAI_API_KEY` + `AZURE_OPENAI_ENDPOINT` - For Azure OpenAI
Learn more: [Installation & Setup](https://parlant.io/docs/quickstart/installation)
---
## Full Example: Customer Service Agent
```python
import parlant.sdk as p
import asyncio
# Define tools
@p.tool
async def lookup_order(context: p.ToolContext, order_id: str) -> p.ToolResult:
# Simulated order lookup
return p.ToolResult(data={
"order_id": order_id,
"status": "shipped",
"tracking": "1Z999AA10123456784",
})
@p.tool
async def request_refund(context: p.ToolContext, order_id: str, reason: str) -> p.ToolResult:
return p.ToolResult(data={"refund_id": "REF-12345", "status": "processing"})
async def create_order_journey(agent: p.Agent) -> p.Journey:
journey = await agent.create_journey(
title="Order Support",
description="Helps customers check order status or request refunds",
conditions=["Customer asks about an order"],
)
# Step 1: Get order number
t0 = await journey.initial_state.transition_to(
chat_state="Ask the customer for their order number"
)
# Step 2: Look up the order
t1 = await t0.target.transition_to(tool_state=lookup_order)
# Step 3a: Order found - show status
t2 = await t1.target.transition_to(
chat_state="Present the order status and tracking information",
condition="Order was found",
)
# Step 3b: Order not found
await t1.target.transition_to(
chat_state="Apologize and ask them to verify the order number",
condition="Order was not found",
)
# Step 4: Check if they need anything else
t3 = await t2.target.transition_to(
chat_state="Ask if they need help with anything else regarding this order"
)
# Step 5a: They want a refund
t4 = await t3.target.transition_to(
tool_state=request_refund,
condition="Customer requests a refund",
)
await t4.target.transition_to(
chat_state="Confirm the refund has been initiated"
)
# Step 5b: They're satisfied
await t3.target.transition_to(
state=p.END_JOURNEY,
condition="Customer has no more questions",
)
# Journey-specific guidelines
await journey.create_guideline(
condition="Customer is upset about a delayed order",
action="Apologize sincerely and offer expedited shipping on their next order",
)
return journey
async def main() -> None:
async with p.Server() as server:
agent = await server.create_agent(
name="Support Agent",
description="Friendly and efficient customer support representative",
)
# Domain knowledge
await agent.create_term(
name="Express Shipping",
description="2-day delivery, costs $9.99",
)
# Create journey
await create_order_journey(agent)
# Global guidelines (apply everywhere)
await agent.create_guideline(
condition="Customer uses profanity or is abusive",
action="Calmly ask them to be respectful, or offer to end the conversation",
)
await agent.create_guideline(
condition="Customer asks to speak to a human",
action="Provide the support phone number: 1-800-555-0123",
)
# Server runs until shutdown - no additional code needed here.
# When the process exits, the context manager handles cleanup automatically.
if __name__ == "__main__":
asyncio.run(main())
```
---
## Testing Framework
Parlant includes a testing framework for validating agent behavior using NLP-based assertions.
### Basic Test Structure
```python
from parlant.testing import Suite
from parlant.testing.steps import AgentMessage, CustomerMessage
suite = Suite(
server_url="http://localhost:8800",
agent_id="your_agent_id",
)
@suite.scenario
async def test_greeting() -> None:
async with suite.session() as session:
response = await session.send("Hello!")
await response.should("be a friendly greeting or offer to help")
@suite.scenario
async def test_appointment_inquiry() -> None:
async with suite.session() as session:
response = await session.send("Can I schedule an appointment?")
await response.should("acknowledge the request or ask for more details")
```
Run tests with: `parlant-test your_test_file.py`
### NLP-Based Assertions
The `response.should()` method uses NLP to evaluate conditions against the full conversation context:
```python
# Single condition
await response.should("be polite and professional")
# Multiple conditions (evaluated in parallel)
await response.should([
"ask for the reason for the visit",
"be polite",
"not mention pricing",
])
```
### Multi-Turn Conversations with unfold()
Test multi-turn conversations where each step builds on history:
```python
@suite.scenario
async def test_booking_flow() -> None:
async with suite.session() as session:
await session.unfold([
# History-only steps (no assertion)
CustomerMessage("Hello"),
AgentMessage("Hi! How can I help you today?"),
# Steps with assertions create sub-tests
CustomerMessage("I need to book an appointment"),
AgentMessage(
text="What's the reason for your visit?",
should=["ask for the reason", "be polite"],
),
CustomerMessage("Regular checkup"),
AgentMessage(
text="I have Monday at 10am or Wednesday at 2pm available.",
should="offer appointment times",
),
])
```
**How unfold() works:**
- `CustomerMessage(text)` - Customer's message in the conversation
- `AgentMessage(text, should)` - Expected agent response
- `text`: Reference response used as history for subsequent tests
- `should`: Assertion condition(s). Only steps with `should` create sub-tests
- Each sub-test gets a fresh session with prefab history of all prior steps
- Sub-tests run sequentially and report results independently
### Repeated Scenarios
Run the same scenario multiple times for consistency testing:
```python
@suite.scenario(repetitions=3)
async def test_consistent_greeting() -> None:
async with suite.session() as session:
response = await session.send("Hello")
await response.should("greet the customer")
```
### Hooks
```python
@suite.before_all
async def setup() -> None:
# Runs once before all tests
suite.context["api_key"] = "test-key"
@suite.after_all
async def teardown() -> None:
# Runs once after all tests
pass
@suite.before_each
async def before_test(test_name: str) -> None:
# Runs before each test
pass
@suite.after_each
async def after_test(test_name: str, passed: bool, error: str | None) -> None:
# Runs after each test
pass
```
### CLI Options
```bash
# Run all tests
parlant-test tests.py
# Filter by pattern
parlant-test tests.py -k "greeting"
# Run tests in parallel
parlant-test tests.py --parallel
# Custom timeout (seconds)
parlant-test tests.py --timeout 120
```
---
## Links
- Documentation: https://parlant.io/
- GitHub: https://github.com/emcie-co/parlant
- PyPI: https://pypi.org/project/parlant/
- Discord: https://discord.gg/duxWqxKk6J