Crescendo backtracking keeps the refused prompt when the objective target made a tool call
Maintainer thường phản hồi trong vòng 2 ngày
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức phù hợp với người mới
- 30/100
Hướng nghiên cứu
The fixed-count slice is in duplicate_conversation_excluding_last_turn_async in pyrit/memory/memory_interface.py, and its only caller is pyrit/executor/attack/multi_turn/crescendo.py. Run the reproduction script in the issue to see the backtracked conversation keep question 2 and its function_call. Pull request #3253 is already open against this, so read it first. Done means a backtrack removes every message of the refused turn, including tool-call and tool-output messages, and leaves no orphan function_call.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Describe the bug
When CrescendoAttack backtracks after a refusal, duplicate_conversation_excluding_last_turn_async is supposed to drop the last turn, "before the last user request" per its docstring. It instead drops a fixed number of sequences: 1 if the last message is user or system, otherwise 2. That assumes a turn is one user message plus one assistant message.
An OpenAIResponseTarget with tools (custom_functions, tools or tool_providers) returns several messages per turn: the function call, the tool output and the final answer, and PromptNormalizer stores each one as its own sequence. If the refused turn made a tool call, the backtracked conversation keeps the refused prompt and that turn's function call, and drops only the tool output and the refusal. The next turn then sends the refused prompt again, plus a function_call with no matching function_call_output.
This is the only caller of the method in the library (crescendo.py).
Steps/Code to Reproduce
import asyncio
import json
from pyrit.memory import CentralMemory
from pyrit.models import MessagePiece
from pyrit.setup import IN_MEMORY, initialize_pyrit_async
CONVERSATION_ID = "crescendo-with-tools"
def tool_calling_turn(sequence: int, prompt: str, call_id: str, answer: str) -> list[MessagePiece]:
# What OpenAIResponseTarget's tool loop stores for one turn: the prompt, the model's function_call,
# the tool output and the final answer, each as its own message.
return [
MessagePiece(role="user", original_value=prompt, conversation_id=CONVERSATION_ID, sequence=sequence),
MessagePiece(
role="assistant",
original_value=json.dumps({"type": "function_call", "call_id": call_id, "name": "lookup", "arguments": "{}"}),
original_value_data_type="function_call",
conversation_id=CONVERSATION_ID,
sequence=sequence + 1,
),
MessagePiece(
role="tool",
original_value=json.dumps({"type": "function_call_output", "call_id": call_id, "output": "result"}),
original_value_data_type="function_call_output",
conversation_id=CONVERSATION_ID,
sequence=sequence + 2,
),
MessagePiece(role="assistant", original_value=answer, conversation_id=CONVERSATION_ID, sequence=sequence + 3),
]
async def main() -> None:
await initialize_pyrit_async(memory_db_type=IN_MEMORY)
memory = CentralMemory.get_memory_instance()
pieces = tool_calling_turn(0, "question 1", "call_1", "answer 1")
pieces += tool_calling_turn(4, "question 2", "call_2", "I can't help with that")
await memory.add_message_pieces_to_memory_async(message_pieces=pieces)
# What CrescendoAttack does after the refusal scorer flags "question 2"
backtracked_id = await memory.duplicate_conversation_excluding_last_turn_async(conversation_id=CONVERSATION_ID)
for piece in await memory.get_message_pieces_async(conversation_id=backtracked_id):
print(piece.api_role, piece.original_value_data_type, piece.original_value)
if __name__ == "__main__":
asyncio.run(main())
Expected Results
Only the first turn is kept:
user text question 1
assistant function_call {"type": "function_call", "call_id": "call_1", "name": "lookup", "arguments": "{}"}
tool function_call_output {"type": "function_call_output", "call_id": "call_1", "output": "result"}
assistant text answer 1
Actual Results
The refused prompt and its function call are kept:
user text question 1
assistant function_call {"type": "function_call", "call_id": "call_1", "name": "lookup", "arguments": "{}"}
tool function_call_output {"type": "function_call_output", "call_id": "call_1", "output": "result"}
assistant text answer 1
user text question 2
assistant function_call {"type": "function_call", "call_id": "call_2", "name": "lookup", "arguments": "{}"}
With a real OpenAIResponseTarget (only the OpenAI client mocked) and PromptNormalizer, the request sent after the backtrack still contains the refused question, and its input has function_call ids ['call_1', 'call_2'] but function_call_output ids only ['call_1'].
Versions
- OS: macOS (Apple Silicon)
- Python version: 3.12.13
- PyRIT version: installed from main (68487b2e) in editable mode
- Ngôn ngữ chính
- Python
- Star
- 4.6k
- Fork
- 944
- Merge trung bình
- 2 ngày 18 giờ
- Pull request đã merge (30 ngày)
- 358
Chuẩn bị môi trường
Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Không có hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của microsoft/PyRIT
-
BUG: label queries only return the final conversation of an attackCó thể đã có người làm @u7k4rs6 đã nhận 2 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
microsoft/PyRIT#3095 · 4 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 2 ngày
-
BUG Configuration keeps runtime-status errors after polling recoversCó thể đã có người làm @joeyvictorino đã nhận 5 ngày trước. Đang mởBug: triage GUI help wanted
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 86/100
microsoft/PyRIT#2868 · 6 bình luận ·
Maintainer thường phản hồi trong vòng 2 ngày
-
BUG BinAsciiConverter UUencode output cannot be decoded when all words are convertedCó thể đã có người làm @barkha1research-ai đã nhận hôm nay. Đang mởBug: triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 25/100
Maintainer thường phản hồi trong vòng 2 ngày
-
FEAT Proposal: red-team decision models as targets (decision flip-rate testing)Có thể đã có người làm @shashank03-dev đã nhận hôm nay. Đang mở
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 15/100
microsoft/PyRIT#3246 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 2 ngày
-
BUG: QRCodeConverter and PDFConverter identifiers ignore module colors, layout and injection itemsCó thể đã có người làm @ShivamSharma43 đã nhận 1 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 25/100
Maintainer thường phản hồi trong vòng 2 ngày
Tất cả issue của microsoft/PyRIT
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
huggingface/sentence-transformers#4186 ·
Maintainer thường phản hồi trong vòng 5 ngày
-
documentation
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 92/100
QuantEcon/QuantEcon.py#988 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
fabriziosalmi/certmate#1247 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
Maintainer thường phản hồi trong vòng 1 ngày
-
documentation good first issue
Độ khó 2/5 Nửa ngày Mức phù hợp với người mới 72/100
sujithputta02/Esapay#13 ·