Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit77456ce

Browse files
authored
Updating examples with conversation history (#25)
* Updating examples with conversation history* fixing lint errors
1 parentbdb0de6 commit77456ce

File tree

11 files changed

+261
-104
lines changed

11 files changed

+261
-104
lines changed

‎docs/quickstart.md‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,38 @@ asyncio.run(main())
8181

8282
**That's it!** Your existing OpenAI code now includes automatic guardrail validation based on your pipeline configuration. Just use`response.llm_response` instead of`response`.
8383

84+
##Multi-Turn Conversations
85+
86+
When maintaining conversation history across multiple turns,**only append messages after guardrails pass**. This prevents blocked input messages from polluting your conversation context.
87+
88+
```python
89+
messages: list[dict]= []
90+
91+
whileTrue:
92+
user_input=input("You:")
93+
94+
try:
95+
# ✅ Pass user input inline (don't mutate messages first)
96+
response=await client.chat.completions.create(
97+
messages=messages+ [{"role":"user","content": user_input}],
98+
model="gpt-4o"
99+
)
100+
101+
response_content= response.llm_response.choices[0].message.content
102+
print(f"Assistant:{response_content}")
103+
104+
# ✅ Only append AFTER guardrails pass
105+
messages.append({"role":"user","content": user_input})
106+
messages.append({"role":"assistant","content": response_content})
107+
108+
except GuardrailTripwireTriggered:
109+
# ❌ Guardrail blocked - message NOT added to history
110+
print("Message blocked by guardrails")
111+
continue
112+
```
113+
114+
**Why this matters**: If you append the user message before the guardrail check, blocked messages remain in your conversation history and get sent on every subsequent turn, even though they violated your safety policies.
115+
84116
##Guardrail Execution Error Handling
85117

86118
Guardrails supports two error handling modes for guardrail execution failures:

‎examples/basic/azure_implementation.py‎

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,24 @@
5454
}
5555

5656

57-
asyncdefprocess_input(guardrails_client:GuardrailsAsyncAzureOpenAI,user_input:str)->None:
58-
"""Process user input with complete response validation using GuardrailsClient."""
57+
asyncdefprocess_input(
58+
guardrails_client:GuardrailsAsyncAzureOpenAI,
59+
user_input:str,
60+
messages:list[dict],
61+
)->None:
62+
"""Process user input with complete response validation using GuardrailsClient.
63+
64+
Args:
65+
guardrails_client: GuardrailsAsyncAzureOpenAI instance.
66+
user_input: User's input text.
67+
messages: Conversation history (modified in place after guardrails pass).
68+
"""
5969
try:
60-
# Use GuardrailsClient to handle all guardrail checks and LLM calls
70+
# Pass user input inline WITHOUT mutating messages first
71+
# Only add to messages AFTER guardrails pass and LLM call succeeds
6172
response=awaitguardrails_client.chat.completions.create(
6273
model=AZURE_DEPLOYMENT,
63-
messages=[{"role":"user","content":user_input}],
74+
messages=messages+[{"role":"user","content":user_input}],
6475
)
6576

6677
# Extract the response content from the GuardrailsResponse
@@ -69,11 +80,16 @@ async def process_input(guardrails_client: GuardrailsAsyncAzureOpenAI, user_inpu
6980
# Only show output if all guardrails pass
7081
print(f"\nAssistant:{response_text}")
7182

83+
# Guardrails passed - now safe to add to conversation history
84+
messages.append({"role":"user","content":user_input})
85+
messages.append({"role":"assistant","content":response_text})
86+
7287
exceptGuardrailTripwireTriggeredase:
7388
# Extract information from the triggered guardrail
7489
triggered_result=e.guardrail_result
7590
print(" Input blocked. Please try a different message.")
7691
print(f" Full result:{triggered_result}")
92+
# Guardrail blocked - user message NOT added to history
7793
raise
7894
exceptBadRequestErrorase:
7995
# Handle Azure's built-in content filter errors
@@ -97,6 +113,8 @@ async def main():
97113
api_version="2025-01-01-preview",
98114
)
99115

116+
messages:list[dict]= []
117+
100118
whileTrue:
101119
try:
102120
prompt=input("\nEnter a message: ")
@@ -105,7 +123,7 @@ async def main():
105123
print("Goodbye!")
106124
break
107125

108-
awaitprocess_input(guardrails_client,prompt)
126+
awaitprocess_input(guardrails_client,prompt,messages)
109127
except (EOFError,KeyboardInterrupt):
110128
break
111129
except (GuardrailTripwireTriggered,BadRequestError):

‎examples/basic/multiturn_chat_with_alignment.py‎

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -226,14 +226,21 @@ async def main(malicious: bool = False) -> None:
226226
ifnotuser_input:
227227
continue
228228

