
Context Fundamentals
- 66 installs
- 17.6k repo stars
- Updated August 2, 2026
- muratcankoylan/agent-skills-for-context-engineering
Helps with ai & agent building tasks during AI-assisted development.
About
context-fundamentals is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- context-fundamentals
- AI & Agent Building
- AI-coding skill
Context Fundamentals by the numbers
- 66 all-time installs (skills.sh)
- +9 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #6,001 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/muratcankoylan/agent-skills-for-context-engineering --skill context-fundamentalsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 66 |
|---|---|
| repo stars | ★ 17.6k |
| Last updated | August 2, 2026 |
| Repository | muratcankoylan/agent-skills-for-context-engineering ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Context Engineering Fundamentals
Context is the complete state available to a language model at inference time — system instructions, tool definitions, retrieved documents, message history, and tool outputs. Context engineering is the discipline of curating the smallest high-signal token set that maximizes the likelihood of desired outcomes. Every paragraph below earns its tokens by teaching a non-obvious technique or providing an actionable threshold.
When to Activate
Activate this skill when:
- Designing new agent systems or modifying existing architectures
- Debugging unexpected agent behavior that may relate to context
- Optimizing context usage to reduce token costs or improve performance
- Onboarding new team members to context engineering concepts
- Reviewing context-related design decisions
Core Concepts
Treat context as a finite attention budget, not a storage bin. Every token added competes for the model's attention and depletes a budget that cannot be refilled mid-inference. The engineering problem is maximizing utility per token against three constraints: the hard token limit, the softer effective-capacity ceiling (typically 60-70% of the advertised window), and the U-shaped attention curve that penalizes information placed in the middle of context.
Apply four principles when assembling context:
1. Informativity over exhaustiveness — include only what matters for the current decision; design systems that can retrieve additional information on demand. 2. Position-aware placement — place critical constraints at the beginning and end of context, where recall accuracy runs 85-95%; the middle drops to 76-82% (the "lost-in-the-middle" effect). 3. Progressive disclosure — load skill names and summaries at startup; load full content only when a skill activates for a specific task. 4. Iterative curation — context engineering is not a one-time prompt-writing exercise but an ongoing discipline applied every time content is passed to the model.
Detailed Topics
The Anatomy of Context
System Prompts Organize system prompts into distinct sections using XML tags or Markdown headers (background, instructions, tool guidance, output format). System prompts persist throughout the conversation, so place the most critical constraints at the beginning and end where attention is strongest.
Calibrate instruction altitude to balance two failure modes. Too-low altitude hardcodes brittle logic that breaks when conditions shift. Too-high altitude provides vague guidance that fails to give concrete signals for desired behavior. Aim for heuristic-driven instructions: specific enough to guide behavior, flexible enough to generalize — for example, numbered steps with room for judgment at each step.
Start minimal, then add instructions reactively based on observed failure modes rather than preemptively stuffing edge cases. Curate diverse, canonical few-shot examples that portray expected behavior instead of listing every possible scenario.
Tool Definitions Write tool descriptions that answer three questions: what the tool does, when to use it, and what it returns. Include usage context, parameter defaults, and error cases — agents cannot disambiguate tools that a human engineer cannot disambiguate either.
Keep the tool set minimal. Consolidate overlapping tools because bloated tool sets create ambiguous decision points and consume disproportionate context after JSON serialization (tool schemas typically inflate 2-3x compared to equivalent plain-text descriptions).
Retrieved Documents Maintain lightweight identifiers (file paths, stored queries, web links) and load data into context dynamically using just-in-time retrieval. This mirrors human cognition — maintain an index, not a copy. Strong identifiers (e.g., customer_pricing_rates.json) let agents locate relevant files even without search tools; weak identifiers (e.g., data/file1.json) force unnecessary loads.
When chunking large documents, split at natural semantic boundaries (section headers, paragraph breaks) rather than arbitrary character limits that sever mid-concept.
Message History Message history serves as the agent's scratchpad memory for tracking progress, maintaining task state, and preserving reasoning across turns. For long-running tasks, it can grow to dominate context usage — monitor and apply compaction before it crowds out active instructions.
Cyclically refine history: once a tool has been called deep in the conversation, the raw result rarely needs to remain verbatim. Replace stale tool outputs with compact summaries or references to reduce low-signal bulk.
Tool Outputs Tool outputs typically dominate context — research shows observations can reach 83.9% of total tokens in agent trajectories. Apply observation masking: replace verbose outputs with compact references once the agent has processed the result. Retain only the five most recently accessed file contents; compress or evict older ones.
Context Windows and Attention Mechanics
The Attention Budget For n tokens, the attention mechanism computes n-squared pairwise relationships. As context grows, the model's ability to maintain these relationships degrades — not as a hard cliff but as a performance gradient. Models trained predominantly on shorter sequences have fewer specialized parameters for context-wide dependencies, creating an effective ceiling well below the nominal window size.
Design for this gradient: assume effective capacity is 60-70% of the advertised window. A 200K-token model starts degrading around 120-140K tokens, and complex retrieval accuracy can drop to as low as 15% at extreme lengths.
Position Encoding Limits Position encoding interpolation extends sequence handling beyond training lengths but introduces degradation in positional precision. Expect reduced accuracy for information retrieval and long-range reasoning at extended contexts compared to performance on shorter inputs.
Progressive Disclosure in Practice Implement progressive disclosure at three levels:
1. Skill selection — load only names and descriptions at startup; activate full skill content on demand. 2. Document loading — load summaries first; fetch detail sections only when the task requires them. 3. Tool result retention — keep recent results in full; compress or evict older results.
Keep the boundary crisp: if a skill or document is activated, load it fully rather than partially — partial loads create confusing gaps that degrade reasoning quality.
Context Quality Versus Quantity
Reject the assumption that larger context windows solve memory problems. Processing cost grows disproportionately with context length — not just linear cost scaling, but degraded model performance beyond effective capacity thresholds. Long inputs remain expensive even with prefix caching.
Apply the signal-density test: for each piece of context, ask whether removing it would change the model's output. If not, remove it. Redundant content does not merely waste tokens — it actively dilutes attention from high-signal content.
Practical Guidance
File-System-Based Access
Agents with filesystem access implement progressive disclosure naturally. Store reference materials, documentation, and data externally. Load files only when the current task requires them. Leverage the filesystem's own structure as metadata: file sizes suggest complexity, naming conventions hint at purpose, timestamps serve as proxies for relevance.
Hybrid Context Strategies
Pre-load stable context for speed (CLAUDE.md files, project rules, core instructions) but enable autonomous exploration for dynamic content. The decision boundary depends on content volatility:
- Low volatility (project conventions, team standards): pre-load at session start.
- High volatility (code state, external data, user-specific info): retrieve just-in-time to avoid stale context.
For complex multi-hour tasks, maintain a structured notes file (e.g., NOTES.md) that the agent updates as it works. This enables coherence across context resets without keeping everything in the active window.
Context Budgeting
Allocate explicit budgets per component and monitor during development. Implement compaction triggers at 70-80% utilization — do not wait for the window to fill. Design systems that degrade gracefully: when compaction fires, preserve architectural decisions, unresolved bugs, and implementation details while discarding redundant outputs.
For sub-agent architectures, enforce a compression ratio: a sub-agent may explore using tens of thousands of tokens but must return a condensed summary of 1,000-2,000 tokens. This converts exploration breadth into context-efficient results.
Examples
Example 1: Organizing System Prompts
<BACKGROUND_INFORMATION>
You are a Python expert helping a development team.
Current project: Data processing pipeline in Python 3.9+
</BACKGROUND_INFORMATION>
<INSTRUCTIONS>
- Write clean, idiomatic Python code
- Include type hints for function signatures
- Add docstrings for public functions
- Follow PEP 8 style guidelines
</INSTRUCTIONS>
<TOOL_GUIDANCE>
Use bash for shell operations, python for code tasks.
File operations should use pathlib for cross-platform compatibility.
</TOOL_GUIDANCE>
<OUTPUT_DESCRIPTION>
Provide code blocks with syntax highlighting.
Explain non-obvious decisions in comments.
</OUTPUT_DESCRIPTION>Example 2: Progressive Document Loading
# Instead of loading all documentation at once:
# Step 1: Load summary
docs/api_summary.md # Lightweight overview
# Step 2: Load specific section as needed
docs/api/endpoints.md # Only when API calls needed
docs/api/authentication.md # Only when auth context neededGuidelines
1. Treat context as a finite resource with diminishing returns 2. Place critical information at attention-favored positions (beginning and end) 3. Use progressive disclosure to defer loading until needed 4. Organize system prompts with clear section boundaries 5. Monitor context usage during development 6. Implement compaction triggers at 70-80% utilization 7. Design for context degradation rather than hoping to avoid it 8. Prefer smaller high-signal context over larger low-signal context
Gotchas
1. Nominal window is not effective capacity: A model advertising 200K tokens begins degrading around 120-140K. Budget for 60-70% of the nominal window as usable capacity. Exceeding this threshold causes sudden accuracy drops, not gradual degradation — test at realistic context sizes, not toy examples.
2. Character-based token estimates silently drift: The ~4 characters/token heuristic for English prose breaks down for code (2-3 chars/token), URLs and file paths (each slash, dot, and colon is a separate token), and non-English text (often 1-2 chars/token). Use the provider's actual tokenizer (e.g., tiktoken for OpenAI models, Anthropic's token counting API) for any budget-critical calculation.
3. Tool schemas inflate 2-3x after JSON serialization: A tool definition that looks compact in source code expands significantly when serialized — brackets, quotes, colons, and commas each consume tokens. Ten tools with moderate schemas can consume 5,000-8,000 tokens before a single message is sent. Audit serialized tool token counts, not source-code line counts.
4. Message history balloons silently in agentic loops: Each tool call adds both the request and the full response to history. After 20-30 iterations, history can consume 70-80% of the window while the agent shows no visible symptoms until reasoning quality collapses. Set a hard token ceiling on history and trigger compaction proactively.
5. Critical instructions in the middle get lost: The U-shaped attention curve means the middle of context receives 10-40% less recall accuracy than the beginning and end. Never place safety constraints, output format requirements, or behavioral guardrails in the middle of a long system prompt — anchor them at the top or bottom.
6. Progressive disclosure that loads too eagerly defeats its purpose: Loading every "potentially relevant" skill or document at the first hint of relevance recreates the context-stuffing problem. Set strict activation thresholds — a skill should load only when the task explicitly matches its trigger conditions, not when the topic is merely adjacent.
7. Mixing instruction altitudes causes inconsistent behavior: Combining hyper-specific rules ("always use exactly 3 bullet points") with vague directives ("be helpful") in the same prompt creates conflicting signals. Group instructions by altitude level and keep each section internally consistent — either heuristic-driven or prescriptive, not both interleaved.
Integration
This skill provides foundational context that all other skills build upon. It should be studied first before exploring:
- context-degradation - Understanding how context fails
- context-optimization - Techniques for extending context capacity
- multi-agent-patterns - How context isolation enables multi-agent systems
- tool-design - How tool definitions interact with context
References
Internal reference:
- Context Components Reference - Read when: debugging a specific context component (system prompts, tool definitions, message history, tool outputs) or implementing chunking, observation masking, or budget allocation tables
Related skills in this collection:
- context-degradation - Read when: agent performance drops as conversations grow or context fills beyond 60% capacity
- context-optimization - Read when: token costs are too high or compaction/compression strategies are needed
External resources:
- Anthropic's "Effective Context Engineering for AI Agents" — production patterns for compaction, sub-agents, and hybrid retrieval
- Research on transformer attention mechanisms and the lost-in-the-middle effect
- Tokenomics research on agentic software engineering token distribution
---
Skill Metadata
Created: 2025-12-20 Last Updated: 2026-03-17 Author: Agent Skills for Context Engineering Contributors Version: 2.0.0
Context Components: Technical Reference
This document provides detailed technical reference for each context component in agent systems.
System Prompt Engineering
Section Structure
Organize system prompts into distinct sections with clear boundaries. A recommended structure:
<BACKGROUND_INFORMATION>
Context about the domain, user preferences, or project-specific details
</BACKGROUND_INFORMATION>
<INSTRUCTIONS>
Core behavioral guidelines and task instructions
</INSTRUCTIONS>
<TOOL_GUIDANCE>
When and how to use available tools
</TOOL_GUIDANCE>
<OUTPUT_DESCRIPTION>
Expected output format and quality standards
</OUTPUT_DESCRIPTION>This structure allows agents to locate relevant information quickly and enables selective context loading in advanced implementations.
Altitude Calibration
The "altitude" of instructions refers to the level of abstraction. Consider these examples:
Too Low (Brittle):
If the user asks about pricing, check the pricing table in docs/pricing.md.
If the table shows USD, convert to EUR using the exchange rate in
config/exchange_rates.json. If the user is in the EU, add VAT at the
applicable rate from config/vat_rates.json. Format the response with
the currency symbol, two decimal places, and a note about VAT.Too High (Vague):
Help users with pricing questions. Be helpful and accurate.Optimal (Heuristic-Driven):
For pricing inquiries:
1. Retrieve current rates from docs/pricing.md
2. Apply user location adjustments (see config/location_defaults.json)
3. Format with appropriate currency and tax considerations
Prefer exact figures over estimates. When rates are unavailable,
say so explicitly rather than projecting.The optimal altitude provides clear steps while allowing flexibility in execution.
Tool Definition Specification
Schema Structure
Each tool should define:
{
"name": "tool_function_name",
"description": "Clear description of what the tool does and when to use it",
"parameters": {
"type": "object",
"properties": {
"param_name": {
"type": "string",
"description": "What this parameter controls",
"default": "reasonable_default_value"
}
},
"required": ["param_name"]
},
"returns": {
"type": "object",
"description": "What the tool returns and its structure"
}
}Description Engineering
Tool descriptions should answer: what the tool does, when to use it, and what it produces. Include usage context, examples, and edge cases.
Weak Description:
Search the database for customer information.Strong Description:
Retrieve customer information by ID or email.
Use when:
- User asks about a specific customer's details, history, or status
- User provides a customer identifier and needs related information
Returns customer object with:
- Basic info (name, email, account status)
- Order history summary
- Support ticket count
Returns null if customer not found. Returns error if database unreachable.Retrieved Document Management
Identifier Design
Design identifiers that convey meaning and enable efficient retrieval:
Poor identifiers:
data/file1.jsonref/ref.md2024/q3/report
Strong identifiers:
customer_pricing_rates.jsonengineering_onboarding_checklist.md2024_q3_revenue_report.pdf
Strong identifiers allow agents to locate relevant files even without search tools.
Document Chunking Strategy
For large documents, chunk strategically to preserve semantic coherence:
# Pseudocode for semantic chunking
def chunk_document(content):
"""Split document at natural semantic boundaries."""
boundaries = find_section_headers(content)
boundaries += find_paragraph_breaks(content)
boundaries += find_logical_breaks(content)
chunks = []
for i in range(len(boundaries) - 1):
chunk = content[boundaries[i]:boundaries[i+1]]
if len(chunk) > MIN_CHUNK_SIZE and len(chunk) < MAX_CHUNK_SIZE:
chunks.append(chunk)
return chunksAvoid arbitrary character limits that split mid-sentence or mid-concept.
Message History Management
Turn Representation
Structure message history to preserve key information:
{
"role": "user" | "assistant" | "tool",
"content": "message text",
"reasoning": "optional chain-of-thought",
"tool_calls": [list if role="assistant"],
"tool_output": "output if role="tool"",
"summary": "compact summary if conversation is long"
}Summary Injection Pattern
For long conversations, inject summaries at intervals:
def inject_summaries(messages, summary_interval=20):
"""Inject summaries at regular intervals to preserve context."""
summarized = []
for i, msg in enumerate(messages):
summarized.append(msg)
if i > 0 and i % summary_interval == 0:
summary = generate_summary(summarized[-summary_interval:])
summarized.append({
"role": "system",
"content": f"Conversation summary: {summary}",
"is_summary": True
})
return summarizedTool Output Optimization
Response Formats
Provide response format options to control token usage:
def get_customer_response_format():
return {
"format": "concise | detailed",
"fields": ["id", "name", "email", "status", "history_summary"]
}The concise format returns essential fields only; detailed returns complete objects.
Observation Masking
For verbose tool outputs, consider masking patterns:
def mask_observation(output, max_length=500):
"""Replace long observations with compact references."""
if len(output) <= max_length:
return output
reference_id = store_observation(output)
return f"[Previous observation elided. Full content stored at reference {reference_id}]"This preserves information access while reducing token usage.
Context Budget Estimation
Token Counting Approximation
For planning purposes, estimate tokens at approximately 4 characters per token for English text:
1000 words ≈ 7500 characters ≈ 1800-2000 tokensThis is a rough approximation; actual tokenization varies by model and content type.
Context Budget Allocation
Allocate context budget across components:
| Component | Typical Range | Notes |
|---|---|---|
| System prompt | 500-2000 tokens | Stable across session |
| Tool definitions | 100-500 per tool | Grows with tool count |
| Retrieved documents | Variable | Often largest consumer |
| Message history | Variable | Grows with conversation |
| Tool outputs | Variable | Can dominate context |
Monitor actual usage during development to establish baseline allocations.
Progressive Disclosure Implementation
Skill Activation Pattern
def activate_skill_context(skill_name, task_description):
"""Load skill context when task matches skill description."""
skill_metadata = load_all_skill_metadata()
relevant_skills = []
for skill in skill_metadata:
if skill_matches_task(skill, task_description):
relevant_skills.append(skill)
# Load full content only for most relevant skills
for skill in relevant_skills[:MAX_CONCURRENT_SKILLS]:
skill_context = load_skill_content(skill)
inject_into_context(skill_context)Reference Loading Pattern
def get_reference(file_reference):
"""Load reference file only when explicitly needed."""
if not file_reference.is_loaded:
file_reference.content = read_file(file_reference.path)
file_reference.is_loaded = True
return file_reference.contentThis pattern ensures files are loaded once and cached for the session.
"""
Context Management Utilities for Agent Systems.
Public API
----------
Functions:
estimate_token_count — Rough token estimate from text (demo only).
estimate_message_tokens — Token estimate for a message list.
count_tokens_by_type — Break down token usage by context component.
truncate_context — Trim a context string to a token budget.
truncate_messages — Trim message history while preserving structure.
validate_context_structure — Detect empty, oversized, or duplicate sections.
build_agent_context — Assemble an optimized context dict from parts.
Classes:
ContextBuilder — Priority-aware context assembly with budgets.
ProgressiveDisclosureManager — Lazy file loading with caching.
Usage
-----
Import individual utilities or use `build_agent_context` as the high-level
entry point:
from context_manager import build_agent_context
result = build_agent_context(
task="Refactor auth module",
system_prompt="You are a senior Python engineer.",
documents=["# Auth module docs ..."],
)
print(result["usage_report"])
Run this module directly (`python context_manager.py`) for an interactive demo
that builds a sample context and prints the usage report.
Note: Token estimation in this module uses a character-ratio heuristic. For
production systems, replace `estimate_token_count` with a real tokenizer
(tiktoken for OpenAI, Anthropic's token-counting API, etc.).
"""
from __future__ import annotations
import hashlib
from typing import Any, Dict, List, Optional
__all__ = [
"estimate_token_count",
"estimate_message_tokens",
"count_tokens_by_type",
"truncate_context",
"truncate_messages",
"validate_context_structure",
"build_agent_context",
"ContextBuilder",
"ProgressiveDisclosureManager",
]
# ---------------------------------------------------------------------------
# Token estimation
# ---------------------------------------------------------------------------
def estimate_token_count(text: str) -> int:
"""Return a rough token estimate for *text*.
Uses the ~4 characters-per-token heuristic for English prose.
Use when: quick budget checks during development or logging. Do NOT rely
on this for hard budget enforcement — code, URLs, and non-English text
tokenize at very different ratios (see module docstring).
WARNING: Production systems must use a real tokenizer:
- OpenAI models → ``tiktoken``
- Anthropic → Anthropic token-counting API
- Others → provider-specific tokenizer
"""
return len(text) // 4
def estimate_message_tokens(messages: List[Dict[str, Any]]) -> int:
"""Estimate total tokens across a list of chat messages.
Use when: deciding whether to trigger compaction on message history.
Each message adds ~10 tokens of role/formatting overhead on top of
its content tokens.
"""
total = 0
for msg in messages:
content = msg.get("content", "")
total += estimate_token_count(content)
total += 10 # Overhead for role/formatting
return total
def count_tokens_by_type(context: Dict[str, Any]) -> Dict[str, int]:
"""Break down token usage by context component type.
Use when: profiling where tokens are spent so the highest-cost
component can be targeted for compression first.
Recognized keys in *context*: ``system``, ``tools`` (list),
``documents`` (list), ``messages`` (list).
"""
breakdown: Dict[str, int] = {
"system_prompt": 0,
"tool_definitions": 0,
"retrieved_documents": 0,
"message_history": 0,
"tool_outputs": 0,
"other": 0,
}
if "system" in context:
breakdown["system_prompt"] = estimate_token_count(context["system"])
if "tools" in context:
for tool in context["tools"]:
breakdown["tool_definitions"] += estimate_token_count(str(tool))
if "documents" in context:
for doc in context["documents"]:
breakdown["retrieved_documents"] += estimate_token_count(doc)
if "messages" in context:
breakdown["message_history"] = estimate_message_tokens(context["messages"])
return breakdown
# ---------------------------------------------------------------------------
# Context Builder
# ---------------------------------------------------------------------------
class ContextBuilder:
"""Build context with priority-aware budget management.
Use when: assembling context from multiple sources (system prompt,
retrieved documents, task description) and enforcing a hard token
ceiling. Higher-priority sections are kept first when the budget is
tight.
Example::
builder = ContextBuilder(context_limit=80_000)
builder.add_section("system", prompt, priority=10)
builder.add_section("task", task_text, priority=9)
built = builder.build()
"""
def __init__(self, context_limit: int = 100_000) -> None:
self.context_limit: int = context_limit
self.sections: Dict[str, Dict[str, Any]] = {}
self.order: List[str] = []
def add_section(
self,
name: str,
content: str,
priority: int = 0,
category: str = "other",
) -> None:
"""Add or replace a named section.
Higher *priority* values are kept first when the budget is tight.
"""
if name not in self.sections:
self.order.append(name)
self.sections[name] = {
"content": content,
"priority": priority,
"category": category,
"tokens": estimate_token_count(content),
}
def build(self, max_tokens: Optional[int] = None) -> str:
"""Assemble context string within the token budget.
Sections are included in descending priority order until the
budget is exhausted. Returns the concatenated text of all
included sections.
"""
limit = max_tokens or self.context_limit
sorted_sections = sorted(
self.order,
key=lambda n: self.sections[n]["priority"],
reverse=True,
)
context_parts: List[str] = []
current_tokens = 0
for name in sorted_sections:
section = self.sections[name]
section_tokens = section["tokens"]
if current_tokens + section_tokens <= limit:
context_parts.append(section["content"])
current_tokens += section_tokens
return "\n\n".join(context_parts)
def get_usage_report(self) -> Dict[str, Any]:
"""Return a summary of current context utilization.
Use when: logging context composition during development or
deciding whether to trigger compaction.
"""
total = sum(s["tokens"] for s in self.sections.values())
return {
"total_tokens": total,
"limit": self.context_limit,
"utilization": total / self.context_limit if self.context_limit else 0,
"by_section": {
name: s["tokens"] for name, s in self.sections.items()
},
"status": self._get_status(total),
}
def _get_status(self, total: int) -> str:
"""Return 'critical', 'warning', or 'healthy' based on utilization."""
ratio = total / self.context_limit if self.context_limit else 0
if ratio > 0.9:
return "critical"
elif ratio > 0.7:
return "warning"
else:
return "healthy"
# ---------------------------------------------------------------------------
# Context Truncation
# ---------------------------------------------------------------------------
def truncate_context(
context: str,
max_tokens: int,
preserve_start: bool = True,
) -> str:
"""Truncate *context* to approximately *max_tokens*.
Use when: a single large text block must fit a hard budget and
semantic chunking is not available.
Set *preserve_start* to ``True`` (default) to keep the beginning
(system prompts, top-of-file content) or ``False`` to keep the end
(most recent information).
"""
tokens = context.split()
if len(tokens) <= max_tokens:
return context
if preserve_start:
kept = tokens[:max_tokens]
else:
kept = tokens[-max_tokens:]
return " ".join(kept)
def truncate_messages(
messages: List[Dict[str, Any]],
max_tokens: int,
) -> List[Dict[str, Any]]:
"""Truncate message history while preserving structural integrity.
Use when: message history exceeds budget and compaction has not yet
been implemented. Keeps: (1) the system prompt, (2) any existing
summary message, and (3) the most recent messages that fit.
Strategy:
1. Always keep the system prompt.
2. Keep any existing summary message.
3. Fill remaining budget with the most recent messages.
"""
system_prompt: Optional[Dict[str, Any]] = None
recent_messages: List[Dict[str, Any]] = []
summary: Optional[Dict[str, Any]] = None
for msg in messages:
if msg.get("role") == "system":
system_prompt = msg
elif msg.get("is_summary"):
summary = msg
else:
recent_messages.append(msg)
tokens_for_system = (
estimate_token_count(system_prompt["content"]) if system_prompt else 0
)
tokens_for_summary = (
estimate_token_count(summary["content"]) if summary else 0
)
available = max_tokens - tokens_for_system - tokens_for_summary
tokens_for_recent = estimate_message_tokens(recent_messages)
if tokens_for_recent > available:
truncated_recent: List[Dict[str, Any]] = []
current_tokens = 0
for msg in reversed(recent_messages):
msg_tokens = estimate_token_count(msg.get("content", ""))
if current_tokens + msg_tokens <= available:
truncated_recent.insert(0, msg)
current_tokens += msg_tokens
recent_messages = truncated_recent
result: List[Dict[str, Any]] = []
if system_prompt:
result.append(system_prompt)
if summary:
result.append(summary)
result.extend(recent_messages)
return result
# ---------------------------------------------------------------------------
# Context Validation
# ---------------------------------------------------------------------------
def validate_context_structure(context: Dict[str, Any]) -> Dict[str, Any]:
"""Validate a context dict for common structural issues.
Use when: testing context assembly before sending to the model.
Checks for empty sections, excessive length, missing recommended
sections, and potential duplicate content.
Returns a dict with ``valid`` (bool), ``issues`` (list), and
``recommendations`` (list).
"""
issues: List[str] = []
recommendations: List[str] = []
# Check for empty sections (skip list-type values like documents
# which are legitimately empty when no documents are retrieved)
for section, content in context.items():
if content is None or (isinstance(content, str) and not content):
issues.append(f"Empty {section} section")
recommendations.append(f"Remove or populate {section}")
# Check for excessive length
total_tokens = sum(estimate_token_count(str(c)) for c in context.values())
if total_tokens > 80_000:
issues.append(
f"Context length ({total_tokens} tokens) exceeds recommended limit"
)
recommendations.append("Consider context compaction or partitioning")
# Check for missing sections
recommended_sections = ["system", "task"]
for section in recommended_sections:
if section not in context:
issues.append(f"Missing recommended section: {section}")
recommendations.append(
f"Add {section} section with relevant information"
)
# Check for duplicate content (first 1000 chars, hashed for consistency)
seen_content: set[str] = set()
for section, content in context.items():
content_str = str(content)[:1000]
content_hash = hashlib.md5(content_str.encode()).hexdigest()
if content_hash in seen_content:
issues.append(f"Potential duplicate content in {section}")
seen_content.add(content_hash)
return {
"valid": len(issues) == 0,
"issues": issues,
"recommendations": recommendations,
}
# ---------------------------------------------------------------------------
# Progressive Disclosure
# ---------------------------------------------------------------------------
class ProgressiveDisclosureManager:
"""Lazy loader for progressive disclosure of file-based context.
Use when: an agent has access to many reference files but should
only pay the token cost for files that the current task actually
needs. Summaries are loaded first; detail files are loaded on demand
and cached for the session.
Example::
pdm = ProgressiveDisclosureManager(base_dir="docs")
overview = pdm.load_summary("docs/api_summary.md")
# ... later, when detail is needed ...
detail = pdm.load_detail("docs/api/endpoints.md")
"""
def __init__(self, base_dir: str = ".") -> None:
self.base_dir: str = base_dir
self.loaded_files: Dict[str, str] = {}
def load_summary(self, summary_path: str) -> str:
"""Load a summary file, returning cached content if available."""
if summary_path in self.loaded_files:
return self.loaded_files[summary_path]
try:
with open(summary_path, "r") as f:
content = f.read()
self.loaded_files[summary_path] = content
return content
except FileNotFoundError:
return ""
def load_detail(self, detail_path: str, force: bool = False) -> str:
"""Load a detail file on demand.
Set *force* to ``True`` to bypass the cache and re-read the file
(useful when the underlying file may have changed).
"""
if not force and detail_path in self.loaded_files:
return self.loaded_files[detail_path]
try:
with open(detail_path, "r") as f:
content = f.read()
self.loaded_files[detail_path] = content
return content
except FileNotFoundError:
return ""
def get_contextual_info(self, reference: Dict[str, Any]) -> str:
"""Return summary or detail based on the reference's flags.
Use when: a reference dict carries both ``summary_path`` and
``detail_path`` and the caller sets ``need_detail=True`` only
when full content is required.
"""
summary_path = reference.get("summary_path")
detail_path = reference.get("detail_path")
need_detail = reference.get("need_detail", False)
if need_detail and detail_path:
return self.load_detail(detail_path)
elif summary_path:
return self.load_summary(summary_path)
else:
return ""
# ---------------------------------------------------------------------------
# High-level entry point
# ---------------------------------------------------------------------------
def build_agent_context(
task: str,
system_prompt: str,
documents: Optional[List[str]] = None,
context_limit: int = 80_000,
) -> Dict[str, Any]:
"""Build an optimized, validated context dict for an agent task.
Use when: assembling context for a single inference call. Combines
system prompt, task description, and optional retrieved documents
into a priority-ordered context string, then validates the result.
Returns a dict with keys ``context`` (str), ``usage_report`` (dict),
and ``validation`` (dict).
"""
builder = ContextBuilder(context_limit=context_limit)
# System prompt — highest priority, persists across turns
builder.add_section("system", system_prompt, priority=10, category="system")
# Task description — second priority
builder.add_section("task", task, priority=9, category="task")
# Retrieved documents — loaded just-in-time
if documents:
for i, doc in enumerate(documents):
builder.add_section(
f"document_{i}",
doc,
priority=5,
category="retrieved",
)
context_dict: Dict[str, Any] = {
"system": system_prompt,
"task": task,
"documents": documents or [],
}
validation = validate_context_structure(context_dict)
return {
"context": builder.build(),
"usage_report": builder.get_usage_report(),
"validation": validation,
}
# ---------------------------------------------------------------------------
# Demo
# ---------------------------------------------------------------------------
if __name__ == "__main__":
print("=== Context Manager Demo ===\n")
sample_prompt = (
"You are a senior Python engineer. Follow PEP 8, use type hints, "
"and write docstrings for all public functions."
)
sample_task = "Refactor the authentication module to use OAuth 2.0."
sample_docs = [
"# OAuth 2.0 Reference\nThe OAuth 2.0 authorization framework...",
"# Current Auth Module\ndef login(user, password): ...",
]
result = build_agent_context(
task=sample_task,
system_prompt=sample_prompt,
documents=sample_docs,
)
report = result["usage_report"]
print(f"Total tokens : {report['total_tokens']}")
print(f"Utilization : {report['utilization']:.1%}")
print(f"Status : {report['status']}")
print(f"\nBreakdown by section:")
for section, tokens in report["by_section"].items():
print(f" {section:20s} : {tokens:,} tokens")
validation = result["validation"]
if validation["valid"]:
print("\nValidation : PASSED")
else:
print(f"\nValidation : FAILED")
for issue in validation["issues"]:
print(f" - {issue}")