我们晚上在京东App上通过京言AI助手点外卖为具体场景,重新走一遍四个模块(LLM、工具、记忆、规划)的协同循环。假设京东App内置了一个智能助手(类似“京言AI助手”),用户对助手说:
““晚上想吃点辣的,不要太油,从京东外卖上帮我选一家附近的店,直接下单。”
下面就是完整的执行过程,对应你给出的那个典型循环(理解 → 规划 → 读记忆 → 调工具 → 存记忆 → 回复)。
1. LLM 接收并理解用户指令
原始输入:自然语言,包含意图(点外卖)、约束条件(辣、不油)、平台限定(京东外卖)、动作(选店并下单)。 LLM 做的事: 识别关键词: 晚上(时间暗示)、辣的(口味偏好)、不要太油(健康约束)、京东外卖(指定工具)、直接下单(执行动作)。明确缺失信息:用户当前位置、具体店铺、菜品、付款方式(但这些可能从记忆中获得)。 输出结构化意图: { action: "order_food", cuisine: "spicy", oil_level: "low", platform: "JD_Food", auto_confirm: true }
2. 规划模块分解任务
规划模块根据当前状态(已有意图、缺失信息)生成一个可动态调整的执行计划。典型计划如下:
“注意:如果步骤4发现没有完全匹配的,规划模块会插入一个重规划子任务(例如放宽“不油”为“中等油量”或换一家店)。
3. 记忆模块提供上下文
在步骤1执行时,记忆模块被查询。它分为两部分:
短期记忆(本次会话):
用户刚刚说的“晚上、辣的、不要太油”。 尚未完成的下单操作状态。 长期记忆(跨会话存储,通常用向量数据库或键值表):
default_address: "北京市朝阳区XX小区12号楼302"payment_method: "京东白条"food_history: [ { date: "2025-10-01", item: "麻辣香锅(少油)", liked: true }, { date: "2025-10-05", item: "辣子鸡", feedback: "油太大" } ]preferred_cuisine: "川菜", "湘菜"
记忆模块将这些信息返回给LLM和规划器,供后续步骤使用。
4. LLM 决定调用工具(第一次:查询外卖)
LLM 读取短期记忆中的计划步骤3:需要调用 京东外卖查询工具。
工具名称: search_jd_food参数: {"location": "北京市朝阳区XX小区12号楼302","keywords": ["辣", "少油"],"radius_km": 3,"max_results": 5}工具返回结果(存短期记忆): [ { "store": "川西坝子·少油麻辣香锅", "distance": "800m", "rating": 4.8, "dishes": [ {"name": "招牌少油香锅", "spicy": true, "oil_level": "low", "price": 38}, {"name": "微辣牛肉卷", "spicy": true, "oil_level": "medium", "price": 32} ]}, { "store": "湘遇·低油菜馆", "distance": "1.2km", "rating": 4.6, "dishes": [ {"name": "少油小炒黄牛肉", "spicy": true, "oil_level": "low", "price": 45} ]}, { "store": "辣么火·传统川菜", "distance": "900m", "rating": 4.5, "dishes": [ {"name": "水煮鱼", "spicy": true, "oil_level": "high", "price": 68} ]}]
5. LLM 结合记忆进行推理和选择
LLM 现在拥有:
用户约束:辣、不油 长期记忆:用户喜欢“少油麻辣香锅”,讨厌“油大”的菜(辣子鸡反馈) 短期记忆:三家候选店铺及菜品油量标注
LLM 的思维链推理(可视为内部提示):
““辣么火的水煮鱼油量高,排除。湘遇的低油小炒黄牛肉符合要求,但距离稍远。川西坝子的招牌少油香锅距离近、评分高,且用户历史记录中对‘麻辣香锅(少油)’点赞,因此优先选择川西坝子的招牌少油香锅。”
最终选择:
店铺:川西坝子·少油麻辣香锅 菜品:招牌少油香锅(微辣,少油) 数量:1份 价格:38元 + 配送费 5元 = 43元
6. LLM 再次决定调用工具(第二次:执行下单)
规划模块步骤5要求调用 下单工具。LLM 生成调用参数(部分来自记忆):
{"tool": "place_jd_food_order","params": {"store_id": "JD_FOOD_7823","dish": "招牌少油香锅","quantity": 1,"address": "北京市朝阳区XX小区12号楼302","payment": "京东白条","special_requests": "少油,微辣" }}工具调用京东外卖的真实API,返回订单结果:
{"order_id": "JD202610160001","total_amount": 43.00,"estimated_delivery_time": "19:45","status": "success"}7. 记忆更新与最终回复
短期记忆:记录本次会话的订单号、金额、预计送达时间。
长期记忆更新:追加一条历史记录
{ date: "2026-10-16 19:00", item: "招牌少油香锅", liked: true, from_store: "川西坝子" },增强用户偏好向量。LLM 生成最终回复:
“
“已按你‘辣但不油’的要求,在京东外卖上找到附近的‘川西坝子·少油麻辣香锅’店,为你下单了一份招牌少油香锅(微辣、少油),共计43元,使用京东白条支付。预计今晚7:45送达至XX小区12号楼302。订单号JD202610160001。如需更改,请尽快告诉我。”
协同流程对照表(点外卖 vs 机票例子)
关键差异与启发
多轮工具调用:点外卖需要至少两次工具调用(查询 + 下单),机票例子只需一次查询。 条件分支:点外卖中如果查询结果没有低油辣菜,规划模块会插入“放宽油量限制”或“换关键词”的重试。 长期记忆的动态更新:下单后立即更新偏好,形成个性化循环。 京东App特有约束:工具调用必须遵循京东API的鉴权、地址格式、支付方式等,这需要LLM严格按schema生成参数。
下面给出一个基于 LangChain 的伪代码实现,演示如何用 LLM + 工具 + 记忆 + 规划 完成“在京东JoyAI上点外卖”任务。
环境准备(伪代码假设)
# 导入 LangChain 相关组件from langchain.agents import initialize_agent, Tool, AgentTypefrom langchain.memory import ConversationBufferMemory, VectorStoreRetrieverMemoryfrom langchain.llms import OpenAI # 假设使用 OpenAI 模型,可替换为任何 LLMfrom langchain.chains import LLMChainfrom langchain.prompts import PromptTemplatefrom langchain.callbacks import StdOutCallbackHandlerimport jsonfrom datetime import datetime# 模拟京东外卖 API 函数 ------------------------------------------------defget_user_location():"""模拟获取用户当前 GPS 位置或默认地址"""# 实际会调用京东 App 的定位接口return"北京市朝阳区XX小区12号楼302"defsearch_jd_food(location, keywords, radius_km=3):"""模拟京东外卖搜索接口"""# 参数示例:location="北京市朝阳区XX小区...", keywords="辣 少油" mock_results = [ {"store": "川西坝子·少油麻辣香锅","distance_km": 0.8,"rating": 4.8,"dishes": [ {"name": "招牌少油香锅", "spicy": True, "oil_level": "low", "price": 38}, {"name": "微辣牛肉卷", "spicy": True, "oil_level": "medium", "price": 32} ] }, {"store": "湘遇·低油菜馆","distance_km": 1.2,"rating": 4.6,"dishes": [ {"name": "少油小炒黄牛肉", "spicy": True, "oil_level": "low", "price": 45} ] }, {"store": "辣么火·传统川菜","distance_km": 0.9,"rating": 4.5,"dishes": [ {"name": "水煮鱼", "spicy": True, "oil_level": "high", "price": 68} ] } ]return mock_resultsdefplace_jd_food_order(store_id, dish_name, quantity, address, payment_method, special_requests=""):"""模拟京东外卖下单接口"""# 实际会调用京东下单 API,这里返回模拟订单结果 order_id = f"JD{datetime.now().strftime('%Y%m%d%H%M%S')}" total_amount = 43.00# 示例金额 estimated_time = "19:45"return {"order_id": order_id,"total_amount": total_amount,"estimated_delivery_time": estimated_time,"status": "success" }定义工具(Tools)
将模拟 API 包装成 LangChain 的 Tool 对象,供 LLM 调用。
# 工具1:获取用户位置defget_location_tool_func(_):return get_user_location()location_tool = Tool( name="GetUserLocation", func=get_location_tool_func, description="获取用户当前默认地址。输入可以为空字符串。")# 工具2:搜索京东外卖defsearch_food_tool_func(query: str) -> str:"""query 格式: "location|keywords" 例如 '北京市...|辣 少油'""" parts = query.split("|") location = parts[0] if len(parts) > 0else get_user_location() keywords = parts[1] if len(parts) > 1else"辣 不油" results = search_jd_food(location, keywords)return json.dumps(results, ensure_ascii=False)search_tool = Tool( name="SearchJDFood", func=search_food_tool_func, description="搜索京东外卖餐厅和菜品。输入格式: '地址|关键词',例如 '北京市朝阳区XX小区|辣 少油'")# 工具3:下单defplace_order_tool_func(order_json: str) -> str:"""order_json 包含 store_id, dish_name, quantity, address, payment_method, special_requests""" data = json.loads(order_json) result = place_jd_food_order( store_id=data["store_id"], dish_name=data["dish_name"], quantity=data.get("quantity", 1), address=data["address"], payment_method=data["payment_method"], special_requests=data.get("special_requests", "") )return json.dumps(result, ensure_ascii=False)order_tool = Tool( name="PlaceJDFoodOrder", func=place_order_tool_func, description="在京东外卖下单。输入格式为 JSON: {'store_id':..., 'dish_name':..., 'quantity':1, 'address':..., 'payment_method':..., 'special_requests':...}")tools = [location_tool, search_tool, order_tool]记忆模块(Memory)
我们需要 短期记忆(保存当前会话上下文)和 长期记忆(保存用户偏好)。这里简化演示:使用 ConversationBufferMemory 作为短期记忆,另外用一个简单的字典模拟长期记忆(实际可用向量数据库)。
# 短期记忆:存储最近对话及工具调用结果short_memory = ConversationBufferMemory( memory_key="chat_history", return_messages=True, output_key="output")# 长期记忆:模拟用户偏好(实际可用 Chroma/Pinecone)long_term_memory = {"default_address": "北京市朝阳区XX小区12号楼302","payment_method": "京东白条","food_history": [ {"date": "2025-10-01", "item": "麻辣香锅(少油)", "liked": True}, {"date": "2025-10-05", "item": "辣子鸡", "feedback": "油太大"} ],"preferred_cuisine": ["川菜", "湘菜"]}# 一个简单的检索函数,将长期记忆注入到 prompt 中defget_long_term_context(user_query: str) -> str:# 实际根据 query 向量检索,这里直接返回整个偏好returnf"用户长期偏好:默认地址 {long_term_memory['default_address']},支付方式 {long_term_memory['payment_method']}," \f"历史喜欢的菜:{long_term_memory['food_history'][0]['item']},讨厌油大的食物。"规划模块(Planning)
LangChain 的 initialize_agent 默认使用 ReAct 风格的 Agent,它本身就具备简单的规划能力(通过 LLM 生成 Thought/Action/Observation 循环)。若需要更复杂的多步规划,可以自定义 AgentExecutor 或使用 PlanAndExecuteAgent。这里我们使用标准 zero-shot-react-description Agent,它的规划隐含在推理步骤中。
为了凸显“规划”,我们写一个显式的规划提示来引导 LLM 按步骤执行,并让 Agent 遵循该规划。
# 自定义规划提示模板planning_prompt = PromptTemplate( input_variables=["input", "agent_scratchpad", "chat_history", "long_term_context"], template="""你是一个京东外卖助手。你需要按照以下规划步骤执行用户的请求:【规划步骤】1. 如果用户没有明确给出地址,先调用 GetUserLocation 工具获取默认地址(从长期记忆获取也可以)。2. 调用 SearchJDFood 工具,使用地址和“辣 少油”作为关键词,获取附近餐厅列表。3. 分析搜索结果,结合用户的长期偏好(尤其是讨厌油太大的食物),选择最符合“辣但不油”的餐厅和菜品。4. 调用 PlaceJDFoodOrder 工具,传入选中的餐厅ID、菜品名、地址、支付方式(从长期记忆获取)等。5. 返回订单确认信息给用户。用户长期偏好:{long_term_context}当前对话历史:{chat_history}用户最新指令:{input}{agent_scratchpad}""")# 初始化 LLM(以 OpenAI 为例,实际可用任何模型)llm = OpenAI(temperature=0, model_name="gpt-4")# 创建 Agentagent = initialize_agent( tools=tools, llm=llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, memory=short_memory, agent_kwargs={"system_message": planning_prompt.template # 注入规划模板 }, verbose=True, # 打印推理过程 handle_parsing_errors=True)执行主流程
defrun_jd_food_agent(user_input: str):# 1. 获取长期记忆上下文 long_context = get_long_term_context(user_input)# 2. 将长期记忆注入到 agent 的额外参数中(简单方式:拼接到输入)# 实际可以通过 callback 或 memory 的额外字段实现 full_input = f"{user_input}\n\n【长期记忆信息】{long_context}"# 3. 调用 Agent 执行 response = agent.run(full_input)# 4. 执行后,根据结果更新长期记忆(例如记录本次点的菜)# 这里简单示例:从 response 中提取订单信息,更新 food_historyif"订单号"in response: new_record = {"date": datetime.now().strftime("%Y-%m-%d %H:%M"), "item": "招牌少油香锅", "liked": True} long_term_memory["food_history"].append(new_record) print("长期记忆已更新")return response# 模拟用户输入user_query = "晚上想吃辣的,不要太油,帮我从京东外卖上选一家附近的店直接下单"result = run_jd_food_agent(user_query)print("\n=== 最终回复 ===\n", result)模拟运行输出(Agent 的推理日志)
由于是伪代码,我们模拟一下 Agent 内部产生的 Thought/Action/Observation 序列:
> Entering new AgentExecutor chain...Thought: 用户要求晚上点辣且不油的外卖。我需要先获得用户地址,然后搜索京东外卖,再选择符合要求的餐厅下单。Action: GetUserLocationAction Input: ""Observation: 北京市朝阳区XX小区12号楼302Thought: 地址已获得。现在需要搜索京东外卖,关键词为“辣 少油”。Action: SearchJDFoodAction Input: "北京市朝阳区XX小区12号楼302|辣 少油"Observation: [{"store": "川西坝子·少油麻辣香锅", "dishes": [{"name": "招牌少油香锅", "oil_level": "low"}...]}, ...]Thought: 搜索结果中有三家店。根据长期记忆,用户讨厌油大的食物,且历史喜欢麻辣香锅(少油)。川西坝子的招牌少油香锅完全满足“辣且低油”,距离最近,评分最高。因此选择这家店和这个菜品。Action: PlaceJDFoodOrderAction Input: {"store_id": "川西坝子·少油麻辣香锅", "dish_name": "招牌少油香锅", "quantity": 1, "address": "北京市朝阳区XX小区12号楼302", "payment_method": "京东白条", "special_requests": "少油,微辣"}Observation: {"order_id": "JD20261016190001", "total_amount": 43.0, "estimated_delivery_time": "19:45", "status": "success"}Thought: 下单成功,我已获得订单信息。现在回复用户。Final Answer: 已按你“辣但不油”的要求,在京东外卖上找到附近的“川西坝子·少油麻辣香锅”店,为你下单了一份招牌少油香锅(微辣、少油),共计43元,使用京东白条支付。预计今晚7:45送达至XX小区12号楼302。订单号JD20261016190001。如需更改,请尽快告诉我。> Finished chain.长期记忆已更新=== 最终回复 ===已按你“辣但不油”的要求...关键点解释
LLM:OpenAI 模型(或任何兼容模型)作为推理核心。 工具:三个自定义工具分别对应定位、搜索、下单。每个工具都有清晰的描述,Agent 知道何时调用。 记忆: 短期: ConversationBufferMemory记录对话历史,保证多轮交互。长期:简单字典存储用户偏好,并在每次请求时注入到 prompt;执行结束后更新。 规划:通过自定义 prompt 模板,明确写出了规划步骤(1→2→3→4→5),Agent 会按照这个顺序思考,但实际执行时仍依赖于 ReAct 的 Thought/Action 循环,不会机械执行——LLM 会根据观察动态调整(例如如果搜索结果不理想,可能重新搜索)。
这种实现方式已经足够模拟“点外卖”场景下的协同工作。
夜雨聆风