LangChain Academy Notes
These are my notes from working through LangChain Academy. The courses follow the lifecycle of an agent:
- Build
- Build agents with the LangChain SDK: Introduction to LangChain - Python
- Test
- Monitor
- Deploy
Table of contents
- Introduction to LangChain - Python
Introduction to LangChain - Python
Module 1: Create Agent
- Can create chat model with
init_chat_modelor agent withcreate_agent- Able to customize parameters directly within init or invoke as needed (stream mode)
HumanMessageandAIMessagecan be passed in directly to agent for context
- Can define tools for agents to use + provide descriptions for them to understand + invoke similarly how we invoke functions
- Pass tools in via
create_agentand get backToolMessageif invoked - Good one to mention is Tavily - its search given a search query allows you to search the web for most up to date info
- Pass tools in via
- Agents don’t have memory so can use
InMemorySaverto record context automatically - Can pass in image/audio via base64 to agent for multimodal messages
Module 2: Advanced Agent
- MCP framework agnostic standard for agents to connect to tools and data sources ~= USB
- AI apps used to have differing ways of getting resources tools they wanted which made them act irrationally in different ways
- Now agent can act as MCP Client and connect to MCP server (local or online) which exposes tools/resources/prompts available for Client to utilize
- Can define context for agents to refer to as needed for more accurate responses
- Need to pass directly to agent to define shape + define tools that get/set the context
- Can define subagents with
create_agentand then call/pass them as tools normally to main agent to divide and conquer work
Module 3: Production-Ready Agent
-
Middleware allows you to inspect current data within your agent state diagram and perform actions on it in some way (don’t show PII, get human involved if X shown, etc)
- Can run middleware once, before, or after each run AND multiple times before or after each model call
- For example too many messages passed to the agent can overload its context window
- Can use
SummarizationMiddlewareto summarize messages once context window hits certain limit- Can define a custom middleware to trim messages from state if
ToolMessage
- Can define a custom middleware to trim messages from state if
- Can use
-
Include human in the loop for agentic workflows if sensitive actions, missing context, or debugging agents are required
-
Can use
HumanInTheLoopMiddlewareto interrupt on certain tool calls with certain messages which will generateInterruptevents for humans to act on- Can approve decision by invoking agent with decision of
type approve - Can reject decision by invoking agent with decision of
type reject - Can edit decision and run tool call with changes with decision of
type editand specifyingedited_actionwith tool name and tool args so don’t have to rely on rejecting model and model needing to retry
from langgraph.types import Command agent.invoke( Command(resume={ "decisions": [ { "type": "edit", "edited_action": { "name": "send_email", # tool name "args": { #tool args "to": "robert@acme.com", "subject": "Hi", "body": "...", }, }, } ] }), config=config, ) - Can approve decision by invoking agent with decision of
-
-
Normally middleware is node style as explained above but is inefficient if you want to change the underlying model itself so would want to use wrap style middleware instead
- Would pass in the
ModelRequestas a param to middleware function alongside a handler and decide either to return a custom prompt or invoke handler a certain way
- Would pass in the
-
Middleware Comparisons
- Wrap Style Middleware takes in a
ModelRequestand will adjust an agent’s overall tools, prompt, model while running
@wrap_model_call def retry_then_fallback(request, handler): for attempt in range(2): try: return handler(request) except Exception: pass # Both attempts failed, so try a different model for this one call return handler(request.override(model=fallback_model))@wrap_model_call def pick_model(request, handler): if len(request.state["messages"]) > 10: return handler(request.override(model=big_model)) return handler(request.override(model=small_model))@dynamic_prompt def role_based_prompt(request) -> str: ctx = request.runtime.context base = f"You are a helpful assistant. The user's name is {ctx.user_name}." if ctx.role == "admin": return base + " You may discuss internal system details." return base + " Do not reveal internal system details."- Node Style Middleware takes in an
AgentStateandRuntimeand will adjust an agent’s state while running
@before_model(can_jump_to=["end"]) def stop_if_too_long(state: AgentState, runtime: Runtime) -> dict | None: if len(state["messages"]) >= 50: return { "messages": [{"role": "assistant", "content": "Conversation limit reached."}], "jump_to": "end", } return None # no change, continue as normal@after_model def log_usage(state: AgentState, runtime: Runtime) -> None: last = state["messages"][-1] print("tool calls requested:", [tc["name"] for tc in last.tool_calls]) return None- Tool Calls takes in a
ToolRuntimeand allows an agent to adjust its own state or retrieve runtime context
@tool def get_weather(city: str, units: str = "fahrenheit") -> str: """Get the current weather for a city. Args: city: City name, e.g. "New York". units: "fahrenheit" or "celsius". """ # call your real API here return f"72 degrees {units} and sunny in {city}" - Wrap Style Middleware takes in a