纲要

  • LangGraph 核心概念
    • StateGraphMessagesState
    • 节点 (Node) 与边 (Edge)
    • 条件路由 (Conditional Edges)
    • 检查点 (Checkpointer) 与持久化
  • 系统架构与工作流
    • 多节点协作模式
    • 状态路由与循环逻辑
  • 核心实现解析
    • 状态管理 (MessagesState)
    • 需求收集节点 (info_node)
    • 工具结果处理 (add_tool_message)
    • 提示词生成节点 (prompt_node)
    • 条件路由函数 (get_state)
  • 完整可运行项目
    • 项目结构
    • 环境配置与依赖
    • 主程序实现
  • API 速览

项目背景

编写高质量的提示词(Prompt)是充分发挥大模型能力的关键环节。然而,许多用户难以清晰、完整地表达自己的需求。借助 LangGraph 框架,可以构建一个提示词生成助手,通过多轮对话引导用户明确目标、变量、约束和要求,最终自动生成结构化的提示词模板。该流程模拟了“需求分析师”与“提示词工程师”的协作模式。

LangGraph 是一个用于构建状态化、多角色应用的低级编排框架,提供持久化执行、流式传输、人机交互和时光回溯调试等能力。其核心是 StateGraph,一种通过共享状态进行节点间通信的图结构。

系统架构

工作流由三个主要节点构成,通过条件路由实现智能跳转:

  • info_node:与用户对话,收集提示词的目标、变量、约束和要求。若信息不完整则继续追问;若收集完毕则触发工具调用。
  • add_tool_message:处理工具调用的结果,为生成节点准备上下文。
  • prompt_node:根据收集到的全部需求,调用大语言模型生成最终的提示词模板。

信息不完整

调用工具

用户退出

开始

info_node 收集需求

add_tool_message 处理工具结果

prompt_node 生成提示词

结束

核心实现

状态设计

工作流的状态直接使用 MessagesState。这是 LangGraph 预置的状态类,专门为对话历史与工具调用场景设计,其内部包含一个使用 add_messages 归约器(Reducer)的 messages 键,用于自动合并消息列表。

节点实现

info_node:需求收集节点
该节点向用户提问,收集提示词的目标、变量、约束和要求。使用 with_structured_output 方法将 LLM 的输出解析为 PromptRequirements Pydantic 模型,从而结构化地提取用户回复中的关键信息。当所有字段均被成功提取后,节点会构造一个包含 generate_prompt 工具调用的 AIMessage,以触发后续流程。

add_tool_message:工具结果处理节点
info_node 触发工具调用后,路由函数会将流程导向此节点。该节点模拟工具的成功执行,构造一个 ToolMessage 并注入消息流,为 prompt_node 准备上下文。

prompt_node:提示词生成节点
该节点从消息历史中提取 info_node 收集到的需求信息,将其填入提示词模板,调用 LLM 生成最终的提示词模板,并将结果以 AIMessage 的形式返回。

条件路由

get_state 函数根据最新消息的类型和内容决定路由:

  • 若 AI 消息包含工具调用,路由至 add_tool_message
  • 若最新消息为 ToolMessage,路由至 prompt_node
  • 若用户消息内容为退出指令(如 “q”),路由至 END
  • 否则,继续在 info_node 循环。

完整可运行项目

项目结构

prompt_assistant_demo/
├── prompt_assistant.py
├── .env
└── requirements.txt

环境配置与依赖

依赖安装

pip install langgraph langchain-openai python-dotenv pydantic

环境变量 (.env)

OPENAI_API_KEY=sk-xxxx

主程序 (prompt_assistant.py)

import os
import uuid
from typing import Literal
from dotenv import load_dotenv
from langgraph.graph import StateGraph, END, MessagesState
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field

load_dotenv()

# ----- 1. 定义结构化输出模型 -----
class PromptRequirements(BaseModel):
    goal: str = Field(description="提示词的目标")
    variables: str = Field(description="传入模板的变量,用逗号分隔")
    constraints: str = Field(description="输出的约束条件")
    requirements: str = Field(description="输出必须遵循的要求")

# ----- 2. 初始化 LLM -----
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
structured_llm = llm.with_structured_output(PromptRequirements)

# ----- 3. 提示词模板 -----
INFO_SYSTEM_PROMPT = """你是一个提示词需求收集助手。你的任务是与用户对话,收集他们想要创建的提示词的以下信息:
1. 目标:提示词要完成什么任务?
2. 变量:哪些变量会被传入模板?
3. 约束:输出不应该包含什么?
4. 要求:输出必须遵循什么规则?

如果你无法从用户的回答中辨别所有信息,请继续提问,直到信息完整。
当你认为信息已经完整时,调用 `generate_prompt` 工具来触发提示词生成。"""

PROMPT_GENERATION_TEMPLATE = """根据以下需求,编写一个高质量的提示词模板:
目标:{goal}
变量:{variables}
约束:{constraints}
要求:{requirements}

请生成一个完整的提示词模板,用花括号包裹变量名。"""

# ----- 4. 节点函数 -----
def info_node(state: MessagesState) -> dict:
    """收集用户需求,如果信息完整则调用工具"""
    messages = state["messages"]
    # 将系统提示作为第一条消息
    full_messages = [HumanMessage(content=INFO_SYSTEM_PROMPT)] + messages
    response = structured_llm.invoke(full_messages)

    # 判断是否所有字段都已填写
    if response.goal and response.variables and response.constraints and response.requirements:
        # 信息完整,构造一个工具调用消息
        tool_call_msg = AIMessage(
            content="信息已收集完整,正在生成提示词...",
            tool_calls=[{
                "name": "generate_prompt",
                "args": {
                    "goal": response.goal,
                    "variables": response.variables,
                    "constraints": response.constraints,
                    "requirements": response.requirements
                },
                "id": "call_prompt_gen"
            }]
        )
        return {"messages": [tool_call_msg]}
    else:
        # 信息不完整,生成追问
        follow_up = f"请提供更多信息。目标:{response.goal or '未知'},变量:{response.variables or '未知'},约束:{response.constraints or '未知'},要求:{response.requirements or '未知'}"
        return {"messages": [AIMessage(content=follow_up)]}