229-
messages.append({"role":"user","content":user_input})
230-
229+
# Passuser input inline WITHOUT mutating messages first
230+
# Only add to messages AFTER guardrails pass and LLM call succeeds
231231
try:
232-
resp=awaitclient.chat.completions.create(model="gpt-4.1-nano",messages=messages,tools=tools)
232+
resp=awaitclient.chat.completions.create(
233+
model="gpt-4.1-nano",
234+
messages=messages+ [{"role":"user","content":user_input}],
235+
tools=tools,
236+
)
233237
print_guardrail_results("initial",resp)
234238
choice=resp.llm_response.choices[0]
235239
message=choice.message
236240
tool_calls=getattr(message,"tool_calls", [])or []
241+
242+
# Guardrails passed - now safe to add user message to conversation history
243+
messages.append({"role":"user","content":user_input})
237244
exceptGuardrailTripwireTriggeredase:
238245
info=getattr(e,"guardrail_result",None)
239246
info=info.infoifinfoelse {}
@@ -252,29 +259,29 @@ async def main(malicious: bool = False) -> None:
252259
border_style="red",
253260
)
254261
)
262+
# Guardrail blocked - user message NOT added to history
255263
continue
256264

257265
iftool_calls:
258-
# Add assistant message with tool calls to conversation
259-
messages.append(
260-
{
261-
"role":"assistant",
262-
"content":message.content,
263-
"tool_calls": [
264-
{
265-
"id":call.id,
266-
"type":"function",
267-
"function": {
268-
"name":call.function.name,
269-
"arguments":call.function.argumentsor"{}",
270-
},
271-
}
272-
forcallintool_calls
273-
],
274-
}
275-
)
276-
277-
# Execute tool calls
266+
# Prepare assistant message with tool calls (don't append yet)
267+
assistant_message= {
268+
"role":"assistant",
269+
"content":message.content,
270+
"tool_calls": [
271+
{
272+
"id":call.id,
273+
"type":"function",
274+
"function": {
275+
"name":call.function.name,
276+
"arguments":call.function.argumentsor"{}",
277+
},
278+
}
279+
forcallintool_calls
280+
],
281+
}
282+
283+
# Execute tool calls and collect results (don't append yet)
284+
tool_messages= []
278285
forcallintool_calls:
279286
fname=call.function.name
280287
fargs=json.loads(call.function.argumentsor"{}")
@@ -293,7 +300,7 @@ async def main(malicious: bool = False) -> None:
293300
"ssn":"123-45-6789",
294301
"credit_card":"4111-1111-1111-1111",
295302
}
296-
messages.append(
303+
tool_messages.append(
297304
{
298305
"role":"tool",
299306
"tool_call_id":call.id,
@@ -302,7 +309,7 @@ async def main(malicious: bool = False) -> None:
302309
}
303310
)
304311
else:
305-
messages.append(
312+
tool_messages.append(
306313
{
307314
"role":"tool",
308315
"tool_call_id":call.id,
@@ -311,9 +318,13 @@ async def main(malicious: bool = False) -> None:
311318
}
312319
)
313320

314-
# Final call
321+
# Final call with tool results (pass inline without mutating messages)
315322
try:
316-
resp=awaitclient.chat.completions.create(model="gpt-4.1-nano",messages=messages,tools=tools)
323+
resp=awaitclient.chat.completions.create(
324+
model="gpt-4.1-nano",
325+
messages=messages+ [assistant_message]+tool_messages,
326+
tools=tools,
327+
)
317328

318329
print_guardrail_results("final",resp)
319330
final_message=resp.llm_response.choices[0].message
@@ -325,7 +336,9 @@ async def main(malicious: bool = False) -> None:
325336
)
326337
)
327338

328-
# Add final assistant response to conversation
339+
# Guardrails passed - now safe to add all messages to conversation history
340+
messages.append(assistant_message)
341+
messages.extend(tool_messages)
329342
messages.append({"role":"assistant","content":final_message.content})
330343
exceptGuardrailTripwireTriggeredase:
331344
info=getattr(e,"guardrail_result",None)
@@ -345,6 +358,7 @@ async def main(malicious: bool = False) -> None:
345358
border_style="red",
346359
)
347360
)
361+
# Guardrail blocked - tool results NOT added to history
348362
continue
349363
else:
350364
# No tool calls; just print assistant content and add to conversation

‎examples/basic/pii_mask_example.py‎

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,20 @@
6969
asyncdefprocess_input(
7070
guardrails_client:GuardrailsAsyncOpenAI,
7171
user_input:str,
72+
messages:list[dict],
7273
)->None:
7374
"""Process user input using GuardrailsClient with automatic PII masking.
7475
7576
Args:
7677
guardrails_client: GuardrailsClient instance with PII masking configuration.
7778
user_input: User's input text.
79+
messages: Conversation history (modified in place after guardrails pass).
7880
"""
7981
try:
80-
# Use GuardrailsClient - it handles all PII masking automatically
82+
# Pass user input inline WITHOUT mutating messages first
83+
# Only add to messages AFTER guardrails pass and LLM call succeeds
8184
response=awaitguardrails_client.chat.completions.create(
82-
messages=[
83-
{
84-
"role":"system",
85-
"content":"You are a helpful assistant. Comply with the user's request.",
86-
},
87-
{"role":"user","content":user_input},
88-
],
85+
messages=messages+ [{"role":"user","content":user_input}],
8986
model="gpt-4",
9087
)
9188

