Graph

简介

在要求Agent执行可预测性的场景,往往通过Graph来编排工作流,tRPC-Agent-Python现在提供Graph能力,不光支持编排业务自己的Node,还支持编排框架的各个组件(各类Agent、MCP、知识库、代码执行器等),方便业务基于框架的已有生态,快速构建适用于业务场景的工作流。

架构设计

如下所示,用户通过框架提供的Graph接口来构建图,框架的Graph支持通用图的构建,也支持将框架已有的组件或者业务自己开发的Agent引入图中。图执行使用GraphAgent来执行,它底层封装了LangGraph作为图执行引擎,生产可用。

环境准备

建议按下面约束配置环境:

  • 用户自定义节点必须使用 async def来定义,防止同步异步混用导致的各类问题(比如阻塞EventLoop)
  • 使用Python 3.12此项约束受限于图执行引擎LangGraph的要求,Graph引擎封装需要Node执行过程中流式返回各类信息,Python在3.11及以上支持了这个能力
  • LangGraph 版本推荐使用 1.0.x 正式版

快速开始

下面示例演示如何构建 Graph、如何运行 Graph:通过 add_conditional_edges 按条件路由到不同节点(add_node、add_llm_node、add_agent_node)。

import os
from typing import Any

from typing_extensions import Annotated

from trpc_agent_sdk.agents import LlmAgent
from trpc_agent_sdk.models import OpenAIModel
from trpc_agent_sdk.tools import FunctionTool
from trpc_agent_sdk.types import GenerateContentConfig
from trpc_agent_sdk.dsl.graph import (
    GraphAgent,
    NodeConfig,
    STATE_KEY_LAST_RESPONSE,
    STATE_KEY_USER_INPUT,
    State,
    StateGraph,
    append_list,
)


# 1. 定义图的状态:继承 State,业务字段 + Reducer 字段
class DemoState(State):
    tool_result: str
    agent_reply: str
    # append_list reducer:多个节点写入时自动追加,不会覆盖
    node_execution_history: Annotated[list[dict[str, Any]], append_list]


# 2. 定义普通函数节点(必须 async def)
async def hello(state: DemoState) -> dict[str, Any]:
    """入口节点:打印欢迎信息,后续由条件边决定下一跳"""
    print("Hello from graph")
    return {}


# 3. 定义路由函数:根据用户输入返回不同的路由 key
def route_choice(state: DemoState) -> str:
    text = state[STATE_KEY_USER_INPUT].strip().lower()
    if "weather" in text:
        return "llm"       # 天气相关 → 走 llm_node
    return "agent"         # 其他 → 走 agent_node


# 4. 定义输出格式化节点:将上游结果写入 last_response 供 Runner 返回
async def format_output(state: DemoState) -> dict[str, Any]:
    content = state.get("agent_reply") or state.get("tool_result") or "(No content)"
    return {STATE_KEY_LAST_RESPONSE: content}


# 5. 定义工具函数,供 llm_node 中模型 function_call 调用
async def weather_tool(location: str) -> dict[str, str]:
    return {"location": location, "weather": "sunny"}


