MAF支持后台响应,用于处理可能需要较长时间才能完成的长时间运行操作。此功能使Agent能够开始处理请求并返回一个ContinationToken,该ContinationToken可用于轮询结果或恢复中断的数据流。后台响应特别适合:

  • 需要大量处理时间的复杂推理任务;
  • 可能因网络问题或客户端超时而中断的操作;
  • 以及需要启动长时间运行的任务并在稍后查看结果的场景;

虽然LangChain针对Agent的调用不支持类似的后台响应,但是LangGraph SDK提供的客户端允许我们以后台任务的方式调用Agent,并返回一个Run对象来跟踪任务的状态和结果,也起到了类似的作用。下面我们来看看MAF和LangChain是如何支持Agent的后台响应的。

1. MAF

后台响应使用ContinationToken机制来处理长时间运行的操作。当您向启用了后台响应的Agent发送请求时,会发生以下两种情况之一:

  • 立即完成:Agent快速完成任务并返回最终响应,无需ContinationToken
  • 后台处理:Agent在后台开始处理,并返回一个ContinationToken,而不是最终结果;

ContinationToken包含所有必要信息,可用于使用非流式Agent API轮询完成状态,或使用流式Agent API恢复中断的流。当ContinationToken为空时,操作完成;这种情况发生在后台响应已完成、失败或无法继续进行时。

1.1 非流式轮询

如果后台响应被应用到AIAgent的非流式RunAsync方法中,如果Agent在处理请求时返回了一个ContinationToken,意味着Agent正在后台处理这个请求,我们可以采用如下的方式利用这个ContinationToken来轮询结果,直到操作完成。

using Azure.AI.Projects;
using dotenv.net;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Responses;
using System.ClientModel;

DotEnv.Load();

var model = Environment.GetEnvironmentVariable("MODEL")!;
var apiKey = Environment.GetEnvironmentVariable("API_KEY")!;
var endpoint = Environment.GetEnvironmentVariable("OPENAI_URL")!;

var agent = new OpenAIClient(
    credential: new ApiKeyCredential(key: apiKey),
    options: new OpenAIClientOptions { Endpoint = new Uri(endpoint) })
    .GetResponsesClient()
    .AsAIAgent(model: model);

var session = await agent.CreateSessionAsync();
var options = new ChatClientAgentRunOptions
{
    AllowBackgroundResponses = true,    
};

var index = 1;
var response =await agent.RunAsync(
    message: "制定一份赴日旅游攻略,行程计划5天。",
    options: options,
    session: session);

PrintResponse(response);
var token = response.ContinuationToken;

while (token is not null)
{
    await Task.Delay(2000);
    options.ContinuationToken = token;
    response = await agent.RunAsync(session: session,options: options);    
    token = response.ContinuationToken;
    PrintResponse(response);
}

void PrintResponse(AgentResponse response)
{
    Console.WriteLine($"----------------------------------------{index ++}----------------------------------------");
    var hasContinuationToken = response.ContinuationToken is null ? "No" : "Yes";
    var text = response.Text ?? "No content.";
    Console.WriteLine($"""
HasContinuationToken: {hasContinuationToken}
ResponseID: {response.ResponseId}
Text: {text} 
""");
}

在上面的演示程序中,我们首先创建了一个AIAgent对象,并调用RunAsync方法来请求制定一份赴日旅游攻略的行程计划。我们创建了一个ChatClientAgentRunOptions配置选项作为调用RunAsync方法的参数,此对象的AllowBackgroundResponses属性被设置为True,进而开启的后台响应功能。在得到代表响应结果的AgentResponse对象之后,我们调用PrintResponse方法输出相关信息(是否具有ContinuationToken、当前响应ID和文本内容)。

所有我们开启了一个循环来轮询结果,在每次迭代中,我们首先检查当前的AgentResponse对象是否包含ContinuationToken,如果没有,说明操作已经完成,我们就退出循环;如果包含,就将这个ContinuationToken赋值给ChatClientAgentRunOptions对象的ContinuationToken属性,并再次调用RunAsync方法来获取最新的响应结果。我们在每次得到新的响应结果之后都调用PrintResponse方法来输出相关信息。程序执行后的输出如下所示:

