Day 6:LangChain 入门——框架是双刃剑
系列Java工程师转AI Agent 3个月学习计划作者宸丶一| 28岁Java程序员规划狂魔正在被AI Agent按头学习今日目标用 LangChain 框架重写 Day 5 的 Agent对比手写 vs 框架个人格言代码改不改变世界我不知道但先让我准时下班。前言大家好我是宸一一个28岁的Java程序员。今天是第6天主题是LangChain 入门。昨天我们花了一整天手写了一个完整 AgentDay 5踩了 tool_call_id 的坑理解了工具调用的底层流程。今天换个思路用 LangChain 框架重写同一个 Agent看看框架帮我们省了多少事又带来了什么代价。有个意外收获我们亲历了 LangChain 0.x → 1.3 的 Breaking Change旧教程全部失效。这恰好验证了我们的学习路线——先手写理解原理框架变了也不慌。一、今日学习路线01_langchain_basics.pyLangChain 基础02_langchain_agent.py用框架实现 Agent03_comparison.py手写 vs 框架对比核心对比Day 5 手写 Agent OpenAI TOOLS_DEFINITION FullAgent 类~300行 LangChain Agent ChatOpenAI tool create_agent()~100行二、LangChain 四大组件2.1 用后端思维理解1. ModelChatOpenAI FeignClient2. Tooltool 装饰器 Strategy 策略模式3. Chainprompt | llm Pipeline 流水线4. Agentcreate_agent Controller 控制器LangChain 概念Java 对应作用ChatOpenAIFeignClient调用大模型 APItoolComponent注册可调用的工具ChainPipeline把多个步骤串起来AgentController决策用哪个工具、怎么回答2.2 tool 装饰器做了什么# LangChain 方式约 10 行/工具tooldefget_weather(city:str)-str:获取指定城市的天气信息。 Args: city: 城市名称如北京、上海 returnf{city}今天晴天25°Ctool 做了三件事1. 注册 —— 告诉框架这个函数是个可调用的工具 2. 提取 —— 自动从函数名、docstring、类型注解生成工具描述 3. 包装 —— 把普通函数包装成框架能识别的 Tool 对象对比 Day 5 手写约 30 行/工具# Day 5 手写方式TOOLS_DEFINITION[{type:function,function:{name:get_weather,description:获取指定城市的天气信息,parameters:{type:object,properties:{city:{type:string,description:城市名称}},required:[city]}}}]三、用 LangChain 重写 Agent3.1 核心代码fromlangchain_openaiimportChatOpenAIfromlangchain_core.toolsimporttoolfromlangchain.agentsimportcreate_agentfromlangchain_core.messagesimportHumanMessage# 1. 定义工具tooldefget_weather(city:str)-str:获取指定城市的天气信息。returnf{city}今天晴天25°Ctooldefcalculate(expression:str)-str:计算数学表达式。returnstr(eval(expression))# 2. 创建 ModelllmChatOpenAI(modelmimo-v2-flash,api_keyAPI_KEY,base_urlBASE_URL)# 3. 创建 Agent一行搞定agentcreate_agent(modelllm,tools[get_weather,calculate],system_prompt你是一个友好的AI助手。使用工具来回答问题。,)# 4. 调用 Agent一行搞定resultagent.invoke({messages:[HumanMessage(content北京天气怎么样)]})对比 Day 5 的 FullAgent 类~200行Day 5手写_execute_tool()、_build_messages()、chat()等方法LangChaincreate_agent()一行创建agent.invoke()一行调用3.2 运行效果 测试1查天气 回答北京今天晴天温度25°C适合出行☀️ 测试2算数学 回答(1527)*3 126 测试3查时间 回答现在是2026年6月5日下午2点07分。 测试4搜索知识 回答Python 是一种高级编程语言特点是简洁易读。四、重点tool 和 tool_call_id 的区别4.1 我的理解偏差随堂检测 Q2 问tool 怎么解决 tool_call_id 问题的我的错误回答“我感觉就是在 tool 注册时给到了 tool_call_id然后给到大模型大模型就可以调用到了。”这个理解是错的。我把两个不同的环节混在一起了。4.2 正确理解三个阶段三个主角整个工具调用流程分三个阶段 阶段1注册你写代码时做的事 ← tool 在这里 阶段2决策大模型做的事 ← tool_call_id 在这里生成 阶段3执行框架帮你做的事 ← tool_call_id 在这里传递阶段1注册 —— tool 在这里起作用时间点程序启动时 主角你 tool 目的告诉大模型我有哪些工具 tool 做了什么 ┌─────────────────────────────────────────────┐ │ 函数名 get_weather → 工具的 name │ │ docstring → 工具的 description │ │ 类型注解 city: str → 参数的 JSON schema │ └─────────────────────────────────────────────┘ 这时候大模型知道了我有一个叫 get_weather 的工具可以用。 但还没有人调用它。tool_call_id 还不存在。阶段2决策 —— 大模型在这里起作用时间点用户发消息北京天气怎么样 主角大模型 目的判断要不要调用工具 大模型返回 tool_calls [{ id: call_abc123, ← 这就是 tool_call_id function: { name: get_weather, arguments: {city: 北京} } }] 注意 - id: call_abc123 ← 大模型自动生成的唯一 ID - 这个 ID 和 tool 没有任何关系 - tool 是你注册工具时用的 - tool_call_id 是大模型决定调用工具时生成的阶段3执行 —— 框架在这里起作用时间点收到大模型的 tool_calls 主角create_agent 背后的框架 目的执行工具把结果正确返回给大模型 框架做的三步 第一步找到工具 tool_calls[0].function.name get_weather → 框架在 tool 注册的工具列表里找到它 第二步执行工具 get_weather(city北京) → 得到结果北京今天晴天25°C 第三步回传结果关键 { role: tool, tool_call_id: call_abc123, ← 必须带上这个 ID content: 北京今天晴天25°C }4.3 为什么 tool_call_id 很重要因为大模型可能一次返回多个 tool_calls call_abc → get_weather(北京) call_def → get_weather(上海) call_ghi → calculate(11) 三个结果各自带着自己的 key 回去 call_abc → 北京晴天 25°C call_def → 上海多云 28°C call_ghi → 2 没有 tool_call_id大模型分不清谁是谁。 就像 Java 里异步调用要关联 requestId 一样。4.4 总结阶段 主角 做什么 和 tool_call_id 的关系 ───────────────────────────────────────────────────────────────── 注册 tool 生成工具描述 无关 决策 大模型 决定调用哪个工具 它生成 tool_call_id 执行 框架 执行回传结果 它传递 tool_call_id Day 5 踩的坑阶段3回传结果时漏了 tool_call_id → 报错 LangChain 帮你做的事阶段3完全自动化你不用管 tool_call_id五、手写 vs 框架对比5.1 代码量对比--------------------------------------------------------------------- | 对比项 | Day 5 手写 | LangChain 框架 | --------------------------------------------------------------------- | 工具定义 | 手写 JSON schema (30行) | tool 装饰器 (10行) | | 工具调用 | 手写 tool_call_id (50行) | 框架自动处理 (0行) | | Agent 创建 | 自己写类 (200行) | create_agent() (1行) | | 对话历史 | deque JSON 文件 | Messages 列表 | | 错误处理 | 自己实现重试 | 内置重试机制 | | 代码量 | ~300 行 | ~100 行 | ---------------------------------------------------------------------5.2 框架的代价今天我们亲历了 LangChain 的 Breaking Change旧版0.x from langchain.agents import create_tool_calling_agent, AgentExecutor agent create_tool_calling_agent(llm, tools, prompt) executor AgentExecutor(agentagent, toolstools) 新版1.3 from langchain.agents import create_agent agent create_agent(modelllm, toolstools, system_prompt...) 变化 - AgentExecutor 没了 → 底层换成了 LangGraph - input/chat_history → 统一用 messages 列表 - 更简洁但旧教程全部失效框架的三个代价1. 黑盒相对的 闭源产品 → 完全看不到代码出问题只能等官方 开源框架 → 能看源码但要花时间理解 自己写的代码 → 100% 透明 2. Breaking Change 版本更新可能不兼容旧教程失效 3. 灵活性受限 自定义需求可能被框架限制5.3 什么时候用框架什么时候手写---------------------------------------------------------------------- | 场景 | 建议 | ---------------------------------------------------------------------- | 快速原型验证 | 用 LangChain快速出活 | | 学习原理 | 先手写再用框架我们就是这么做的 | | 生产环境 - 标准功能 | 用 LangChain社区维护 | | 生产环境 - 高度定制 | 手写核心逻辑完全可控 | | 生产环境 - 性能敏感 | 手写减少框架开销 | | 团队协作 | 用 LangChain统一标准 | | 个人项目 | 手写更灵活 | ----------------------------------------------------------------------六、用后端思维总结LangChain 概念Java 对应本例实现ChatOpenAIFeignClient调用小米 MiMo APItool 装饰器Component 接口注册 4 个工具函数ChatPromptTemplateString.format定义系统提示create_agentBean 工厂方法一行创建 Agentagent.invoke()controller.method()调用 Agent 处理请求tool_call_idrequestId工具调用的唯一标识七、今日收获7.1 核心公式LangChain Agent ChatOpenAI tool create_agent() 对比 Day 5 Day 5 Agent OpenAI TOOLS_DEFINITION FullAgent 类300行 LangChain Agent ChatOpenAI tool create_agent()100行7.2 最大的收获不是学会了 LangChain而是理解了框架的本质。框架 把重复的样板代码封装起来让你专注于业务逻辑 但框架不是银弹 - 版本更新可能 breaking change - 灵活性可能受限 - 出问题可能不好调试 所以 学习原理 → 先手写 提高效率 → 再用框架 生产选型 → 看场景7.3 学习路线的价值Day 1-5 手写 → Day 6 学框架✅ 好处 - 深入理解了 tool_call_id、消息格式等底层细节 - 知道框架帮你省了什么 - 框架变了也不慌底层知识永远有用 ❌ 代价 - 花了更多时间 - 没按规划的时间节点完成 结论 跑偏不一定是坏事。 学习路线不是直线而是螺旋上升。八、明日计划Day 7主题部署入门 - FastAPI Docker 基础- 用 FastAPI 把 Agent 包装成 HTTP 服务 - 写 Dockerfile 容器化 - 测试 API 接口 - 思考生产环境还需要什么一句话总结框架是双刃剑帮你省时间但也可能坑你。先手写理解原理再用框架提高效率。Day 6 最大的收获不是学会了 LangChain而是理解了 tool 和 tool_call_id 是两个不同的环节——注册是注册调用是调用别混为一谈。