Annotated,Sequence,add_messages
from typing import Sequence
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
from typing_extensions import Annotated, TypedDict
class State(TypedDict):
messages: Annotated[Sequence[BaseMessage], add_messages]
language: str
workflow = StateGraph(state_schema=State)
def call_model(state: State):
prompt = prompt_template.invoke(state)
response = model.invoke(prompt)
return {"messages": [response]}
workflow.add_edge(START, "model")
workflow.add_node("model", call_model)
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)
Annotated
Python 中注解是一种为函数、类、变量等添加元数据(meta)的语法机制,是一种实用的工具,它可以提高代码的质量和可维护性。注解不会直接影响代码的运行逻辑,不会对类型进行强制约束,但可以被工具(如 IDE、静态类型检查器、文档生成器等)利用。
最常见的注解:冒号后跟数据类型提示(变量注解,函数参数注解,返回值注解)
a: int = 10
name: str = "Alice"
def add(x: int, y: int) -> int:
return x + y
# Python 不会强制执行类型检查,一般情况下有注解和无注解效果一样的。
高级注解 Annotated
Annotated
允许你在类型提示中添加任意的元数据,这些元数据可以是任何 Python 对象,包括函数、类、字符串等。
param: Annotated[int, "This is a description"] = 10
messages: Annotated[list[BaseMessage], add_messages]
def metadata_func(x):
return f"Metadata for {x}"
x: Annotated[int, metadata_func] = 42
比较容易理解的使用场景:FastAPI 中检查数据是否符合约束。类似的场景还有mypy、pydantic
from fastapi import FastAPI, Query
from typing import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Annotated[str, Query(description="Query string", min_length=3, max_length=50)] = "default"
):
return {"q": q}
# 这里面的 Query 函数就在发挥多维度校验的作用了
fastapi.Query这个函数用于数据验证,而函数langgraph.graph.message.add_messages 这个函数则用于数据更新。
另外,如果不想深入去了解其机制,那么换一种写法就很好理解了(以下这两段是完全等效的):
from typing import Sequence
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
from typing_extensions import Annotated, TypedDict
class State(TypedDict):
messages: Annotated[Sequence[BaseMessage], add_messages]
language: str
等效于:
from langgraph.graph import MessagesState
class State(MessagesState):
language: str
# 去查看 MessagesState 的源代码就知道了,MessagesState 也是继自 TypedDict, 其内部只有一个成员:messages: Annotated[Sequence[BaseMessage], add_messages]
函数add_messages的内部逻辑:add_messages 会检查新消息列表中的每个消息,如果其 ID 与现有消息列表中的某个消息相同,则替换旧消息(同时也实现了去重);否则,将新消息追加到列表中。
既然add_messages已经实现将新消息追加到列表中,为什么还需要MemorySaver介入呢?--> 因为他们分工不同,二者结合,才能实现一个真正能记住历史的聊天机器人!
- MessagesState 只是管理 messages 追加逻辑,不持久化
- MemorySaver() 负责跨请求保存 messages,确保对话历史不会丢失