----------------------------------------1----------------------------------------
HasContinuationToken: Yes
ResponseID: resp_0e602ed1ac4f8533006a06d836cea08194aebd3332cbbbe92f
Text:
----------------------------------------2----------------------------------------
HasContinuationToken: Yes
ResponseID: resp_0e602ed1ac4f8533006a06d836cea08194aebd3332cbbbe92f
Text:
----------------------------------------3----------------------------------------
HasContinuationToken: Yes
ResponseID: resp_0e602ed1ac4f8533006a06d836cea08194aebd3332cbbbe92f
Text:
----------------------------------------4----------------------------------------
HasContinuationToken: Yes
ResponseID: resp_0e602ed1ac4f8533006a06d836cea08194aebd3332cbbbe92f
Text:
----------------------------------------5----------------------------------------
HasContinuationToken: Yes
ResponseID: resp_0e602ed1ac4f8533006a06d836cea08194aebd3332cbbbe92f
Text:
----------------------------------------6----------------------------------------
HasContinuationToken: Yes
ResponseID: resp_0e602ed1ac4f8533006a06d836cea08194aebd3332cbbbe92f
Text:
----------------------------------------7----------------------------------------
HasContinuationToken: Yes
ResponseID: resp_0e602ed1ac4f8533006a06d836cea08194aebd3332cbbbe92f
Text:
----------------------------------------8----------------------------------------
HasContinuationToken: No
ResponseID: resp_0e602ed1ac4f8533006a06d836cea08194aebd3332cbbbe92f
Text: 下面是一份**5天日本旅游攻略**(以**东京+周边**为例,适合首次赴日、节奏适中、兼顾文化/购物/美食)。如果你有**具体出发城市、季节、预算或偏好**(如动漫/自然/购物),我可以再为你定制升级版。

---

## 🇯🇵 日本5天旅游行程(东京+周边)

### ✈️ 行前准备
- **签证**:中国大陆需办理日本旅游签证
- **交通卡**:Suica / PASMO(到达机场即可购买或手机绑定)
- **网络**:eSIM / 日本电话卡 / 移动WiFi
- **支付**:现金 + 信用卡(部分小店只收现金)
- **APP推荐**:Google Maps、乘换案内、PayPay、Tabelog

---

## 🗓 行程安排

### **Day 1|抵达东京 · 浅草 + 晴空塔**
**上午 / 中午**
- 抵达东京(成田 / 羽田)
- 入住酒店(建议上野、浅草、新宿)

**下午**
- 浅草寺(东京最古老寺庙)
- 仲见世商店街(和果子、纪念品)

**晚上**
- 东京晴空塔(登塔或商场用餐)
- 推荐美食:天妇罗、鳗鱼饭

✅ 住宿建议:浅草 / 上野
✅ 亮点:传统文化 + 城市夜景

---

### **Day 2|东京市区 · 原宿 + 涩谷 + 新宿**
**上午**
- 明治神宫(都市中的森林)
- 原宿竹下通(年轻潮流)

**下午**
- 涩谷十字路口(打卡地标)
- 涩谷SKY展望台(建议提前预约)

**晚上**
- 新宿歌舞伎町
- 新宿居酒屋体验

✅ 购物:Shibuya 109、唐吉诃德
✅ 美食:拉面、一兰、寿司

---

### **Day 3|富士山一日游**
**推荐方式**
- 报团 or 自由行(巴士/电车)

**行程示例**
- 河口湖
- 天上山缆车
- 八木崎公园(樱花/枫叶季)
- 忍野八海

**晚上**
- 返回东京

✅ 亮点:富士山全景
✅ 季节建议:4月樱花 / 10-11月红叶

---

### **Day 4|镰仓或迪士尼(二选一)**

#### 🎎 方案A:镰仓一日游(文化)
- 镰仓大佛
- 长谷寺
- 镰仓高校前(灌篮高手取景地)
- 小町通购物

#### 🎢 方案B:东京迪士尼(娱乐)
- 东京迪士尼乐园 或 海洋
- 建议早入园+官方APP

✅ 适合:情侣/亲子/动漫迷

---

### **Day 5|上野 + 秋叶原 · 返程**
**上午**
- 上野公园 / 博物馆
- 阿美横町购物

**下午**
- 秋叶原(动漫、电器、手办)

**晚上**
- 前往机场返程

---

## 💰 预算参考(人均)
| 项目 | 费用 |
|----|----|
| 机票 | ¥3000–6000 RMB |
| 酒店 | ¥600–1000 RMB / 晚 |
| 吃喝 | ¥200–300 RMB / 天 |
| 交通 | ¥200–300 RMB |
| 总计 | ¥7000–12000 RMB |

---

## 🍣 日本必吃美食
- 寿司 / 刺身
- 拉面(博多、东京豚骨)
- 烧肉 / 和牛
- 抹茶甜品

