Building Production-Ready Blockchain-Enabled Agents: Zero to Hero - Part 2

In our previous article, we established a foundational architecture for blockchain-enabled agents using LLMs. Now, we'll focus on optimizing two critical components - LLM Integration and Context Management - to create a more robust, production-ready system.


Zero to Hero

Enhanced LLM Integration with Langchain

Our initial implementation used a basic LLamaModel setup. While functional, production environments demand more sophisticated capabilities. Let's enhance our implementation using Langchain with the Qwen2.5-7B-Instruct model:

class OptimizedBlockchainLLM:

    def __init__(self, model_config, web3_provider, context_manager):

        # Initialize Qwen model through Langchain

        self.llm = Qwen(

            model_name="Qwen/Qwen2.5-7B-Instruct",

            temperature=0.7,

            max_tokens=2048,

            streaming=True,  # Enable streaming for better response handling

        )

        self.web3 = Web3(web3_provider)

        self.context_manager = context_manager


        # Create reusable prompt templates

        self.tx_analysis_template = PromptTemplate(

            input_variables=["network", "gas_price", "tx_data", "related_context"],

            template="""

            Network: {network}

            Current Gas Price: {gas_price}

            Related Context:

            {related_context}


            Analyze the following blockchain transaction:

            {tx_data}

            

            Provide insights on:

            1. Transaction efficiency and gas optimization

            2. Potential security implications

            3. Impact on network conditions

            """,

        )


        # Initialize analysis chain

        self.analysis_chain = LLMChain(llm=self.llm, prompt=self.tx_analysis_template)


    async def analyze_transaction(self, tx_hash):

        try:

            # Fetch transaction data

            tx = await self.web3.eth.get_transaction(tx_hash)

            network = await self.web3.eth.get_chain_id()

            gas_price = await self.web3.eth.gas_price

            

            # Fetch related context from EnhancedContextManager

            related_context = await self.context_manager.retrieve_context(str(tx))

            

            # Generate insights using LLMChain

            response = await self.analysis_chain.arun(

                {

                    "network": network,

                    "gas_price": gas_price,

                    "tx_data": tx,

                    "related_context": related_context

                    or "No related context available",

                }

            )

            # Add generated insights to context manager for future use

            await self.context_manager.add_context(

                {

                    "tx_hash": tx_hash,

                    "analysis": response,

                    "network": network,

                    "gas_price": gas_price,

                }

            )

            return response

        except Exception as e:

            logger.error(f"Transaction analysis failed: {str(e)}")

            raise TransactionAnalysisError(f"Analysis failed: {str(e)}")


Benefits of Langchain Integration 

Langchain serves as an intelligent orchestration layer that significantly enhances our blockchain agent's capabilities when working with the model. It transforms raw LLM interactions into a structured framework, similar to how modern web frameworks streamline web development. Instead of writing complex custom code for prompt management, error handling, and context maintenance, Langchain provides these features out of the box.

The framework introduces reusable prompt templates that can be versioned and refined, robust error handling with automatic retries, and real-time streaming of model outputs. Its memory management system maintains conversation history and context across multiple transaction analyses, ensuring each analysis benefits from previous insights. This makes our blockchain agent not just more capable, but also more reliable and maintainable in production environments.

Benefits of Qwen Model

Qwen2.5-7B offers significant advantages over LLaMA models when it comes to production deployment. The model stands out for its exceptional throughput performance, particularly when processing medium to large amounts of text - it consistently achieves higher tokens-per-second rates compared to similarly-sized LLaMA models. This translates to faster real-world response times, which is crucial for blockchain applications where timely analysis matters. 

The time-to-first-token (TTFT) performance of Qwen2.5 is notably superior, meaning your blockchain agent will start generating responses more quickly, enhancing the overall user experience. These characteristics make Qwen2.5 particularly well-suited for high-performance production environments where both speed and reliability are essential.

Advanced Context Management

Vector Database

In LLM applications, circular buffers lack the scalability and advanced querying required for production use cases. Replacing them with vector databases like Qdrant enables efficient hybrid searches combining semantic understanding and metadata filtering, as more explained in the previous article

Context Management and Resolution

In blockchain environments, context management isn't just about storing data – it's about maintaining accurate and meaningful information that influences critical decisions. When multiple transactions or events occur, we often encounter duplicate information that could skew our analysis if not properly handled. For instance, the same transaction might be reported through different channels, or similar market conditions might be recorded multiple times.

