pixie-examples 0.1.1.dev5__tar.gz → 0.1.1.dev14__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/PKG-INFO +2 -2
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/langchain/basic_agent.py +11 -2
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/langchain/customer_support.py +33 -35
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/langchain/personal_assistant.py +16 -29
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/langchain/sql_agent.py +15 -25
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/langgraph/langgraph_rag.py +43 -35
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/langgraph/langgraph_sql_agent.py +23 -34
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/openai_agents_sdk/customer_service.py +55 -46
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/openai_agents_sdk/financial_research_agent.py +109 -91
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/openai_agents_sdk/llm_as_a_judge.py +20 -20
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/openai_agents_sdk/routing.py +62 -19
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/pydantic_ai/bank_support.py +29 -28
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/pydantic_ai/flight_booking.py +70 -66
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/pydantic_ai/question_graph.py +42 -19
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/pydantic_ai/sql_gen.py +20 -12
- pixie_examples-0.1.1.dev14/examples/quickstart/problem_solver.py +38 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/quickstart/sleepy_poet.py +27 -27
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/pyproject.toml +2 -2
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/LICENSE +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/README.md +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/__init__.py +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/langchain/README.md +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/langchain/__init__.py +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/langgraph/__init__.py +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/openai_agents_sdk/README.md +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/openai_agents_sdk/__init__.py +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/pydantic_ai/.env.example +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/pydantic_ai/README.md +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/pydantic_ai/__init__.py +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/pydantic_ai/structured_output.py +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/quickstart/__init__.py +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/quickstart/chatbot.py +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/quickstart/weather_agent.py +0 -0
- {pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/sql_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pixie-examples
|
|
3
|
-
Version: 0.1.1.
|
|
3
|
+
Version: 0.1.1.dev14
|
|
4
4
|
Summary: examples for using Pixie
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -24,7 +24,7 @@ Requires-Dist: openinference-instrumentation-crewai (>=0.1.17,<0.2.0)
|
|
|
24
24
|
Requires-Dist: openinference-instrumentation-dspy (>=0.1.33,<0.2.0)
|
|
25
25
|
Requires-Dist: openinference-instrumentation-google-adk (>=0.1.8,<0.2.0)
|
|
26
26
|
Requires-Dist: openinference-instrumentation-openai-agents (>=1.4.0,<2.0.0)
|
|
27
|
-
Requires-Dist: pixie-sdk (>=0.1.1.
|
|
27
|
+
Requires-Dist: pixie-sdk (>=0.1.1.dev36,<0.2.0)
|
|
28
28
|
Requires-Dist: pydantic (>=2.7.4,<3.0.0)
|
|
29
29
|
Requires-Dist: pydantic-ai-slim (>=1.39.0,<2.0.0)
|
|
30
30
|
Requires-Dist: pymarkdownlnt (>=0.9.34,<0.10.0)
|
|
@@ -12,6 +12,15 @@ from langfuse.langchain import CallbackHandler
|
|
|
12
12
|
import pixie
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
basic_weather_agent_prompt = pixie.create_prompt(
|
|
16
|
+
"basic_weather_agent",
|
|
17
|
+
description="Helpful weather assistant",
|
|
18
|
+
)
|
|
19
|
+
interactive_weather_agent_prompt = pixie.create_prompt(
|
|
20
|
+
"interactive_weather_agent",
|
|
21
|
+
description="Interactive weather assistant that answers questions about weather",
|
|
22
|
+
)
|
|
23
|
+
|
|
15
24
|
langfuse_handler = CallbackHandler()
|
|
16
25
|
|
|
17
26
|
|
|
@@ -37,7 +46,7 @@ async def langchain_basic_weather_agent(query: str) -> str:
|
|
|
37
46
|
agent = create_agent(
|
|
38
47
|
model=model,
|
|
39
48
|
tools=[get_weather],
|
|
40
|
-
system_prompt=
|
|
49
|
+
system_prompt=basic_weather_agent_prompt.compile(),
|
|
41
50
|
)
|
|
42
51
|
|
|
43
52
|
# Run the agent
|
|
@@ -66,7 +75,7 @@ async def langchain_interactive_weather_agent() -> pixie.PixieGenerator[str, str
|
|
|
66
75
|
agent = create_agent(
|
|
67
76
|
model=model,
|
|
68
77
|
tools=[get_weather],
|
|
69
|
-
system_prompt=
|
|
78
|
+
system_prompt=interactive_weather_agent_prompt.compile(),
|
|
70
79
|
)
|
|
71
80
|
|
|
72
81
|
# Send welcome message
|
{pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/langchain/customer_support.py
RENAMED
|
@@ -7,7 +7,7 @@ as it moves through different states of a workflow.
|
|
|
7
7
|
Based on: https://docs.langchain.com/oss/python/langchain/multi-agent/handoffs-customer-support
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from typing import Literal, NotRequired
|
|
10
|
+
from typing import Literal, NotRequired, cast
|
|
11
11
|
from langchain.agents import create_agent, AgentState
|
|
12
12
|
from langchain.chat_models import init_chat_model
|
|
13
13
|
from langchain.tools import tool, ToolRuntime
|
|
@@ -17,6 +17,9 @@ from langgraph.types import Command
|
|
|
17
17
|
from typing import Callable
|
|
18
18
|
|
|
19
19
|
from langfuse.langchain import CallbackHandler
|
|
20
|
+
from langchain_core.messages import (
|
|
21
|
+
SystemMessage,
|
|
22
|
+
)
|
|
20
23
|
import pixie
|
|
21
24
|
|
|
22
25
|
|
|
@@ -90,54 +93,42 @@ def provide_solution(solution: str) -> str:
|
|
|
90
93
|
return f"Solution provided: {solution}"
|
|
91
94
|
|
|
92
95
|
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
class IssueClassifierPromptVariables(pixie.PromptVariables):
|
|
97
|
+
warranty_status: Literal["in_warranty", "out_of_warranty"]
|
|
95
98
|
|
|
96
|
-
CURRENT STAGE: Warranty Verification
|
|
97
99
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
class ResolutionSpecialistPromptVariables(IssueClassifierPromptVariables):
|
|
101
|
+
issue_type: Literal["hardware", "software"]
|
|
100
102
|
|
|
101
|
-
Be polite and professional."""
|
|
102
|
-
|
|
103
|
-
ISSUE_CLASSIFIER_PROMPT = """You are a customer support agent classifying technical issues.
|
|
104
|
-
|
|
105
|
-
CURRENT STAGE: Issue Classification
|
|
106
|
-
CUSTOMER INFO: Warranty status is {warranty_status}
|
|
107
|
-
|
|
108
|
-
Ask the customer to describe their issue, then determine if it's:
|
|
109
|
-
- HARDWARE: Physical problems (cracked screen, battery, ports, buttons)
|
|
110
|
-
- SOFTWARE: App crashes, performance, settings, updates
|
|
111
|
-
|
|
112
|
-
Use record_issue_type to record the classification and move to resolution."""
|
|
113
|
-
|
|
114
|
-
RESOLUTION_SPECIALIST_PROMPT = """You are a customer support agent helping with device issues.
|
|
115
|
-
|
|
116
|
-
CURRENT STAGE: Resolution
|
|
117
|
-
CUSTOMER INFO: Warranty status is {warranty_status}, issue type is {issue_type}
|
|
118
|
-
|
|
119
|
-
At this step, you need to:
|
|
120
|
-
1. For SOFTWARE issues: provide troubleshooting steps using provide_solution
|
|
121
|
-
2. For HARDWARE issues:
|
|
122
|
-
- If IN WARRANTY: explain warranty repair process using provide_solution
|
|
123
|
-
- If OUT OF WARRANTY: escalate_to_human for paid repair options
|
|
124
|
-
|
|
125
|
-
Be specific and helpful in your solutions."""
|
|
126
103
|
|
|
104
|
+
warranty_collector_prompt = pixie.create_prompt(
|
|
105
|
+
"warranty_collector_agent",
|
|
106
|
+
description="Customer support agent that collects warranty information",
|
|
107
|
+
)
|
|
108
|
+
issue_classifier_prompt = pixie.create_prompt(
|
|
109
|
+
"issue_classifier_agent",
|
|
110
|
+
IssueClassifierPromptVariables,
|
|
111
|
+
description="Customer support agent that classifies technical issues as hardware or software",
|
|
112
|
+
)
|
|
113
|
+
resolution_specialist_prompt = pixie.create_prompt(
|
|
114
|
+
"resolution_specialist_agent",
|
|
115
|
+
ResolutionSpecialistPromptVariables,
|
|
116
|
+
description="Customer support agent that provides resolutions based on issue type and warranty status",
|
|
117
|
+
)
|
|
127
118
|
# Step configuration
|
|
128
119
|
STEP_CONFIG = {
|
|
129
120
|
"warranty_collector": {
|
|
130
|
-
"prompt":
|
|
121
|
+
"prompt": warranty_collector_prompt,
|
|
131
122
|
"tools": [record_warranty_status],
|
|
132
123
|
"requires": [],
|
|
133
124
|
},
|
|
134
125
|
"issue_classifier": {
|
|
135
|
-
"prompt":
|
|
126
|
+
"prompt": issue_classifier_prompt,
|
|
136
127
|
"tools": [record_issue_type],
|
|
137
128
|
"requires": ["warranty_status"],
|
|
138
129
|
},
|
|
139
130
|
"resolution_specialist": {
|
|
140
|
-
"prompt":
|
|
131
|
+
"prompt": resolution_specialist_prompt,
|
|
141
132
|
"tools": [provide_solution, escalate_to_human],
|
|
142
133
|
"requires": ["warranty_status", "issue_type"],
|
|
143
134
|
},
|
|
@@ -165,7 +156,14 @@ def apply_step_config(
|
|
|
165
156
|
# Note: In a production implementation, you would inject the formatted prompt
|
|
166
157
|
# and tools into the request. For simplicity, we'll let the handler process
|
|
167
158
|
# the request and handle tool selection based on state.
|
|
168
|
-
|
|
159
|
+
prompt = cast(pixie.Prompt, stage_config["prompt"])
|
|
160
|
+
if prompt.variables_definition:
|
|
161
|
+
vars = prompt.variables_definition(**request.state)
|
|
162
|
+
prompt_txt = prompt.compile(vars)
|
|
163
|
+
else:
|
|
164
|
+
prompt_txt = prompt.compile(None)
|
|
165
|
+
|
|
166
|
+
request.system_message = SystemMessage(prompt_txt)
|
|
169
167
|
|
|
170
168
|
# The middleware pattern here would need deeper integration with LangChain's
|
|
171
169
|
# internal APIs. For now, we pass through to the handler.
|
{pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/langchain/personal_assistant.py
RENAMED
|
@@ -16,6 +16,19 @@ from langfuse.langchain import CallbackHandler
|
|
|
16
16
|
import pixie
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
calendar_agent_prompt = pixie.create_prompt(
|
|
20
|
+
"calendar_agent",
|
|
21
|
+
description="Calendar scheduling assistant that parses natural language requests",
|
|
22
|
+
)
|
|
23
|
+
email_agent_prompt = pixie.create_prompt(
|
|
24
|
+
"email_agent",
|
|
25
|
+
description="Email assistant that composes and sends professional emails",
|
|
26
|
+
)
|
|
27
|
+
supervisor_agent_prompt = pixie.create_prompt(
|
|
28
|
+
"supervisor_agent",
|
|
29
|
+
description="Personal assistant coordinator that handles calendar and email tasks",
|
|
30
|
+
)
|
|
31
|
+
|
|
19
32
|
langfuse_handler = CallbackHandler()
|
|
20
33
|
|
|
21
34
|
|
|
@@ -46,32 +59,6 @@ def send_email(to: list[str], subject: str, body: str, cc: list[str] = []) -> st
|
|
|
46
59
|
return f"Email sent to {', '.join(to)} - Subject: {subject}"
|
|
47
60
|
|
|
48
61
|
|
|
49
|
-
# System prompts for specialized agents
|
|
50
|
-
CALENDAR_AGENT_PROMPT = (
|
|
51
|
-
"You are a calendar scheduling assistant. "
|
|
52
|
-
"Parse natural language scheduling requests (e.g., 'next Tuesday at 2pm') "
|
|
53
|
-
"into proper ISO datetime formats. "
|
|
54
|
-
"Use get_available_time_slots to check availability when needed. "
|
|
55
|
-
"Use create_calendar_event to schedule events. "
|
|
56
|
-
"Always confirm what was scheduled in your final response."
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
EMAIL_AGENT_PROMPT = (
|
|
60
|
-
"You are an email assistant. "
|
|
61
|
-
"Compose professional emails based on natural language requests. "
|
|
62
|
-
"Extract recipient information and craft appropriate subject lines and body text. "
|
|
63
|
-
"Use send_email to send the message. "
|
|
64
|
-
"Always confirm what was sent in your final response."
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
SUPERVISOR_PROMPT = (
|
|
68
|
-
"You are a helpful personal assistant. "
|
|
69
|
-
"You can schedule calendar events and send emails. "
|
|
70
|
-
"Break down user requests into appropriate tool calls and coordinate the results. "
|
|
71
|
-
"When a request involves multiple actions, use multiple tools in sequence."
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
|
|
75
62
|
@pixie.app
|
|
76
63
|
async def langchain_personal_assistant() -> pixie.PixieGenerator[str, str]:
|
|
77
64
|
"""Multi-agent personal assistant with calendar and email subagents.
|
|
@@ -90,14 +77,14 @@ async def langchain_personal_assistant() -> pixie.PixieGenerator[str, str]:
|
|
|
90
77
|
calendar_agent = create_agent(
|
|
91
78
|
model,
|
|
92
79
|
tools=[create_calendar_event, get_available_time_slots],
|
|
93
|
-
system_prompt=
|
|
80
|
+
system_prompt=calendar_agent_prompt.compile(),
|
|
94
81
|
)
|
|
95
82
|
|
|
96
83
|
# Create email subagent
|
|
97
84
|
email_agent = create_agent(
|
|
98
85
|
model,
|
|
99
86
|
tools=[send_email],
|
|
100
|
-
system_prompt=
|
|
87
|
+
system_prompt=email_agent_prompt.compile(),
|
|
101
88
|
)
|
|
102
89
|
|
|
103
90
|
# Wrap subagents as tools for the supervisor
|
|
@@ -131,7 +118,7 @@ async def langchain_personal_assistant() -> pixie.PixieGenerator[str, str]:
|
|
|
131
118
|
supervisor_agent = create_agent(
|
|
132
119
|
model,
|
|
133
120
|
tools=[schedule_event, manage_email],
|
|
134
|
-
system_prompt=
|
|
121
|
+
system_prompt=supervisor_agent_prompt.compile(),
|
|
135
122
|
checkpointer=InMemorySaver(),
|
|
136
123
|
)
|
|
137
124
|
|
|
@@ -26,32 +26,18 @@ import pixie
|
|
|
26
26
|
from ..sql_utils import SQLDatabase, SQLDatabaseToolkit
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
# System prompt for SQL agent
|
|
33
|
-
SQL_AGENT_PROMPT = """
|
|
34
|
-
You are an agent designed to interact with a SQL database.
|
|
35
|
-
Given an input question, create a syntactically correct {dialect} query to run,
|
|
36
|
-
then look at the results of the query and return the answer. Unless the user
|
|
37
|
-
specifies a specific number of examples they wish to obtain, always limit your
|
|
38
|
-
query to at most {top_k} results.
|
|
39
|
-
|
|
40
|
-
You can order the results by a relevant column to return the most interesting
|
|
41
|
-
examples in the database. Never query for all the columns from a specific table,
|
|
42
|
-
only ask for the relevant columns given the question.
|
|
43
|
-
|
|
44
|
-
You MUST double check your query before executing it. If you get an error while
|
|
45
|
-
executing a query, rewrite the query and try again.
|
|
29
|
+
class SqlAgentPromptVariables(pixie.PromptVariables):
|
|
30
|
+
dialect: str
|
|
31
|
+
top_k: int
|
|
46
32
|
|
|
47
|
-
DO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the
|
|
48
|
-
database.
|
|
49
33
|
|
|
50
|
-
|
|
51
|
-
|
|
34
|
+
sql_agent_prompt = pixie.create_prompt(
|
|
35
|
+
"langchain_sql_agent",
|
|
36
|
+
SqlAgentPromptVariables,
|
|
37
|
+
description="SQL agent that interacts with databases and creates queries based on natural language",
|
|
38
|
+
)
|
|
52
39
|
|
|
53
|
-
|
|
54
|
-
"""
|
|
40
|
+
langfuse_handler = CallbackHandler()
|
|
55
41
|
|
|
56
42
|
|
|
57
43
|
def setup_database():
|
|
@@ -99,7 +85,9 @@ async def langchain_sql_query_agent(question: str) -> str:
|
|
|
99
85
|
tools = toolkit.get_tools()
|
|
100
86
|
|
|
101
87
|
# Format system prompt with database info
|
|
102
|
-
system_prompt =
|
|
88
|
+
system_prompt = sql_agent_prompt.compile(
|
|
89
|
+
SqlAgentPromptVariables(dialect=db.dialect, top_k=5)
|
|
90
|
+
)
|
|
103
91
|
|
|
104
92
|
# Create agent
|
|
105
93
|
agent = create_agent(model, tools, system_prompt=system_prompt)
|
|
@@ -134,7 +122,9 @@ async def langchain_interactive_sql_agent() -> pixie.PixieGenerator[str, str]:
|
|
|
134
122
|
tools = toolkit.get_tools()
|
|
135
123
|
|
|
136
124
|
# Format system prompt
|
|
137
|
-
system_prompt =
|
|
125
|
+
system_prompt = sql_agent_prompt.compile(
|
|
126
|
+
SqlAgentPromptVariables(dialect=db.dialect, top_k=5)
|
|
127
|
+
)
|
|
138
128
|
|
|
139
129
|
# Create agent with checkpointer for conversation memory
|
|
140
130
|
agent = create_agent(
|
{pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/langgraph/langgraph_rag.py
RENAMED
|
@@ -11,7 +11,7 @@ Based on: https://docs.langchain.com/oss/python/langgraph/agentic-rag
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
from pydantic import BaseModel, Field
|
|
14
|
-
from typing import Literal
|
|
14
|
+
from typing import Literal, cast
|
|
15
15
|
from langchain.chat_models import init_chat_model
|
|
16
16
|
from langchain.tools import tool
|
|
17
17
|
from langchain.messages import HumanMessage
|
|
@@ -28,6 +28,36 @@ import requests
|
|
|
28
28
|
from bs4 import BeautifulSoup
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
class GradePromptVariables(pixie.PromptVariables):
|
|
32
|
+
context: str
|
|
33
|
+
question: str
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class RewritePromptVariables(pixie.PromptVariables):
|
|
37
|
+
question: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class GeneratePromptVariables(pixie.PromptVariables):
|
|
41
|
+
question: str
|
|
42
|
+
context: str
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
rag_grade_prompt = pixie.create_prompt(
|
|
46
|
+
"rag_grade_documents",
|
|
47
|
+
GradePromptVariables,
|
|
48
|
+
description="Grades relevance of retrieved documents to user questions",
|
|
49
|
+
)
|
|
50
|
+
rag_rewrite_prompt = pixie.create_prompt(
|
|
51
|
+
"rag_rewrite_question",
|
|
52
|
+
RewritePromptVariables,
|
|
53
|
+
description="Rewrites questions to improve semantic understanding",
|
|
54
|
+
)
|
|
55
|
+
rag_generate_prompt = pixie.create_prompt(
|
|
56
|
+
"rag_generate_answer",
|
|
57
|
+
GeneratePromptVariables,
|
|
58
|
+
description="Generates concise answers from retrieved context",
|
|
59
|
+
)
|
|
60
|
+
|
|
31
61
|
langfuse_handler = CallbackHandler()
|
|
32
62
|
|
|
33
63
|
|
|
@@ -102,14 +132,6 @@ def create_rag_graph(retriever, model):
|
|
|
102
132
|
description="Relevance score: 'yes' if relevant, or 'no' if not relevant"
|
|
103
133
|
)
|
|
104
134
|
|
|
105
|
-
GRADE_PROMPT = (
|
|
106
|
-
"You are a grader assessing relevance of a retrieved document to a user question. \n "
|
|
107
|
-
"Here is the retrieved document: \n\n {context} \n\n"
|
|
108
|
-
"Here is the user question: {question} \n"
|
|
109
|
-
"If the document contains keyword(s) or semantic meaning related to the user question, grade it as relevant. \n"
|
|
110
|
-
"Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question."
|
|
111
|
-
)
|
|
112
|
-
|
|
113
135
|
grader_model = init_chat_model("gpt-4o", temperature=0)
|
|
114
136
|
|
|
115
137
|
# Conditional edge: Grade documents
|
|
@@ -117,10 +139,12 @@ def create_rag_graph(retriever, model):
|
|
|
117
139
|
state: MessagesState,
|
|
118
140
|
) -> Literal["generate_answer", "rewrite_question"]:
|
|
119
141
|
"""Determine whether the retrieved documents are relevant to the question."""
|
|
120
|
-
question = state["messages"][0].content
|
|
121
|
-
context = state["messages"][-1].content
|
|
142
|
+
question = cast(str, state["messages"][0].content)
|
|
143
|
+
context = cast(str, state["messages"][-1].content)
|
|
122
144
|
|
|
123
|
-
prompt =
|
|
145
|
+
prompt = rag_grade_prompt.compile(
|
|
146
|
+
GradePromptVariables(question=question, context=context)
|
|
147
|
+
)
|
|
124
148
|
response = grader_model.with_structured_output(GradeDocuments).invoke(
|
|
125
149
|
[{"role": "user", "content": prompt}],
|
|
126
150
|
config={"callbacks": [langfuse_handler]},
|
|
@@ -133,20 +157,11 @@ def create_rag_graph(retriever, model):
|
|
|
133
157
|
return "rewrite_question"
|
|
134
158
|
|
|
135
159
|
# Node: Rewrite question
|
|
136
|
-
REWRITE_PROMPT = (
|
|
137
|
-
"Look at the input and try to reason about the underlying semantic intent / meaning.\n"
|
|
138
|
-
"Here is the initial question:"
|
|
139
|
-
"\n ------- \n"
|
|
140
|
-
"{question}"
|
|
141
|
-
"\n ------- \n"
|
|
142
|
-
"Formulate an improved question:"
|
|
143
|
-
)
|
|
144
|
-
|
|
145
160
|
def rewrite_question(state: MessagesState):
|
|
146
161
|
"""Rewrite the original user question."""
|
|
147
162
|
messages = state["messages"]
|
|
148
|
-
question = messages[0].content
|
|
149
|
-
prompt =
|
|
163
|
+
question = cast(str, messages[0].content)
|
|
164
|
+
prompt = rag_rewrite_prompt.compile(RewritePromptVariables(question=question))
|
|
150
165
|
response = model.invoke(
|
|
151
166
|
[{"role": "user", "content": prompt}],
|
|
152
167
|
config={"callbacks": [langfuse_handler]},
|
|
@@ -154,20 +169,13 @@ def create_rag_graph(retriever, model):
|
|
|
154
169
|
return {"messages": [HumanMessage(content=response.content)]}
|
|
155
170
|
|
|
156
171
|
# Node: Generate answer
|
|
157
|
-
GENERATE_PROMPT = (
|
|
158
|
-
"You are an assistant for question-answering tasks. "
|
|
159
|
-
"Use the following pieces of retrieved context to answer the question. "
|
|
160
|
-
"If you don't know the answer, just say that you don't know. "
|
|
161
|
-
"Use three sentences maximum and keep the answer concise.\n"
|
|
162
|
-
"Question: {question} \n"
|
|
163
|
-
"Context: {context}"
|
|
164
|
-
)
|
|
165
|
-
|
|
166
172
|
def generate_answer(state: MessagesState):
|
|
167
173
|
"""Generate an answer."""
|
|
168
|
-
question = state["messages"][0].content
|
|
169
|
-
context = state["messages"][-1].content
|
|
170
|
-
prompt =
|
|
174
|
+
question = cast(str, state["messages"][0].content)
|
|
175
|
+
context = cast(str, state["messages"][-1].content)
|
|
176
|
+
prompt = rag_generate_prompt.compile(
|
|
177
|
+
GeneratePromptVariables(question=question, context=context)
|
|
178
|
+
)
|
|
171
179
|
response = model.invoke(
|
|
172
180
|
[{"role": "user", "content": prompt}],
|
|
173
181
|
config={"callbacks": [langfuse_handler]},
|
{pixie_examples-0.1.1.dev5 → pixie_examples-0.1.1.dev14}/examples/langgraph/langgraph_sql_agent.py
RENAMED
|
@@ -22,6 +22,21 @@ from langfuse.langchain import CallbackHandler
|
|
|
22
22
|
import pixie
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
class LanggraphSqlPromptVariables(pixie.PromptVariables):
|
|
26
|
+
dialect: str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
langgraph_sql_generate_prompt = pixie.create_prompt(
|
|
30
|
+
"langgraph_sql_generate_query",
|
|
31
|
+
LanggraphSqlPromptVariables,
|
|
32
|
+
description="Generates SQL queries from natural language questions",
|
|
33
|
+
)
|
|
34
|
+
langgraph_sql_check_prompt = pixie.create_prompt(
|
|
35
|
+
"langgraph_sql_check_query",
|
|
36
|
+
LanggraphSqlPromptVariables,
|
|
37
|
+
description="Reviews and validates SQL queries for common mistakes",
|
|
38
|
+
)
|
|
39
|
+
|
|
25
40
|
langfuse_handler = CallbackHandler()
|
|
26
41
|
|
|
27
42
|
|
|
@@ -84,22 +99,11 @@ def create_sql_graph(db: SQLDatabase, model):
|
|
|
84
99
|
return {"messages": [response]}
|
|
85
100
|
|
|
86
101
|
# Node: Generate query
|
|
87
|
-
generate_query_prompt = f"""
|
|
88
|
-
You are an agent designed to interact with a SQL database.
|
|
89
|
-
Given an input question, create a syntactically correct {db.dialect} query to run,
|
|
90
|
-
then look at the results of the query and return the answer. Unless the user
|
|
91
|
-
specifies a specific number of examples they wish to obtain, always limit your
|
|
92
|
-
query to at most 5 results.
|
|
93
|
-
|
|
94
|
-
You can order the results by a relevant column to return the most interesting
|
|
95
|
-
examples in the database. Never query for all the columns from a specific table,
|
|
96
|
-
only ask for the relevant columns given the question.
|
|
97
|
-
|
|
98
|
-
DO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the database.
|
|
99
|
-
"""
|
|
100
|
-
|
|
101
102
|
def generate_query(state: MessagesState):
|
|
102
|
-
|
|
103
|
+
generate_query_prompt_text = langgraph_sql_generate_prompt.compile(
|
|
104
|
+
LanggraphSqlPromptVariables(dialect=db.dialect)
|
|
105
|
+
)
|
|
106
|
+
system_message = {"role": "system", "content": generate_query_prompt_text}
|
|
103
107
|
llm_with_tools = model.bind_tools([run_query_tool])
|
|
104
108
|
response = llm_with_tools.invoke(
|
|
105
109
|
[system_message] + state["messages"],
|
|
@@ -108,28 +112,13 @@ DO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the databa
|
|
|
108
112
|
return {"messages": [response]}
|
|
109
113
|
|
|
110
114
|
# Node: Check query
|
|
111
|
-
check_query_prompt = f"""
|
|
112
|
-
You are a SQL expert with a strong attention to detail.
|
|
113
|
-
Double check the {db.dialect} query for common mistakes, including:
|
|
114
|
-
- Using NOT IN with NULL values
|
|
115
|
-
- Using UNION when UNION ALL should have been used
|
|
116
|
-
- Using BETWEEN for exclusive ranges
|
|
117
|
-
- Data type mismatch in predicates
|
|
118
|
-
- Properly quoting identifiers
|
|
119
|
-
- Using the correct number of arguments for functions
|
|
120
|
-
- Casting to the correct data type
|
|
121
|
-
- Using the proper columns for joins
|
|
122
|
-
|
|
123
|
-
If there are any of the above mistakes, rewrite the query. If there are no mistakes,
|
|
124
|
-
just reproduce the original query.
|
|
125
|
-
|
|
126
|
-
You will call the appropriate tool to execute the query after running this check.
|
|
127
|
-
"""
|
|
128
|
-
|
|
129
115
|
def check_query(state: MessagesState):
|
|
130
116
|
from langchain.messages import AIMessage as AI
|
|
131
117
|
|
|
132
|
-
|
|
118
|
+
check_query_prompt_text = langgraph_sql_check_prompt.compile(
|
|
119
|
+
LanggraphSqlPromptVariables(dialect=db.dialect)
|
|
120
|
+
)
|
|
121
|
+
system_message = {"role": "system", "content": check_query_prompt_text}
|
|
133
122
|
last_message = state["messages"][-1]
|
|
134
123
|
# Only AIMessage has tool_calls
|
|
135
124
|
if isinstance(last_message, AI) and last_message.tool_calls:
|
|
@@ -26,10 +26,23 @@ from agents import (
|
|
|
26
26
|
function_tool,
|
|
27
27
|
handoff,
|
|
28
28
|
)
|
|
29
|
-
from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
|
|
30
29
|
import pixie
|
|
31
30
|
|
|
32
31
|
|
|
32
|
+
faq_agent_prompt = pixie.create_prompt(
|
|
33
|
+
"airline_faq_agent",
|
|
34
|
+
description="FAQ agent that answers customer questions using knowledge base lookup",
|
|
35
|
+
)
|
|
36
|
+
seat_booking_agent_prompt = pixie.create_prompt(
|
|
37
|
+
"airline_seat_booking_agent",
|
|
38
|
+
description="Seat booking agent that updates flight seat assignments",
|
|
39
|
+
)
|
|
40
|
+
triage_agent_prompt = pixie.create_prompt(
|
|
41
|
+
"airline_triage_agent",
|
|
42
|
+
description="Triage agent that delegates customer inquiries to appropriate specialists",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
33
46
|
# ============================================================================
|
|
34
47
|
# CONTEXT
|
|
35
48
|
# ============================================================================
|
|
@@ -134,62 +147,58 @@ async def on_seat_booking_handoff(
|
|
|
134
147
|
|
|
135
148
|
|
|
136
149
|
# ============================================================================
|
|
137
|
-
# AGENTS
|
|
150
|
+
# AGENTS (Lazy initialization to avoid compile() at module import time)
|
|
138
151
|
# ============================================================================
|
|
139
152
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
153
|
+
_faq_agent: Agent[AirlineAgentContext] | None = None
|
|
154
|
+
_seat_booking_agent: Agent[AirlineAgentContext] | None = None
|
|
155
|
+
_triage_agent: Agent[AirlineAgentContext] | None = None
|
|
156
|
+
_agents_initialized: bool = False
|
|
144
157
|
|
|
145
|
-
You are an FAQ agent. If you are speaking to a customer, you probably were
|
|
146
|
-
transferred to from the triage agent.
|
|
147
158
|
|
|
148
|
-
|
|
159
|
+
def _initialize_agents() -> None:
|
|
160
|
+
"""Initialize all agents with proper handoffs."""
|
|
161
|
+
global _faq_agent, _seat_booking_agent, _triage_agent, _agents_initialized
|
|
149
162
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
2. Use the faq lookup tool to answer the question. Do not rely on your own knowledge.
|
|
153
|
-
3. If you cannot answer the question, transfer back to the triage agent.""",
|
|
154
|
-
tools=[faq_lookup_tool],
|
|
155
|
-
)
|
|
163
|
+
if _agents_initialized:
|
|
164
|
+
return
|
|
156
165
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
166
|
+
_faq_agent = Agent[AirlineAgentContext](
|
|
167
|
+
name="FAQ Agent",
|
|
168
|
+
handoff_description="A helpful agent that can answer questions about the airline.",
|
|
169
|
+
instructions=faq_agent_prompt.compile(),
|
|
170
|
+
tools=[faq_lookup_tool],
|
|
171
|
+
)
|
|
161
172
|
|
|
162
|
-
|
|
163
|
-
|
|
173
|
+
_seat_booking_agent = Agent[AirlineAgentContext](
|
|
174
|
+
name="Seat Booking Agent",
|
|
175
|
+
handoff_description="A helpful agent that can update a seat on a flight.",
|
|
176
|
+
instructions=seat_booking_agent_prompt.compile(),
|
|
177
|
+
tools=[update_seat],
|
|
178
|
+
)
|
|
164
179
|
|
|
165
|
-
|
|
180
|
+
_triage_agent = Agent[AirlineAgentContext](
|
|
181
|
+
name="Triage Agent",
|
|
182
|
+
handoff_description="A triage agent that can delegate a customer's request to the appropriate agent.",
|
|
183
|
+
instructions=triage_agent_prompt.compile(),
|
|
184
|
+
handoffs=[
|
|
185
|
+
_faq_agent,
|
|
186
|
+
handoff(agent=_seat_booking_agent, on_handoff=on_seat_booking_handoff),
|
|
187
|
+
],
|
|
188
|
+
)
|
|
166
189
|
|
|
167
|
-
#
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
3. Use the update seat tool to update the seat on the flight.
|
|
190
|
+
# Set up bidirectional handoffs
|
|
191
|
+
_faq_agent.handoffs.append(_triage_agent)
|
|
192
|
+
_seat_booking_agent.handoffs.append(_triage_agent)
|
|
171
193
|
|
|
172
|
-
|
|
173
|
-
triage agent.""",
|
|
174
|
-
tools=[update_seat],
|
|
175
|
-
)
|
|
194
|
+
_agents_initialized = True
|
|
176
195
|
|
|
177
|
-
triage_agent = Agent[AirlineAgentContext](
|
|
178
|
-
name="Triage Agent",
|
|
179
|
-
handoff_description="A triage agent that can delegate a customer's request to the appropriate agent.",
|
|
180
|
-
instructions=(
|
|
181
|
-
f"{RECOMMENDED_PROMPT_PREFIX} "
|
|
182
|
-
"You are a helpful triaging agent. You can use your tools to delegate questions to other appropriate agents."
|
|
183
|
-
),
|
|
184
|
-
handoffs=[
|
|
185
|
-
faq_agent,
|
|
186
|
-
handoff(agent=seat_booking_agent, on_handoff=on_seat_booking_handoff),
|
|
187
|
-
],
|
|
188
|
-
)
|
|
189
196
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
197
|
+
def get_triage_agent() -> Agent[AirlineAgentContext]:
|
|
198
|
+
"""Get the triage agent (entry point)."""
|
|
199
|
+
_initialize_agents()
|
|
200
|
+
assert _triage_agent is not None
|
|
201
|
+
return _triage_agent
|
|
193
202
|
|
|
194
203
|
|
|
195
204
|
@pixie.app
|
|
@@ -210,7 +219,7 @@ async def openai_agents_airline_customer_service() -> pixie.PixieGenerator[str,
|
|
|
210
219
|
Receives:
|
|
211
220
|
User messages via InputRequired
|
|
212
221
|
"""
|
|
213
|
-
current_agent: Agent[AirlineAgentContext] =
|
|
222
|
+
current_agent: Agent[AirlineAgentContext] = get_triage_agent()
|
|
214
223
|
input_items: list[TResponseInputItem] = []
|
|
215
224
|
context = AirlineAgentContext()
|
|
216
225
|
|