Skip to main content

The Context Engineering framework for building smarter, more reliable AI Agents.

Project description

Architext

English | 中文

PyPI version License: MIT Python 3.11+

Architext: The Context Engineering framework for building smarter, more reliable AI Agents.


Architext (from "Architecture" + "Text") is a Python library designed for Large Language Model (LLM) applications, focusing on Context Engineering. It provides an elegant, powerful, and object-oriented set of tools that allow you to precisely and dynamically construct and reorganize the input context for LLMs, much like a software engineer designs an architecture.

Say goodbye to scattered string concatenation and complex construction logic, and enter a new era where context is treated as an operable, composable, and evolvable engineered entity.

🤔 What is Context Engineering?

When building complex AI Agents, the quality and structure of the context provided to the LLM (i.e., the messages list) directly determine its performance ceiling. Context Engineering is an emerging discipline that focuses on:

  • Structuring: How to organize information from various data sources (files, code, databases, APIs) into a structure that LLMs can most easily understand?
  • Dynamism: How to dynamically add, remove, or rearrange context content as the conversation progresses to maintain its relevance and timeliness?
  • Optimization: How to intelligently filter and present the most valuable information within a limited context window to maximize performance and minimize cost?

Architext is designed to solve these engineering challenges.

✨ Architext: Born for Context Engineering

The core philosophy of Architext is to elevate the context construction process from ad-hoc "craftsmanship" to systematic "engineering."

  • Declarative & Dynamic: Seamlessly construct prompts with Python's f-strings, embedding dynamic, stateful components directly within your text.
  • Context as a Mutable Structure: Messages are no longer static text but a container of Provider objects that can be manipulated in real-time. You can perform precise pop, insert, append, and even slicing operations.
  • Granular State Management: Each piece of context is a Provider that can be individually updated, cached, and even hidden from rendering without being removed.
  • Think like an Architect: You can lay out the structure of SystemMessage and UserMessage as clearly as designing a software architecture, and dynamically adjust it through a unified interface to handle different task scenarios.

🚀 Core Features

  • Intuitive F-String Integration: Build complex prompts naturally with f-strings, embedding providers like Texts(), Files(), and Tools() directly.
  • Object-Oriented Context Modeling: Treat SystemMessage, UserMessage, etc., as first-class, operable Python objects.
  • Provider-Driven Architecture: Extensible ContextProvider system (Texts, Files, Images, Tools) to connect any data source.
  • Dynamic Content with lambda: Texts(lambda: ...) providers can execute code to generate content on-the-fly during rendering.
  • Powerful List-like Operations: Manipulate messages with pop(), insert(), append(), indexing (messages[0]), slicing (messages[1:3]), and even slice assignment (messages[1:] = ...).
  • Pythonic & Idiomatic: Enjoy a natural coding experience. Messages can be concatenated with +, content accessed via dictionary-style keys (msg['content']), and internal providers accessed via list-style indexing (msg[0]).
  • Visibility Control: Toggle providers on and off with .visible = False without removing them, enabling dynamic context filtering.
  • Bulk Operations: Use ProviderGroup to manage multiple providers with the same name simultaneously (e.g., messages.provider("explanation").visible = False).
  • Intelligent Caching: Built-in mechanism automatically refreshes content only when the source changes, boosting performance.
  • Unified Pass-Through Interface: Access and update any provider from the top-level Messages object via messages.provider("name").
  • Native Multimodal Support: Effortlessly create messages containing both text and images.

📦 Installation

pip install architext

🚀 Quick Start: A Context Engineering Practice

The following examples showcase Architext's most powerful features, ordered from the most unique to the foundational.

Example 1: The Magic of F-String Prompt Construction (Highlight Feature)

Forget manual string joining. Build prompts declaratively and dynamically using the tools you already know and love: f-strings.

import asyncio
from architext import Messages, UserMessage, Texts, Tools, Files
from datetime import datetime

async def example_1():
    # Define providers that will be embedded in the f-string
    os_provider = Texts("MacOS Sonoma", name="os_version")
    tools_provider = Tools([{"name": "read_file"}])
    files_provider = Files(["main.py", "utils.py"])
    time_provider = Texts(lambda: datetime.now().isoformat()) # Dynamic content!

    # Create dummy files for the example
    with open("main.py", "w") as f: f.write("print('hello')")
    with open("utils.py", "w") as f: f.write("def helper(): pass")

    # Construct the entire message with a single f-string!
    # Architext automatically detects and manages the embedded providers.
    prompt = f"""
    System Information:
    - OS: {os_provider}
    - Time: {time_provider}

    Available Tools: {tools_provider}

    File Contents:
    {files_provider}

    User Request:
    Based on the files, what is the primary function of this project?
    """

    messages = Messages(UserMessage(prompt))

    # Render the fully constructed message
    print("--- F-String Render Result ---")
    for msg in await messages.render_latest():
        print(msg['content'])

    # Clean up dummy files
    import os
    os.remove("main.py")
    os.remove("utils.py")

asyncio.run(example_1())

Expected Output: The f-string is fully resolved with content from all providers, including the dynamically generated timestamp and file contents.


Example 2: Dynamic Context Refactoring & Visibility Control

Adapt the context in real-time based on application logic. Here, we move a tool definition and then hide multiple "explanation" providers at once.

import asyncio
from architext import Messages, SystemMessage, UserMessage, Texts, Tools

