Files
ZNJJ-api-server/src/agent/graph.py
T
Eric Wang 5020903791 feat(agent): add LangGraph scaffold for assignment 1
- src/agent/state.py: minimal five-field AgentState
- src/agent/graph.py: START -> normalize_input -> reply -> END,
  compiled with InMemorySaver; reply emits <state>1002</state> prefix
- examples/langgraph_smoke.py: offline smoke, turns 1/2/1 verified
- test/agent/test_scaffold.py: turn accumulation, thread isolation,
  fixed-template assertions (3 passed)
- requirements.txt: pin langgraph==1.2.9

Known limit: InMemorySaver state disappears on process exit.
2026-10-19 13:32:43 +08:00

94 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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())