节点 ↔ Prompt 对应关系全景报告
- 工程 :
agent-canteen/agent(食堂 Agent,LangGraph Phase One 单图运行时) - 日期:2026-10-15
- 事实来源 (均为本次逐文件核实,非推断):
- 图拓扑与路由:src/core/orchestration.py(
NODE_ORDER、next_node、build_orchestration_graph) - 节点实现:src/core/orchestration_nodes.py
- LLM 调用与 Prompt 解析:src/nodes/phase_one/llm_runtime.py
- 各节点 LLM 调用点:
intent.py:831、slot_filling.py:596、planning.py:696、response.py:835/1243/1339/1755、realtime_comparison.py:296-348、copy_finalizer(orchestration_nodes.py:642-675)、review_handler.py:33-34 - Prompt 加载/热更:src/agent/prompt_contract.py、src/agent/unified_resources.py、src/agent/builder.py、src/main.py:648-734
- 图拓扑与路由:src/core/orchestration.py(
1. 图拓扑(8 个节点,实际执行顺序)
┌─────────────────────────────────────────────┐
▼ │
Entry ──▶ IntentRecognizer ──▶ SlotFiller ──▶ Planner ──▶ ToolExecutor ──▶ ResponseGenerator ──▶ CopyFinalizer ──▶ END
│ │ ▲ ▲ │ ▲ │ │ ▲
│ route_to=fallback│ │ │ │ │ │ │ route_to=fallback │
│ route_to=response│ │ └────────┼──┘ └──────────┼───────────────────┤
└──────────────────┼────┘ (slot_filler 回补槽位) (planner 回流/推进) │
▼ │
Fallback ────────────────────────────────────────────────┘
(terminal=failed → END;否则 → ResponseGenerator)
路由规则(orchestration.py: next_node,由每个节点写回 state 的 route_to 决定):
| 节点 | 可能的下一跳 |
|---|---|
| Entry | → IntentRecognizer(无条件) |
| IntentRecognizer | fallback→Fallback;response_generator→ResponseGenerator;其余→SlotFiller |
| SlotFiller | fallback→Fallback;response_generator→ResponseGenerator;其余→Planner |
| Planner | intent_recognizer→IntentRecognizer;slot_filler→SlotFiller;fallback→Fallback;tool_executor→ToolExecutor;其余→ResponseGenerator |
| ToolExecutor | fallback→Fallback;slot_filler→SlotFiller;planner 或 plan 含步骤→Planner(推进下一步);其余→ResponseGenerator |
| Fallback | terminal_state=failed→END;否则→ResponseGenerator |
| ResponseGenerator | → CopyFinalizer(无条件,末站) |
| CopyFinalizer | → END |
注意:每个业务轮最多触发 1---4 次 LLM 调用 (意图 1 + 槽位 0/1 + 计划 0/1 + 响应 0/1)+ 0---2 次文案润色调用(卡片/解释/比较 + 终端润色)。大量请求在节点内部被确定性快捷路径短路,根本不到 LLM(见 §3 各节"先于 LLM 的快捷路径")。
2. 总表:节点 ↔ Prompt ↔ 代码调用点 ↔ 加载方式
| # | 图节点 | 用到的 Prompt 文件 | Nacos key | 加载方式 | 代码调用点 |
|---|---|---|---|---|---|
| 1 | Entry | ---(无 LLM) | --- | --- | orchestration_nodes.entry |
| 2 | IntentRecognizer | unified_intent_candidates.md |
canteen-agent-unified-intent-candidates |
PhaseOnePromptProvider.get():Registry→启动快照→本地 |
intent.py:831-837,output_model=RecommendationAwareIntent |
| 3 | SlotFiller | ① slot_filler.md(主路径) ② realtime_comparison_copy.md(UC03 比较快捷路径) |
① canteen-agent-slot-filler ② canteen-agent-realtime-comparison-copy |
① get() ② get_copy_prompt() + main.py watch 热更 |
① slot_filling.py:596-607,output_model=SlotResult ② realtime_comparison.py:296-348 |
| 4 | Planner | planner.md(仅当无 server plan 时) |
canteen-agent-planner |
get() |
planning.py:696-701,output_model=PlanResult |
| 5 | ToolExecutor | ---(无 LLM,纯 MCP 工具执行) | --- | --- | tool_execution.py |
| 6 | ResponseGenerator | 按分支,优先级从上到下: ① recommendation_explanation_copy.md(UC08 解释) ② evaluation_extractor.md / canteen_feedback_extractor.md(UC06 表单预填) ③ card_copy.md(菜单/推荐卡片文案) ④ response_generator.md(通用兜底) |
① canteen-agent-recommendation-explanation-copy ② 无 (只读本地文件) ③ canteen-agent-card-bubble-text ④ canteen-agent-response-generator |
① get_copy_prompt() + watch ② 每次调用直接读文件 ③ prompt_group 快照 + watch 热更 ④ get() |
① response.py:1331-1344 ② review_handler.py:190/249 ③ response.py:1243(菜单)/response.py:1755(推荐) ④ response.py:835-846,output_model=ResponseCopyResult |
| 7 | Fallback | ---(无 LLM,错误码映射固定文案) | --- | --- | fallback.py: fallback_node |
| 8 | CopyFinalizer | terminal_copy.md(整轮末端润色) |
canteen-agent-terminal-copy |
每轮现查 get()(注释:Nacos 改动下一轮即生效) |
orchestration_nodes.py:642-675 → terminal_copy.py: polish_terminal_state |
不属于图、但在请求链路上的 Prompt 消费方 :无(ReviewHandler 由 ResponseGenerator 节点内调用,已在 #6 中)。
3. 逐节点详解
3.1 Entry --- 无 Prompt
- 代码:
orchestration_nodes.entry(包装nodes/phase_one/entry.py) - 职责:初始化会话状态、review 相关字段清零;menu 模板下清洗遗留槽位。
- 无 LLM、无 Prompt。
3.2 IntentRecognizer --- unified_intent_candidates.md
- 代码:
build_modelized_intent_node(intent.py:692),节点入口orchestration_nodes.intent_recognizer - 先于 LLM 的确定性快捷路径 (命中即直接返回,不调模型):
- 过敏信息提问 / 不确定过敏推荐 → 边界答复或澄清(intent.py:714-728)
- 固定属性 FAQ(
fixed_attribute_faq,attribute_faq.py) - 园区/具名热力图规则命中(
resolve_campus_heatmap_scope,intent.py:751-766)
- LLM 调用 :
intent.py:831-837。生产路径上所有请求都包在recommendation_frame里走统一意图契约RecommendationAwareIntent(输出result外壳 +recommendation_status/recommendation_evidence);max_retries=0,失败 →_intent_failure进 Fallback。 - LLM 之后的确定性后处理 :
semantic_terminal(hours_faq、能力边界等可在此直接终结整轮);意图改判器(_correct_*系列);authorize_intent权限闸;导航激活;记忆场景标记。 - 失败降级 :
modelized_nodes_enabled=false或 LLM 不可用 →LLM_UNAVAILABLE→ Fallback。旧intent_recognizer提示词路径已退役(intent.py:782-784 注释明确不再复活)。 - 对话历史:
invoke_node_json会把最近 3 轮(可配)对话以带护栏的「最近对话」块附加到 system prompt 末尾(llm_runtime.py:378-385,护栏文本在_CONVERSATION_GUARD),仅用于理解指代,禁止当成本轮事实。以下所有invoke_node_json节点同理。
3.3 SlotFiller --- slot_filler.md +(快捷路径)realtime_comparison_copy.md
- 代码:
build_modelized_slot_node(slot_filling.py),节点入口orchestration_nodes.slot_filler - 节点内顺序执行的快捷路径 (全部先于主 LLM 调用,命中即终结本节点):
- 导航运行时(
navigation.slot_update) - UC03 跨目标比较
prepare_realtime_comparison(realtime_comparison.py)------此路径会调 LLM 文案,用的就是realtime_comparison_copy.md(_resolve_copy_prompt→get_copy_prompt),产出后直接给出终态响应(comparison/clarification/fallback),不再走 Planner/ToolExecutor ;reply_copy_facts.realtime_comparison_prepolished=True会让 CopyFinalizer 跳过润色 - UC03 单目标实时
prepare_realtime_target(确定性,直接出实时卡片响应) - 具名菜单目标
prepare_named_menu_target - UC07 评价查询
prepare_review_query/ UC06 评价目标prepare_review_targets(确定性工具调用)
- 导航运行时(
- 主 LLM 调用 :
slot_filling.py:596-607,promptslot_filler.md,output_model=SlotResult。槽位信任顺序(代码注释原文):model < candidate < page < confirmed < navigation------模型输出永远只是候选。 - 失败降级:
modelized_nodes_enabled=false→ 旧确定性slot_filler_node。
3.4 Planner --- planner.md
- 代码:
build_modelized_planner_node(planning.py),节点入口orchestration_nodes.planner - 先于 LLM 的快捷路径 :
- UC03 单食堂实时状态:
project_realtime_response直接由canteen_realtime_reader出卡(orchestration_nodes.py:371-399,确定性,此 intent 不走 LLM planner) - 记忆状态解析
resolve_memory_state(把确认口味等注入 planner 上下文)
- UC03 单食堂实时状态:
- LLM 调用 :
planning.py:694-701,仅当server_plan is None------大部分业务意图(目录/菜单/位置/推荐/设施)都有确定性 server plan(plan_templates.py+build_facility_plan等),不调 LLM 。LLM 只为无模板的长尾意图生成只读工具计划,output_model=PlanResult。 - LLM 计划立即过
validate_plan白名单校验;模型若输出source="legacy"或带 template 字段 →MODEL_TEMPLATE_FORBIDDEN直接失败。口味参数丢失由_restore_taste_arguments兜底修复(planning.py:708-712)。 - 失败降级:
_fallback_plan(确定性最小计划)或failed_plan_update。
3.5 ToolExecutor --- 无 Prompt
- 代码:
orchestration_nodes.tool_executor→build_tool_executor_node/tool_executor_node(tool_execution.py) - 职责:按计划逐步执行 MCP 工具(
list_canteens/get_menu/recommend/find_nearest_canteen/query_queue等);导航意图走navigation_runtime+ POI 工具;930 模拟队列需simulated_queue_enabled门控。 - 无 LLM、无 Prompt。 执行后按
route_to回 Planner(推进/重规划)、回 SlotFiller(补槽)或进 ResponseGenerator。
3.6 ResponseGenerator --- 四类 Prompt,按分支优先级
- 代码:
build_response_node(response.py:502),节点入口orchestration_nodes.response_generator(外层还处理pending_task持久化) - 分支判定顺序(response.py:524 起,自上而下命中即返回):
| 优先级 | 条件 | 用的 Prompt | 说明 |
|---|---|---|---|
| a | intent_reason=fixed_attribute_faq |
无 | 确定性 FAQ 文案 |
| b | recommend_dining + operation=explain |
recommendation_explanation_copy.md |
服务端先构建 ExplanationDraft(事实草稿/允许数字/禁词表),LLM 只润色(response.py:1281-1348 → generate_recommendation_explanation_copy);校验失败回退草稿原文;prepolished 标记让 CopyFinalizer 跳过 |
| c | submit_review_feedback + review_prepared + 员工 + 开关 |
evaluation_extractor.md 或 canteen_feedback_extractor.md |
build_review_form_projection 先按槽位定表单类型(有 stall_id+dish_id→评价;只有 canteen_id→反馈);ReviewHandler 读本地 md 作 system、JSON 作 user,超时 3s,失败→空表单不阻塞(review_handler.py:92-169)。唯一不经 Nacos 的两个 prompt ,且文件内禁 {``{}} |
| d | query_reviews + review_prepared |
无 | 确定性评价列表卡 |
| e | 业务模板:LOCATION | 无 | 确定性位置响应 |
| f | 业务模板:MENU | card_copy.md |
generate_card_copy(response.py:1243);defer_copy=True 时只放占位句,推迟到 CopyFinalizer 统一润色 |
| g | 推荐帧命中 recommend_dining |
card_copy.md |
generate_grounded_copy + validate_recommendation_card_copy(response.py:1751-1764);辣味菜品命中确定性文案 spicy_dish_presentation 时不调模型 |
| h | 其余(clarification / fallback / 无卡 business) | response_generator.md |
invoke_node_json,output_model=ResponseCopyResult(response.py:835-846);模型返回的 response_source 必须与输入一致,不一致记 warning 并丢弃候选,只取确定性文案(response.py:850-862) |
- 通用铁律(response.py:855 注释原文):"模型只提供候选文案;绑定、数据和 Action 始终取确定性投影。" 模型在任何分支都换不掉卡片数据。
3.7 Fallback --- 无 Prompt
- 代码:
fallback.py: fallback_node - 职责:错误码分类------可解释错误(
UNKNOWN_INTENT/CAPABILITY_BOUNDARY/TOOL_NOT_ENABLED/QUEUE_DATA_UNAVAILABLE等 8 种)→ 给出对应说明文案后仍可进 ResponseGenerator 补卡;致命错误 → 固定话术「食堂服务暂时不可用,请稍后重试。」直达终态。 - 无 LLM、无 Prompt。
3.8 CopyFinalizer --- terminal_copy.md
- 代码:
orchestration_nodes.copy_finalizer(orchestration_nodes.py:642-675)→polish_terminal_state(terminal_copy.py) - 每轮请求现查 prompt (registry → 启动快照 → 本地文件,orchestration_nodes.py:650-652 注释明确:Nacos 改
canteen-agent-terminal-copy下一轮即生效)。 - 输入
draft_segments:卡片前/后文案、answer、气泡文案;LLM 只润色;_valid_segments硬校验(数字⊆facts、专名不丢、整轮 ≤1 问号、失败不许洗白、过敏免责话术禁止),校验失败 → 保留已验证草稿,用户看到的永远是有据文案。 - 跳过润色的两种情况 :
reply_copy_facts带realtime_comparison_prepolished/recommendation_explanation_prepolished(已各自润色过),或 copy_model 不可用。 - 顺带职责:若线上(Nacos)prompt 与本地版本不同,运行时会把本地版要求追加到 system 末尾(terminal_copy.py:330-333「当前代码版本的末端文案要求优先执行」),保证代码侧新约束不因旧 Nacos 内容而失效。
4. Prompt 资产的三种管理模式(重要)
| 管理模式 | 覆盖的 Prompt | 生效方式 |
|---|---|---|
契约组 (PromptGroup,协议头 + 组校验) |
card_copy、terminal_copy、slot_filler、planner、response_generator | 启动时 load_prompt_group(unified_resources.py:63-68):逐 role Nacos→本地,整组协议/peer 校验,失败全组回本地;record() 打 prompt_effective_group 日志(含 sha256)。运行中:card_copy 有 Nacos watch 热更(main.py:648-682);terminal_copy 每轮现查;slot_filler/planner/response_generator 每次节点执行时经 PhaseOnePromptProvider.get() 现查 |
独立文案资产 (get_copy_prompt + watch) |
realtime_comparison_copy、recommendation_explanation_copy | 不在发布清单里,独立 watch(main.py:684-734),Nacos 编辑下一请求生效;使用前过 validate_copy_only_prompt |
| 纯本地文件 | evaluation_extractor、canteen_feedback_extractor | ReviewHandler 每次调用直接读 agent/prompts/*.md;无 Nacos、无协议头、文件内出现 {``{}} 直接抛错(review_handler.py:103-104) |
Nacos key 汇总(改线上 prompt 找这些 key):
| 文件 | Nacos key |
|---|---|
| card_copy.md | canteen-agent-card-bubble-text |
| terminal_copy.md | canteen-agent-terminal-copy |
| slot_filler.md | canteen-agent-slot-filler |
| planner.md | canteen-agent-planner |
| response_generator.md | canteen-agent-response-generator |
| unified_intent_candidates.md | canteen-agent-unified-intent-candidates |
| realtime_comparison_copy.md | canteen-agent-realtime-comparison-copy |
| recommendation_explanation_copy.md | canteen-agent-recommendation-explanation-copy |
| evaluation_extractor.md | ---(本地 only) |
| canteen_feedback_extractor.md | ---(本地 only) |
5. 一次典型请求的 Prompt 触发时间线
以「推荐一个口味偏辣的菜品」(UC08,全链路)为例:
Entry(无LLM)
→ IntentRecognizer [LLM #1: unified_intent_candidates] → intent=UC08, slots{preferences:[辣]}
→ SlotFiller [LLM #2: slot_filler] → complete=true
→ Planner (server plan 命中推荐模板,不调 LLM)
→ ToolExecutor (MCP: recommend,无 LLM)
→ ResponseGenerator [LLM #3: card_copy] → 卡片下方文案(defer → 占位句)
→ CopyFinalizer [LLM #4: terminal_copy] → 整轮 3--5 句润色 → END
对照「现在哪个食堂排队更短」(UC03 比较):
Entry → IntentRecognizer [LLM #1: unified_intent_candidates]
→ SlotFiller 内部 prepare_realtime_comparison:
拉目录 → 排序 → [LLM #2: realtime_comparison_copy] → 直接出终态
→ ResponseGenerator(透传终态,无 LLM)
→ CopyFinalizer(prepolished,跳过润色)→ END ← 全程仅 2 次 LLM
对照「宫保鸡丁太难吃了」(UC06 评价):
Entry → IntentRecognizer [LLM #1: unified_intent_candidates] → UC06 review_kind=dish
→ SlotFiller:prepare_review_targets(目录核实菜名,无 LLM)
→ Planner(server plan,无 LLM)→ ToolExecutor(list_stalls 核实,无 LLM)
→ ResponseGenerator 分支 c:
ReviewHandler → [LLM #2: evaluation_extractor] → 预填评价表单卡
→ CopyFinalizer [LLM #3: terminal_copy] → END
6. 与 Prompt 无关但容易混淆的路径
| 路径 | 位置 | 为什么容易误认为有 Prompt |
|---|---|---|
semantic_terminal(hours_faq、能力边界、取消) |
intent_policy.py |
输出的是终态答案,但文案来自 intent_messages 消息表(services/intent_messages),不是 LLM prompt |
fixed_attribute_faq |
attribute_faq.py |
FAQ 命中表,纯映射 |
| 目录/位置/菜单业务模板响应 | plan_templates.py + response.py e 分支 |
「模板」指工具计划模板,响应文案是确定性 message + (菜单时)card_copy |
实时状态出卡 project_realtime_response |
realtime_response.py |
数值→文案规则化生成 |
| 导航运行时 | navigation_runtime.py |
A2A 调 agent-navigation + 确定性卡 |
| MCP Server / BFF 子包 | mcpserver/ bff/ |
不含任何 LLM prompt;点选路径纯 REST |
7. 新人上手检查清单
- 想找某句话是哪个 prompt 出的 :先看是不是消息表文案(
intent.unsupported、response.menu_unavailable等,查services/intent_messages.py),再按 §3.6 优先级查分支,最后查 terminal_copy 润色层。 - 想改 prompt :对照 §4 表确认管理模式------契约组的 5 个文件改本地 md 只是改兜底,线上要改 Nacos 同 key;2 个 copy 资产同理;2 个 extractor 只改本地文件即可生效。
- 想确认线上跑的是哪版 :查日志
prompt_effective_group(启动时整组)与phase_one_prompt_resolved node=... source=registry|static(每次现查)。 - 想加新节点级 prompt :必须在
prompt_contract.PROTOCOLS注册协议 + 加协议头 +llm_runtime._NODE_NAMES白名单放行(llm_runtime.py:33-36),缺一不可。 - 排查某轮为什么没润色/没出文案 :查
reply_copy_generated=false+reply_copy_failure字段(取值validation_failed/model_error/model_unavailable/empty_draft),对应 §3.8 的校验失败与降级分支。
本报告与 2026-10-15 的全量 Prompt 评审报告(<prompt_review_2026-10-15.md>)互补:那份管「每个 prompt 写得好不好」,这份管「每个 prompt 在哪被谁调用」。
节点 ↔ Prompt 对应关系全景报告
- 工程 :
agent-canteen/agent(食堂 Agent,LangGraph Phase One 单图运行时) - 日期:2026-10-15
- 事实来源 (均为本次逐文件核实,非推断):
- 图拓扑与路由:src/core/orchestration.py(
NODE_ORDER、next_node、build_orchestration_graph) - 节点实现:src/core/orchestration_nodes.py
- LLM 调用与 Prompt 解析:src/nodes/phase_one/llm_runtime.py
- 各节点 LLM 调用点:
intent.py:831、slot_filling.py:596、planning.py:696、response.py:835/1243/1339/1755、realtime_comparison.py:296-348、copy_finalizer(orchestration_nodes.py:642-675)、review_handler.py:33-34 - Prompt 加载/热更:src/agent/prompt_contract.py、src/agent/unified_resources.py、src/agent/builder.py、src/main.py:648-734
- 图拓扑与路由:src/core/orchestration.py(
1. 图拓扑(8 个节点,实际执行顺序)
┌─────────────────────────────────────────────┐
▼ │
Entry ──▶ IntentRecognizer ──▶ SlotFiller ──▶ Planner ──▶ ToolExecutor ──▶ ResponseGenerator ──▶ CopyFinalizer ──▶ END
│ │ ▲ ▲ │ ▲ │ │ ▲
│ route_to=fallback│ │ │ │ │ │ │ route_to=fallback │
│ route_to=response│ │ └────────┼──┘ └──────────┼───────────────────┤
└──────────────────┼────┘ (slot_filler 回补槽位) (planner 回流/推进) │
▼ │
Fallback ────────────────────────────────────────────────┘
(terminal=failed → END;否则 → ResponseGenerator)
路由规则(orchestration.py: next_node,由每个节点写回 state 的 route_to 决定):
| 节点 | 可能的下一跳 |
|---|---|
| Entry | → IntentRecognizer(无条件) |
| IntentRecognizer | fallback→Fallback;response_generator→ResponseGenerator;其余→SlotFiller |
| SlotFiller | fallback→Fallback;response_generator→ResponseGenerator;其余→Planner |
| Planner | intent_recognizer→IntentRecognizer;slot_filler→SlotFiller;fallback→Fallback;tool_executor→ToolExecutor;其余→ResponseGenerator |
| ToolExecutor | fallback→Fallback;slot_filler→SlotFiller;planner 或 plan 含步骤→Planner(推进下一步);其余→ResponseGenerator |
| Fallback | terminal_state=failed→END;否则→ResponseGenerator |
| ResponseGenerator | → CopyFinalizer(无条件,末站) |
| CopyFinalizer | → END |
注意:每个业务轮最多触发 1---4 次 LLM 调用 (意图 1 + 槽位 0/1 + 计划 0/1 + 响应 0/1)+ 0---2 次文案润色调用(卡片/解释/比较 + 终端润色)。大量请求在节点内部被确定性快捷路径短路,根本不到 LLM(见 §3 各节"先于 LLM 的快捷路径")。
2. 总表:节点 ↔ Prompt ↔ 代码调用点 ↔ 加载方式
| # | 图节点 | 用到的 Prompt 文件 | Nacos key | 加载方式 | 代码调用点 |
|---|---|---|---|---|---|
| 1 | Entry | ---(无 LLM) | --- | --- | orchestration_nodes.entry |
| 2 | IntentRecognizer | unified_intent_candidates.md |
canteen-agent-unified-intent-candidates |
PhaseOnePromptProvider.get():Registry→启动快照→本地 |
intent.py:831-837,output_model=RecommendationAwareIntent |
| 3 | SlotFiller | ① slot_filler.md(主路径) ② realtime_comparison_copy.md(UC03 比较快捷路径) |
① canteen-agent-slot-filler ② canteen-agent-realtime-comparison-copy |
① get() ② get_copy_prompt() + main.py watch 热更 |
① slot_filling.py:596-607,output_model=SlotResult ② realtime_comparison.py:296-348 |
| 4 | Planner | planner.md(仅当无 server plan 时) |
canteen-agent-planner |
get() |
planning.py:696-701,output_model=PlanResult |
| 5 | ToolExecutor | ---(无 LLM,纯 MCP 工具执行) | --- | --- | tool_execution.py |
| 6 | ResponseGenerator | 按分支,优先级从上到下: ① recommendation_explanation_copy.md(UC08 解释) ② evaluation_extractor.md / canteen_feedback_extractor.md(UC06 表单预填) ③ card_copy.md(菜单/推荐卡片文案) ④ response_generator.md(通用兜底) |
① canteen-agent-recommendation-explanation-copy ② 无 (只读本地文件) ③ canteen-agent-card-bubble-text ④ canteen-agent-response-generator |
① get_copy_prompt() + watch ② 每次调用直接读文件 ③ prompt_group 快照 + watch 热更 ④ get() |
① response.py:1331-1344 ② review_handler.py:190/249 ③ response.py:1243(菜单)/response.py:1755(推荐) ④ response.py:835-846,output_model=ResponseCopyResult |
| 7 | Fallback | ---(无 LLM,错误码映射固定文案) | --- | --- | fallback.py: fallback_node |
| 8 | CopyFinalizer | terminal_copy.md(整轮末端润色) |
canteen-agent-terminal-copy |
每轮现查 get()(注释:Nacos 改动下一轮即生效) |
orchestration_nodes.py:642-675 → terminal_copy.py: polish_terminal_state |
不属于图、但在请求链路上的 Prompt 消费方 :无(ReviewHandler 由 ResponseGenerator 节点内调用,已在 #6 中)。
3. 逐节点详解
3.1 Entry --- 无 Prompt
- 代码:
orchestration_nodes.entry(包装nodes/phase_one/entry.py) - 职责:初始化会话状态、review 相关字段清零;menu 模板下清洗遗留槽位。
- 无 LLM、无 Prompt。
3.2 IntentRecognizer --- unified_intent_candidates.md
- 代码:
build_modelized_intent_node(intent.py:692),节点入口orchestration_nodes.intent_recognizer - 先于 LLM 的确定性快捷路径 (命中即直接返回,不调模型):
- 过敏信息提问 / 不确定过敏推荐 → 边界答复或澄清(intent.py:714-728)
- 固定属性 FAQ(
fixed_attribute_faq,attribute_faq.py) - 园区/具名热力图规则命中(
resolve_campus_heatmap_scope,intent.py:751-766)
- LLM 调用 :
intent.py:831-837。生产路径上所有请求都包在recommendation_frame里走统一意图契约RecommendationAwareIntent(输出result外壳 +recommendation_status/recommendation_evidence);max_retries=0,失败 →_intent_failure进 Fallback。 - LLM 之后的确定性后处理 :
semantic_terminal(hours_faq、能力边界等可在此直接终结整轮);意图改判器(_correct_*系列);authorize_intent权限闸;导航激活;记忆场景标记。 - 失败降级 :
modelized_nodes_enabled=false或 LLM 不可用 →LLM_UNAVAILABLE→ Fallback。旧intent_recognizer提示词路径已退役(intent.py:782-784 注释明确不再复活)。 - 对话历史:
invoke_node_json会把最近 3 轮(可配)对话以带护栏的「最近对话」块附加到 system prompt 末尾(llm_runtime.py:378-385,护栏文本在_CONVERSATION_GUARD),仅用于理解指代,禁止当成本轮事实。以下所有invoke_node_json节点同理。
3.3 SlotFiller --- slot_filler.md +(快捷路径)realtime_comparison_copy.md
- 代码:
build_modelized_slot_node(slot_filling.py),节点入口orchestration_nodes.slot_filler - 节点内顺序执行的快捷路径 (全部先于主 LLM 调用,命中即终结本节点):
- 导航运行时(
navigation.slot_update) - UC03 跨目标比较
prepare_realtime_comparison(realtime_comparison.py)------此路径会调 LLM 文案,用的就是realtime_comparison_copy.md(_resolve_copy_prompt→get_copy_prompt),产出后直接给出终态响应(comparison/clarification/fallback),不再走 Planner/ToolExecutor ;reply_copy_facts.realtime_comparison_prepolished=True会让 CopyFinalizer 跳过润色 - UC03 单目标实时
prepare_realtime_target(确定性,直接出实时卡片响应) - 具名菜单目标
prepare_named_menu_target - UC07 评价查询
prepare_review_query/ UC06 评价目标prepare_review_targets(确定性工具调用)
- 导航运行时(
- 主 LLM 调用 :
slot_filling.py:596-607,promptslot_filler.md,output_model=SlotResult。槽位信任顺序(代码注释原文):model < candidate < page < confirmed < navigation------模型输出永远只是候选。 - 失败降级:
modelized_nodes_enabled=false→ 旧确定性slot_filler_node。
3.4 Planner --- planner.md
- 代码:
build_modelized_planner_node(planning.py),节点入口orchestration_nodes.planner - 先于 LLM 的快捷路径 :
- UC03 单食堂实时状态:
project_realtime_response直接由canteen_realtime_reader出卡(orchestration_nodes.py:371-399,确定性,此 intent 不走 LLM planner) - 记忆状态解析
resolve_memory_state(把确认口味等注入 planner 上下文)
- UC03 单食堂实时状态:
- LLM 调用 :
planning.py:694-701,仅当server_plan is None------大部分业务意图(目录/菜单/位置/推荐/设施)都有确定性 server plan(plan_templates.py+build_facility_plan等),不调 LLM 。LLM 只为无模板的长尾意图生成只读工具计划,output_model=PlanResult。 - LLM 计划立即过
validate_plan白名单校验;模型若输出source="legacy"或带 template 字段 →MODEL_TEMPLATE_FORBIDDEN直接失败。口味参数丢失由_restore_taste_arguments兜底修复(planning.py:708-712)。 - 失败降级:
_fallback_plan(确定性最小计划)或failed_plan_update。
3.5 ToolExecutor --- 无 Prompt
- 代码:
orchestration_nodes.tool_executor→build_tool_executor_node/tool_executor_node(tool_execution.py) - 职责:按计划逐步执行 MCP 工具(
list_canteens/get_menu/recommend/find_nearest_canteen/query_queue等);导航意图走navigation_runtime+ POI 工具;930 模拟队列需simulated_queue_enabled门控。 - 无 LLM、无 Prompt。 执行后按
route_to回 Planner(推进/重规划)、回 SlotFiller(补槽)或进 ResponseGenerator。
3.6 ResponseGenerator --- 四类 Prompt,按分支优先级
- 代码:
build_response_node(response.py:502),节点入口orchestration_nodes.response_generator(外层还处理pending_task持久化) - 分支判定顺序(response.py:524 起,自上而下命中即返回):
| 优先级 | 条件 | 用的 Prompt | 说明 |
|---|---|---|---|
| a | intent_reason=fixed_attribute_faq |
无 | 确定性 FAQ 文案 |
| b | recommend_dining + operation=explain |
recommendation_explanation_copy.md |
服务端先构建 ExplanationDraft(事实草稿/允许数字/禁词表),LLM 只润色(response.py:1281-1348 → generate_recommendation_explanation_copy);校验失败回退草稿原文;prepolished 标记让 CopyFinalizer 跳过 |
| c | submit_review_feedback + review_prepared + 员工 + 开关 |
evaluation_extractor.md 或 canteen_feedback_extractor.md |
build_review_form_projection 先按槽位定表单类型(有 stall_id+dish_id→评价;只有 canteen_id→反馈);ReviewHandler 读本地 md 作 system、JSON 作 user,超时 3s,失败→空表单不阻塞(review_handler.py:92-169)。唯一不经 Nacos 的两个 prompt ,且文件内禁 {``{}} |
| d | query_reviews + review_prepared |
无 | 确定性评价列表卡 |
| e | 业务模板:LOCATION | 无 | 确定性位置响应 |
| f | 业务模板:MENU | card_copy.md |
generate_card_copy(response.py:1243);defer_copy=True 时只放占位句,推迟到 CopyFinalizer 统一润色 |
| g | 推荐帧命中 recommend_dining |
card_copy.md |
generate_grounded_copy + validate_recommendation_card_copy(response.py:1751-1764);辣味菜品命中确定性文案 spicy_dish_presentation 时不调模型 |
| h | 其余(clarification / fallback / 无卡 business) | response_generator.md |
invoke_node_json,output_model=ResponseCopyResult(response.py:835-846);模型返回的 response_source 必须与输入一致,不一致记 warning 并丢弃候选,只取确定性文案(response.py:850-862) |
- 通用铁律(response.py:855 注释原文):"模型只提供候选文案;绑定、数据和 Action 始终取确定性投影。" 模型在任何分支都换不掉卡片数据。
3.7 Fallback --- 无 Prompt
- 代码:
fallback.py: fallback_node - 职责:错误码分类------可解释错误(
UNKNOWN_INTENT/CAPABILITY_BOUNDARY/TOOL_NOT_ENABLED/QUEUE_DATA_UNAVAILABLE等 8 种)→ 给出对应说明文案后仍可进 ResponseGenerator 补卡;致命错误 → 固定话术「食堂服务暂时不可用,请稍后重试。」直达终态。 - 无 LLM、无 Prompt。
3.8 CopyFinalizer --- terminal_copy.md
- 代码:
orchestration_nodes.copy_finalizer(orchestration_nodes.py:642-675)→polish_terminal_state(terminal_copy.py) - 每轮请求现查 prompt (registry → 启动快照 → 本地文件,orchestration_nodes.py:650-652 注释明确:Nacos 改
canteen-agent-terminal-copy下一轮即生效)。 - 输入
draft_segments:卡片前/后文案、answer、气泡文案;LLM 只润色;_valid_segments硬校验(数字⊆facts、专名不丢、整轮 ≤1 问号、失败不许洗白、过敏免责话术禁止),校验失败 → 保留已验证草稿,用户看到的永远是有据文案。 - 跳过润色的两种情况 :
reply_copy_facts带realtime_comparison_prepolished/recommendation_explanation_prepolished(已各自润色过),或 copy_model 不可用。 - 顺带职责:若线上(Nacos)prompt 与本地版本不同,运行时会把本地版要求追加到 system 末尾(terminal_copy.py:330-333「当前代码版本的末端文案要求优先执行」),保证代码侧新约束不因旧 Nacos 内容而失效。
4. Prompt 资产的三种管理模式(重要)
| 管理模式 | 覆盖的 Prompt | 生效方式 |
|---|---|---|
契约组 (PromptGroup,协议头 + 组校验) |
card_copy、terminal_copy、slot_filler、planner、response_generator | 启动时 load_prompt_group(unified_resources.py:63-68):逐 role Nacos→本地,整组协议/peer 校验,失败全组回本地;record() 打 prompt_effective_group 日志(含 sha256)。运行中:card_copy 有 Nacos watch 热更(main.py:648-682);terminal_copy 每轮现查;slot_filler/planner/response_generator 每次节点执行时经 PhaseOnePromptProvider.get() 现查 |
独立文案资产 (get_copy_prompt + watch) |
realtime_comparison_copy、recommendation_explanation_copy | 不在发布清单里,独立 watch(main.py:684-734),Nacos 编辑下一请求生效;使用前过 validate_copy_only_prompt |
| 纯本地文件 | evaluation_extractor、canteen_feedback_extractor | ReviewHandler 每次调用直接读 agent/prompts/*.md;无 Nacos、无协议头、文件内出现 {``{}} 直接抛错(review_handler.py:103-104) |
Nacos key 汇总(改线上 prompt 找这些 key):
| 文件 | Nacos key |
|---|---|
| card_copy.md | canteen-agent-card-bubble-text |
| terminal_copy.md | canteen-agent-terminal-copy |
| slot_filler.md | canteen-agent-slot-filler |
| planner.md | canteen-agent-planner |
| response_generator.md | canteen-agent-response-generator |
| unified_intent_candidates.md | canteen-agent-unified-intent-candidates |
| realtime_comparison_copy.md | canteen-agent-realtime-comparison-copy |
| recommendation_explanation_copy.md | canteen-agent-recommendation-explanation-copy |
| evaluation_extractor.md | ---(本地 only) |
| canteen_feedback_extractor.md | ---(本地 only) |
5. 一次典型请求的 Prompt 触发时间线
以「推荐一个口味偏辣的菜品」(UC08,全链路)为例:
Entry(无LLM)
→ IntentRecognizer [LLM #1: unified_intent_candidates] → intent=UC08, slots{preferences:[辣]}
→ SlotFiller [LLM #2: slot_filler] → complete=true
→ Planner (server plan 命中推荐模板,不调 LLM)
→ ToolExecutor (MCP: recommend,无 LLM)
→ ResponseGenerator [LLM #3: card_copy] → 卡片下方文案(defer → 占位句)
→ CopyFinalizer [LLM #4: terminal_copy] → 整轮 3--5 句润色 → END
对照「现在哪个食堂排队更短」(UC03 比较):
Entry → IntentRecognizer [LLM #1: unified_intent_candidates]
→ SlotFiller 内部 prepare_realtime_comparison:
拉目录 → 排序 → [LLM #2: realtime_comparison_copy] → 直接出终态
→ ResponseGenerator(透传终态,无 LLM)
→ CopyFinalizer(prepolished,跳过润色)→ END ← 全程仅 2 次 LLM
对照「宫保鸡丁太难吃了」(UC06 评价):
Entry → IntentRecognizer [LLM #1: unified_intent_candidates] → UC06 review_kind=dish
→ SlotFiller:prepare_review_targets(目录核实菜名,无 LLM)
→ Planner(server plan,无 LLM)→ ToolExecutor(list_stalls 核实,无 LLM)
→ ResponseGenerator 分支 c:
ReviewHandler → [LLM #2: evaluation_extractor] → 预填评价表单卡
→ CopyFinalizer [LLM #3: terminal_copy] → END
6. 与 Prompt 无关但容易混淆的路径
| 路径 | 位置 | 为什么容易误认为有 Prompt |
|---|---|---|
semantic_terminal(hours_faq、能力边界、取消) |
intent_policy.py |
输出的是终态答案,但文案来自 intent_messages 消息表(services/intent_messages),不是 LLM prompt |
fixed_attribute_faq |
attribute_faq.py |
FAQ 命中表,纯映射 |
| 目录/位置/菜单业务模板响应 | plan_templates.py + response.py e 分支 |
「模板」指工具计划模板,响应文案是确定性 message + (菜单时)card_copy |
实时状态出卡 project_realtime_response |
realtime_response.py |
数值→文案规则化生成 |
| 导航运行时 | navigation_runtime.py |
A2A 调 agent-navigation + 确定性卡 |
| MCP Server / BFF 子包 | mcpserver/ bff/ |
不含任何 LLM prompt;点选路径纯 REST |
7. 新人上手检查清单
- 想找某句话是哪个 prompt 出的 :先看是不是消息表文案(
intent.unsupported、response.menu_unavailable等,查services/intent_messages.py),再按 §3.6 优先级查分支,最后查 terminal_copy 润色层。 - 想改 prompt :对照 §4 表确认管理模式------契约组的 5 个文件改本地 md 只是改兜底,线上要改 Nacos 同 key;2 个 copy 资产同理;2 个 extractor 只改本地文件即可生效。
- 想确认线上跑的是哪版 :查日志
prompt_effective_group(启动时整组)与phase_one_prompt_resolved node=... source=registry|static(每次现查)。 - 想加新节点级 prompt :必须在
prompt_contract.PROTOCOLS注册协议 + 加协议头 +llm_runtime._NODE_NAMES白名单放行(llm_runtime.py:33-36),缺一不可。 - 排查某轮为什么没润色/没出文案 :查
reply_copy_generated=false+reply_copy_failure字段(取值validation_failed/model_error/model_unavailable/empty_draft),对应 §3.8 的校验失败与降级分支。
本报告与 2026-10-15 的全量 Prompt 评审报告(<prompt_review_2026-10-15.md>)互补:那份管「每个 prompt 写得好不好」,这份管「每个 prompt 在哪被谁调用」。