LangChain Feature Development Skill
Overview
This skill provides a structured workflow for designing, implementing, and testing new features using the LangChain framework within the aap_langchain package. For comprehensive LangChain best practices (architecture, RAG systems, agent design, tool calling, prompt engineering, error handling, observability, performance, and testing), refer to best-practices.md.
Workflow
1. Research & Design
- Consult Best Practices: Review best-practices.md for architecture patterns, RAG systems, agent design, and tool calling guidelines.
- Identify Components: Determine which LangChain primitives are required:
BaseChatModel: For LLM interaction.
BaseTool: For tool/function calling.
BaseRetriever: For information retrieval.
BaseMessage subclasses (HumanMessage, AIMessage, SystemMessage, ToolMessage): For conversation state.
Runnable (LCEL): For composing chains.
- Define Data Flow: Map how
AgentMessage (from aap_core) will be transformed into LangChain messages and how the results will be converted back.
- Design for Extensibility: Use Pydantic models and
PrivateAttr for internal state to ensure compatibility with LangChain's architecture.
2. Component Implementation
- Consult Best Practices: Review best-practices.md for tool design principles, prompt engineering patterns, and error handling strategies.
- Implement Adapters: If using existing LangChain components, create adapters (e.g.,
RetrieverAdapter) to bridge them with aap_core types.
- Implement Tools: Create new tools by subclassing
BaseTool or using the @tool decorator. Ensure they handle input/output types correctly and provide clear descriptions. Follow the production tool pattern from best-practices.md.
- Implement Chains (LCEL focus):
- For complex logic, leverage LangChain Expression Language (LCEL) using
RunnableSequence, RunnableParallel, and RunnableLambda.
- Extend
BaseCausalMultiTurnsChain to wrap these LCEL chains, ensuring they can handle AgentMessage inputs and outputs.
- Follow Python Standards: Adhere to
@file:python-development.instructions.md.
3. Integration & Composition
- Consult Best Practices: Review best-practices.md for multi-agent architecture patterns, state management, and observability guidelines.
- Compose the Chain: Use LCEL to link the prompt, model, tools, and retrievers (e.g.,
chain = prompt | model.bind_tools(tools) | output_parser).
- Handle State: Ensure the chain correctly manages message history and tool call sequences. The
_prepare_conversation method in ChatCausalMultiTurnsChain is the key bridge for converting AgentMessage history to List[BaseMessage].
- Token Usage: Implement logic to extract and convert
UsageMetadata from AIMessage to TokenUsage (refer to aap_langchain.utils.token_from_response).
- Error Handling: Implement retry with exponential backoff and fallback strategies as described in best-practices.md.
4. Testing (LangChain Specific)
Follow @file:writing-and-running-tests.instructions.md and apply these LangChain-specific strategies. See best-practices.md for testing guidelines and production checklist.
- Mocking LLMs:
- NEVER make real API calls.
- Use
unittest.mock.MagicMock to mock BaseChatModel.invoke.
- Configure the mock to return
AIMessage objects with desired content, tool_calls, and usage_metadata.
- Message Sequence Testing:
- Test
_prepare_conversation logic: verify that AgentMessage history is correctly converted to a list of BaseMessage objects (including HumanMessage, AIMessage, SystemMessage, and ToolMessage) in the right order.
- Verify
SystemMessage and HumanMessage formatting.
- Tool Calling Tests:
- Test that
AIMessage.tool_calls are correctly processed by the chain.
- Verify that
ToolMessage is correctly appended to the conversation after tool execution and that it correctly references the tool_call_id.
- Retriever Integration Tests:
- Test that
RetrieverAdapter correctly populates the context field in AgentMessage.
- Verify that retrieved
Document content is correctly extracted and formatted.
- Test wrapping complex
Runnable chains in RetrieverAdapter to support advanced retrieval (e.g., reranking).
- Round-trip Validation:
- Ensure that the output of a chain can be successfully converted back into an
AgentMessage.
5. Validation & Cleanup
- Lint & Format: Run
uv run ruff check --fix and uv run ruff format.
- Full Test Suite: Run
uv run pytest -v within the src/langchain directory.
Implementation Examples
1. Creating a Tool
from langchain_core.tools import tool
@tool
def get_weather(location: str) -> str:
"""Get the current weather for a given location."""
return f"The weather in {location} is sunny and 25°C."
2. Implementing a Chain using LCEL
from langchain_core.prompts import ChatPromptTemplate
from aap_langchain.chain import ChatCausalMultiTurnsChain
from langchain_core.language_models import BaseChatModel
class WeatherChain(ChatCausalMultiTurnsChain):
def __init__(self, model: BaseChatModel, tools: list):
# The chain must accept List[BaseMessage] as input
# ChatCausalMultiTurnsChain passes the conversation list to self._chain.invoke
self._chain = model.bind_tools(tools)
super().__init__(
model=model,
system_prompt="You are a helpful weather assistant.",
tools=tools
)
3. Testing a Chain with Mocks
import pytest
from unittest.mock import MagicMock
from langchain_core.messages import AIMessage, HumanMessage
from aap_langchain.chain import ChatCausalMultiTurnsChain
from aap_core.types import AgentMessage, TokenUsage
def test_weather_chain_response():
# Arrange
mock_model = MagicMock()
mock_response = AIMessage(
content="The weather is sunny.",
tool_calls=[],
usage_metadata={"input_tokens": 10, "output_tokens": 5, "total_tokens": 15}
)
mock_model.invoke.return_value = mock_response
# Act
chain = WeatherChain(model=mock_model, tools=[])
msg = AgentMessage(query="What is the weather?")
response = chain.call(msg)
# Assert
assert response.query == "What is the weather?"
assert response.responses[0][1] == "The weather is sunny."
4. Implementing a Prompt Augmenter
from langchain_core.prompts import ChatPromptTemplate
from aap_langchain.chain import ChatCausalMultiTurnsChain
from aap_core.prompt_augmenter import BasePromptAugmenter
from aap_core.types import AgentMessage
class LangChainPromptAugmenter(BasePromptAugmenter):
def __init__(self, chain: ChatCausalMultiTurnsChain, **kwargs):
super().__init__(**kwargs)
self.chain = chain
def augment(self, message: AgentMessage, **kwargs) -> AgentMessage:
# Use the LangChain chain to rewrite the query
# This assumes the chain is designed to return a response that can be used as a new query
response = self.chain.call(message)
# Update the message with the augmented query
message.query = response.responses[0][1]
return message
Source: quanghona/agent_design_pattern — distributed by TomeVault.
1---2name: langchain-development-23description: Use when working with a structured workflow for designing, implementing, and testing new features using the LangChain framework within the `aap_langchain` package. This skill ensures that all new components are compatible with the `aap_core` architecture and follow LangChain best practices.4---56# LangChain Feature Development Skill78## Overview910This skill provides a structured workflow for designing, implementing, and testing new features using the LangChain framework within the `aap_langchain` package. For comprehensive LangChain best practices (architecture, RAG systems, agent design, tool calling, prompt engineering, error handling, observability, performance, and testing), refer to [best-practices.md](best-practices.md).1112## Workflow1314### 1. Research & Design15- **Consult Best Practices**: Review [best-practices.md](best-practices.md) for architecture patterns, RAG systems, agent design, and tool calling guidelines.16- **Identify Components**: Determine which LangChain primitives are required:17 - `BaseChatModel`: For LLM interaction.18 - `BaseTool`: For tool/function calling.19 - `BaseRetriever`: For information retrieval.20 - `BaseMessage` subclasses (`HumanMessage`, `AIMessage`, `SystemMessage`, `ToolMessage`): For conversation state.21 - `Runnable` (LCEL): For composing chains.22- **Define Data Flow**: Map how `AgentMessage` (from `aap_core`) will be transformed into LangChain messages and how the results will be converted back.23- **Design for Extensibility**: Use Pydantic models and `PrivateAttr` for internal state to ensure compatibility with LangChain's architecture.2425### 2. Component Implementation26- **Consult Best Practices**: Review [best-practices.md](best-practices.md) for tool design principles, prompt engineering patterns, and error handling strategies.27- **Implement Adapters**: If using existing LangChain components, create adapters (e.g., `RetrieverAdapter`) to bridge them with `aap_core` types.28- **Implement Tools**: Create new tools by subclassing `BaseTool` or using the `@tool` decorator. Ensure they handle input/output types correctly and provide clear descriptions. Follow the production tool pattern from best-practices.md.29- **Implement Chains (LCEL focus)**:30 - For complex logic, leverage LangChain Expression Language (LCEL) using `RunnableSequence`, `RunnableParallel`, and `RunnableLambda`.31 - Extend `BaseCausalMultiTurnsChain` to wrap these LCEL chains, ensuring they can handle `AgentMessage` inputs and outputs.32- **Follow Python Standards**: Adhere to `@file:python-development.instructions.md`.3334### 3. Integration & Composition35- **Consult Best Practices**: Review [best-practices.md](best-practices.md) for multi-agent architecture patterns, state management, and observability guidelines.36- **Compose the Chain**: Use LCEL to link the prompt, model, tools, and retrievers (e.g., `chain = prompt | model.bind_tools(tools) | output_parser`).37- **Handle State**: Ensure the chain correctly manages message history and tool call sequences. The `_prepare_conversation` method in `ChatCausalMultiTurnsChain` is the key bridge for converting `AgentMessage` history to `List[BaseMessage]`.38- **Token Usage**: Implement logic to extract and convert `UsageMetadata` from `AIMessage` to `TokenUsage` (refer to `aap_langchain.utils.token_from_response`).39- **Error Handling**: Implement retry with exponential backoff and fallback strategies as described in [best-practices.md](best-practices.md).4041### 4. Testing (LangChain Specific)42Follow `@file:writing-and-running-tests.instructions.md` and apply these LangChain-specific strategies. See [best-practices.md](best-practices.md) for testing guidelines and production checklist.4344- **Mocking LLMs**:45 - **NEVER** make real API calls.46 - Use `unittest.mock.MagicMock` to mock `BaseChatModel.invoke`.47 - Configure the mock to return `AIMessage` objects with desired `content`, `tool_calls`, and `usage_metadata`.48- **Message Sequence Testing**:49 - Test `_prepare_conversation` logic: verify that `AgentMessage` history is correctly converted to a list of `BaseMessage` objects (including `HumanMessage`, `AIMessage`, `SystemMessage`, and `ToolMessage`) in the right order.50 - Verify `SystemMessage` and `HumanMessage` formatting.51- **Tool Calling Tests**:52 - Test that `AIMessage.tool_calls` are correctly processed by the chain.53 - Verify that `ToolMessage` is correctly appended to the conversation after tool execution and that it correctly references the `tool_call_id`.54- **Retriever Integration Tests**:55 - Test that `RetrieverAdapter` correctly populates the `context` field in `AgentMessage`.56 - Verify that retrieved `Document` content is correctly extracted and formatted.57 - Test wrapping complex `Runnable` chains in `RetrieverAdapter` to support advanced retrieval (e.g., reranking).58- **Round-trip Validation**:59 - Ensure that the output of a chain can be successfully converted back into an `AgentMessage`.606162### 5. Validation & Cleanup63- **Lint & Format**: Run `uv run ruff check --fix` and `uv run ruff format`.64- **Full Test Suite**: Run `uv run pytest -v` within the `src/langchain` directory.6566## Implementation Examples6768### 1. Creating a Tool69```python70from langchain_core.tools import tool7172@tool73def get_weather(location: str) -> str:74 """Get the current weather for a given location."""75 return f"The weather in {location} is sunny and 25°C."76```7778### 2. Implementing a Chain using LCEL79```python80from langchain_core.prompts import ChatPromptTemplate81from aap_langchain.chain import ChatCausalMultiTurnsChain82from langchain_core.language_models import BaseChatModel8384class WeatherChain(ChatCausalMultiTurnsChain):85 def __init__(self, model: BaseChatModel, tools: list):86 # The chain must accept List[BaseMessage] as input87 # ChatCausalMultiTurnsChain passes the conversation list to self._chain.invoke88 self._chain = model.bind_tools(tools)89 super().__init__(90 model=model,91 system_prompt="You are a helpful weather assistant.",92 tools=tools93 )94```9596### 3. Testing a Chain with Mocks97```python98import pytest99from unittest.mock import MagicMock100from langchain_core.messages import AIMessage, HumanMessage101from aap_langchain.chain import ChatCausalMultiTurnsChain102from aap_core.types import AgentMessage, TokenUsage103104def test_weather_chain_response():105 # Arrange106 mock_model = MagicMock()107 mock_response = AIMessage(108 content="The weather is sunny.",109 tool_calls=[],110 usage_metadata={"input_tokens": 10, "output_tokens": 5, "total_tokens": 15}111 )112 mock_model.invoke.return_value = mock_response113114 # Act115 chain = WeatherChain(model=mock_model, tools=[])116 msg = AgentMessage(query="What is the weather?")117 response = chain.call(msg)118119 # Assert120 assert response.query == "What is the weather?"121 assert response.responses[0][1] == "The weather is sunny."122```123124### 4. Implementing a Prompt Augmenter125```python126from langchain_core.prompts import ChatPromptTemplate127from aap_langchain.chain import ChatCausalMultiTurnsChain128from aap_core.prompt_augmenter import BasePromptAugmenter129from aap_core.types import AgentMessage130131class LangChainPromptAugmenter(BasePromptAugmenter):132 def __init__(self, chain: ChatCausalMultiTurnsChain, **kwargs):133 super().__init__(**kwargs)134 self.chain = chain135136 def augment(self, message: AgentMessage, **kwargs) -> AgentMessage:137 # Use the LangChain chain to rewrite the query138 # This assumes the chain is designed to return a response that can be used as a new query139 response = self.chain.call(message)140141 # Update the message with the augmented query142 message.query = response.responses[0][1]143 return message144```145146---147> Source: [quanghona/agent_design_pattern](https://github.com/quanghona/agent_design_pattern) — distributed by [TomeVault](https://tomevault.io).148<!-- tomevault:4.0:skill_md:2026-05-23 -->