---

## 📌 小贴士
- 地铁复杂但准时,别怕换乘
- 垃圾分类严格,街头垃圾桶少
- 公共场所保持安静
- 排队文化非常重要

---

如果你愿意,我可以:
✅ 按**大阪/京都/北海道**重做路线
✅ 制定**情侣/亲子/二次元主题行程**
✅ 生成**详细交通换乘表+地图链接**

告诉我你的偏好即可 😊

从输出结果看出,对于执行RunAsync<T>方法返回的AgentResponse对象,如果它的ContinuationToken不为null,说明Agent正在后台处理这个请求。由于RunAsync方法属于阻塞式调用,结果只能在后台处理完成之后才能返回,所以如果有ContinuationToken,就不会有结果(Text属性为null)。当AgentResponse对象的ContinuationToken为null时,说明后台处理已经完成,这时Text属性才会包含最终的结果。虽然我们对此调用了RunAsync方法,但是ResponseId保持不变,说明这是同一个请求的不同阶段的响应结果。

1.2 流式中断恢复

当后台响应被应用到AIAgentRunStreamingAsync方法中,如果我们在迭代处理实时响应内容时将ResponseContinuationToken保存下来,当迭代被中断(如网络问题或客户端超时)时,我们可以利用这个ResponseContinuationToken来恢复流式响应的处理。我们可以采用如下的方式来实现流式中断恢复:

using Azure.AI.Projects;
using dotenv.net;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Responses;
using System.ClientModel;

DotEnv.Load();

var model = Environment.GetEnvironmentVariable("MODEL")!;
var apiKey = Environment.GetEnvironmentVariable("API_KEY")!;
var endpoint = Environment.GetEnvironmentVariable("OPENAI_URL")!;

var agent = new OpenAIClient(
    credential: new ApiKeyCredential(key: apiKey),
    options: new OpenAIClientOptions { Endpoint = new Uri(endpoint) })
    .GetResponsesClient()
    .AsAIAgent(model: model);

var session = await agent.CreateSessionAsync();
var options = new ChatClientAgentRunOptions
{
    AllowBackgroundResponses = true,
};

ResponseContinuationToken? continuationToken = null;
var index = 0;
await foreach (var update in agent.RunStreamingAsync(
    message: "制定一份赴日旅游攻略,行程计划5天。",
    options: options,
    session: session))
{
    continuationToken = update.ContinuationToken;
    var text = update.Text;
    if (!string.IsNullOrWhiteSpace(text))
    { 
        Console.Write(text);
    }
    index++;
    if (index == 200)
    {
        Console.WriteLine("\n\n-------------模拟中断,稍后继续-------------\n");
        break;
    }
}

options.ContinuationToken = continuationToken;
await foreach (var update in agent.RunStreamingAsync(
    options: options,
    session: session))
{
    continuationToken = update.ContinuationToken;
    var text = update.Text;
    if (!string.IsNullOrWhiteSpace(text))
    {
        Console.Write(text);
    }
}

如上面的演示程序所示,我们首先调用RunStreamingAsync方法来请求制定一份赴日旅游攻略的行程计划,并在迭代处理实时响应内容的过程中将每次迭代得到的ResponseContinuationToken保存下来。当迭代处理到第200次时,我们模拟了一个中断,此时我们就退出了第一次的迭代循环。接着我们将之前保存的ResponseContinuationToken赋值给ChatClientAgentRunOptions对象的ContinuationToken属性,并再次调用RunStreamingAsync方法来继续处理剩余的流式响应内容。程序执行后的输出如下所示:

下面是一份**适合第一次赴日的5天日本旅游攻略**,以**东京 + 京都/大阪**为主线,兼顾城市、文化、美食与购物。如果你有特殊偏好(如亲子、动漫、购物、自然风景),我也可以再为你定制调整。

---

## 一、行程总览**Day1:抵达东京|浅草·晴空塔****Day2:东京|原宿·涩谷·新宿****Day3:东京 → 京都|清水寺·祇园****Day4:京都|伏见稻荷·岚山****Day5:大阪|心斋桥·道顿堀·返程**

---

## 二、详细行程安排### **Day1|抵达东京 ·传统与现代**
**上午 / 中午**
- 抵

-------------模拟中断,稍后继续-------------