def create_agent() -> GraphAgent:
    # 初始化模型(通过环境变量配置)
    model = OpenAIModel(
        model_name=os.getenv("TRPC_AGENT_MODEL_NAME", "deepseek-chat"),
        api_key=os.getenv("TRPC_AGENT_API_KEY", ""),
        base_url=os.getenv("TRPC_AGENT_BASE_URL", ""),
    )
    weather = FunctionTool(weather_tool)
    tools = {"weather_tool": weather}

    # 构建一个子 Agent,后续作为 agent_node 接入图中
    delegate_agent = LlmAgent(
        name="query_orchestrator",
        description="Agent called by graph agent_node",
        model=model,
        instruction="Answer user query briefly.",
        generate_content_config=GenerateContentConfig(
            temperature=0.2,
            max_output_tokens=200,
        )
    )

    # 构建图
    graph = StateGraph(DemoState)

    # 普通函数节点:入口,打印后由条件边路由
    graph.add_node(
        name="hello",
        action=hello,
        config=NodeConfig(name="hello", description="Say hello and route by user input"),
    )

    # LLM 节点:直接调用模型,支持 function_call → 工具执行 → 回填的内置循环
    graph.add_llm_node(
        name="plan_with_llm",
        model=model,
        instruction="If weather is asked, call weather_tool; otherwise answer directly.",
        tools=tools,
        tool_parallel=False,
        max_tool_iterations=4,
        generation_config=GenerateContentConfig(
            temperature=0.2,
            max_output_tokens=200,
        ),
        config=NodeConfig(name="plan_with_llm", description="LLM node"),
    )

    # Agent 节点:将已有的 LlmAgent 作为图中的一个节点
    graph.add_agent_node(
        node_id="delegate_to_agent",
        agent=delegate_agent,
        config=NodeConfig(name="delegate_to_agent", description="Agent node"),
    )

    # 输出格式化节点
    graph.add_node(
        name="format_output",
        action=format_output,
        config=NodeConfig(name="format_output", description="Build final response"),
    )

    # 连边
    graph.set_entry_point("hello")            # START → hello
    graph.set_finish_point("format_output")   # format_output → END

    # 条件边:hello 之后根据 route_choice 的返回值路由到不同节点
    graph.add_conditional_edges(
        source="hello",
        path=route_choice,
        path_map={"llm": "plan_with_llm", "agent": "delegate_to_agent"},
    )
    graph.add_edge(start="plan_with_llm", end="format_output")
    graph.add_edge(start="delegate_to_agent", end="format_output")

    # 编译图并包装为 GraphAgent,交给 Runner 执行
    return GraphAgent(
        name="graph_demo",
        description="Simple graph build demo",
        graph=graph.compile(),
    )

运行方式(Runner):

import asyncio
import uuid

from trpc_agent_sdk.runners import Runner
from trpc_agent_sdk.sessions import InMemorySessionService
from trpc_agent_sdk.types import Content, Part


async def main() -> None:
    app_name = "graph_demo_app"
    user_id = "demo_user"
    session_id = str(uuid.uuid4())

    session_service = InMemorySessionService()
    await session_service.create_session(
        app_name=app_name,
        user_id=user_id,
        session_id=session_id,
        state={},
    )

    runner = Runner(
        app_name=app_name,
        agent=create_agent(),
        session_service=session_service,
    )

    content = Content(parts=[Part.from_text(text="What's the weather in Seattle?")])
    async for event in runner.run_async(user_id=user_id, session_id=session_id, new_message=content):
        if event.content and event.content.parts:
            for part in event.content.parts:
                if part.text:
                    print(part.text, end="", flush=True)

    await runner.close()


if __name__ == "__main__":
    asyncio.run(main())

完整示例:examples/graph,该示例完整演示了框架各个组件的对接,如下所示:

graph TD
  START --> extract
  extract --> decide_from_query
  decide_from_query -->|preview| preview
  decide_from_query -->|summarize| summarize_llm_node
  decide_from_query -->|tool| request_stats_llm_node
  decide_from_query -->|subgraph| delegate_agent_node
  decide_from_query -->|llm_agent| query_orchestrator_agent
  decide_from_query -->|code| code_exec_code_node
  decide_from_query -->|mcp| prepare_mcp_request
  decide_from_query -->|knowledge| knowledge_search_node
  preview --> format_output
  summarize_llm_node --> format_output
  request_stats_llm_node --> format_output
  delegate_agent_node --> format_output
  query_orchestrator_agent -.calls weather_tool.-> weather_tool_node
  query_orchestrator_agent -.transfer_to_agent.-> domain_explainer_agent
  query_orchestrator_agent --> format_output
  code_exec_code_node --> format_output
  prepare_mcp_request --> mcp_call
  mcp_call --> format_output
  knowledge_search_node --> format_output
  format_output --> END