@@ -125,11 +122,16 @@ async def process_input(
125122
)
126123
)
127124

125+
# Guardrails passed - now safe to add to conversation history
126+
messages.append({"role":"user","content":user_input})
127+
messages.append({"role":"assistant","content":content})
128+
128129
exceptGuardrailTripwireTriggeredasexc:
129130
stage_name=exc.guardrail_result.info.get("stage_name","unknown")
130131
guardrail_name=exc.guardrail_result.info.get("guardrail_name","unknown")
131132
console.print(f"[bold red]Guardrail '{guardrail_name}' triggered in stage '{stage_name}'![/bold red]")
132133
console.print(Panel(str(exc.guardrail_result),title="Guardrail Result",border_style="red"))
134+
# Guardrail blocked - user message NOT added to history
133135
raise
134136

135137

@@ -138,14 +140,21 @@ async def main() -> None:
138140
# Initialize GuardrailsAsyncOpenAI with PII masking configuration
139141
guardrails_client=GuardrailsAsyncOpenAI(config=PIPELINE_CONFIG)
140142

143+
messages:list[dict]= [
144+
{
145+
"role":"system",
146+
"content":"You are a helpful assistant. Comply with the user's request.",
147+
}
148+
]
149+
141150
withsuppress(KeyboardInterrupt,asyncio.CancelledError):
142151
whileTrue:
143152
try:
144153
user_input=input("\nEnter a message: ").strip()
145154
ifuser_input.lower()=="exit":
146155
break
147156

148-
awaitprocess_input(guardrails_client,user_input)
157+
awaitprocess_input(guardrails_client,user_input,messages)
149158

150159
exceptEOFError:
151160
break

‎examples/basic/structured_outputs_example.py‎

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,39 +23,64 @@ class UserInfo(BaseModel):
2323
"version":1,
2424
"guardrails": [
2525
{"name":"Moderation","config": {"categories": ["hate","violence"]}},
26+
{
27+
"name":"Custom Prompt Check",
28+
"config": {
29+
"model":"gpt-4.1-nano",
30+
"confidence_threshold":0.7,
31+
"system_prompt_details":"Check if the text contains any math problems.",
32+
},
33+
},
2634
],
2735
},
2836
}
2937

3038

31-
asyncdefextract_user_info(guardrails_client:GuardrailsAsyncOpenAI,text:str)->UserInfo:
32-
"""Extract user information using responses_parse with structured output."""
39+
asyncdefextract_user_info(
40+
guardrails_client:GuardrailsAsyncOpenAI,
41+
text:str,
42+
previous_response_id:str|None=None,
43+
)->tuple[UserInfo,str]:
44+
"""Extract user information using responses.parse with structured output."""
3345
try:
46+
# Use responses.parse() for structured outputs with guardrails
47+
# Note: responses.parse() requires input as a list of message dicts
3448
response=awaitguardrails_client.responses.parse(
35-
input=[{"role":"system","content":"Extract user information from the provided text."}, {"role":"user","content":text}],
49+
input=[
50+
{"role":"system","content":"Extract user information from the provided text."},
51+
{"role":"user","content":text},
52+
],
3653
model="gpt-4.1-nano",
3754
text_format=UserInfo,
55+
previous_response_id=previous_response_id,
3856
)
3957

4058
# Access the parsed structured output
4159
user_info=response.llm_response.output_parsed
4260
print(f"✅ Successfully extracted:{user_info.name},{user_info.age},{user_info.email}")
4361

44-
returnuser_info
62+
# Return user info and response ID (only returned if guardrails pass)
63+
returnuser_info,response.llm_response.id
4564

46-
exceptGuardrailTripwireTriggeredasexc:
47-
print(f"❌ Guardrailtriggered:{exc}")
65+
exceptGuardrailTripwireTriggered:
66+
# Guardrailblocked - no response ID returned, conversation history unchanged
4867
raise
4968

5069

5170
asyncdefmain()->None:
52-
"""Interactive loop demonstrating structured outputs."""
71+
"""Interactive loop demonstrating structured outputs with conversation history."""
5372
# Initialize GuardrailsAsyncOpenAI
5473
guardrails_client=GuardrailsAsyncOpenAI(config=PIPELINE_CONFIG)
74+
75+
# Use previous_response_id to maintain conversation history with responses API
76+
response_id:str|None=None
77+
5578
whileTrue:
5679
try:
5780
text=input("Enter text to extract user info. Include name, age, and email: ")
58-
user_info=awaitextract_user_info(guardrails_client,text)
81+
82+
# Extract user info - only updates response_id if guardrails pass
83+
user_info,response_id=awaitextract_user_info(guardrails_client,text,response_id)
5984

6085
# Demonstrate structured output clearly
6186
print("\n✅ Parsed structured output:")
@@ -66,6 +91,7 @@ async def main() -> None:
6691
print("\nExiting.")
6792
break
6893
exceptGuardrailTripwireTriggeredasexc:
94+
# Guardrail blocked - response_id unchanged, so blocked message not in history
6995
print(f"🛑 Guardrail triggered:{exc}")
7096
continue
7197
exceptExceptionase:

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp