Prompt 到答案,中间发生了 7 件事
用户说"我的订单到哪了",半秒后收到回复。黑盒里是这样的。
全流程一览
| 步骤 | 发生了什么 | 在哪一层 |
|---|---|---|
| 1 | 前端打包请求,POST /chat | 前端 |
| 2 | 构建 AgentExecutor | FastAPI |
| 3 | 把上下文塞进 Prompt | Prompt 层 |
| 4 | LLM 第一次推理,选工具 | LLM |
| 5 | 工具执行,查数据库 | 工具层 |
| 6 | LLM 第二次推理,生成答案 | LLM |
| 7 | 存记忆,返回前端 | Memory |
步骤 1 --- 前端打包请求
json
{
"input": "订单 ORD-001 到哪了",
"session_id": "sess_abc123"
}
session_id 是前端启动时随机生成的,后端靠它区分不同用户的对话历史。
步骤 2 --- 构建 AgentExecutor
FastAPI 收到请求,调 build_executor(session_id)。
这个函数把四件东西组装在一起:
- LLM --- DeepSeek,temperature=0,输出格式稳定
- Tools --- 8 个
@tool函数 - Memory --- 按 session_id 隔离,保留最近 10 轮对话
- Prompt --- ReAct 模板
python
executor = build_executor(body.session_id)
result = executor.invoke({"input": body.input})
步骤 3 --- Prompt 构建
发给 LLM 之前,5 个变量自动填充:
{tools} ← 8 个工具的名称和 docstring
{tool_names} ← 工具名列表
{chat_history} ← 历史对话
{input} ← 用户这次的输入
{agent_scratchpad} ← 推理暂存区(空的,后面会填)
LLM 靠读 {tools} 里的 docstring 决定调哪个工具。
docstring 写得越清楚,命中率越高。
步骤 4 --- LLM 第一次推理
LLM 读完 Prompt,输出:
Thought: 用户在问订单 ORD-001 的状态,需要调 query_order 工具
Action: query_order
Action Input: ORD-001
AgentExecutor 解析这段输出,找到 query_order,准备执行。
步骤 5 --- 工具执行
python
@tool
def query_order(order_id: str) -> str:
"""
查询订单的状态和物流信息。
当用户询问订单到哪里了、是否发货时调用。
输入格式:ORD-XXXXX
"""
order = db.query(Order).filter(Order.id == order_id).first()
return f"订单 {order_id}:{order.product_name},已发货,快递 {order.tracking_no},明天到"
返回字符串即 Observation,追加进 {agent_scratchpad},再次发给 LLM。
步骤 6 --- LLM 第二次推理
LLM 看到 Observation,判断信息够了:
Thought: 已经拿到订单信息,可以直接回答
Final Answer: 您的订单 ORD-001(iPhone 15)已发货,
快递单号 SF1234567890,预计明天送达
看到 Final Answer,循环结束。
如果信息不够,LLM 会继续选工具,最多循环 5 次。
步骤 7 --- 存记忆,返回前端
对话存进 Memory:
python
memory.save_context(
{"input": "订单 ORD-001 到哪了"},
{"output": "您的订单 ORD-001 已发货..."}
)
结果打包返回:
json
{
"output": "您的订单 ORD-001 已发货...",
"intermediate_steps": [
[{"tool": "query_order", "tool_input": "ORD-001", "log": "..."},
"订单 ORD-001:iPhone 15,已发货,快递 SF1234567890,明天到"]
]
}
intermediate_steps 就是前端 Debug 面板展示的推理链路。
三个关键点
LLM 调用了两次,一次选工具,一次生成答案。工具越多调用越多,响应越慢。
docstring 比代码更重要,LLM 靠它判断要不要调这个工具,写得模糊工具就不会被选中。
调试看 intermediate_steps,每一步 Thought / Action / Observation 都记录在里面,不用猜。
遇到问题怎么排查
工具没被调用 → 检查 docstring,描述不够清楚
工具调了结果错 → 检查工具函数,数据库查询是否正确
格式解析失败 → temperature 设成 0,Prompt 模板变量是否完整
响应很慢 → 减少 max_iterations,或者工具数量太多