晚上,我用京言AI助手点了一份烧烤

我们晚上在京东App上通过京言AI助手点外卖为具体场景,重新走一遍四个模块(LLM、工具、记忆、规划)的协同循环。假设京东App内置了一个智能助手(类似“京言AI助手”),用户对助手说:

“晚上想吃点辣的,不要太油,从京东外卖上帮我选一家附近的店,直接下单。”

下面就是完整的执行过程,对应你给出的那个典型循环(理解 → 规划 → 读记忆 → 调工具 → 存记忆 → 回复)。

1. LLM 接收并理解用户指令

  • 原始输入:自然语言,包含意图(点外卖)、约束条件(辣、不油)、平台限定(京东外卖)、动作(选店并下单)。
  • LLM 做的事
    • 识别关键词:晚上(时间暗示)、辣的(口味偏好)、不要太油(健康约束)、京东外卖(指定工具)、直接下单(执行动作)。
    • 明确缺失信息:用户当前位置、具体店铺、菜品、付款方式(但这些可能从记忆中获得)。
    • 输出结构化意图:{ action: "order_food", cuisine: "spicy", oil_level: "low", platform: "JD_Food", auto_confirm: true }

2. 规划模块分解任务

规划模块根据当前状态(已有意图、缺失信息)生成一个可动态调整的执行计划。典型计划如下:

步骤动作所需模块说明
1读取用户长期记忆记忆获取默认地址、历史口味偏好、常用支付方式
2获取实时位置(若记忆中无地址或用户移动了)工具调用京东App的GPS定位接口
3查询京东外卖API,获取附近餐厅及菜品工具传入位置、关键词“辣 低油”,返回候选列表
4让LLM评估候选结果,选择最佳餐厅和菜品LLM结合“辣但不油”约束和记忆中的历史评分
5调用下单API工具传入餐厅、菜品、地址、支付方式
6将订单信息写入长期记忆记忆记录本次消费,用于未来推荐
7生成自然语言回复LLM告知用户下单结果

注意:如果步骤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 机票例子)

步骤机票例子京东点外卖例子
LLM 接收指令“查明天北京到上海机票,总结表格”“晚上想吃辣的不要太油,京东外卖选店下单”
规划分解①查票 ②获取 ③总结①读记忆 ②定位 ③查外卖 ④评估 ⑤下单 ⑥存记忆 ⑦回复
从记忆读取“喜欢早班机”默认地址、支付方式、历史喜欢“少油麻辣香锅”
第一次调工具机票查询API京东外卖搜索API(传入位置+辣+低油)
工具结果存入短期记忆航班列表候选店铺+菜品油量标注
LLM 决策选择早班机并制表根据历史偏好和油量选择“川西坝子少油香锅”
第二次调工具(无,直接总结)京东外卖下单API
最终回复表格形式的航班信息订单确认+送达时间

关键差异与启发

  1. 多轮工具调用:点外卖需要至少两次工具调用(查询 + 下单),机票例子只需一次查询。
  2. 条件分支:点外卖中如果查询结果没有低油辣菜,规划模块会插入“放宽油量限制”或“换关键词”的重试。
  3. 长期记忆的动态更新:下单后立即更新偏好,形成个性化循环。
  4. 京东App特有约束:工具调用必须遵循京东API的鉴权、地址格式、支付方式等,这需要LLM严格按schema生成参数。

下面给出一个基于 LangChain 的伪代码实现,演示如何用 LLM + 工具 + 记忆 + 规划 完成“在京东JoyAI上点外卖”任务。

环境准备(伪代码假设)

# 导入 LangChain 相关组件
from langchain.agents import initialize_agent, Tool, AgentType
from langchain.memory import ConversationBufferMemory, VectorStoreRetrieverMemory
from langchain.llms import OpenAI  # 假设使用 OpenAI 模型,可替换为任何 LLM
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.callbacks import StdOutCallbackHandler
import json
from datetime import datetime

# 模拟京东外卖 API 函数 ------------------------------------------------
def get_user_location():
    """模拟获取用户当前 GPS 位置或默认地址"""
    # 实际会调用京东 App 的定位接口
    return"北京市朝阳区XX小区12号楼302"

def search_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_results

def place_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:获取用户位置
def get_location_tool_func(_):
    return get_user_location()

location_tool = Tool(
    name="GetUserLocation",
    func=get_location_tool_func,
    description="获取用户当前默认地址。输入可以为空字符串。"
)

# 工具2:搜索京东外卖
def search_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:下单
def place_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 中
def get_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")

# 创建 Agent
agent = 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
)

执行主流程

def run_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_history
    if"订单号"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: GetUserLocation
Action Input: ""
Observation: 北京市朝阳区XX小区12号楼302

Thought: 地址已获得。现在需要搜索京东外卖,关键词为“辣 少油”。
Action: SearchJDFood
Action Input: "北京市朝阳区XX小区12号楼302|辣 少油"
Observation: [{"store": "川西坝子·少油麻辣香锅", "dishes": [{"name": "招牌少油香锅", "oil_level": "low"}...]}, ...]

Thought: 搜索结果中有三家店。根据长期记忆,用户讨厌油大的食物,且历史喜欢麻辣香锅(少油)。川西坝子的招牌少油香锅完全满足“辣且低油”,距离最近,评分最高。因此选择这家店和这个菜品。
Action: PlaceJDFoodOrder
Action 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.
长期记忆已更新

=== 最终回复 ===
已按你“辣但不油”的要求...

关键点解释

  1. LLM:OpenAI 模型(或任何兼容模型)作为推理核心。
  2. 工具:三个自定义工具分别对应定位、搜索、下单。每个工具都有清晰的描述,Agent 知道何时调用。
  3. 记忆
    • 短期:ConversationBufferMemory 记录对话历史,保证多轮交互。
    • 长期:简单字典存储用户偏好,并在每次请求时注入到 prompt;执行结束后更新。
  4. 规划:通过自定义 prompt 模板,明确写出了规划步骤(1→2→3→4→5),Agent 会按照这个顺序思考,但实际执行时仍依赖于 ReAct 的 Thought/Action 循环,不会机械执行——LLM 会根据观察动态调整(例如如果搜索结果不理想,可能重新搜索)。

这种实现方式已经足够模拟“点外卖”场景下的协同工作。

声明:来自程序员千羽,仅代表创作者观点。链接:https://eyangzhen.com/8592.html

程序员千羽的头像程序员千羽

相关推荐

添加微信
添加微信
Ai学习群
返回顶部