We've raised a $125M Series B to build the platform for agent engineering.Read more.
Expand for full code snippet
import bs4from langchain.agentsimport AgentState, create_agentfrom langchain_community.document_loadersimport WebBaseLoaderfrom langchain.messagesimport MessageLikeRepresentationfrom langchain_text_splittersimport RecursiveCharacterTextSplitter# Load and chunk contents of the blogloader= WebBaseLoader( web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",), bs_kwargs=dict( parse_only=bs4.SoupStrainer( class_=("post-content","post-title","post-header") ) ),)docs= loader.load()text_splitter= RecursiveCharacterTextSplitter(chunk_size=1000,chunk_overlap=200)all_splits= text_splitter.split_documents(docs)# Index chunks_= vector_store.add_documents(documents=all_splits)# Construct a tool for retrieving context@tool(response_format="content_and_artifact")def retrieve_context(query:str): """Retrieve information to help answer a query.""" retrieved_docs= vector_store.similarity_search(query,k=2) serialized= "\n\n".join( (f"Source:{doc.metadata}\nContent:{doc.page_content}") for docin retrieved_docs ) return serialized, retrieved_docstools= [retrieve_context]# If desired, specify custom instructionsprompt= ( "You have access to a tool that retrieves context from a blog post. " "Use the tool to help answer user queries.")agent= create_agent(model, tools,system_prompt=prompt)query= "What is task decomposition?"for stepin agent.stream( {"messages": [{"role":"user","content": query}]}, stream_mode="values",): step["messages"][-1].pretty_print()================================ Human Message =================================What is task decomposition?================================== Ai Message ==================================Tool Calls: retrieve_context (call_xTkJr8njRY0geNz43ZvGkX0R) Call ID: call_xTkJr8njRY0geNz43ZvGkX0R Args: query: task decomposition================================= Tool Message =================================Name: retrieve_contextSource: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}Content: Task decomposition can be done by...Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}Content: Component One: Planning...================================== Ai Message ==================================Task decomposition refers to...pip install langchain langchain-text-splitters langchain-community bs4export LANGSMITH_TRACING="true"export LANGSMITH_API_KEY="..."import getpassimport osos.environ["LANGSMITH_TRACING"]= "true"os.environ["LANGSMITH_API_KEY"]= getpass.getpass()pip install -U "langchain[openai]"import osfrom langchain.chat_modelsimport init_chat_modelos.environ["OPENAI_API_KEY"]= "sk-..."model= init_chat_model("gpt-4.1")pip install -U "langchain-openai"import getpassimport osif not os.environ.get("OPENAI_API_KEY"):os.environ["OPENAI_API_KEY"]= getpass.getpass("Enter API key for OpenAI: ")from langchain_openaiimport OpenAIEmbeddingsembeddings= OpenAIEmbeddings(model="text-embedding-3-large")pip install -U "langchain-core"from langchain_core.vectorstoresimport InMemoryVectorStorevector_store= InMemoryVectorStore(embeddings)Documents into smaller chunks. This is useful both for indexing data and passing it into a model, as large chunks are harder to search over and won’t fit in a model’s finite context window.
WebBaseLoader, which usesurllib to load HTML from web URLs andBeautifulSoup to parse it to text. We can customize the HTML -> text parsing by passing in parameters into theBeautifulSoup parser viabs_kwargs (seeBeautifulSoup docs). In this case only HTML tags with class “post-content”, “post-title”, or “post-header” are relevant, so we’ll remove all others.import bs4from langchain_community.document_loadersimport WebBaseLoader# Only keep post title, headers, and content from the full HTML.bs4_strainer= bs4.SoupStrainer(class_=("post-title","post-header","post-content"))loader= WebBaseLoader( web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",), bs_kwargs={"parse_only": bs4_strainer},)docs= loader.load()assert len(docs)== 1print(f"Total characters:{len(docs[0].page_content)}")Total characters: 43131print(docs[0].page_content[:500]) LLM Powered Autonomous AgentsDate: June 23, 2023 | Estimated Reading Time: 31 min | Author: Lilian WengBuilding agents with LLM (large language model) as its core controller is a cool concept. Several proof-of-concepts demos, such as AutoGPT, GPT-Engineer and BabyAGI, serve as inspiring examples. The potentiality of LLM extends beyond generating well-written copies, stories, essays and programs; it can be framed as a powerful general problem solver.Agent System Overview#InDocumentLoader: Object that loads data from a source as list ofDocuments.BaseLoader: API reference for the base interface.Document into chunks for embedding and vector storage. This should help us retrieve only the most relevant parts of the blog post at run time.As in thesemantic search tutorial, we use aRecursiveCharacterTextSplitter, which will recursively split the document using common separators like new lines until each chunk is the appropriate size. This is the recommended text splitter for generic text use cases.from langchain_text_splittersimport RecursiveCharacterTextSplittertext_splitter= RecursiveCharacterTextSplitter( chunk_size=1000,# chunk size (characters) chunk_overlap=200,# chunk overlap (characters) add_start_index=True,# track index in original document)all_splits= text_splitter.split_documents(docs)print(f"Split blog post into{len(all_splits)} sub-documents.")Split blog post into 66 sub-documents.TextSplitter: Object that splits a list ofDocument objects into smallerchunks for storage and retrieval.document_ids= vector_store.add_documents(documents=all_splits)print(document_ids[:3])['07c18af6-ad58-479a-bfb1-d508033f9c64', '9000bf8e-1993-446f-8d4d-f4e507ba4b8f', 'ba3b5d14-bed9-4f5f-88be-44c88aedc2e6']Embeddings: Wrapper around a text embedding model, used for converting text to embeddings.VectorStore: Wrapper around a vector database, used for storing and querying embeddings.
from langchain.toolsimport tool@tool(response_format="content_and_artifact")def retrieve_context(query:str): """Retrieve information to help answer a query.""" retrieved_docs= vector_store.similarity_search(query,k=2) serialized= "\n\n".join( (f"Source:{doc.metadata}\nContent:{doc.page_content}") for docin retrieved_docs ) return serialized, retrieved_docsquery argument, as in the above example. You canforce the LLM to specify additional search parameters by adding arguments— for example, a category:from typingimport Literaldef retrieve_context(query:str,section: Literal["beginning","middle","end"]):from langchain.agentsimport create_agenttools= [retrieve_context]# If desired, specify custom instructionsprompt= ( "You have access to a tool that retrieves context from a blog post. " "Use the tool to help answer user queries.")agent= create_agent(model, tools,system_prompt=prompt)query= ( "What is the standard method for Task Decomposition?\n\n" "Once you get the answer, look up common extensions of that method.")for eventin agent.stream( {"messages": [{"role":"user","content": query}]}, stream_mode="values",): event["messages"][-1].pretty_print()================================ Human Message =================================What is the standard method for Task Decomposition?Once you get the answer, look up common extensions of that method.================================== Ai Message ==================================Tool Calls: retrieve_context (call_d6AVxICMPQYwAKj9lgH4E337) Call ID: call_d6AVxICMPQYwAKj9lgH4E337 Args: query: standard method for Task Decomposition================================= Tool Message =================================Name: retrieve_contextSource: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}Content: Task decomposition can be done...Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}Content: Component One: Planning...================================== Ai Message ==================================Tool Calls: retrieve_context (call_0dbMOw7266jvETbXWn4JqWpR) Call ID: call_0dbMOw7266jvETbXWn4JqWpR Args: query: common extensions of the standard method for Task Decomposition================================= Tool Message =================================Name: retrieve_contextSource: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}Content: Task decomposition can be done...Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}Content: Component One: Planning...================================== Ai Message ==================================The standard method for Task Decomposition often used is the Chain of Thought (CoT)...| ✅ Benefits | ⚠️ Drawbacks |
|---|---|
| Search only when needed – The LLM can handle greetings, follow-ups, and simple queries without triggering unnecessary searches. | Two inference calls – When a search is performed, it requires one call to generate the query and another to produce the final response. |
Contextual search queries – By treating search as a tool with aquery input, the LLM crafts its own queries that incorporate conversational context. | Reduced control – The LLM may skip searches when they are actually needed, or issue extra searches when unnecessary. |
| Multiple searches allowed – The LLM can execute several searches in support of a single user query. |
from langchain.agents.middlewareimport dynamic_prompt, ModelRequest@dynamic_promptdef prompt_with_context(request: ModelRequest) ->str: """Inject context into state messages.""" last_query= request.state["messages"][-1].text retrieved_docs= vector_store.similarity_search(last_query) docs_content= "\n\n".join(doc.page_contentfor docin retrieved_docs) system_message= ( "You are a helpful assistant. Use the following context in your response:" f"\n\n{docs_content}" ) return system_messageagent= create_agent(model,tools=[],middleware=[prompt_with_context])query= "What is task decomposition?"for stepin agent.stream( {"messages": [{"role":"user","content": query}]}, stream_mode="values",): step["messages"][-1].pretty_print()================================ Human Message =================================What is task decomposition?================================== Ai Message ==================================Task decomposition is...Returning source documents
from typingimport Anyfrom langchain_core.documentsimport Documentfrom langchain.agents.middlewareimport AgentMiddleware, AgentStateclass State(AgentState): context: list[Document]class RetrieveDocumentsMiddleware(AgentMiddleware[State]): state_schema= State def before_model(self,state: AgentState) -> dict[str, Any]| None: last_message= state["messages"][-1] retrieved_docs= vector_store.similarity_search(last_message.text) docs_content= "\n\n".join(doc.page_contentfor docin retrieved_docs) augmented_message_content= ( f"{last_message.text}\n\n" "Use the following context to answer the query:\n" f"{docs_content}" ) return { "messages": [last_message.model_copy(update={"content": augmented_message_content})], "context": retrieved_docs, }agent= create_agent( model, tools=[], middleware=[RetrieveDocumentsMiddleware()],)create_agent, we can easily incorporate new features and go deeper:Was this page helpful?