def add_tool_message(state: MessagesState) -> dict:
    """模拟工具执行,返回成功消息"""
    return {"messages": [ToolMessage(content="工具调用成功,正在生成提示词模板...", tool_call_id="call_prompt_gen")]}

def prompt_node(state: MessagesState) -> dict:
    """根据需求生成最终提示词模板"""
    # 从消息历史中提取需求
    requirements = None
    for msg in state["messages"]:
        if isinstance(msg, AIMessage) and hasattr(msg, "tool_calls") and msg.tool_calls:
            requirements = msg.tool_calls[0]["args"]
            break
    if not requirements:
        return {"messages": [AIMessage(content="未能提取到需求信息。")]}

    prompt = ChatPromptTemplate.from_template(PROMPT_GENERATION_TEMPLATE)
    chain = prompt | llm
    result = chain.invoke(requirements)
    return {"messages": [result]}

def get_state(state: MessagesState) -> Literal["add_tool", "prompt", "end", "info"]:
    """根据最新消息决定路由"""
    if not state["messages"]:
        return "end"
    last_msg = state["messages"][-1]
    # 如果 AI 消息包含工具调用
    if isinstance(last_msg, AIMessage) and hasattr(last_msg, "tool_calls") and last_msg.tool_calls:
        return "add_tool"
    # 如果工具执行完毕
    if isinstance(last_msg, ToolMessage):
        return "prompt"
    # 如果用户的最新消息是 "q" 或 "退出"
    if isinstance(last_msg, HumanMessage) and last_msg.content.lower() in ["q", "quit", "退出"]:
        return "end"
    # 否则继续收集信息
    return "info"

# ----- 5. 构建图 -----
builder = StateGraph(MessagesState)
builder.add_node("info", info_node)
builder.add_node("add_tool", add_tool_message)
builder.add_node("prompt", prompt_node)

builder.set_entry_point("info")
builder.add_conditional_edges("info", get_state, {
    "add_tool": "add_tool",
    "prompt": "prompt",
    "end": END,
    "info": "info"
})
builder.add_edge("add_tool", "prompt")
builder.add_edge("prompt", END)

memory = MemorySaver()
app = builder.compile(checkpointer=memory)

# ----- 6. 交互式测试 -----
if __name__ == "__main__":
    config = {"configurable": {"thread_id": str(uuid.uuid4())}}
    print("===== 提示词生成助手 =====")
    print("请描述你的需求,输入 'q' 退出。")

    # 模拟多轮对话
    test_inputs = [
        "我想生成一个写诗的提示词",
        "目标是写一首绝句,变量是标题和写作风格",
        "约束条件是不超过20个字",
        "要求是必须用绝句的格式,四句"
    ]

    for user_input in test_inputs:
        print(f"\n用户: {user_input}")
        result = app.invoke(
            {"messages": [HumanMessage(content=user_input)]},
            config
        )
        # 打印最新回复
        latest_msg = result["messages"][-1]
        if hasattr(latest_msg, "content"):
            print(f"助手: {latest_msg.content}")

        # 如果已生成最终提示词,退出循环
        if "模板" in str(latest_msg.content) or "提示词" in str(latest_msg.content) and len(result["messages"]) > 3:
            break

API 速览

API 所属模块 说明
StateGraph langgraph.graph 图构建器,用于定义具有共享状态的图。节点签名为 State -> Partial<State>
MessagesState langgraph.graph 预置的状态类,包含一个使用 add_messages 归约器的 messages 键,用于管理对话历史。
add_messages langgraph.graph 归约器函数,用于合并消息列表。默认追加新消息,若消息 ID 相同则更新。
MemorySaver langgraph.checkpoint.memory 内存检查点保存器,用于在开发与测试阶段持久化图状态。
START / END langgraph.graph 图的入口与出口常量。
add_node StateGraph 向图中添加节点。
add_edge StateGraph 添加普通边,定义确定性的节点流转。
add_conditional_edges StateGraph 添加条件边,根据状态动态路由。
compile StateGraph 编译图构建器为可执行图,支持 invokestream 等方法。

参考文档

官方文档

参考链接

总结

本文完整呈现了基于 LangGraph 构建提示词生成助手的技术路径,覆盖了从状态设计、节点实现到条件路由与图编译的全过程。核心要点如下:

  • 状态管理:利用 MessagesState 预置状态类简化对话历史管理。
  • 结构化输出:通过 with_structured_output 将 LLM 输出解析为 Pydantic 模型,实现需求信息的精准提取。
  • 工具调用模拟:通过在 AIMessage 中构造 tool_calls 字段,触发条件路由,模拟工具调用流程。
  • 条件路由:使用 add_conditional_edges 根据消息类型动态决定工作流走向,实现智能跳转。
  • 持久化:通过 MemorySaver 检查点机制为图添加短期记忆,支持多轮对话。

这种“信息收集 → 工具调用 → 内容生成”的模式具有高度的可扩展性,可广泛应用于表单填写、问卷调研、需求分析等场景。通过调整节点逻辑与路由规则,开发者可以轻松定制各类智能助手。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