AI Agent入门实战从零搭建一个AI私厨应用配套视频黑马程序员2026最新版LangChainLangGraph开发实战引言在人工智能飞速发展的今天AI Agent智能体已经成为大模型应用的主流形态。与简单的问答不同Agent 能够感知环境、决策行动、调用工具、保持记忆从而完成复杂的多步骤任务。本篇文章我们将从零开始手把手教你搭建一个多模态 AI 私厨应用用户上传冰箱/厨房的照片AI 自动识别食材搜索相关食谱并按营养价值和难度排序推荐给用户。整个项目基于LangChain LangGraph架构使用FastAPI提供后端 RESTful 接口前端采用Next.js构建单页应用。核心技术点包括多模态大模型支持图片 文本的联合输入Web 搜索工具实时搜索食谱 Tavily 搜索Agent 记忆系统基于 SQLite 的会话历史持久化流式输出SSE 实时推送 AI 响应一、环境准备1.1 依赖安装在项目根目录下创建pyproject.toml声明所有依赖[project] name food-recipe-recommender version 0.1.0 description AI-powered recipe recommender based on uploaded food images requires-python 3.10 dependencies [ langchain0.3.0, langchain-community0.3.0, langchain-openai0.2.0, langchain-core0.3.0, langchain-tavily0.1.0, streamlit1.40.0, pillow11.0.0, python-dotenv1.0.0, fastapi0.109.0, uvicorn0.27.0, python-multipart0.0.6, alibabacloud-oss-v21.2.4, langgraph-checkpoint-sqlite3.0.3, ]安装依赖uv sync# 或使用 pippip install-r requirements.txt1.2 API Key 配置在项目根目录创建.env文件# 阿里百炼通义千问API - 支持多模态输入 DASHSCOPE_API_KEY你的API_KEY DASHSCOPE_BASE_URLhttps://dashscope.aliyuncs.com/compatible-mode/v1 # Tavily Web 搜索 API - 用于搜索食谱 TAVILY_API_KEY你的API_KEY # LangSmith 可选 - 用于调试和监控 Agent 运行时 LANGSMITH_API_KEY你的API_KEY LANGSMITH_TRACINGtrue LANGSMITH_PROJECTlc-course # 阿里云 OSS可选- 用于图片上传 OSS_ACCESS_KEY_ID你的AccessKey_ID OSS_ACCESS_KEY_SECRET你的AccessKey_Secret OSS_BUCKET你的Bucket名称 OSS_ENDPOINToss-cn-heyuan.aliyuncs.com1.3 项目结构项目根目录/ ├── app/ │ ├── __init__.py │ ├── main.py │ ├── agents/ │ │ ├── __init__.py │ │ └── personal_chief.py │ ├── api/ │ │ └── v1/ │ │ ├── __init__.py │ │ ├── chat.py │ │ └── oss.py │ ├── models/ │ │ ├── __init__.py │ │ └── schemas.py │ ├── common/ │ │ ├── __init__.py │ │ └── logger.py │ └── db/ # SQLite数据库目录自动创建 ├── .env └── pyproject.toml二、完整源码2.1app/__init__.py# app/__init__.py# 包初始化文件可以为空2.2app/common/__init__.py# app/common/__init__.py# 公共模块初始化文件2.3app/common/logger.py# app/common/logger.pyimportloggingimportsys# 配置日志格式时间 - 级别 - 模块 - 消息LOG_FORMAT%(asctime)s - %(levelname)s - %(name)s - %(message)sdefsetup_logging(): 初始化日志配置。 - StreamHandler将日志输出到控制台stdout方便在终端实时查看 - FileHandler注释掉如果需要持久化到文件可取消注释 logging.basicConfig(levellogging.INFO,# INFO级别起步生产环境可改为WARNINGformatLOG_FORMAT,handlers[logging.StreamHandler(sys.stdout),# 输出到控制台# logging.FileHandler(app.log) # 追加模式写入文件])# 创建一个全局的logger实例供其他模块直接调用# 使用__name__可以自动显示调用方的模块名便于定位日志来源loggerlogging.getLogger(personal_chief)2.4app/models/__init__.py# app/models/__init__.py# 数据模型模块初始化文件2.5app/models/schemas.py# app/models/schemas.pyfromtypingimportOptional,ListfrompydanticimportBaseModelclassChatRequest(BaseModel): 聊天请求的数据模型。 使用Pydantic做运行时类型校验FastAPI自动从请求体解析字段。 message:str# 用户输入的文本内容image_url:Optional[str]None# 可选图片的OSS URLthread_id:str# 会话线程ID用于关联同一轮对话的所有消息2.6app/agents/__init__.py# app/agents/__init__.py# Agent模块初始化文件2.7app/agents/personal_chief.py这是整个应用最核心的文件包含了 Agent 的完整逻辑# app/agents/personal_chief.pyfromlangchain.chat_modelsimportinit_chat_modelfromlangchain_core.messagesimportHumanMessage,AIMessageChunk,AIMessagefromlangchain_core.toolsimporttoolfromlangchain_tavilyimportTavilySearchfromlangchain.agentsimportcreate_agentfromapp.common.loggerimportloggerimportosfromlanggraph.checkpoint.sqliteimportSqliteSaverimportsqlite3# 加载环境变量fromdotenvimportload_dotenv load_dotenv()# web搜索工具使用tavily作为web搜索工具# max_results5每次搜索最多返回5条结果# topicgeneral通用主题搜索tavilyTavilySearch(max_results5,topicgeneral)# 多模态模型# 使用通义千问的qwen3-omni-flash多模态模型# model_provideropenai因为通义兼容OpenAI的接口协议所以填openaimodelinit_chat_model(modelqwen3-omni-flash,# 模型名称支持图片、文本、音频、视频多模态输入model_provideropenai,base_urlos.getenv(DASHSCOPE_BASE_URL),api_keyos.getenv(DASHSCOPE_API_KEY))# 初始化checkpointer# 连接SQLite数据库文件用于持久化Agent的对话历史# check_same_threadFalse允许跨线程访问uvicorn多worker时需要checkpointerSqliteSaver(sqlite3.connect(db/personal_chief.db,check_same_threadFalse))# 自动建表checkpointer.setup()# Agent系统提示词# 这是给AI的角色设定 操作流程说明书# 提示词的质量直接决定Agent的行为是否符合预期system_prompt 你是一名私人厨师。收到用户提供的食材照片或清单后请按以下流程操作 1.识别和评估食材若用户提供照片首先辨识所有可见食材。基于食材的外观状态评估其新鲜度与可用量整理出一份当前可用食材清单。 2.智能食谱检索优先调用 web_search 工具以可用食材清单为核心关键词查找可行菜谱。 3.多维度评估与排序从营养价值和制作难度两个维度对检索到的候选食谱进行量化打分并根据得分排序制作简单且营养丰富的排名靠前。 4.结构化方案输出把排序后的食谱整理为一份结构清晰的建议报告要包含食谱信息、得分、推荐理由、食谱的参考图片帮助用户快速做出决策。 请严格按照流程优先调用 web_search 工具搜索食谱搜索不到的情况下才能自己发挥。 # 创建代理# create_agent是LangChain的高级封装内部基于LangGraph实现# 只需传入model、tools、prompt框架自动构建推理循环agentcreate_agent(modelmodel,# 多模态大模型大脑tools[tavily],# 工具列表手脚checkpointercheckpointer,# 记忆存储笔记system_promptsystem_prompt# 行为规范手册)# 流式对话asyncdefsearch_recipes(prompt:str,image:str,thread_id:str): 调用agent搜索食谱支持流式输出打字机效果。 Args: prompt: 用户输入的文本 image: 可选的图片URL来自OSS上传 thread_id: 会话线程ID Yields: str: AI响应的文本片段用于SSE流式推送 logger.info(f[用户]:{prompt}, image:{image}, thread_id:{thread_id})try:# 判断是否有图片封装不同格式的消息# LangChain的HumanMessage支持多模态content列表形式ifnotimageorimage.strip():# 纯文本场景直接传字符串messageHumanMessage(contentprompt)else:# 多模态场景content是list包含image URL textmessageHumanMessage(content[{type:image,url:image},# 图片URL不传base64省内存{type:text,text:prompt}# 配合图片的文本问题])# 流式调用Agentforchunk,metadatainagent.stream({messages:[message]},{configurable:{thread_id:thread_id}},# 注入thread_id关联记忆stream_modemessages):ifisinstance(chunk,AIMessageChunk)andchunk.content:yieldchunk.contentexceptExceptionase:logger.error(f\n[错误]:{str(e)})yield信息检索失败试试看手动输入食物列表# 清空会话defclear_messages(thread_id:str):清空会话logger.info(f清空历史消息thread_id:{thread_id})checkpointer.delete_thread(thread_id)# 查询会话历史defget_messages(thread_id:str)-list[dict[str,str]]: 获取会话历史。 LangChain的checkpoint中存储了完整的消息列表 我们从中提取HumanMessage用户和AIMessageAI进行返回。 logger.info(f获取历史消息thread_id:{thread_id})# 根据thread_id查询checkpointcheckpointcheckpointer.get({configurable:{thread_id:thread_id}})# 如果不存在返回空列表ifnotcheckpoint:return[]# 安全获取messageschannel_valuescheckpoint.get(channel_values)ifnotchannel_values:return[]messageschannel_values.get(messages,[])ifnotmessages:return[]# 转换消息格式result[]formsginmessages:ifnotmsg.content:continueifisinstance(msg,HumanMessage):result.append({role:user,content:msg.content})elifisinstance(msg,AIMessage):result.append({role:assistant,content:msg.content})returnresult2.8app/api/__init__.py# app/api/__init__.py# API模块初始化文件2.9app/api/v1/__init__.py# app/api/v1/__init__.py# API v1版本初始化文件2.10app/api/v1/chat.py# app/api/v1/chat.pyfromfastapiimportAPIRouterfromapp.models.schemasimportChatRequestfromfastapi.responsesimportStreamingResponsefromapp.agents.personal_chiefimportsearch_recipes,get_messages,clear_messages routerAPIRouter()router.post(/chat/stream)asyncdefchat_endpoint(request:ChatRequest):流式聊天接口returnStreamingResponse(search_recipes(request.message,request.image_url,request.thread_id),media_typetext/event-stream)router.get(/chat/messages)asyncdefget_chat_messages(thread_id:str):获取历史消息messagesget_messages(thread_id)return{messages:messages}router.delete(/chat/messages)asyncdefclear_chat_messages(thread_id:str):清空历史消息clear_messages(thread_id)return{success:True}2.11app/api/v1/oss.py# app/api/v1/oss.pyimportalibabacloud_oss_v2asossfromfastapiimportAPIRouterfromdatetimeimporttimedeltaimportos# 加载环境变量fromdotenvimportload_dotenv load_dotenv()routerAPIRouter()# 从环境变量中加载凭证信息用于身份验证# 使用EnvironmentVariableCredentialsProvider密钥不落地代码credentials_provideross.credentials.EnvironmentVariableCredentialsProvider()# 加载SDK的默认配置并设置凭证提供者cfgoss.config.load_default()cfg.credentials_providercredentials_provider# 方式一只填写Region推荐# 必须指定Region IDSDK会根据Region自动构造HTTPS访问域名cfg.regioncn-heyuan# 使用配置好的信息创建OSS客户端clientoss.Client(cfg)# OSS 域名配置OSS_ENDPOINTos.getenv(OSS_ENDPOINT,oss-cn-heyuan.aliyuncs.com)OSS_BUCKETos.getenv(OSS_BUCKET)router.get(/oss/presign)defchat_endpoint(filename:str): 返回一个预签名的PUT请求URL。 前端拿这个URL直接上传文件到OSS不经过我们的服务器。 这样设计的好处 1. 服务器不承担文件传输省带宽 2. OSS原生支持HTTPS安全 3. 签名URL有时效过期无法上传防滥用 # 根据文件扩展名判断Content-TypeOSS上传需要准确类型content_type_map{jpg:image/jpeg,jpeg:image/jpeg,png:image/png,gif:image/gif,webp:image/webp,}extfilename.split(.)[-1].lower()if.infilenameelsejpgcontent_typecontent_type_map.get(ext,application/octet-stream)# 预签名URL有效期1小时pre_resultclient.presign(oss.PutObjectRequest(bucketOSS_BUCKET,keyfilename,content_typecontent_type,),expirestimedelta(seconds3600))# 返回上传URL和可访问的图片路径return{uploadUrl:pre_result.url.strip(),# 前端用这个URL上传contentType:content_type,accessUrl:fhttps://{OSS_BUCKET}.{OSS_ENDPOINT}/{filename}# 上传后的访问地址}2.12app/main.py# app/main.pyimportosfromfastapiimportFastAPIfromfastapi.responsesimportFileResponsefromfastapi.staticfilesimportStaticFilesfromfastapi.middleware.corsimportCORSMiddlewarefromapp.api.v1importchatfromapp.api.v1importossfromapp.common.loggerimportsetup_logging# 初始化日志配置最早执行确保后续所有模块都能正确记录日志setup_logging()# 创建FastAPI应用实例appFastAPI(titlePersonal Chief API,description私厨,version0.1.0)# 配置跨域资源共享 (CORS)# 因为我们的前端可能是独立部署的Next.js静态导出# 需要允许跨域请求否则浏览器会阻止AJAX请求app.add_middleware(CORSMiddleware,allow_origins[*],# 生产环境建议指定具体域名allow_credentialsTrue,allow_methods[*],allow_headers[*],)# 挂载路由# /api/v1/chat/* - chat.py中的接口# /api/v1/oss/* - oss.py中的接口app.include_router(chat.router,prefix/api/v1,tags[对话])app.include_router(oss.router,prefix/api/v1,tags[申请上传签名url])# 挂载前端资源如果有static目录的话static_diros.path.join(os.path.dirname(__file__),static)ifos.path.exists(static_dir):app.mount(/,StaticFiles(directorystatic_dir,htmlTrue),namestatic)# 前端fallback路由 - 只处理非API请求# Next.js是单页应用SPA所有路由都由index.html处理# FastAPI不知道前端有哪些路由所以对未匹配的路径返回index.htmlapp.get(/{path:path},include_in_schemaFalse)asyncdefserve_frontend(path:str):# 排除API路径ifpath.startswith(api/):fromfastapi.responsesimportJSONResponsereturnJSONResponse({error:Not Found},status_code404)# 如果请求的是静态文件直接返回file_pathos.path.join(static_dir,path)ifos.path.isfile(file_path):returnFileResponse(file_path)# 否则返回index.htmlSPA fallbackindex_pathos.path.join(static_dir,index.html)ifos.path.exists(index_path):returnFileResponse(index_path)return{message:你的独家私厨上线了~,status:ok}if__name____main__:importuvicorn# 启动命令python -m app.mainuvicorn.run(app.main:app,host127.0.0.1,port8001,reloadTrue)三、快速运行3.1 创建所有文件按照上面的项目结构创建好所有目录和文件将对应的代码复制进去。3.2 启动服务# 1. 安装依赖uvsync# 2. 启动服务python-mapp.main# 3. 打开浏览器访问# http://localhost:8001四、运行效果演示4.1 基础聊天界面启动后访问http://localhost:8001可以看到完整的聊天界面支持图片上传点击上传按钮选择冰箱/厨房照片支持纯文本对话直接输入食材列表AI自动识别食材并搜索食谱4.2 上传食材图片上传一张冰箱图片后AI 会自动识别食材4.3 智能食谱推荐AI 调用 Tavily 搜索相关食谱并按营养价值 制作难度打分排序4.4 多轮对话Agent 记忆用户我喜欢第1道菜可以说得更详细点吗 AI当然可以下面是三文鱼西兰花烤盘料理的详细步骤... 包含食材清单、详细步骤、小贴士、营养分析Agent 自动记住了之前的上下文知道第1道菜指的是什么食谱。4.5 LangSmith 调试界面LangChain 提供了基于 LangSmith 的 GUI 控制台可以方便地调试 Agent4.6 详细的调用过程追踪五、总结与扩展5.1 Agent 核心原理回顾┌─────────────────────────────────────────────┐ │ Agent 架构 │ ├─────────────────────────────────────────────┤ │ ┌─────────┐ ┌──────────┐ ┌────────┐ │ │ │ Model │◄──►│ Tools │◄──►│ Memory │ │ │ │ (大脑) │ │ (手脚) │ │ (笔记) │ │ │ └────┬────┘ └──────────┘ └────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ 推理循环 │ │ │ │ (Agent Loop)│ │ │ └─────────────┘ │ │ │ │ 1. 接收用户输入文本 图片 │ │ 2. 模型理解意图决定是否调用工具 │ │ 3. 执行工具Tavily 搜索 │ │ 4. 将工具结果注入模型生成最终回复 │ │ 5. 将本轮对话存入 Memory供后续使用 │ └─────────────────────────────────────────────┘组件技术选型作用Modelqwen3-omni-flash通义多模态理解 文本生成ToolsTavilySearch实时 Web 搜索食谱MemorySqliteSaver多轮对话历史持久化FrameworkLangChaincreate_agentAgent 编排框架5.2 扩展练习扩展 1添加更多工具目前 Agent 只有 Web 搜索一个工具。可以尝试添加天气 API根据当地天气推荐适合的菜品如雨天推荐热汤营养计算器接入营养数据库计算每道菜的热量/碳水/蛋白质扩展 2支持语音输入利用浏览器的Web Speech API采集语音通过 ASR 转文字后发送给 Agent实现拍照 语音提问的交互方式。扩展 3换用不同的模型将qwen3-omni-flash替换为gpt-4o或claude-3-opus只需修改model和base_url/api_key提示词和工具代码无需改动——这就是 LangChain 接口统一抽象的好处。推荐阅读类型名称/描述链接视频教程黑马程序员2026最新版LangChainLangGraph开发实战B站视频 BV178w1z7EHQ官方文档LangChain 官方文档https://python.langchain.com/官方文档LangGraph 快速入门https://langchain-ai.github.io/langgraph/官方文档Tavily Search APIhttps://tavily.com/官方文档阿里云百炼 APIhttps://bailian.console.aliyun.com/注意事项API Key 安全生产环境中务必将 Key 放在环境变量或密钥管理服务中切勿硬编码到代码OSS 权限示例中将 Bucket 设置为公共读仅用于演示生产环境应设为私有并通过 CDN 对外暴露流式输出兼容性部分 HTTP 客户端如某些版本requests不支持 SSE请使用浏览器 Fetch API 或 Postman 测试