输入示例:

  • subgraph: Please respond in a friendly tone. → 触发子图 agent_node
  • llm_agent: What's the weather in Seattle? → 触发 query_orchestrator_agent 并调用 weather_tool(固定返回 sunny)
  • llm_agent: child: What is retrieval augmented generation? → 触发 query_orchestrator_agent,并转交给其子 domain_explainer_agent
  • tool: Count words for this text. → 触发带工具调用能力的 llm_node
  • code: run python analysis → 触发 code_node,执行内置 Python 统计脚本
  • mcp: {"operation": "add", "a": 3, "b": 5} → 触发 mcp_node,通过 stdio 调用本地 MCP Server 的 calculate 工具
  • knowledge: What is retrieval augmented generation? → 触发 knowledge_node,搜索知识库(需启用 ENABLE_KNOWLEDGE
  • 长文本(40 词以上)→ 触发 summarize LLM 节点
  • 短文本 → 触发 preview(会用 EventWriter 流式输出)

多轮对话示例见:examples/graph_multi_turns

StateGraph API介绍

图中可以接入框架的多种组件,通过不同的 add_* 方法将模型、工具、Agent、代码执行、知识库检索、MCP 工具等接入图中,如下所示:

函数 作用 常见场景
add_node(name, action, ...) 添加通用异步函数节点 业务处理、数据清洗、路由前置处理
add_llm_node(name, model, instruction, ...) 添加直接调用模型的节点(可选工具循环) 分类、改写、摘要、工具增强问答
add_code_node(name, code_executor, code, language, ...) 添加代码执行节点 执行 Python/Shell 脚本并写回状态
add_knowledge_node(name, query, tool, ...) 添加知识检索节点 RAG 检索、知识库问答前置
add_mcp_node(name, mcp_toolset, selected_tool_name, req_src_node, ...) 添加 MCP 工具调用节点 调用外部 MCP Server 工具
add_agent_node(node_id, agent, ...) 添加子 Agent 节点 多 Agent 协作、子流程委托
add_edge(start, end) 添加静态有向边 固定执行链路
add_conditional_edges(source, path, path_map) 添加条件路由边 意图分流、多分支流程
set_entry_point(key) 设置入口节点(START -> key) 指定工作流起点
set_finish_point(key) 设置结束节点(key -> END) 指定工作流终点
compile(memory_saver_option=...) 编译图并返回可执行对象 Runner 执行前的最后一步

下面介绍每种节点如何添加、适用场景与常用选项。

add_node

场景:接入一个普通异步函数节点(纯业务逻辑、数据加工、路由决策等)。

graph.add_node(
    name="extract",
    action=extract_document,
    config=NodeConfig(name="extract", description="Extract input"),
)

常用选项: - name:节点 ID(用于连边、路由、回调定位) - action:必须是 async def - config:公共配置(name / description / metadata) - callbacks:设置当前节点的回调

add_llm_node

场景:在节点内直接调用模型;若配置 tools,会在同一 llm_node 内完成 function_call → 执行工具 → 回填 function_response → 再次调用模型的循环。

from trpc_agent_sdk.tools import FunctionTool
from trpc_agent_sdk.types import GenerateContentConfig

async def my_tool(query: str) -> dict[str, str]:
    return {"result": f"Processed: {query}"}

tools = {"my_tool": FunctionTool(my_tool)}

graph.add_llm_node(
    name="classifier",
    model=model,
    instruction="Classify user intent into one label.",
    tools=tools,
    tool_parallel=False,
    max_tool_iterations=4,
    generation_config=GenerateContentConfig(
        temperature=0.1,
        max_output_tokens=64,
    ),
    config=NodeConfig(name="classifier", description="Intent classifier"),
)

常用选项: - tools:工具字典({tool_name: tool_instance}),不需要时传 {} - tool_parallel:同一轮内多个工具调用是否并行 - max_tool_iterations:单次 llm_node 内最多 model→tool 循环轮数 - generation_config:模型配置(temperature、max_output_tokens 等) - config / callbacks:同 add_node

add_code_node

场景:在图中执行一段静态代码(如 Python/Shell),结果写入内置状态;可通过 STATE_KEY_NODE_RESPONSES[name] 取该节点输出。

from trpc_agent_sdk.code_executors import UnsafeLocalCodeExecutor

graph.add_code_node(
    name="run_script",
    code_executor=UnsafeLocalCodeExecutor(timeout=30, work_dir="", clean_temp_files=True),
    code="result = 1 + 1",
    language="python",
    config=NodeConfig(name="run_script", description="Run inline code"),
)

参数说明: - code_executor:BaseCodeExecutor 实例(如 UnsafeLocalCodeExecutorContainerCodeExecutor),由调用方自行构建 - code:要执行的源码 - language:python / bash / sh - config / callbacks:同 add_node

add_knowledge_node

场景:在图中接入一个知识检索节点,使用已有的 LangchainKnowledgeSearchTool 做检索,结果写入状态。

from trpc_agent_sdk.server.knowledge.tools import LangchainKnowledgeSearchTool
from trpc_agent_sdk.server.knowledge.trag_adapter import TragAuthParams
from trpc_agent_sdk.server.knowledge.trag_adapter import TragDocumentLoader
from trpc_agent_sdk.server.knowledge.trag_adapter import TragDocumentLoaderParams
from trpc_agent_sdk.server.knowledge.trag_knowledge import TragKnowledge


def _create_trag_knowledge(auth_params: TragAuthParams) -> TragKnowledge:
    document_loader = TragDocumentLoader(TragDocumentLoaderParams(file_paths=[]))
    return TragKnowledge(auth_params=auth_params, document_loader=document_loader)


# Build the knowledge tool: auth_params from your config (e.g. create_trag_auth_params_xxx())
auth_params = get_auth_params()  # from .config or TragAuthParams(...)
knowledge = _create_trag_knowledge(auth_params=auth_params)
my_knowledge_tool = LangchainKnowledgeSearchTool(
    rag=knowledge,
    top_k=5,
    min_score=0.0,
    knowledge_filter=None,  # optional static filter
)

graph.add_knowledge_node(
    name="search_kb",
    query="user_query",  # or callable: lambda state: state.get("query", ""),
    tool=my_knowledge_tool,
    config=NodeConfig(name="search_kb", description="Knowledge search"),
)

常用选项: - query:检索 query,可为字符串或 (state) -> str 的可调用对象 - tool:预构建的 LangchainKnowledgeSearchTool 实例,不可为 None - config / callbacks:同 add_node

add_mcp_node

场景:在图中调用 MCP 的某一个工具;参数来自上游节点写入的 state[STATE_KEY_NODE_RESPONSES][req_src_node]

注意:需先有上游节点把 MCP 请求参数写入 node_responses[req_src_node];执行失败会直接抛错,便于排查错误请求。

graph.add_mcp_node(
    name="call_mcp",
    mcp_toolset=my_mcp_toolset,
    selected_tool_name="my_mcp_tool",
    req_src_node="build_request",
    config=NodeConfig(name="call_mcp", description="Invoke MCP tool"),
)

常用选项: - mcp_toolset:已配置的 MCPToolset 实例 - selected_tool_name:要调用的 MCP 工具名(精确匹配) - req_src_node:提供请求参数的上游节点 ID(其输出在 node_responses[req_src_node]) - config / callbacks:同 add_node

add_agent_node

场景:将任意 BaseAgent(如 LlmAgent/GraphAgent)作为图中一个节点。

graph.add_agent_node(
    node_id="delegate",
    agent=delegate_agent,
    isolated_messages=True,
    input_from_last_response=False,
    event_scope="delegate_scope",
    input_mapper=StateMapper.rename({"query_text": STATE_KEY_USER_INPUT}),
    output_mapper=StateMapper.merge_response("delegate_reply"),
    config=NodeConfig(name="delegate", description="Delegate to child agent"),
)

常用选项: - isolated_messages:是否隔离父会话消息历史 - input_from_last_response:是否将父状态 last_response 映射为子节点 user_input - event_scope:子 Agent 事件分支前缀 - input_mapper / output_mapper:父子状态映射(推荐显式配置) - config / callbacks:同 add_node

GraphAgent 不需要(也不支持)通过 sub_agents 注册 Agent 节点;组合关系统一用 add_agent_node 完成。

进阶用法

本节介绍连边与路由、状态与 Reducer、编译与执行、节点签名、StateMapper、回调、状态常量、Interrupt 等进阶内容。

边与路由

场景:静态链路用 add_edge,条件分支用 add_conditional_edges;入口/出口可用 set_entry_point / set_finish_point 简写。若需在路由前做输入预处理(如校验、归一化 user_input 的 prepare_input 节点),可在入口后接一个 add_node 再连到条件边。

graph.add_edge(start="extract", end="decide")

graph.add_conditional_edges(
    source="decide",
    path=route_choice,
    path_map={
        "preview": "preview",
        "summarize": "summarize",
    },
)

graph.set_entry_point("extract")
graph.set_finish_point("format_output")
  • add_edge(start, end):从 start 到 end 的有向边
  • add_conditional_edges(source, path, path_map):path(state) 返回下一跳 key,path_map 将 key 映射到节点名
  • set_entry_point(key):等价于 add_edge(START, key)
  • set_finish_point(key):等价于 add_edge(key, END)

State 与 Reducer 用法

Graph 的状态是一个 TypedDict(State)在节点间传递。推荐做法是:

  • 继承框架的 State 定义业务字段
  • 对“会被多节点累积更新”的字段,使用 Annotated[..., reducer]
  • 节点返回 dict 增量,框架按 reducer 规则合并

定义 State:

from typing import Any

from google.genai.types import Content
from typing_extensions import Annotated

from trpc_agent_sdk.dsl.graph import (
    State,
    append_list,
    merge_dict,
    messages_reducer,
)


class ArticleState(State):
    article: str
    summary: str

    execution_log: Annotated[list[dict[str, Any]], append_list]
    node_outputs: Annotated[dict[str, Any], merge_dict]
    messages: Annotated[list[Content], messages_reducer]

Reducer 行为: append_list(追加列表)、merge_dict(浅合并字典)、messages_reducer(消息列表追加)。像 route、summary 等单节点写入即可的字段无需 Annotated。

编译与执行

图构建完成后调用 compile;可选配置 checkpoint 持久化。

compiled = graph.compile(
    memory_saver_option=MemorySaverOption(
        auto_persist=False,
        persist_writes=False,
    ),
)
agent = GraphAgent(
    name="my_workflow",
    description="My graph workflow",
    graph=compiled,
)

说明: - 编译后得到 CompiledStateGraph,应将其传入 GraphAgentgraph 参数,通过 Runner 执行(见上文「快速开始」),而不是直接调用 compiled.astream(...)。 - 若需访问编译结果:可用 compiled.source 取回原 StateGraph,用 compiled.get_node_config(name) 按节点名查配置。 - 构造图时可通过 StateGraph(MyState, callbacks=global_callbacks) 为整图挂统一的 before/after/error 回调(日志、检查、指标)。 - Runner 运行时,状态持久化主路径是 Event.actions.state_delta → SessionService;使用 InMemorySessionService 时进程重启后状态不保留,使用 Redis/SQL 等持久化 SessionService 时可支持分布式部署。

节点签名与依赖注入

节点定义必须为异步方法,也即 async def

Graph 根据方法定义自动注入能力,常见签名如下:

async def node(state: State) -> dict: ...
async def node(state: State, writer: EventWriter) -> dict: ...
async def node(state: State, async_writer: AsyncEventWriter) -> dict: ...
async def node(state: State, ctx: InvocationContext) -> dict: ...
async def node(state: State, writer: EventWriter, ctx: InvocationContext) -> dict: ...
async def node(state: State, async_writer: AsyncEventWriter, ctx: InvocationContext) -> dict: ...
async def node(state: State, writer: EventWriter, async_writer: AsyncEventWriter, ctx: InvocationContext) -> dict: ...
async def node(state: State, callback_ctx, callbacks) -> dict: ...

说明: - writer:同步写事件 - async_writer:需 await 控制事件刷出时使用 - ctx:读取 session / user_id / session_id 等 - callback_ctx / callbacks:节点内细粒度回调(高级用法)

StateMapper(add_agent_node 输入输出映射)

StateMapper 用于显式控制 agent_node 的数据流,分为两步:

  • input_mapper:Graph State -> Agent Node 输入 State
  • output_mapper:Agent Node 结果 -> Graph State 更新

下面用一个具体例子说明:

  • Graph 在调用 Agent Node 前的状态包含:{"query_text": "What is RAG?", "route": "llm_agent"}
  • Agent Node 只识别 STATE_KEY_USER_INPUT(即 user_input)
  • Agent Node 执行完成后,我们希望把它的最终回复写回 Graph 的 query_reply
from trpc_agent_sdk.dsl.graph import StateMapper, STATE_KEY_USER_INPUT

graph.add_agent_node(
    node_id="query_orchestrator",
    agent=orchestrator_agent,
    input_mapper=StateMapper.rename({"query_text": STATE_KEY_USER_INPUT}),
    output_mapper=StateMapper.merge_response("query_reply"),
)

上面这段配置的效果是:

  • 调用 Agent Node 前:把 Graph query_text 重命名为 Agent Node 的 user_input
  • Agent Node 结束后:把 Agent Node 的 last_response 写入 Graph query_reply

如需更精细的控制,可组合使用以下 mapper:

  • pick: 只传白名单字段给子节点。例如 StateMapper.pick("query", "context") 只将父状态中的 query 和 context 传给子 Agent,其余字段丢弃。
  • exclude: 排除指定字段,其余都传给子节点。例如 StateMapper.exclude("api_key", "password") 把除 api_key、password 以外的父状态全部传给子 Agent。
  • rename: 将父状态键名映射为子节点键名,格式为 {父键: 子键}。例如 StateMapper.rename({"query_text": STATE_KEY_USER_INPUT, "docs": "context"}) 把父状态的 query_text 映射为子节点的 user_input,docs 映射为 context。
  • combine: 组合多个 input mapper,结果取并集,同键时后者覆盖前者。例如 StateMapper.combine(StateMapper.pick("context"), StateMapper.rename({"query_text": STATE_KEY_USER_INPUT})) 先取 context 字段,再把 query_text 重命名为 user_input,两者合并传给子节点。
  • merge_response: 将子节点 last_response 写入父状态指定字段,仅用于 output_mapper。例如 StateMapper.merge_response("search_results") 把子 Agent 的回复写入父状态的 search_results。
  • identity: 原样透传父状态,不做任何裁剪或重命名。例如 StateMapper.identity() 把父状态完整传给子 Agent。
  • filter_keys: 按谓词函数过滤父状态的键。例如 StateMapper.filter_keys(lambda k: k.startswith("user_")) 只传以 user_ 开头的字段给子节点。

NodeCallbacks

可以通过 NodeCallbacks 在节点执行前后注入日志、检查或其他埋点,支持两种粒度:

  • Graph级:StateGraph(..., callbacks=global_callbacks),作用于全图节点
  • 节点级:add_node(..., callbacks=node_callbacks),只作用于当前节点
from trpc_agent_sdk.dsl.graph import NodeCallbacks, StateGraph


global_callbacks = NodeCallbacks()
node_callbacks = NodeCallbacks()


async def before_node(ctx, state):
    print(f"[before] step={ctx.step_number} node={ctx.node_id}")
    return None


async def after_node(ctx, state, result, error):
    print(f"[after ] step={ctx.step_number} node={ctx.node_id}")
    return None


global_callbacks.register_before_node(before_node)
node_callbacks.register_after_node(after_node)

graph = StateGraph(MyState, callbacks=global_callbacks)
graph.add_node(name="extract", action=extract_node, callbacks=node_callbacks)

常用回调类型:

  • register_before_node
  • register_after_node
  • register_on_error
  • register_agent_event

回调合并规则:

  • before_node / on_error:图级先执行,再执行节点级
  • after_node:节点级先执行,再执行图级(便于节点级先修改输出)

State Key 常量与取值方式

Graph 模块内置了一组常用状态 Key 常量,建议统一使用常量读写状态,避免业务代码里散落硬编码字符串。

常量 Key 对照表

常量 实际 Key 说明
STATE_KEY_USER_INPUT user_input 当前轮用户输入
STATE_KEY_MESSAGES messages 会话消息列表
STATE_KEY_LAST_RESPONSE last_response 最近一次回复文本
STATE_KEY_LAST_RESPONSE_ID last_response_id 最近一次回复 ID
STATE_KEY_LAST_TOOL_RESPONSE last_tool_response 最近一次工具执行结果
STATE_KEY_NODE_RESPONSES node_responses 按节点聚合的响应结果
STATE_KEY_NODE_STRUCTURED node_structured 按节点聚合的结构化结果

在 Graph 运行中读取

from trpc_agent_sdk.dsl.graph import (
    State,
    STATE_KEY_USER_INPUT,
    STATE_KEY_LAST_RESPONSE,
    STATE_KEY_NODE_RESPONSES,
)


async def inspect_state_node(state: State) -> dict[str, str]:
    user_input = state.get(STATE_KEY_USER_INPUT, "")
    last_response = state.get(STATE_KEY_LAST_RESPONSE, "")
    summarize_text = state.get(STATE_KEY_NODE_RESPONSES, {}).get("summarize", "")
    return {
        "echo_input": user_input,
        "debug_last_response": last_response,
        "debug_summarize_text": summarize_text,
    }

在 Graph 外读取

from trpc_agent_sdk.dsl.graph import STATE_KEY_LAST_RESPONSE, STATE_KEY_NODE_RESPONSES


session = await session_service.get_session(
    app_name=app_name,
    user_id=user_id,
    session_id=session_id,
)

if session and session.state:
    last_response = session.state.get(STATE_KEY_LAST_RESPONSE, "")
    node_responses = session.state.get(STATE_KEY_NODE_RESPONSES, {})

Interrupt

Graph 提供 interrupt(...),可在节点中暂停执行并等待外部决策:

from trpc_agent_sdk.dsl.graph import interrupt


async def approval_gate(state: State) -> dict[str, Any]:
    decision = interrupt({
        "title": "Approval Required",
        "options": ["approved", "rejected"],
    })
    status = "approved"
    if isinstance(decision, dict):
        status = str(decision.get("status", "approved"))
    return {"approval_status": status}

Runner 侧会收到 LongRunningEvent,客户端通过 FunctionResponse 恢复:

from trpc_agent_sdk.events import LongRunningEvent
from trpc_agent_sdk.types import Content, FunctionResponse, Part

async for event in runner.run_async(...):
    if isinstance(event, LongRunningEvent):
        resume_payload = {"status": "approved", "note": "Proceed"}
        resume_response = FunctionResponse(
            id=event.function_response.id,
            name=event.function_response.name,
            response=resume_payload,
        )
        resume_content = Content(role="user", parts=[Part(function_response=resume_response)])

        async for _ in runner.run_async(..., new_message=resume_content):
            pass

完整示例: - examples/graph_with_interrupt - interrupt + resume 示例