Prompt Engineering — System Prompts, Few-Shot, CoT
Think of class attributes like shared global variables for all instances of that class. If you change a mutable class attribute from one instance, that change is immediately visible to all other instances because they are all pointing to the exact same object in memory.
__init__ or construct them immutably within the execution scope to prevent state leakage in concurrent systems.The Setup
You are building a multi-user chatbot API using FastAPI. You define an agent class with a base system prompt and a method to dynamically append user-specific metadata or few-shot examples before sending the prompt to the LLM.
What Does This Print?
import asyncio
class TranslationAgent:
# Shared class-level prompt template state
system_prompt = {"role": "system", "content": "Translate the text."}
def __init__(self, user_lang: str):
self.user_lang = user_lang
async def build_messages(self, user_input: str) -> list:
# Mutating the shared dictionary directly
self.system_prompt["content"] += f" Translate specifically to {self.user_lang}."
return [self.system_prompt, {"role": "user", "content": user_input}]
async def run_concurrent():
agent_a = TranslationAgent("Spanish")
agent_b = TranslationAgent("Japanese")
# Simulate simultaneous user requests arriving in the async loop
return await asyncio.gather(
agent_a.build_messages("Hello"),
agent_b.build_messages("Hello")
)
print(asyncio.run(run_concurrent()))
The Output
Both agents output a corrupted prompt containing instruction mutations from both tasks. Because system_prompt is a class-level variable, it is bound to the class namespace and shared globally across all instances. Mutating it in place leaks state between requests, causing hallucinated translations or security boundaries breaking.
Why Python Does This
In Python, class-level attributes are created when the class definition is executed and are stored in the class's __dict__. When you access self.system_prompt, Python's attribute lookup resolution first checks the instance dictionary self.__dict__. Finding nothing there, it traverses up to the class dictionary. By executing self.system_prompt["content"] += ..., you do not assign a new attribute to the instance; instead, you mutate the mutable dict object stored in the class definition. Since all instances of TranslationAgent reference the exact same dictionary in memory, modifications are immediately visible to all instances, and concurrent operations in an async event loop interleave these mutation operations unpredictably.
The Fix
class TranslationAgent:
def __init__(self, user_lang: str):
self.user_lang = user_lang
# Define instance-level system prompt to isolate state
# or use deep copies to guarantee safety
self.system_prompt = {"role": "system", "content": "Translate the text."}
async def build_messages(self, user_input: str) -> list:
# Construct prompt dynamically without mutating base class dictionaries
resolved_content = f"{self.system_prompt['content']} Translate specifically to {self.user_lang}."
return [
{"role": "system", "content": resolved_content},
{"role": "user", "content": user_input}
]
Moving the system_prompt initialization to the __init__ method ensures that each TranslationAgent instance gets its own independent copy of the prompt context. This prevents any instance from inadvertently modifying the prompt used by other concurrent agents, maintaining isolated and correct state.
How This Fails in Real Systems
A medical tech startup built an AI triage assistant. The system prompt contained clinical safety guidelines, which were dynamically modified per patient. Due to a shared class-level dictionary mutation bug, patient B's symptoms and private medical history were appended to patient A's system prompt during concurrent webhook handling, causing the LLM to hallucinate diagnosis recommendations based on crossed data.
Key Takeaway
__init__ or construct them immutably within the execution scope to prevent state leakage in concurrent systems.