94 lines
3.1 KiB
Python
94 lines
3.1 KiB
Python
"""LangGraph 最小脚手架(作业 1)。
|
||||
|
|
|
|||
|
|
图结构:START → normalize_input → reply → END
|
|||
|
|
|
|||
|
|
骨架已装配好:状态、节点注册、边和编译(默认 InMemorySaver)。
|
|||
|
|
两个节点的具体逻辑留待实现(见函数体内的 TODO)。
|
|||
|
|
|
|||
|
|
离线运行示例:
|
|||
|
|
|
|||
|
|
python examples/langgraph_smoke.py
|
|||
|
|
|
|||
|
|
对应测试:test/agent/test_scaffold.py
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from langgraph.checkpoint.memory import InMemorySaver
|
|||
|
|
from langgraph.graph import END, START, StateGraph
|
|||
|
|
|
|||
|
|
from .state import AgentState
|
|||
|
|
|
|||
|
|
# 脚手架阶段固定返回的阶段码;作业 3 起由转换表驱动。
|
|||
|
|
DEFAULT_STAGE_CODE = "1002"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def normalize_input(state: AgentState) -> dict:
|
|||
|
|
"""归一化本轮输入,为回复节点准备上下文。
|
|||
|
|
|
|||
|
|
TODO(作业 1): 实现该节点。建议职责:
|
|||
|
|
- 校验/清理 ``state["input_text"]``(例如去除首尾空白);
|
|||
|
|
- 返回需要写入状态的字段增量,例如
|
|||
|
|
``{"input_text": cleaned}``;
|
|||
|
|
- 保持纯函数:不读全局状态、不做 IO。
|
|||
|
|
"""
|
|||
|
|
cleaned = state["input_text"].strip()
|
|||
|
|
return {"input_text": cleaned}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def reply(state: AgentState) -> dict:
|
|||
|
|
"""生成固定模板回复,并把轮数加一。
|
|||
|
|
|
|||
|
|
TODO(作业 1): 实现该节点。验收要求:
|
|||
|
|
- 每次调用使 ``turn_count`` 恰好 +1;首轮会话没有历史
|
|||
|
|
checkpoint,用 ``state.get("turn_count") or 0`` 起步;
|
|||
|
|
- ``response_text`` 使用固定模板,**包含本轮输入文本**
|
|||
|
|
(``state["input_text"]``);
|
|||
|
|
- 响应文本需带公共协议前缀,例如
|
|||
|
|
``<state>1002</state>...``(``stage_code`` 用
|
|||
|
|
``DEFAULT_STAGE_CODE``,API 层负责解析该前缀);
|
|||
|
|
- ``stage_code`` 写入 ``DEFAULT_STAGE_CODE``。
|
|||
|
|
|
|||
|
|
参考实现::
|
|||
|
|
|
|||
|
|
turn_count = (state.get("turn_count") or 0) + 1
|
|||
|
|
response_text = (
|
|||
|
|
f"<state>{DEFAULT_STAGE_CODE}</state>"
|
|||
|
|
f"已收到:{state['input_text']}"
|
|||
|
|
)
|
|||
|
|
return {
|
|||
|
|
"turn_count": turn_count,
|
|||
|
|
"response_text": response_text,
|
|||
|
|
"stage_code": DEFAULT_STAGE_CODE,
|
|||
|
|
}
|
|||
|
|
"""
|
|||
|
|
turn_count = (state.get("turn_count") or 0) + 1
|
|||
|
|
response_text = (
|
|||
|
|
f"<state>{DEFAULT_STAGE_CODE}</state>"
|
|||
|
|
f"已收到:{state['input_text']}"
|
|||
|
|
)
|
|||
|
|
return {
|
|||
|
|
"turn_count": turn_count,
|
|||
|
|
"response_text": response_text,
|
|||
|
|
"stage_code": DEFAULT_STAGE_CODE,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def build_graph(checkpointer=None):
|
|||
|
|
"""装配并编译最小图。
|
|||
|
|
|
|||
|
|
参数:
|
|||
|
|
checkpointer: 自定义 checkpointer;缺省使用 ``InMemorySaver``。
|
|||
|
|
作业 7 会替换为 ``AsyncPostgresSaver``。
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
编译后的图。调用方通过
|
|||
|
|
``config={"configurable": {"thread_id": <会话 ID>}}``
|
|||
|
|
区分会话。
|
|||
|
|
"""
|
|||
|
|
builder = StateGraph(AgentState)
|
|||
|
|
builder.add_node("normalize_input", normalize_input)
|
|||
|
|
builder.add_node("reply", reply)
|
|||
|
|
builder.add_edge(START, "normalize_input")
|
|||
|
|
builder.add_edge("normalize_input", "reply")
|
|||
|
|
builder.add_edge("reply", END)
|
|||
|
|
return builder.compile(checkpointer=checkpointer or InMemorySaver())
|