从十年 Java 到智能体开发:我的全套 Python 智能体实战课程(第二帖)5-记忆与提示词
对了有个事情之前忘了说了大家的deepseek记得要充钱哦我的是有余额的所以之前没在意不好意思上一篇我们说了llm的接口调用本篇我们来实战一下。之前的项目只是一个llm接口调用并不能算一个真正的智能体接下来我们通过创建智能体的方式修改代码我们创建py文件名字 main_agent # 从 langchain_openai 库导入 ChatOpenAI 类用于调用兼容 OpenAI 接口的大模型如 DeepSeek from langchain_core.messages import AIMessage from langchain_openai import ChatOpenAI # 从 langchain.agents 中导入 create_agent 函数这是 LangChain 1.0 官方推荐的创建智能体的标准入口 from langchain.agents import create_agent # 从 langgraph.checkpoint.memory 导入 MemorySaver 类用于将对话状态存储在内存中仅在程序运行期间有效 from langgraph.checkpoint.memory import MemorySaver # 从当前目录的 config.py 文件中导入 Config 类用于读取 API 密钥等配置 from config import Config from http_utils import LoggingHttpClient # 调用 Config 类的 validate 方法检查 DEEPSEEK_API_KEY 是否已正确设置若未设置则抛出异常 Config.validate() # 创建带日志的客户端传入 ChatOpenAI http_client: LoggingHttpClient LoggingHttpClient(timeout60.0) # 可自定义超时 # 初始化 ChatOpenAI 实例用于与 DeepSeek API 通信传入模型名称、密钥、基础地址和温度参数 llm ChatOpenAI( modelConfig.LLM_MODEL, # 模型名称从配置中读取 api_keyConfig.DEEPSEEK_API_KEY, # API 密钥从配置中读取 base_urlConfig.DEEPSEEK_BASE_URL, # API 基础地址从配置中读取 temperature0.7, # 温度参数控制回答的随机性0~1 http_clienthttp_client ) # 创建 MemorySaver 实例用于在内存中存储对话的检查点checkpoint即对话历史 memory MemorySaver() # 调用 create_agent 函数创建一个智能体Agent它内部基于 LangGraph 构建具备工具调用和多轮对话能力 agent create_agent( modelllm, # 指定智能体使用的大模型实例 tools[], # 工具列表这里为空表示没有额外工具 system_prompt你是一个智能体助手名字叫小智, # 系统提示词设定智能体的角色和行为准则 checkpointermemory # 传入 checkpointer即记忆组件使智能体能够记住历史对话 ) # 打印启动界面的分隔线和提示信息 print( * 40) print(DocMind 智能体带记忆) print(输入 exit 退出) print( * 40) # 定义一个固定的会话线程 ID用于标识同一用户的对话从而实现持久记忆 thread_id user_001 # 进入无限循环不断接收用户输入并返回智能体的回复 while True: # 提示用户输入并读取用户输入的内容 user_input input(\n你: ) # 如果用户输入的小写形式是 exit则跳出循环结束程序 if user_input.lower() exit: break # 如果用户输入的是空字符串或仅包含空白字符则跳过本次循环不处理 if not user_input.strip(): continue # 调用智能体的 invoke 方法传入用户消息和配置以获取回复 result agent.invoke( {messages: [{role: user, content: user_input}]}, # 将用户输入包装成消息列表角色为 user config{configurable: {thread_id: thread_id}} # 通过 config 指定 thread_id确保同一线程共享记忆 ) print( * 40) print(f响应对象{result}) # 从返回结果中提取最后一条消息即智能体的回复的内容并打印出来 print( * 40) # --- 稳健获取 AI 回复仅取纯文本的 AIMessage --- last_ai_msg None # 从后往前遍历找到最后一个真正的 AIMessage 且 content 非空 for msg in reversed(result[messages]): if isinstance(msg, AIMessage) and msg.content: last_ai_msg msg break if last_ai_msg: print(fAI: {last_ai_msg.content}) else: print(AI: (未获取到有效回复))有两个点需要注意一下第一点我们添加了一个http请求的日志为了验证我们上一篇的接口调用查看我们的请求报文需要添加py文件名字为 http_utils import httpx import json # 定义一个自定义的 HTTP 客户端重写 send 方法以打印请求体 class LoggingHttpClient(httpx.Client): def send(self, request, *args, **kwargs): # 打印请求 URL 和 headers可选 print(f 请求 URL: {request.url}) print(f Headers: {request.headers}) # 打印请求体body if request.content: try: body_json json.loads(request.content) print( 原始请求体 (JSON):) print(json.dumps(body_json, indent2, ensure_asciiFalse)) except: print( 原始请求体 (文本):, request.content.decode()) # 继续正常发送请求 return super().send(request, *args, **kwargs)这样我们就可以看到如下的请求接口内容 原始请求体 (JSON):{messages: [{content: 你是一个智能体助手名字叫小智,role: system},{content: 我是阿岳你是谁,role: user},{content: 你好啊阿岳我是你的智能助手小智随时准备为你解答问题、提供建议或者陪你聊天。有什么可以帮你的吗,role: assistant},{content: 我喜欢布偶猫你可以说一下它的优点吗,role: user},{content: 布偶猫简直就是猫中小天使先说颜值它们有湛蓝的眼睛像宝石柔软的长毛摸起来像云朵尤其是那个标志性的“布偶”体型抱在怀里特别有分量感。性格更是一绝——不仅粘人还超级温柔你回家它会迎门口你一坐下它就赖腿上求摸甚至被抱着揉肚子都不会伸爪子。还有它的“社交达人”属性家里来客人也能淡定撒娇几乎不搞破坏。养一只布偶猫相当于同时拥有毛绒玩具暖心小跟班哦,role: assistant},{content: 有多暖心,role: user}],model: deepseek-v4-flash,stream: false,temperature: 0.7}大家可以看到请求的数据结构与我们上一篇讲的一致而且因为我们添加了系统提示词所以llm知道他叫小智还有就是我的问题 有多暖心 这句话单独拿出来语义是很割裂的如果没有上下文的话大模型根本不会回答的这么准确上面的请求返回如下AI: 当然阿岳布偶猫的“暖心”是刻在骨子里的给你几个真实感极强的例子——1. **“膝盖雷达”模式**你刚坐下沙发它会慢悠悠走过来用脑袋蹭你的手然后“啪叽”一声瘫在你大腿上像个小毛毯一样团起来。哪怕你打游戏、看书它也要贴着你偶尔抬头对你“喵”一声仿佛在说“别太累啦我陪着你呢。”2. **你的情绪侦探**如果你加班回家倒头就躺它会跳上床用小爪子轻轻拍你的脸或者把毛茸茸的头枕在你肩膀上咕噜咕噜地“按摩”式踩奶。有次我朋友心情不好她家布偶猫居然叼来最爱的玩具放在她枕头边眼神湿漉漉地看着她。3. **温柔的“跟屁虫”**你上厕所它蹲在门口你做饭它趴在厨房地垫上甚至你洗澡时它也守着玻璃门——不是捣乱就是想确认你在视线里。半夜翻身找不到它别担心它可能正缩在你脖子边呼噜声比助眠白噪音还治愈。4. **顶级“暖宝宝”**冬天它简直是移动暖水袋尤其喜欢钻被窝钻进你的臂弯里睡觉体温加上柔软的长毛抱着它就像抱着会呼吸的毛绒小熊。最戳人的是你假装生气时它会用脑袋顶你的下巴或者翻肚皮假装“认输”求原谅。**总结**布偶猫的暖不是“讨好”而是它们天生信任人类把陪伴当成本能。养久了你会觉得——这猫怕不是觉得自己是个人类幼崽吧要听它撒娇的小故事吗我还可以再编个番外篇~第二点就是我们在代码中打印了result对象这是为了我们更好的拿出llm给我们返回的内容响应对象{messages: [HumanMessage(content我是阿岳你是谁, additional_kwargs{}, response_metadata{}, id77506947-55b5-4bdd-9648-47e3046e2aed), AIMessage(content你好啊阿岳我是你的智能助手小智随时准备为你解答问题、提供建议或者陪你聊天。有什么可以帮你的吗, additional_kwargs{refusal: None}, response_metadata{token_usage: {completion_tokens: 113, prompt_tokens: 18, total_tokens: 131, completion_tokens_details: {accepted_prediction_tokens: None, audio_tokens: None, reasoning_tokens: 79, rejected_prediction_tokens: None}, prompt_tokens_details: {audio_tokens: None, cache_write_tokens: None, cached_tokens: 0}, prompt_cache_hit_tokens: 0, prompt_cache_miss_tokens: 18}, model_provider: openai, model_name: deepseek-v4-flash, system_fingerprint: fp_8b330d02d0_prod0820_fp8_kvcache_20260402, id: eddb20cb-cc91-4d75-9215-337cf2819c37, finish_reason: stop, logprobs: None}, idlc_run--019f573f-5b22-72b2-8192-f037ad2d3538-0, tool_calls[], invalid_tool_calls[], usage_metadata{input_tokens: 18, output_tokens: 113, total_tokens: 131, input_token_details: {cache_read: 0}, output_token_details: {reasoning: 79}})]}这行代码 if isinstance(msg, AIMessage) and msg.content: 就是我们判断拿到类型为AIMessage且不为空的消息。这行代码的上一行的reversed(...)方法这是Python的内置函数作用是反转迭代顺序。它不会改动原来的列表只是让你在遍历时从列表的最后一个元素开始往前取。因为ai返回的都是在消息后面部分。最后总结一下llm的接口每次都是单独http请求调用的也就是说其实所谓的记忆都是每次单次调用接口的时候我们加上去的是我们人为给大模型构造出的记忆。

相关新闻