Conflicting information presents an even bigger challenge. Consider a scenario where two different sources report different gas prices for the same time period – without proper handling, our agent might make decisions based on incorrect or outdated information. Similarly, when dealing with similar but distinct pieces of content (like related transactions from the same address), we need sophisticated mechanisms to maintain the subtle differences while recognizing their relationships.

Most importantly, missing context can lead to incomplete or incorrect analysis. By implementing proper context handling, our system can identify gaps in information and either seek to fill them or explicitly account for these uncertainties in its analysis. 

The following functions were added to ensure our blockchain agent makes decisions based on accurate, complete, and properly deduplicated information:

  • handle_duplicate_information manages cases where the same information appears multiple times, maintaining a history of changes.
  • handle_conflicting_information resolves conflicts between different pieces of information using timestamps and source reliability.
  • handle_similar_content processes related but distinct pieces of information while preserving their relationships.
  • handle_missing_context identifies gaps in the context and suggests potential sources for completing the missing information.
  • from sentence_transformers import SentenceTransformer

    from qdrant_client import QdrantClient

    from qdrant_client.models import Distance, VectorParams


    class EnhancedContextManager:

        def __init__(self, qdrant_url):

            self.embedding_model = SentenceTransformer(

                "sentence-transformers/all-MiniLM-L12-v2"

            )

            self.vector_store = QdrantClient(url=qdrant_url)


            # Create collection with appropriate vector parameters

            self.vector_store.create_collection(

                collection_name="blockchain_context",

                vectors_config=VectorParams(size=384, distance=Distance.COSINE),

            )


        async def retrieve_context(self, query_data):

            # Generate embedding for the query

            query_embedding = self.embedding_model.encode(str(query_data))


            # Search for related contexts

            similar_contexts = await self.vector_store.search(

                collection_name="blockchain_context",

                query_vector=query_embedding,

                limit=5,

                score_threshold=0.85,

            )


            # Combine related context information into a single string

            return "\n".join([context.payload for context in similar_contexts])

       

        async def handle_duplicate_information(self, new_context, existing_context):

            """

            Resolve duplicate information by keeping the most recent version

            and maintaining a history of changes.

            """

            if new_context['timestamp'] > existing_context['timestamp']:

                merged_context = {

                    **existing_context,

                    **new_context,

                    'history': existing_context.get('history', []) + [existing_context]

                }

                return merged_context

            return existing_context


        async def handle_conflicting_information(self, contexts):

            """

            Resolve conflicts between different pieces of information

            using timestamp, source reliability, and consensus mechanisms.

            """

            # Sort contexts by reliability score and timestamp

            sorted_contexts = sorted(

                contexts,

                key=lambda x: (x['source_reliability'], x['timestamp']),

                reverse=True

            )

            

            # Use the most reliable and recent information

            primary_context = sorted_contexts[0]

            

            # Store conflicting versions in metadata

            primary_context['conflicts'] = sorted_contexts[1:]

            return primary_context


        async def handle_similar_content(self, new_context, similar_contexts, similarity_threshold=0.95):

            """

            Process similar but distinct pieces of content while preserving

            important differences and relationships.

            """

            if not similar_contexts:

                return new_context


            # Group very similar contexts together

            for similar in similar_contexts:

                if cosine_similarity(new_context['vector'], similar['vector']) > similarity_threshold:

                    # Create a relationship between similar contexts

                    new_context['related_contexts'] = new_context.get('related_contexts', []) + [similar['id']]

                    # Update existing context with new relationships

                    await self.vector_store.update(

                        collection_name="blockchain_context",

                        points=[{

                            'id': similar['id'],

                            'payload': {

                                'related_contexts': similar.get('related_contexts', []) + [new_context['id']]

                            }

                        }]

                    )

            return new_context


        async def handle_missing_context(self, context_data):

            """

            Identify and handle missing information in the context.

            Returns context with missing fields marked and potential sources for completion.

            """

            required_fields = {'timestamp', 'block_number', 'gas_price', 'network_state'}

            missing_fields = required_fields - set(context_data.keys())

            

            if missing_fields:

                context_data['missing_fields'] = list(missing_fields)

                context_data['completion_sources'] = {

                    field: self.get_completion_source(field)

                    for field in missing_fields

                }

            return context_data


        async def add_context(self, context_data):

            # Generate embedding for the new context

            context_embedding = self.embedding_model.encode(str(context_data))

            

            # Check for similar existing contexts

            similar = await self.vector_store.search(

                collection_name="blockchain_context",

                query_vector=context_embedding,

                limit=5,

                score_threshold=0.90

            )

            

            # Process the context through our handling pipeline

            processed_context = context_data

            

            # Handle missing information first

            processed_context = await self.handle_missing_context(processed_context)

            

            if similar:

                # Check for and handle duplicates

                for existing in similar:

                    if existing.score > 0.98:  # Very similar - potential duplicate

                        processed_context = await self.handle_duplicate_information(

                            processed_context, existing.payload

                        )

                

                # Handle conflicting information if multiple similar contexts exist

                if len(similar) > 1:

                    processed_context = await self.handle_conflicting_information(

                        [s.payload for s in similar] + [processed_context]

                    )

                

                # Process similar but distinct content

                processed_context = await self.handle_similar_content(

                    processed_context, [s.payload for s in similar]

                )

            

            # Store the processed context

            await self.vector_store.upsert(

                collection_name="blockchain_context",

                points=[{

                    "id": generate_uuid(),

                    "vector": context_embedding,

                    "payload": processed_context

                }]

            )

    Why different model for Embeddings?

    While Qwen2.5 excels at reasoning and analysis tasks, it's not the optimal choice for generating embeddings in our system. Instead, we use all-MiniLM-L12-v2, which is specifically designed for the task of converting text into numerical vectors. This model creates compact 384-dimensional vectors, compared to the much larger 1024+ dimensional vectors from models like Qwen2.5. Think of it as having a specialized tool for a specific job – while Qwen2.5 is like a powerful general-purpose engine, all-MiniLM-L12-v2 is like a finely-tuned instrument for measuring text similarity.

    Main Processing Engine: Qwen2.5-7B-Instruct

    The Qwen model serves as our primary intelligence engine, processing complex blockchain data and generating insightful analyses. This model excels at understanding the nuances of transaction patterns and market conditions, producing detailed reports that combine technical accuracy with clear explanations.

    Context Processing Engine: all-MiniLM-L12-v2

    This specialized model works behind the scenes to organize and retrieve our knowledge base. It transforms blockchain data and analysis results into mathematical representations that allow for quick and accurate similarity matching, ensuring we can always find relevant historical context.

    The model has been specifically trained for similarity search tasks, making it exceptionally good at identifying related content. This specialization, combined with its smaller size, means we can generate embeddings much faster while using fewer resources. In a production environment, this efficiency translates to better performance without sacrificing the quality of our context matching.

    User and Data Flow

    Understanding how different components interact in our system is crucial for both development and maintenance. Our architecture leverages two specialized models that work together to provide intelligent blockchain analysis while maintaining efficient context management. When our system processes a blockchain transaction, information flows through several stages:

    Stage 1: Initial Data Reception

    The system receives new transaction data from the blockchain, which triggers our analysis pipeline. This raw data includes transaction details, network conditions, and any relevant market information.

    Stage 2: Context Retrieval

    The all-MiniLM-L12-v2 model converts the incoming transaction data into a vector representation, using this to search our Qdrant database for similar past transactions and relevant context.

    Stage 3: Analysis Processing

    The gathered context and new transaction data are combined into a comprehensive prompt for the Qwen2.5 model, which then performs the detailed analysis and generates insights.

    Stage 4: Knowledge Update

    After analysis, the system stores new learnings by converting them into embeddings using all-MiniLM-L12-v2 and saving them in Qdrant, enriching our knowledge base for future analyses.

    This orchestrated flow ensures that each component focuses on its strengths: Qwen2.5 handles complex reasoning while all-MiniLM-L12-v2 manages efficient information storage and retrieval. Together, they create a system that's both intelligent and efficient at processing blockchain transactions.

    Conclusion

    To build production-ready blockchain-enabled agents, optimizing LLM integration and context management is critical. By integrating Langchain with the Qwen2.5-7B-Instruct model, we enhanced the agent’s capabilities, leveraging reusable prompt templates, robust error handling, and efficient streaming for better reliability and maintainability. 

    The Qwen model’s performance advantages make it ideal for real-world deployments where speed and scalability are essential. Advanced context management using vector databases like Qdrant ensures accurate, complete, and deduplicated insights by addressing duplicates, conflicts, and missing data. Together, these enhancements create a more robust framework for deploying intelligent blockchain agents in production environments.

    Comments

    Popular posts from this blog

    CAP Theorem and blockchain

    Length extension attack

    Contract upgrade anti-patterns