Conceptual guide
This section contains introductions to key parts of LangChain.dart
Architecture
LangChain.dart as a framework consists of a number of packages.
langchain_core
This package contains base abstractions of different components and ways to compose them together. The interfaces for core components like LLMs, vector stores, retrievers and more are defined here. No third party integrations are defined here.
Depend on this package to build frameworks on top of .dart.dart or to interoperate with it.
langchain
Contains higher-level and use-case specific chains, agents, and retrieval algorithms that are at the core of the application's cognitive architecture.
Depend on this package to build LLM applications with .dart.dart.
This package exposes
langchain_core
so you don't need to depend on it explicitly.
langchain_community
Contains third-party integrations and community-contributed components that are not part of the core LangChain.dart API.
Depend on this package if you want to use any of the integrations or components it provides.
Integration-specific packages
Popular third-party integrations (e.g. langchain_openai
, langchain_google
, langchain_ollama
, etc.) are moved to their own packages so that they can be imported independently without depending on the entire langchain_community
package.
Depend on an integration-specific package if you want to use the specific integration.
See Integrations to integrate with a specific package.
LangChain Expression Language (LCEL)
LangChain Expression Language, or LCEL, is a declarative way to easily compose chains together. LCEL was designed from day 1 to support putting prototypes in production, with no code changes, from the simplest “prompt + LLM” chain to the most complex chains (we’ve seen folks successfully run LCEL chains with 100s of steps in production). To highlight a few of the reasons you might want to use LCEL:
- First-class streaming support: When you build your chains with LCEL you get the best possible time-to-first-token (time elapsed until the first chunk of output comes out). For some chains this means eg. we stream tokens straight from an LLM to a streaming output parser, and you get back parsed, incremental chunks of output at the same rate as the LLM provider outputs the raw tokens.
- Optimized concurrent execution: Whenever your LCEL chains have steps that can be executed concurrently (eg if you fetch documents from multiple retrievers) we automatically do it for the smallest possible latency.
- Retries and fallbacks: Configure retries and fallbacks for any part of your LCEL chain. This is a great way to make your chains more reliable at scale.
- Access intermediate results: For more complex chains it’s often very useful to access the results of intermediate steps even before the final output is produced. This can be used to let end-users know something is happening, or even just to debug your chain.
Runnable interface
To make it as easy as possible to create custom chains, LangChain provides a Runnable
interface that most components implement, including chat models, LLMs, output parsers, retrievers, prompt templates, and more.
This is a standard interface, which makes it easy to define custom chains as well as invoke them in a standard way. The standard interface includes:
invoke
: call the chain on an input and return the output.stream
: call the chain on an input and stream the output.batch
: call the chain on a list of inputs and return a list of outputs.
The type of the input and output varies by component:
Component | Input Type | Output Type |
---|---|---|
PromptTemplate | Map<String, dynamic> | PromptValue |
ChatMessagePromptTemplate | Map<String, dynamic> | PromptValue |
LLM | PromptValue | LLMResult |
ChatModel | PromptValue | ChatResult |
OutputParser | Any object | Parser output type |
Retriever | String | List<Document> |
DocumentTransformer | List<Document> | List<Document> |
Tool | Map<String, dynamic> | String |
Chain | Map<String, dynamic> | Map<String, dynamic> |
Components
Chat models
Language models that use a sequence of messages as inputs and return chat messages as outputs (as opposed to using plain text).
These are traditionally newer models (older models are generally LLMs
, see below).
Chat models support the assignment of distinct roles to conversation messages, helping to distinguish messages from the AI, users, and instructions such as system messages.
Although the underlying models are messages in, message out, the LangChain wrappers also allow these models to take a string as input. This means you can easily use chat models in place of LLMs.
When a string is passed in as input, it is converted to a HumanMessage
and then passed to the underlying model.
LangChain does not host any Chat Models, rather we rely on third party integrations.
We have some standardized parameters when constructing ChatModels:
model
: the name of the modeltemperature
: the sampling temperaturetimeout
: request timeoutmaxTokens
: max tokens to generateapiKey
: API key for the model providerbaseUrl
: endpoint to send requests to
Some important things to note:
- standard params only apply to model providers that expose parameters with the intended functionality. For example, some providers do not expose a configuration for maximum output tokens, so max_tokens can't be supported on these.
- standard params are currently only enforced on integrations that have their own integration packages (e.g.
langchain-openai
,langchain-anthropic
, etc.), they're not enforced on models inlangchain-community
.
ChatModels also accept other parameters that are specific to that integration. To find all the parameters supported by a ChatModel head to the API reference for that model.
LLMs
Pure text-in/text-out LLMs tend to be older or lower-level. Many popular models are best used as chat completion models, even for non-chat use cases.
You are probably looking for the section above instead.
Language models that takes a string as input and returns a string. These are traditionally older models (newer models generally are Chat Models, see above).
Although the underlying models are string in, string out, the LangChain wrappers also allow these models to take messages as input. This gives them the same interface as Chat Models. When messages are passed in as input, they will be formatted into a string under the hood before being passed to the underlying model.
LangChain.dart does not host any LLMs, rather we rely on third party integrations. See (/docs/integrations)
Messages
Some language models take a list of messages as input and return a message.
LangChain provides several objects to easily distinguish between different roles:
HumanChatMessage
This represents a message from the user.
AIChatMessage
This represents a message from the model.
SystemChatMessage
This represents a system message, which tells the model how to behave. Not every model provider supports this.
FunctionChatMessage / ToolChatMessage
These represent a decision from an language model to call a tool. They're a subclass of a AIChatMessage. FunctionChatMessage is a legacy message type corresponding to OpenAI's legacy function-calling API.
Prompt Templates
Most LLM applications do not pass user input directly into an LLM
. Usually they will add the user input to a larger piece of text, called a prompt template, that provides additional context on the specific task at hand.
In the previous example, the text we passed to the model contained instructions to generate a company name. For our application, it would be great if the user only had to provide the description of a company/product, without having to worry about giving the model instructions.
PromptTemplates
help with exactly this! They bundle up all the logic for going from user input into a fully formatted prompt. This can start off very simple - for example, a prompt to produce the above string would just be:
final prompt = PromptTemplate.fromTemplate(
'What is a good name for a company that makes {product}?',
);
final res = prompt.format({'product': 'colorful socks'});
print(res);
// 'What is a good name for a company that makes colorful socks?'
However, the advantages of using these over raw string formatting are several. You can "partial" out variables - e.g. you can format only some of the variables at a time. You can compose them together, easily combining different templates into a single prompt.
For specifics on how to use prompt templates, see the relevant how-to guides here.
PromptTemplates
can also be used to produce a list of messages. In this case, the prompt not only contains information about the content, but also each message (its role, its position in the list, etc) Here, what happens most often is a ChatPromptTemplate
is a list of ChatMessagePromptTemplates
. Each ChatMessagePromptTemplate
contains instructions for how to format that ChatMessage
- its role, and then also its content. Let's take a look at this below:
const template = 'You are a helpful assistant that translates {input_language} to {output_language}.';
const humanTemplate = '{text}';
final chatPrompt = ChatPromptTemplate.fromTemplates([
(ChatMessageType.system, template),
(ChatMessageType.human, humanTemplate),
]);
final res = chatPrompt.formatMessages({
'input_language': 'English',
'output_language': 'French',
'text': 'I love programming.',
});
print(res);
// [
// SystemChatMessage(content='You are a helpful assistant that translates English to French.'),
// HumanChatMessage(content='I love programming.')
// ]
ChatPromptTemplates
can also be constructed in other ways - For specifics on how to use prompt templates, see the relevant how-to guides here.