达东京(成田或羽田机场)
-乘坐机场快线前往市区(N'EX / 京成Skyliner)
- 酒店寄存行李(推荐住在上野、浅草、新宿)

**下午**
- 浅草寺(东京最古老寺庙)
- 仲见世商店街(和风小吃、伴手礼)

**傍晚**
- 东京晴空塔(登塔或拍夜景)
- 晚餐推荐:天妇罗、鳗鱼饭、拉面✅ **住宿**:东京---

### **Day2|东京潮流与都市体验**
**上午**
- 明治神宫(感受宁静森林神社)
- 原宿竹下通(潮流、甜品)

**下午**
- 涩谷十字路口- 忠犬八公像- 涩谷购物(SHIBUYA109、Loft)

**晚上**
- 新宿歌舞伎町夜景- 都厅展望台(免费夜景)
- 可体验居酒屋文化✅ **住宿**:东京---

### **Day3|东京 → 京都(新干线)**
**上午**
- 搭乘新干线(东京 → 京都,约2.5小时)

**下午**
- 清水寺(世界文化遗产)
- 二年坂、三年坂(传统街道)

**傍晚**
-祇园花见小路(有机会遇见艺伎)
- 晚餐推荐:怀石料理、京都汤豆腐✅ **住宿**:京都(推荐町屋或和式酒店)

---

### **Day4|京都深度游**
**上午**
-伏见稻荷大社(千本鸟居)

**中午**
- 京都拉面或抹茶甜品**下午**
- 岚山竹林- 天龙寺- 渡月桥**晚上**
- 可泡温泉或夜游鸭川✅ **住宿**:京都 / 大阪---

### **Day5|大阪 · 美食与购物**
**上午**
- 前往大阪(京都 → 大阪约30分钟)

**游览**
- 大阪城公园(外观)
- 心斋桥购物街- 道顿堀美食:章鱼烧、大阪烧、串炸**返程**
- 前往关西机场返程---

## 三、交通建议- **交通卡**:Suica / ICOCA(地铁、便利店通用)
- **新干线**:东京 → 京都(提前购票或JR Pass)
- 市内以地铁+步行为主---

## 四、预算参考(人均)
-机票:¥3000–6000(视出发地)
-住宿:¥600–1200 / 晚- 餐饮:¥150–300 / 天-交通+门票:¥500–800---

## 五、实用小贴士✅ 日本现金+信用卡并用✅ 提前预约热门景点/餐厅✅ 注意垃圾分类,公共场所保持安静✅便利店美食很值得尝试---

如果你需要:
- **亲子 / 情侣 / 独自旅行版本**
- **动漫巡礼 /购物 / 温泉主题**
- **具体酒店和餐厅推荐**

2. LangChain

虽然我们不能以后台响应的方式来调用LangChain的Agent,但是当我们使用LangGraph客户端SDK来调用Web服务器承载的Agent时,可以以后台任务的方式来调用Agent,并返回一个Run对象来跟踪任务的状态和结果。为了演示这个功能,我们得先构建一个Agent服务器。

2.1 构建Agent服务器

为了我们构建了一个极简的Agent服务器,根目录下只有如下所示的几个必要的文件:

root/
├── .env            # 存放环境变量
├── langgraph.json  # 配置Graph
├── agent.py        # 定义Graph
└── pyproject.toml  # 项目配置文件

我们在agent.py调用create_deep_agent函数来创建一个Agent,并将其赋值给一个名为graph的变量。我们指定了模型为ChatOpenAI,并设置了模型名称为gpt-5.2-chat

from deepagents  import create_deep_agent
from langchain_openai import ChatOpenAI
graph = create_deep_agent(model=ChatOpenAI(model="gpt-5.2-chat"))

我们执行命令行langgraph dev以dev模式部署我们定义的Agent,并启动作为宿主的Web服务器。

E:\agent> langgraph dev
INFO:langgraph_api.cli:

        Welcome to

╦  ┌─┐┌┐┌┌─┐╔═╗┬─┐┌─┐┌─┐┬ ┬
║  ├─┤││││ ┬║ ╦├┬┘├─┤├─┘├─┤
╩═╝┴ ┴┘└┘└─┘╚═╝┴└─┴ ┴┴  ┴ ┴

- 🚀 API: http://127.0.0.1:2024
- 🎨 Studio UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
- 📚 API Docs: http://127.0.0.1:2024/docs

2.2 以后台任务的方式调用Agent

在如下所示的客户端程序中,我们调用get_client函数来创建一个LangGraph客户端,并指定url参数为我们Agent服务器的地址。接着我们调用client.runs.create方法来以后台任务的方式调用Agent,并将返回的Run对象中的thread_idrun_id保存下来,以便后续查询任务状态和结果。在随后的while循环中,我们通过不断地调用client.runs.get方法来查询任务的状态,直到任务完成(状态为successerrortimeoutinterrupted)。最后我们调用client.runs.join方法来获取最终的结果,是不是和前面演示的以后台响应方式调用Agent的RunAsync方法很相似。

from langgraph_sdk import get_client
from langgraph_sdk.client import RunStatus
from datetime import datetime
import asyncio

async def main():
    async with get_client(url="http://localhost:2024") as client:
        run = await client.runs.create(
            thread_id=None,
            assistant_id="travel_agent",
            input={
                "messages": [
                    {
                        "role": "user",
                        "content": "制定一份赴日旅游攻略,行程计划5天。"
                    }
                ]
            },
            after_seconds=5,
            on_completion= "keep"
        )
        thread_id = run["thread_id"]
        run_id = run["run_id"]
        status:RunStatus = run["status"]
        while True:
            run = await client.runs.get(thread_id=thread_id, run_id=run_id)
            status = run["status"]
            print(f"[{datetime.now()}]当前状态: {status}")
            if status in ["success", "error","timeout","interrupted"]:
                break              
            await asyncio.sleep(3)         
        
        result = await client.runs.join(thread_id=thread_id,run_id=run_id)
        print (result["messages"][-1].get("content",""))

asyncio.run(main())

输出:

[2026-05-15 17:38:37.855502]当前状态: pending
[2026-05-15 17:38:40.866880]当前状态: pending
[2026-05-15 17:38:43.873628]当前状态: running
[2026-05-15 17:38:46.892703]当前状态: running
[2026-05-15 17:38:49.899890]当前状态: running
[2026-05-15 17:38:52.908758]当前状态: running
[2026-05-15 17:38:55.918140]当前状态: running
[2026-05-15 17:38:58.931679]当前状态: success
以下为**5天日本旅游攻略(东京为主,含富士山一日)**,适合**首次赴日、节奏适中**的行程。

---

## 行程概览
- **D1–D3:东京市区**
- **D4:富士山/箱根一日游**
- **D5:东京购物返程**

---

## D1|抵达东京 · 浅草 & 银座
**上午**
- 抵达成田/羽田机场  
- 购买交通卡:**Suica / PASMO**

**下午**
- 浅草寺(雷门、仲见世街)
- 隅田川散步(可远观晴空塔)

**晚上**
- 银座逛街(优衣库旗舰店、无印良品)
- 晚餐:寿司或和牛烧肉

---

## D2|东京经典 · 原宿 & 涩谷 & 新宿
**上午**
- 明治神宫
- 原宿竹下通(年轻潮流)

**下午**
- 涩谷十字路口
- SHIBUYA SKY(需预约)

**晚上**
- 新宿歌舞伎町
- 东京都厅免费夜景

---

## D3|东京文化 · 上野 & 秋叶原
**上午**
- 上野公园
- 东京国立博物馆(或动物园)

**下午**
- 秋叶原(动漫、电器)
- 女仆咖啡(体验型,可选)

**晚上**
- 回酒店整理行李

---

## D4|富士山一日游(推荐跟团)
**路线示例**
- 河口湖
- 忍野八海
- 富士五合目(视天气)

**提示**
- 冬季/雨季可能取消五合目
- 自由行需提前查JR与巴士班次

---

## D5|购物 · 回程
**上午**
- 表参道 / 新宿 / 银座补买伴手礼
- 唐吉诃德、Loft、药妆店

**下午**
- 前往机场返程

---

## 住宿建议
- **区域**:上野 / 新宿 / 银座
- **类型**:
  - 经济:APA、Super Hotel
  - 舒适:三井花园、Hotel Mystays

---

## 预算参考(人均)
- 机票:4000–7000 RMB  
- 酒店(4晚):2500–4000 RMB  
- 交通 + 餐饮 + 门票:1500–2500 RMB  

---

## 必备准备
- 护照 + 签证
- 转换插头(日本双扁)
- 现金 + 信用卡
- 翻译App / Google Maps

---

如需:
- **亲子 / 二次元 / 美食 / 温泉主题**
- **大阪京都版 / 关东+关西**
- **详细到每小时 or 可打印PDF**

直接告诉我你的偏好即可。

Run在LangGraph SDK中代表针对Agent的一次调用,它是与Agent部署相关的几个核心对象之一(其他对象包括AssistantThreadRunCron Job等),关于Run对象的更多细节,可以参考我们之前的博客文章Agent逻辑在特定任务下的“单次运算实例”

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