async def example_2():
    messages = Messages(
        SystemMessage(
            Texts("You are an AI assistant.", name="intro"),
            Tools([{"name": "run_code"}]) # Initially in SystemMessage
        ),
        UserMessage(
            Texts("First explanation.", name="explanation"),
            Texts("Please run the code.", name="request"),
            Texts("Second explanation.", name="explanation")
        )
    )

    # --- Part A: Move a provider ---
    print(">>> Refactoring: Moving 'tools' to UserMessage for emphasis...")

    # 1. Globally pop the provider from wherever it is
    tools_provider = messages.pop("tools")
    # 2. Insert it into a specific message and position
    if tools_provider:
        messages[1].insert(1, tools_provider)

    print("\n--- After Moving 'tools' ---")
    for msg in await messages.render_latest(): print(msg)

    # --- Part B: Bulk-hide providers ---
    print("\n>>> Hiding all 'explanation' providers...")

    # 1. Get a group of all providers named "explanation"
    explanation_group = messages.provider("explanation")
    # 2. Set visibility for the entire group
    explanation_group.visible = False

    print("\n--- After Hiding Explanations ---")
    for msg in await messages.render_latest(): print(msg)

asyncio.run(example_2())

Expected Output: You will see the <tools> block move from the system to the user message. Then, in the final output, the "First explanation" and "Second explanation" texts will disappear, while the rest of the content remains.


Example 3: Multimodal and Tool-Use Conversations

Architext natively supports complex message structures required for multimodal interactions and tool-use cycles.

import asyncio
from dataclasses import dataclass, field
from architext import Messages, UserMessage, AssistantMessage, Texts, Images, ToolCalls, ToolResults

# Helper dataclasses to simulate tool call objects from libraries like OpenAI's
@dataclass
class MockFunction:
    name: str
    arguments: str

@dataclass
class MockToolCall:
    id: str
    type: str = "function"
    function: MockFunction = field(default_factory=lambda: MockFunction("", ""))

async def example_3():
    # --- Multimodal Example ---
    with open("dummy_image.png", "w") as f: f.write("dummy")

    multimodal_messages = Messages(
        UserMessage(
            "What is in this image?",
            Images("dummy_image.png")
        )
    )
    print("--- Multimodal Render Result ---")
    for msg in await multimodal_messages.render_latest(): print(msg)

    # --- Tool-Use Example ---
    # Simulate a tool call request from the model
    tool_call_request = [
        MockToolCall(id="call_123", function=MockFunction(name="add", arguments='{"a": 5, "b": 10}'))
    ]

    tool_use_messages = Messages(
        UserMessage("What is 5 + 10?"),
        # Represents the model's request to call a tool
        ToolCalls(tool_call_request),
        # Represents the result you provide back to the model
        ToolResults(tool_call_id="call_123", content="15"),
        AssistantMessage("The sum is 15.")
    )
    print("\n--- Tool-Use Render Result ---")
    for msg in await tool_use_messages.render_latest(): print(msg)

    import os
    os.remove("dummy_image.png")

asyncio.run(example_3())

Expected Output: Both examples will render into the precise dictionary format expected by modern LLM APIs (like OpenAI's), handling list-based content for multimodal messages and tool_calls/tool roles correctly.


Example 4: Pass-Through Updates and Automatic Refresh

Update any piece of context from anywhere, and Architext will ensure the changes are reflected in the next render.

import asyncio
from architext import Messages, UserMessage, Files

async def example_4():
    # 1. Initialize with a Files provider
    messages = Messages(UserMessage(Files(name="code_files")))

    # 2. Initially, content is empty
    print("--- Initial State (No files loaded) ---")
    print(await messages.render_latest())

    # 3. Get a handle to the provider and update it
    print("\n>>> Updating files via messages.provider('code_files')...")
    files_provider = messages.provider("code_files")
    if files_provider:
        # Update with content for a new file in memory
        files_provider.update("main.py", "def main():\\n    print('Hello')")

    # 4. Render again. Architext detects the stale provider and refreshes it.
    print("\n--- Render After Update ---")
    for msg in await messages.render_latest(): print(msg)

asyncio.run(example_4())

Expected Output: The first render will be empty. After the update, the second render will correctly show the content of main.py.

🤝 Contributing

Context Engineering is an exciting new field. We welcome contributions of all forms to jointly explore building smarter, more efficient AI Agents. Whether it's reporting a bug, proposing a new feature, or submitting code, your participation is crucial.

📄 License

This project is licensed under the MIT License. See the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

architext-0.1.60.tar.gz (33.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

architext-0.1.60-py3-none-any.whl (13.7 kB view details)

Uploaded Python 3

File details

Details for the file architext-0.1.60.tar.gz.

File metadata

  • Download URL: architext-0.1.60.tar.gz
  • Upload date:
  • Size: 33.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.14

File hashes

Hashes for architext-0.1.60.tar.gz
Algorithm Hash digest
SHA256 e36274d9193b9b02f923e5bcc9a7cc4c8f12802dbf3148b718d8d429e2cc22dd
MD5 3fe8e90d117f87fe34b88377cf591b17
BLAKE2b-256 3d42ee1849eb75885dac4c008a54b600d7dfe397b72a653068de378dfa08380d

See more details on using hashes here.

File details

Details for the file architext-0.1.60-py3-none-any.whl.

File metadata

  • Download URL: architext-0.1.60-py3-none-any.whl
  • Upload date:
  • Size: 13.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.14

File hashes

Hashes for architext-0.1.60-py3-none-any.whl
Algorithm Hash digest
SHA256 3675dbc9d52660f484119559920e7805317b20b48cccf49e2526fd131da0a7d0
MD5 826105fb314fa6462237ad6074955bb8
BLAKE2b-256 df33aca7c8b71da07ae42b9a088a2fb2f5a5b8d073dbe2f28daee40f3cf64c51

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page