# Best Practices for Legal Services Prompt Engineering with Claude: A Complex Prompt Template Approach

> Master legal services prompt engineering with Claude. Learn best practices for complex prompts, separating data from instructions and ensuring traceability for reliable AI.

- Repository: [Anthropic/prompt-eng-interactive-tutorial](https://github.com/anthropics/prompt-eng-interactive-tutorial)
- Tags: best-practices
- Published: 2026-03-09

---

**Separate raw legal data from instructions using input variables and enforce traceability through explicit citation formats to build reliable Claude prompts for legal research.**

The `anthropics/prompt-eng-interactive-tutorial` repository provides a production-ready framework for legal services prompt engineering with Claude, demonstrating how structured templates reduce hallucination risks while maintaining flexibility for iterative refinement. The approach centers on a reusable complex-prompt architecture that isolates dynamic legal content from static system instructions, enabling lawyers to swap case materials without rewriting core prompts.

## The Complex Prompt Architecture

The legal-service implementation in `Anthropic 1P/09_Complex_Prompts_from_Scratch.ipynb` establishes a three-tier structure that separates concerns between data, instructions, and formatting constraints. This modular design aligns with Claude's Messages API expectations while accommodating the variable nature of legal research workflows.

### Input Variables for Legal Data Separation

The template defines distinct **input variables** that inject raw legal documents separately from the prompt text. This separation allows practitioners to update case law or statutes without modifying the underlying prompt logic.

```python

# First input variable – the legal document

LEGAL_RESEARCH = """
<search_results>
<search_result id=1>
... (legal excerpts) ...
</search_result>
<search_result id=2>
... (more excerpts) ...
</search_result>
</search_results>
"""

# Second input variable – the user's question

QUESTION = "Are there any laws about what to do with pets during a hurricane?"

```

By encapsulating the legal research within `LEGAL_RESEARCH` and the client query within `QUESTION`, the system maintains clean boundaries between factual inputs and instructional context.

### Prompt Elements and Role Definition

The architecture mandates specific **prompt elements** that establish Claude's behavioral parameters. According to the source implementation, every Messages API call must begin with a `user` role header to ensure the model treats the legal query as a user request rather than system configuration. The template incorporates system instructions, conversational role definition, and example-based learning within a flexible ordering scheme that supports experimentation.

## Citation and Formatting Requirements

Legal applications demand rigorous source attribution. The tutorial enforces strict citation protocols and output constraints that align with professional legal writing standards.

### Enforcing Traceable Legal Citations

The prompt template requires Claude to place citations in brackets containing the search-index ID followed by a period (e.g., `[1.]`). This convention appears in `09_Complex_Prompts_from_Scratch.ipynb` where the prompt explicitly states that citations must reference the corresponding `<search_result id>` from the injected research data. This bracket-ID format guarantees traceability, allowing attorneys to verify every factual claim against the underlying legal authority.

### Output Structure Constraints

The notebook emphasizes **output constraints** that force Claude to produce concise, well-structured answers using bullet points or numbered lists. These formatting requirements prevent verbose responses and ensure compliance with legal document standards. The implementation notes that "we've **changed around the ordering of a few elements** to showcase that prompt structure can be flexible," enabling rapid A/B testing of different constraint placements without breaking the citation logic.

## Implementation Workflow

Building on the template from `09_Complex_Prompts_from_Scratch.ipynb`, the following workflow integrates legal research into Claude's Messages API:

```python

# 1️⃣ Load legal research (could be from a vector store, file, etc.)

LEGAL_RESEARCH = """
<search_results>
<search_result id=1>
... (legal excerpts) ...
</search_result>
<search_result id=2>
... (more excerpts) ...
</search_result>
</search_results>
"""

# 2️⃣ Define the client's question

QUESTION = "Are there any laws about what to do with pets during a hurricane?"

# 3️⃣ Assemble the complex prompt (simplified)

prompt = f"""
You are a knowledgeable legal assistant. 
Use the provided legal research to answer the user's question. 
Cite each fact with the corresponding <search_result id> in brackets, e.g., [1.].

<legal_research>
{LEGAL_RESEARCH}
</legal_research>

Question: {QUESTION}
"""

# 4️⃣ Call Claude (pseudocode – replace with actual SDK call)

response = claude.messages([{"role": "user", "content": prompt}])

print(response.content)

```

This workflow ensures that raw legal texts remain modular while the prompt maintains consistent instructional framing across different cases.

## Supporting Techniques from the Tutorial

While `09_Complex_Prompts_from_Scratch.ipynb` provides the core template, complementary notebooks in the repository address specific legal AI challenges:

- **Hallucination Prevention**: `Anthropic 1P/08_Avoiding_Hallucinations.ipynb` demonstrates grounding techniques that complement legal prompt design by restricting Claude to provided search results rather than latent knowledge.
- **Output Formatting**: `Anthropic 1P/05_Formatting_Output_and_Speaking_for_Claude.ipynb` details conventions for legal document structures, ensuring AI-generated briefs and memos follow professional formatting standards.

## Summary

- **Separate data from instructions** using input variables like `LEGAL_RESEARCH` and `QUESTION` to enable rapid document swapping without prompt rewriting.
- **Enforce citation traceability** through bracket-ID formats (e.g., `[1.]`) that map directly to search result indices.
- **Begin with user roles** in the Messages API to align with Claude's conversational architecture and prevent instruction hierarchy confusion.
- **Modularize prompt sections** (system instructions, examples, output format) to support iterative A/B testing of legal prompt variations.
- **Combine with grounding techniques** from `08_Avoiding_Hallucinations.ipynb` to minimize fabrication risks in legal analysis.

## Frequently Asked Questions

### How do I prevent Claude from hallucinating case law in legal prompts?

Ground all responses in explicit external data by including the relevant statutes or cases in the `LEGAL_RESEARCH` input variable. According to `08_Avoiding_Hallucinations.ipynb`, instructing Claude to cite only the provided search results using the bracket-ID format significantly reduces hallucination risks in legal research tasks.

### What is the correct message role structure for legal queries in Claude's API?

Always initiate the Messages API call with a `user` role containing the assembled prompt. As implemented in `09_Complex_Prompts_from_Scratch.ipynb`, starting with the `user` role ensures Claude interprets the legal query as a client request rather than system configuration, maintaining proper conversational flow.

### Can I reorder prompt elements without breaking citation functionality?

Yes. The tutorial explicitly showcases flexibility by changing the ordering of non-dependent elements while preserving the citation bracket format. However, maintain the separation between input variables (the legal data) and prompt instructions to ensure consistent traceability.

### How do I format citations to meet legal professional standards?

Require Claude to place citations in brackets containing the search-index ID followed by a period, such as `[1.]`. This format, specified in `09_Complex_Prompts_from_Scratch.ipynb`, creates a clear mapping between factual claims and their source materials within the `<search_results>` block.