The final project of Module 1 - integrating all skills together: using Tavily to search recipes, using InMemorySaver to remember user ingredients, using System Prompt to define chef roles, and build a real and usable personal chef Agent.
Personal Chef Agent is a comprehensive practical project of Module 1, which integrates the core capabilities of all previous courses:
Personal Chef Agent is aReal and usable applications: The user tells the Agent what ingredients are available at home, and the Agent searches for the corresponding recipes, remembers the user's ingredients in subsequent conversations, and can continue to provide suggestions and ask for specific recipes. This is the typical usage scenario of LangChain Agent - combining tool invocation, memory and role setting.
from dotenv import load_dotenv
load_dotenv()
import os
from langchain.chat_models import init_chat_model
from langchain.agents import create_agent
from langchain.messages import HumanMessage
from langchain.tools import tool
from langgraph.checkpoint.memory import InMemorySaver
from tavily import TavilyClient
# ────────────────────────────────────
# 组件 1:模型工厂函数
# ────────────────────────────────────
def deepseek_model(**kwargs):
return init_chat_model(
model="deepseek-chat",
model_provider="openai",
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com",
max_tokens=1000,
**kwargs
)
# ────────────────────────────────────
# 组件 2:Web 搜索工具
# ────────────────────────────────────
tavily_client = TavilyClient()
@tool
def web_search(query: str) -> dict:
"""Search the web for information"""
return tavily_client.search(query)
# ────────────────────────────────────
# 组件 3:System Prompt(厨师角色定义)
# ────────────────────────────────────
system_prompt = """
You are a personal chef. The user will give you a list of ingredients they have left over in their house.
Using the web search tool, search the web for recipes that can be made with the ingredients they have.
Return recipe suggestions and eventually the recipe instructions to the user, if requested.
"""
# ────────────────────────────────────
# 组件 4 + 5:Agent = 模型 + 工具 + System Prompt + 记忆
# ────────────────────────────────────
agent = create_agent(
model=deepseek_model(),
tools=[web_search],
system_prompt=system_prompt,
checkpointer=InMemorySaver()
)
System Prompt is the "soul" of this Agent - it defines the Agent's role, capability boundaries and response methods:
system_prompt = """
You are a personal chef. The user will give you a list of ingredients they have left over in their house.
Using the web search tool, search the web for recipes that can be made with the ingredients they have.
Return recipe suggestions and eventually the recipe instructions to the user, if requested.
"""
This prompt does three things:
LLM has a lot of cooking knowledge,When not instructed to use tools, it may answer directly based on its own knowledge.- This may seem reasonable for a recipe, but may not match the user's existing ingredients, or give inaccurate instructions. Explicitly request "search the web" through the system prompt to ensure that the Agent always gives suggestions based on search results rather than generating them out of thin air.
Here'sweb_searchThe tool is slightly different than the previous one - using@toolSimplified writing method (no name specified, function name automatically used), and description comes from docstring:
@tool # 简化写法:name 自动为 "web_search"
def web_search(query: str) -> dict:
"""Search the web for information""" # docstring 作为 description
return tavily_client.search(query)
Explicit writing(recommend):@tool("web_search", description="Search the web for...")
Simplified writing(this example):@tool, name = function name, description = docstring
Simplified writing is more concise, but make sure the function name is clear (web_searchinstead ofsearch), the docstring description is sufficient.
config = {"configurable": {"thread_id": "1"}}
# 轮次 1:告诉 Agent 有什么食材
response = agent.invoke(
{"messages": [HumanMessage(content="I have some leftover chicken and rice. What can I make?")]},
config
)
print(response['messages'][-1].content)
Agent calledweb_search, searches for "chicken and rice recipes", gives 3 suggestions based on the search results, and invites the user to choose a recipe with detailed steps.
# 轮次 2:用户要鸡肉炒饭的做法(Agent 记住了上一轮的食材)
response2 = agent.invoke(
{"messages": [HumanMessage(content="Can you give me the detailed recipe for chicken fried rice?")]},
config
)
print(response2['messages'][-1].content)
In round 2, the user directly said "give me the detailed recipe for chicken fried rice" without repeatedly mentioning "chicken and rice". The reason why Agent can understand this request and give the correct response is becauseInMemorySaver saves the conversation history of round 1——Agent knows which recipe options were discussed previously.
When the user says "I have some leftover chicken and rice. What can I make?", the complete execution process inside the Agent:
HumanMessage adds to the message list. checkpointer loads the history of the current thread_id (the first round is empty).
The model sees system prompt (chef role) + historical messages + user messages. It reasoned: "The user has chicken and rice, I should use the web_search tool to find the recipe." Output a tool_call.
LangGraph executionweb_search("chicken and rice recipes"), obtain Tavily search results, encapsulate them into ToolMessage and add them to the message history.
The model sees the search results and generates recipe suggestions based on real data. Output the final AIMessage (text answer).
InMemorySaver saves the complete message history of this execution (including HumanMessage + AIMessage + ToolMessage). It will be loaded automatically the next time it is called with the same thread_id.
At this point, all seven Lessons of Module 1 are completed. The following is a complete knowledge panorama:
init_chat_model— Unified initializationcreate_agent— Build Agentinvoke / stream— Calling methodresponse_format=Pydantic— Structured outputstructured_response— Get Pydantic object@toolDecorators — definition toolstools=[...]— Bind to AgentInMemorySaver— Memory checkpointerthread_id— Distinguish conversation contextModule 1 establishes the complete foundation of LangChain Agent: model calling, prompt engineering, tool calling, and memory. Module 2 will explore more complex Agent design patterns in depth - multi-Agent collaboration, state management, more complex memory strategies, etc. The personal chef Agent is a good starting point, and the Agent of the real project is built step by step in this way.