迁移到 AgentAPI
本页介绍从 OpenAI Responses API、LangChain 自定义循环及 Claude Managed Agents/Claude Agent SDK 迁移到 AgentAPI 的方法。
迁移后,应用管理智能体、云端环境、会话及业务状态;AgentAPI 在云端运行智能体循环、执行内置工具并保存事件。请先按下表确定迁移范围。
迁移范围
| 原有架构 | 需要保留在应用中的内容 | 迁移到 AgentAPI 的内容 |
|---|---|---|
| Claude Managed Agents API | 业务鉴权、业务状态和结果展示 | Base URL、MaaS API Key、Agent/Environment/Session 资源,以及 AgentAPI 的 runtime 扩展 |
| Claude Agent SDK(本地运行) | 业务工具实现、审批界面和业务数据 | 云端环境、会话事件流、文件/仓库资源;本地 SDK 的进程生命周期不再由应用管理 |
| OpenAI Responses API | 业务输入输出转换和自定义函数 | 智能体循环、内置工具执行、云端沙箱和事件持久化 |
| LangChain 自定义循环 | 检查点中的业务状态、自定义工具和业务超时 | 智能体版本、会话状态机和事件流 |
使用 CMA Session API 的应用,请先按接入准备验证地址和 MaaS API Key,并核对三项差异:
- 智能体增加 AgentAPI 扩展字段
runtime。 - 结构化 HTTP 输入使用
input.parts。 - SDK 可能仍生成兼容字段
content。
从 OpenAI Responses API 迁移
如果应用通过 responses.create 调用模型、遍历函数调用并将 function_call_output 追加到下一轮输入,那么应用本身承担了智能体循环的职责。迁移到 AgentAPI 后,这部分循环和内置工具执行可以交给云端服务。
执行职责迁移
| 之前 | 之后 |
|---|---|
应用维护 Responses 输入,并在每轮调用中传递已有输出或 previous_response_id。 |
AgentAPI 在服务端保存会话历史;应用只需发送和接收事件。 |
应用遍历 function_call,执行函数,再追加 function_call_output。 |
文件和命令工具在云端沙箱中执行;网页工具按运行时执行。应用通过 agent.custom_tool_use 处理自定义工具。 |
| 应用自行准备沙箱,运行智能体生成的代码。 | AgentAPI 根据云端环境配置准备沙箱,并在其中执行代码、文件操作和 Bash 命令。 |
| 应用自行判断智能体循环何时结束。 | AgentAPI 通过状态事件报告本轮停止,应用需检查停止原因、错误与业务结果。 |
代码对比
迁移前(简化的 Responses API 函数调用循环):
Python
import json
input_items = [{"role": "user", "content": task}]
while True:
response = client.responses.create(
model="gpt-5.6",
input=input_items,
tools=tools,
)
input_items.extend(response.output)
function_calls = [item for item in response.output if item.type == "function_call"]
if not function_calls:
break
for call in function_calls:
result = execute_tool(call.name, json.loads(call.arguments))
input_items.append(
{
"type": "function_call_output",
"call_id": call.call_id,
"output": result,
}
)
TypeScript
const input: any[] = [{ role: "user", content: task }];
while (true) {
const response = await client.responses.create({
model: "gpt-5.6",
input,
tools
});
input.push(...response.output);
const functionCalls = response.output.filter(
(item) => item.type === "function_call"
);
if (functionCalls.length === 0) {
break;
}
for (const call of functionCalls) {
const result = await executeTool(call.name, JSON.parse(call.arguments));
input.push({
type: "function_call_output",
call_id: call.call_id,
output: result
});
}
}
迁移后(AgentAPI):
以下示例假设已按照接入准备配置 SDK 客户端,并创建了云端环境。智能体和云端环境可以跨多个会话复用。
以下内置工具示例显式选择 native。模型从 AGENTAPI_MODEL_ID 读取,请先在七牛 MaaS 模型管理页获取模型 ID,并确认所用 Key 在所选区域可调用该模型。SDK 通过扩展请求参数传入运行时,客户端初始化见通过 API 构建。
cURL
TASK="${TASK:-请分析当前项目并给出改进建议。}"
agent=$(
curl --fail-with-body -sS "$AGENTAPI_BASE_URL/v1/agents" \
-H "x-api-key: $AGENTAPI_API_KEY" \
-H "content-type: application/json" --data @- <<EOF
{
"name": "任务执行助手",
"runtime": "native",
"model": {"id": "$AGENTAPI_MODEL_ID"},
"tools": [{"type": "agent_toolset_20260401"}]
}
EOF
)
agent_id=$(jq -r '.id' <<< "${agent}")
session_id=$(
curl --fail-with-body -sS "$AGENTAPI_BASE_URL/v1/sessions" \
-H "x-api-key: $AGENTAPI_API_KEY" \
-H "content-type: application/json" --data "$(jq -n --argjson a "${agent}" --arg env "${ENVIRONMENT_ID}" \
'{agent: {type: "agent", id: $a.id, version: $a.version}, environment_id: $env}')" \
| jq -r '.id'
)
# 在后台打开 SSE 流,然后发送用户消息。
stream_log=$(mktemp)
curl --fail-with-body -sS -N \
"$AGENTAPI_BASE_URL/v1/sessions/${session_id}/events/stream" \
-H "x-api-key: $AGENTAPI_API_KEY" \
> "${stream_log}" &
stream_pid=$!
curl --fail-with-body -sS \
"$AGENTAPI_BASE_URL/v1/sessions/${session_id}/events" \
-H "x-api-key: $AGENTAPI_API_KEY" \
-H "content-type: application/json" --data "$(jq -n --arg text "${TASK}" \
'{events: [{type: "user.message", input: {parts: [{type: "text", text: $text}]}}]}')" \
> /dev/null
# 等待会话进入空闲状态。grep 在首次匹配时退出,并且
# 通过进程替换读取意味着 shell 不会等待
# tail(前台的 `tail -f | grep -m1` 管道会挂起:tail
# 只会在下一次写入时终止,而流空闲后不会再有写入)。
grep -m1 '"session.status_idle"' <(tail -f -n +1 "${stream_log}") > /dev/null
kill "${stream_pid}" 2>/dev/null || true
rm -f "${stream_log}"
Python
import os
agent = client.beta.agents.create(
name="任务执行助手",
model={"id": os.environ["AGENTAPI_MODEL_ID"]},
tools=[{"type": "agent_toolset_20260401"}],
extra_body={"runtime": "native"},
)
session = client.beta.sessions.create(
agent={"type": "agent", "id": agent.id, "version": agent.version},
environment_id=environment.id,
)
with client.beta.sessions.events.stream(session.id) as stream:
client.beta.sessions.events.send(
session.id,
events=[{"type": "user.message", "content": [{"type": "text", "text": task}]}],
)
for event in stream:
if event.type == "session.status_idle":
break
TypeScript
const agentParams = {
name: "任务执行助手",
model: { id: process.env.AGENTAPI_MODEL_ID! },
tools: [{ type: "agent_toolset_20260401" }]
} satisfies Parameters<typeof client.beta.agents.create>[0];
const agent = await client.beta.agents.create(agentParams, {
body: { ...agentParams, runtime: "native" },
});
const session = await client.beta.sessions.create({
agent: { type: "agent", id: agent.id, version: agent.version },
environment_id: environment.id
});
const stream = await client.beta.sessions.events.stream(session.id);
await client.beta.sessions.events.send(session.id, {
events: [
{
type: "user.message",
content: [{ type: "text", text: task }]
}
]
});
for await (const event of stream) {
if (event.type === "session.status_idle") {
break;
}
}
C#
using Anthropic.Models.Beta.Agents;
using System.Text.Json;
using System.Linq;
var agentParams = new AgentCreateParams
{
Name = "任务执行助手",
Model = new BetaManagedAgentsModelConfigParams { ID = System.Environment.GetEnvironmentVariable("AGENTAPI_MODEL_ID")! },
Tools =
[
new BetaManagedAgentsAgentToolset20260401Params
{
Type = "agent_toolset_20260401",
},
],
};
var agentBody = agentParams.RawBodyData.ToDictionary(entry => entry.Key, entry => entry.Value);
agentBody["runtime"] = JsonSerializer.SerializeToElement("native");
agentParams = AgentCreateParams.FromRawUnchecked(
agentParams.RawHeaderData, agentParams.RawQueryData, agentBody);
var agent = await client.Beta.Agents.Create(agentParams);
var session = await client.Beta.Sessions.Create(new()
{
Agent = new BetaManagedAgentsAgentParams
{
Type = "agent",
ID = agent.ID,
Version = agent.Version,
},
EnvironmentID = environment.ID,
});
var stream = client.Beta.Sessions.Events.StreamStreaming(session.ID);
await client.Beta.Sessions.Events.Send(session.ID, new()
{
Events =
[
new BetaManagedAgentsUserMessageEventParams
{
Type = "user.message",
Content = [new BetaManagedAgentsTextBlock { Type = "text", Text = task }],
},
],
});
await foreach (var streamEvent in stream)
{
if (streamEvent.Value is BetaManagedAgentsSessionStatusIdleEvent)
{
break;
}
}
Go
agent, err := client.Beta.Agents.New(ctx, anthropic.BetaAgentNewParams{
Name: "任务执行助手",
Model: anthropic.BetaManagedAgentsModelConfigParams{
ID: os.Getenv("AGENTAPI_MODEL_ID"),
},
Tools: []anthropic.BetaAgentNewParamsToolUnion{{
OfAgentToolset20260401: &anthropic.BetaManagedAgentsAgentToolset20260401Params{
Type: anthropic.BetaManagedAgentsAgentToolset20260401ParamsTypeAgentToolset20260401,
},
}},
}, option.WithJSONSet("runtime", "native"))
if err != nil {
log.Fatal(err)
}
session, err := client.Beta.Sessions.New(ctx, anthropic.BetaSessionNewParams{
Agent: anthropic.BetaSessionNewParamsAgentUnion{
OfBetaManagedAgentsAgents: &anthropic.BetaManagedAgentsAgentParams{
Type: anthropic.BetaManagedAgentsAgentParamsTypeAgent,
ID: agent.ID,
Version: anthropic.Int(agent.Version),
},
},
EnvironmentID: environment.ID,
})
if err != nil {
log.Fatal(err)
}
stream := client.Beta.Sessions.Events.StreamEvents(ctx, session.ID, anthropic.BetaSessionEventStreamParams{})
defer stream.Close()
_, err = client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
OfUserMessage: &anthropic.BetaManagedAgentsUserMessageEventParams{
Type: anthropic.BetaManagedAgentsUserMessageEventParamsTypeUserMessage,
Content: []anthropic.BetaManagedAgentsUserMessageEventParamsContentUnion{{
OfText: &anthropic.BetaManagedAgentsTextBlockParam{
Type: anthropic.BetaManagedAgentsTextBlockTypeText,
Text: task,
},
}},
},
}},
})
if err != nil {
log.Fatal(err)
}
for stream.Next() {
event := stream.Current()
if event.Type == "session.status_idle" {
break
}
}
if err := stream.Err(); err != nil {
log.Fatal(err)
}
Java
var agent = client.beta().agents().create(
AgentCreateParams.builder()
.putAdditionalBodyProperty("runtime", com.anthropic.core.JsonValue.from("native"))
.name("任务执行助手")
.model(BetaManagedAgentsModelConfigParams.builder().id(System.getenv("AGENTAPI_MODEL_ID")).build())
.addTool(
BetaManagedAgentsAgentToolset20260401Params.builder()
.type(BetaManagedAgentsAgentToolset20260401Params.Type.AGENT_TOOLSET_20260401)
.build()
)
.build()
);
var session = client.beta().sessions().create(
SessionCreateParams.builder()
.agent(
BetaManagedAgentsAgentParams.builder()
.type(BetaManagedAgentsAgentParams.Type.AGENT)
.id(agent.id())
.version(agent.version())
.build()
)
.environmentId(environment.id())
.build()
);
try (var stream = client.beta().sessions().events().streamStreaming(session.id())) {
client.beta().sessions().events().send(
session.id(),
EventSendParams.builder()
.addEvent(
BetaManagedAgentsUserMessageEventParams.builder()
.type(BetaManagedAgentsUserMessageEventParams.Type.USER_MESSAGE)
.addTextContent(task)
.build()
)
.build()
);
stream.stream()
.takeWhile(event -> !event.isSessionStatusIdle())
.forEach(_ -> {});
}
PHP
$agent = $client->beta->agents->create(
name: '任务执行助手',
model: ['id' => getenv('AGENTAPI_MODEL_ID')],
tools: [
BetaManagedAgentsAgentToolset20260401Params::with(
type: 'agent_toolset_20260401',
),
],
requestOptions: ['extraBodyParams' => ['runtime' => 'native']],
);
$session = $client->beta->sessions->create(
agent: BetaManagedAgentsAgentParams::with(
type: 'agent',
id: $agent->id,
version: $agent->version,
),
environmentID: $environment->id,
);
$stream = $client->beta->sessions->events->streamStream($session->id);
$client->beta->sessions->events->send(
$session->id,
events: [
[
'type' => 'user.message',
'content' => [['type' => 'text', 'text' => $task]],
],
],
);
foreach ($stream as $event) {
if ($event->type === 'session.status_idle') {
break;
}
}
Ruby
agent = client.beta.agents.create(
name: "任务执行助手",
model: {id: ENV.fetch("AGENTAPI_MODEL_ID")},
tools: [{type: "agent_toolset_20260401"}],
request_options: {extra_body: {runtime: "native"}}
)
session = client.beta.sessions.create(
agent: {type: "agent", id: agent.id, version: agent.version},
environment_id: environment.id
)
stream = client.beta.sessions.events.stream_events(session.id)
client.beta.sessions.events.send_(
session.id,
events: [{type: "user.message", content: [{type: "text", text: task}]}]
)
stream.each do
break if it.type == :"session.status_idle"
end
应用职责
- 系统提示和模型: 仍由应用决定,但配置从单次模型请求移到智能体定义中。
- 自定义工具: 仍然使用 JSON Schema 声明。执行方式从循环内直接调用,改为处理
agent.custom_tool_use事件并返回结果。请参阅事件和事件流。 - 网页访问:
claudecode和codex使用各自运行时提供的网页能力;native提供web_search和web_fetch,由 AgentAPI 执行,不在自托管 worker 上运行。 - 任务上下文: 可以通过系统提示、文件资源或技能向智能体提供上下文。
从基于 LangChain 的自定义智能体循环迁移
如果应用使用 LangChain 的模型和工具抽象,但通过 bind_tools 或 bindTools 自行维护消息列表、分派工具并控制循环结束条件,那么迁移方式与直接调用模型类似。主要变化是将这套循环交给 AgentAPI 在云端运行,并将原有配置映射为智能体、云端环境、会话和事件。
架构映射
| LangChain 智能体循环 | AgentAPI |
|---|---|
使用 model.bind_tools(...) 或 model.bindTools(...) 绑定工具 |
在智能体定义中配置工具,AgentAPI 在服务端保存配置并管理版本。 |
应用保存消息列表并反复调用 model.invoke(...) |
创建会话并发送用户事件;AgentAPI 保存会话历史并运行智能体循环。 |
应用读取 tool_calls、执行工具并追加 ToolMessage |
内置工具由 AgentAPI 在云端沙箱中执行;自定义工具通过 agent.custom_tool_use 和 user.custom_tool_result 事件衔接。 |
| 本地工具直接访问应用所在机器的文件系统 | 将文件作为会话资源上传或挂载;智能体在隔离的云端沙箱中处理文件。 |
| 每次调用时传入系统提示和模型配置 | 在智能体的 system 和 model 中配置;更新智能体时生成新版本。 |
| 应用自行保存检查点并恢复消息历史 | AgentAPI 持久保存会话和事件;应用通过会话 ID 继续交互。 |
| 应用在循环中实现工具确认逻辑 | 使用 permission_policy 控制工具是否需要确认,并通过 user.tool_confirmation 返回选择。 |
代码对比
迁移前(使用 LangChain 自行维护工具调用循环):
Python
from langchain.chat_models import init_chat_model
from langchain.tools import tool
@tool
def get_weather(city: str) -> str:
"""查询城市当前的天气。"""
return f"{city}:18°C,晴"
model = init_chat_model("openai:gpt-5.6")
model_with_tools = model.bind_tools([get_weather])
messages = [{"role": "user", "content": "东京天气怎么样?"}]
while True:
response = model_with_tools.invoke(messages)
messages.append(response)
if not response.tool_calls:
print(response.text)
break
for tool_call in response.tool_calls:
messages.append(get_weather.invoke(tool_call))
TypeScript
import { initChatModel, tool } from "langchain";
import { z } from "zod";
const getWeather = tool(
({ city }) => `${city}:18°C,晴`,
{
name: "get_weather",
description: "查询城市当前的天气。",
schema: z.object({ city: z.string() })
}
);
const model = await initChatModel("openai:gpt-5.6");
const modelWithTools = model.bindTools([getWeather]);
const messages: any[] = [{ role: "user", content: "东京天气怎么样?" }];
while (true) {
const response = await modelWithTools.invoke(messages);
messages.push(response);
if (!response.tool_calls?.length) {
console.log(response.text);
break;
}
for (const toolCall of response.tool_calls) {
if (toolCall.name === getWeather.name) {
messages.push(await getWeather.invoke(toolCall));
}
}
}
迁移后(AgentAPI):
下面选择 native,由应用处理天气查询工具并回传结果。天气数据在示例中由本地函数返回;接入真实服务时,在该函数中完成鉴权、参数校验、超时和错误处理。
Python
import os
from anthropic import Anthropic
client = Anthropic(
api_key=os.environ["AGENTAPI_API_KEY"],
base_url=os.environ["AGENTAPI_BASE_URL"],
)
agent = client.beta.agents.create(
name="天气助手",
model={"id": os.environ["AGENTAPI_MODEL_ID"]},
system="你是一名简洁、准确的天气助手。",
tools=[
{
"type": "custom",
"name": "get_weather",
"description": "查询城市当前的天气。",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}
],
extra_body={"runtime": "native"},
)
environment = client.beta.environments.create(
name="weather-env",
config={"type": "cloud", "networking": {"type": "unrestricted"}},
)
session = client.beta.sessions.create(
agent={"type": "agent", "id": agent.id, "version": agent.version},
environment_id=environment.id,
)
def get_weather(city: str) -> str:
return f"{city}:18°C,晴"
with client.beta.sessions.events.stream(session.id) as stream:
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [{"type": "text", "text": "东京天气怎么样?"}],
}
],
)
for event in stream:
if event.type == "agent.message":
print(
"".join(block.text for block in event.content if block.type == "text")
)
elif event.type == "agent.custom_tool_use":
result = get_weather(**event.input)
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.custom_tool_result",
"custom_tool_use_id": event.id,
"content": [{"type": "text", "text": result}],
}
],
)
elif (
event.type == "session.status_idle"
and event.stop_reason
and event.stop_reason.type == "end_turn"
):
break
TypeScript
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
apiKey: process.env.AGENTAPI_API_KEY!,
baseURL: process.env.AGENTAPI_BASE_URL,
});
const agentParams = {
name: "天气助手",
model: { id: process.env.AGENTAPI_MODEL_ID! },
system: "你是一名简洁、准确的天气助手。",
tools: [
{
type: "custom",
name: "get_weather",
description: "查询城市当前的天气。",
input_schema: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"]
}
}
]
} satisfies Parameters<typeof client.beta.agents.create>[0];
const agent = await client.beta.agents.create(agentParams, {
body: { ...agentParams, runtime: "native" },
});
const environment = await client.beta.environments.create({
name: "weather-env",
config: { type: "cloud", networking: { type: "unrestricted" } }
});
const session = await client.beta.sessions.create({
agent: { type: "agent", id: agent.id, version: agent.version },
environment_id: environment.id
});
function getWeather({ city }: Record<string, unknown>): string {
return `${city}:18°C,晴`;
}
const stream = await client.beta.sessions.events.stream(session.id);
await client.beta.sessions.events.send(session.id, {
events: [
{
type: "user.message",
content: [{ type: "text", text: "东京天气怎么样?" }]
}
]
});
for await (const event of stream) {
if (event.type === "agent.message") {
for (const block of event.content) {
if (block.type === "text") {
console.log(block.text);
}
}
} else if (event.type === "agent.custom_tool_use") {
const result = getWeather(event.input);
await client.beta.sessions.events.send(session.id, {
events: [
{
type: "user.custom_tool_result",
custom_tool_use_id: event.id,
content: [{ type: "text", text: result }]
}
]
});
} else if (event.type === "session.status_idle" && event.stop_reason?.type === "end_turn") {
break;
}
}
C#
using System.Linq;
using System.Text.Json;
using Anthropic;
using Anthropic.Core;
using Anthropic.Models.Beta.Agents;
using Anthropic.Models.Beta.Environments;
using Anthropic.Models.Beta.Sessions;
using Anthropic.Models.Beta.Sessions.Events;
var client = new AnthropicClient(new ClientOptions
{
ApiKey = System.Environment.GetEnvironmentVariable("AGENTAPI_API_KEY")
?? throw new InvalidOperationException("AGENTAPI_API_KEY is required"),
BaseUrl = System.Environment.GetEnvironmentVariable("AGENTAPI_BASE_URL")
?? throw new InvalidOperationException("AGENTAPI_BASE_URL is required"),
});
var agentParams = new AgentCreateParams
{
Name = "天气助手",
Model = new BetaManagedAgentsModelConfigParams { ID = System.Environment.GetEnvironmentVariable("AGENTAPI_MODEL_ID")! },
System = "你是一名简洁、准确的天气助手。",
Tools =
[
new BetaManagedAgentsCustomToolParams
{
Type = "custom",
Name = "get_weather",
Description = "查询城市当前的天气。",
InputSchema = new()
{
Properties = new Dictionary<string, JsonElement>
{
["city"] = JsonSerializer.SerializeToElement(new { type = "string" }),
},
Required = ["city"],
},
},
],
};
var agentBody = agentParams.RawBodyData.ToDictionary(entry => entry.Key, entry => entry.Value);
agentBody["runtime"] = JsonSerializer.SerializeToElement("native");
agentParams = AgentCreateParams.FromRawUnchecked(
agentParams.RawHeaderData, agentParams.RawQueryData, agentBody);
var agent = await client.Beta.Agents.Create(agentParams);
var environment = await client.Beta.Environments.Create(new()
{
Name = "weather-env",
Config = new BetaCloudConfigParams
{
Networking = new BetaUnrestrictedNetwork(),
},
});
var session = await client.Beta.Sessions.Create(new()
{
Agent = new BetaManagedAgentsAgentParams
{
Type = "agent",
ID = agent.ID,
Version = agent.Version,
},
EnvironmentID = environment.ID,
});
static string GetWeather(string city) => $"{city}:18°C,晴";
using var stream = await client.Beta.Sessions.Events.WithRawResponse.StreamStreaming(session.ID);
await client.Beta.Sessions.Events.Send(session.ID, new()
{
Events =
[
new BetaManagedAgentsUserMessageEventParams
{
Type = "user.message",
Content = [new BetaManagedAgentsTextBlock { Type = "text", Text = "东京天气怎么样?" }],
},
],
});
await foreach (var streamEvent in stream.Enumerate())
{
if (streamEvent.Value is BetaManagedAgentsAgentMessageEvent message)
{
Console.WriteLine(string.Concat(message.Content.Select(block => block.Text)));
}
else if (streamEvent.Value is BetaManagedAgentsAgentCustomToolUseEvent toolUse)
{
var result = GetWeather(toolUse.Input["city"].GetString()!);
await client.Beta.Sessions.Events.Send(session.ID, new()
{
Events =
[
new BetaManagedAgentsUserCustomToolResultEventParams
{
Type = "user.custom_tool_result",
CustomToolUseID = toolUse.ID,
Content =
[
new BetaManagedAgentsTextBlock
{
Type = "text",
Text = result,
},
],
},
],
});
}
else if (streamEvent.Value is BetaManagedAgentsSessionStatusIdleEvent idle
&& idle.StopReason?.Value is BetaManagedAgentsSessionEndTurn)
{
break;
}
}
Go
client := anthropic.NewClient(
option.WithoutEnvironmentDefaults(),
option.WithBaseURL(os.Getenv("AGENTAPI_BASE_URL")),
option.WithAPIKey(os.Getenv("AGENTAPI_API_KEY")),
)
ctx := context.Background()
agent, err := client.Beta.Agents.New(ctx, anthropic.BetaAgentNewParams{
Name: "天气助手",
Model: anthropic.BetaManagedAgentsModelConfigParams{
ID: os.Getenv("AGENTAPI_MODEL_ID"),
},
System: anthropic.String("你是一名简洁、准确的天气助手。"),
Tools: []anthropic.BetaAgentNewParamsToolUnion{{
OfCustom: &anthropic.BetaManagedAgentsCustomToolParams{
Type: anthropic.BetaManagedAgentsCustomToolParamsTypeCustom,
Name: "get_weather",
Description: "查询城市当前的天气。",
InputSchema: anthropic.BetaManagedAgentsCustomToolInputSchemaParam{
Properties: map[string]any{
"city": map[string]any{"type": "string"},
},
Required: []string{"city"},
},
},
}},
}, option.WithJSONSet("runtime", "native"))
if err != nil {
panic(err)
}
environment, err := client.Beta.Environments.New(ctx, anthropic.BetaEnvironmentNewParams{
Name: "weather-env",
Config: anthropic.BetaEnvironmentNewParamsConfigUnion{
OfCloud: &anthropic.BetaCloudConfigParams{
Networking: anthropic.BetaCloudConfigParamsNetworkingUnion{
OfUnrestricted: &anthropic.BetaUnrestrictedNetworkParam{},
},
},
},
})
if err != nil {
panic(err)
}
session, err := client.Beta.Sessions.New(ctx, anthropic.BetaSessionNewParams{
Agent: anthropic.BetaSessionNewParamsAgentUnion{
OfBetaManagedAgentsAgents: &anthropic.BetaManagedAgentsAgentParams{
Type: anthropic.BetaManagedAgentsAgentParamsTypeAgent,
ID: agent.ID,
Version: anthropic.Int(agent.Version),
},
},
EnvironmentID: environment.ID,
})
if err != nil {
panic(err)
}
getWeather := func(city string) string {
return fmt.Sprintf("%s:18°C,晴", city)
}
stream := client.Beta.Sessions.Events.StreamEvents(ctx, session.ID, anthropic.BetaSessionEventStreamParams{})
defer stream.Close()
_, err = client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
OfUserMessage: &anthropic.BetaManagedAgentsUserMessageEventParams{
Type: anthropic.BetaManagedAgentsUserMessageEventParamsTypeUserMessage,
Content: []anthropic.BetaManagedAgentsUserMessageEventParamsContentUnion{{
OfText: &anthropic.BetaManagedAgentsTextBlockParam{
Type: anthropic.BetaManagedAgentsTextBlockTypeText,
Text: "东京天气怎么样?",
},
}},
},
}},
})
if err != nil {
panic(err)
}
loop:
for stream.Next() {
event := stream.Current()
switch event.Type {
case "agent.message":
for _, block := range event.AsAgentMessage().Content {
if block.Type == "text" {
fmt.Println(block.Text)
}
}
case "agent.custom_tool_use":
toolUse := event.AsAgentCustomToolUse()
result := getWeather(toolUse.Input["city"].(string))
if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
OfUserCustomToolResult: &anthropic.BetaManagedAgentsUserCustomToolResultEventParams{
Type: anthropic.BetaManagedAgentsUserCustomToolResultEventParamsTypeUserCustomToolResult,
CustomToolUseID: toolUse.ID,
Content: []anthropic.BetaManagedAgentsUserCustomToolResultEventParamsContentUnion{{
OfText: &anthropic.BetaManagedAgentsTextBlockParam{
Type: anthropic.BetaManagedAgentsTextBlockTypeText,
Text: result,
},
}},
},
}},
}); err != nil {
panic(err)
}
case "session.status_idle":
idle := event.AsSessionStatusIdle()
if _, ok := idle.StopReason.AsAny().(anthropic.BetaManagedAgentsSessionEndTurn); ok {
break loop
}
}
}
if err := stream.Err(); err != nil {
panic(err)
}
Java
import java.util.Map;
import java.util.function.Function;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.beta.agents.AgentCreateParams;
import com.anthropic.models.beta.agents.BetaManagedAgentsCustomToolInputSchema;
import com.anthropic.models.beta.agents.BetaManagedAgentsCustomToolParams;
import com.anthropic.models.beta.agents.BetaManagedAgentsModelConfigParams;
import com.anthropic.models.beta.environments.BetaCloudConfigParams;
import com.anthropic.models.beta.environments.BetaUnrestrictedNetwork;
import com.anthropic.models.beta.environments.EnvironmentCreateParams;
import com.anthropic.models.beta.sessions.BetaManagedAgentsAgentParams;
import com.anthropic.models.beta.sessions.SessionCreateParams;
import com.anthropic.models.beta.sessions.events.BetaManagedAgentsStreamSessionEvents;
import com.anthropic.models.beta.sessions.events.BetaManagedAgentsUserCustomToolResultEventParams;
import com.anthropic.models.beta.sessions.events.BetaManagedAgentsUserMessageEventParams;
import com.anthropic.models.beta.sessions.events.EventSendParams;
var client = AnthropicOkHttpClient.builder()
.apiKey(System.getenv("AGENTAPI_API_KEY"))
.baseUrl(System.getenv("AGENTAPI_BASE_URL"))
.build();
var agent = client.beta().agents().create(AgentCreateParams.builder()
.putAdditionalBodyProperty("runtime", com.anthropic.core.JsonValue.from("native"))
.name("天气助手")
.model(BetaManagedAgentsModelConfigParams.builder().id(System.getenv("AGENTAPI_MODEL_ID")).build())
.system("你是一名简洁、准确的天气助手。")
.addTool(BetaManagedAgentsCustomToolParams.builder()
.type(BetaManagedAgentsCustomToolParams.Type.CUSTOM)
.name("get_weather")
.description("查询城市当前的天气。")
.inputSchema(BetaManagedAgentsCustomToolInputSchema.builder()
.properties(BetaManagedAgentsCustomToolInputSchema.Properties.builder()
.putAdditionalProperty("city", JsonValue.from(Map.of("type", "string")))
.build())
.addRequired("city")
.build())
.build())
.build());
var environment = client.beta().environments().create(EnvironmentCreateParams.builder()
.name("weather-env")
.config(BetaCloudConfigParams.builder()
.networking(BetaUnrestrictedNetwork.builder().build())
.build())
.build());
var session = client.beta().sessions().create(SessionCreateParams.builder()
.agent(BetaManagedAgentsAgentParams.builder()
.type(BetaManagedAgentsAgentParams.Type.AGENT)
.id(agent.id())
.version(agent.version())
.build())
.environmentId(environment.id())
.build());
Function<String, String> getWeather = city -> city + ":18°C,晴";
try (var stream = client.beta().sessions().events().streamStreaming(session.id())) {
client.beta().sessions().events().send(
session.id(),
EventSendParams.builder()
.addEvent(BetaManagedAgentsUserMessageEventParams.builder()
.type(BetaManagedAgentsUserMessageEventParams.Type.USER_MESSAGE)
.addTextContent("东京天气怎么样?")
.build())
.build());
for (var event : (Iterable<BetaManagedAgentsStreamSessionEvents>) stream.stream()::iterator) {
if (event.isAgentMessage()) {
for (var block : event.asAgentMessage().content()) {
block.text().ifPresent(textBlock -> IO.println(textBlock.text()));
}
} else if (event.isAgentCustomToolUse()) {
var toolUse = event.asAgentCustomToolUse();
var city = toolUse.input()._additionalProperties().get("city").asStringOrThrow();
var result = getWeather.apply(city);
client.beta().sessions().events().send(
session.id(),
EventSendParams.builder()
.addEvent(BetaManagedAgentsUserCustomToolResultEventParams.builder()
.type(BetaManagedAgentsUserCustomToolResultEventParams.Type.USER_CUSTOM_TOOL_RESULT)
.customToolUseId(toolUse.id())
.addTextContent(result)
.build())
.build());
} else if (event.isSessionStatusIdle()
&& event.asSessionStatusIdle().stopReason().isEndTurn()) {
break;
}
}
}
PHP
use Anthropic\Client;
use Anthropic\Beta\Agents\BetaManagedAgentsCustomToolInputSchema;
use Anthropic\Beta\Agents\BetaManagedAgentsCustomToolParams;
use Anthropic\Beta\Sessions\BetaManagedAgentsAgentParams;
$client = new Client(
apiKey: getenv('AGENTAPI_API_KEY') ?:
throw new RuntimeException('AGENTAPI_API_KEY is required'),
baseUrl: getenv('AGENTAPI_BASE_URL') ?:
throw new RuntimeException('AGENTAPI_BASE_URL is required'),
);
$agent = $client->beta->agents->create(
name: '天气助手',
model: ['id' => getenv('AGENTAPI_MODEL_ID')],
system: '你是一名简洁、准确的天气助手。',
tools: [
BetaManagedAgentsCustomToolParams::with(
type: 'custom',
name: 'get_weather',
description: '查询城市当前的天气。',
inputSchema: BetaManagedAgentsCustomToolInputSchema::with(
properties: ['city' => ['type' => 'string']],
required: ['city'],
),
),
],
requestOptions: ['extraBodyParams' => ['runtime' => 'native']],
);
$environment = $client->beta->environments->create(
name: 'weather-env',
config: ['type' => 'cloud', 'networking' => ['type' => 'unrestricted']],
);
$session = $client->beta->sessions->create(
agent: BetaManagedAgentsAgentParams::with(
type: 'agent',
id: $agent->id,
version: $agent->version,
),
environmentID: $environment->id,
);
function getWeather(string $city): string
{
return "{$city}:18°C,晴";
}
$stream = $client->beta->sessions->events->streamStream($session->id);
$client->beta->sessions->events->send(
$session->id,
events: [
[
'type' => 'user.message',
'content' => [['type' => 'text', 'text' => "东京天气怎么样?"]],
],
],
);
foreach ($stream as $event) {
if ($event->type === 'agent.message') {
foreach ($event->content as $block) {
if ($block->type === 'text') {
echo $block->text . "\n";
}
}
} elseif ($event->type === 'agent.custom_tool_use') {
$result = getWeather($event->input['city']);
$client->beta->sessions->events->send(
$session->id,
events: [
[
'type' => 'user.custom_tool_result',
'custom_tool_use_id' => $event->id,
'content' => [['type' => 'text', 'text' => $result]],
],
],
);
} elseif ($event->type === 'session.status_idle' && $event->stopReason?->type === 'end_turn') {
break;
}
}
$stream->close();
Ruby
require "anthropic"
client = Anthropic::Client.new(
api_key: ENV.fetch("AGENTAPI_API_KEY"),
base_url: ENV.fetch("AGENTAPI_BASE_URL")
)
agent = client.beta.agents.create(
name: "天气助手",
model: {id: ENV.fetch("AGENTAPI_MODEL_ID")},
system_: "你是一名简洁、准确的天气助手。",
tools: [
{
type: "custom",
name: "get_weather",
description: "查询城市当前的天气。",
input_schema: {
type: "object",
properties: {city: {type: "string"}},
required: ["city"]
}
}
],
request_options: {extra_body: {runtime: "native"}}
)
environment = client.beta.environments.create(
name: "weather-env",
config: {type: "cloud", networking: {type: "unrestricted"}}
)
session = client.beta.sessions.create(
agent: {type: "agent", id: agent.id, version: agent.version},
environment_id: environment.id
)
def get_weather(city)
"#{city}:18°C,晴"
end
stream = client.beta.sessions.events.stream_events(session.id)
client.beta.sessions.events.send_(
session.id,
events: [{type: "user.message", content: [{type: "text", text: "东京天气怎么样?"}]}]
)
stream.each do |event|
case event.type
when :"agent.message"
event.content.each do |block|
puts block.text if block.type == :text
end
when :"agent.custom_tool_use"
result = get_weather(event.input[:city])
client.beta.sessions.events.send_(
session.id,
events: [
{
type: "user.custom_tool_result",
custom_tool_use_id: event.id,
content: [{type: "text", text: result}]
}
]
)
when :"session.status_idle"
break if event.stop_reason&.type == :end_turn
end
end
智能体和云端环境创建后可在多个会话中复用。自定义工具函数仍在应用进程中运行。迁移后,应用通过读取 agent.custom_tool_use 事件并返回工具结果参与执行,模型调用循环由 AgentAPI 管理。
业务逻辑保留范围
迁移到云端智能体循环后,以下与应用业务相关的逻辑仍需要保留:
| 原有逻辑 | AgentAPI 中的处理方式 |
|---|---|
| 消息输入和输出转换 | 在发送 user.message 前或接收 agent.message 后,由应用继续完成格式转换和业务处理。 |
| 自定义工具前后的校验或审计 | 在处理 agent.custom_tool_use 和发送 user.custom_tool_result 时执行。对于 native 内置工具,可使用 permission_policy: always_ask 在执行前请求确认。 |
| LangChain 检查点中的业务状态 | 将业务状态保留在应用自己的存储中;会话消息和执行事件由 AgentAPI 保存。 |
| 循环次数或业务超时限制 | 由应用根据事件和时间进行控制,必要时发送 user.interrupt 中断当前任务。 |
迁移清单
- 创建一个云端环境,配置任务所需的软件包;使用
limited网络时,放行 MCP 服务和软件源的实际主机。 - 将系统提示、模型、运行时和工具配置迁移到智能体定义中,并保存返回的版本号。
- 使用明确的智能体版本创建会话。传入智能体 ID 字符串时,只会在创建会话时解析并固定最新版本;已有会话不会随智能体更新切换。
- 使用
sessions.create和sessions.events.stream替换原有的智能体循环。客户端必须处理历史事件回放、requires_action、interrupted、retries_exhausted和terminated。 - 智能体需要读取本地文件时,通过 Files API 上传文件,并将其作为
resources挂载到会话中。 - 将自定义工具处理程序接入事件循环,用于响应
agent.custom_tool_use事件并返回结果;claudecode、codex还需要服务端已配置自定义工具桥接;本页自定义工具示例选择native,直接通过事件回传结果。 - 用同一批任务对比旧系统与 AgentAPI 的事件、产物和失败恢复;业务状态仍保留在应用自己的存储中。
- 在将生产流量指向新流程之前,先用测试会话进行验证,并为调度任务设计业务幂等键。
更新智能体使用的模型
需要切换模型或模型版本时,更新智能体定义中的 model 即可。更新会生成新的智能体版本;新建会话时引用该版本,现有会话仍然使用创建时固定的智能体版本。
下面将目标模型 ID 保存在 AGENTAPI_MODEL_ID 中。请先在七牛 MaaS 模型管理页确认账户和区域可用的模型,再设置该变量。
cURL
curl --request POST "$AGENTAPI_BASE_URL/v1/agents/$AGENT_ID" \
-H "x-api-key: $AGENTAPI_API_KEY" \
-H "content-type: application/json" \
--data "$(jq -n --argjson version "$AGENT_VERSION" --arg model "$AGENTAPI_MODEL_ID" \
'{version: $version, model: {id: $model}}')"
Python
client.beta.agents.update(
agent.id,
version=agent.version,
model={"id": os.environ["AGENTAPI_MODEL_ID"]},
)
TypeScript
await client.beta.agents.update(agent.id, {
version: agent.version,
model: { id: process.env.AGENTAPI_MODEL_ID! }
});
C#
await client.Beta.Agents.Update(agent.ID, new()
{
Version = agent.Version,
Model = new BetaManagedAgentsModelConfigParams
{
ID = System.Environment.GetEnvironmentVariable("AGENTAPI_MODEL_ID")
?? throw new InvalidOperationException("AGENTAPI_MODEL_ID is required"),
},
});
Go
_, err = client.Beta.Agents.Update(ctx, agent.ID, anthropic.BetaAgentUpdateParams{
Version: agent.Version,
Model: anthropic.BetaManagedAgentsModelConfigParams{
ID: os.Getenv("AGENTAPI_MODEL_ID"),
},
})
if err != nil {
panic(err)
}
Java
client.beta().agents().update(
agent.id(),
AgentUpdateParams.builder()
.version(agent.version())
.model(BetaManagedAgentsModelConfigParams.builder()
.id(System.getenv("AGENTAPI_MODEL_ID"))
.build())
.build()
);
PHP
$client->beta->agents->update(
$agent->id,
version: $agent->version,
model: ['id' => getenv('AGENTAPI_MODEL_ID')],
);
Ruby
client.beta.agents.update(
agent.id,
version: agent.version,
model: {id: ENV.fetch("AGENTAPI_MODEL_ID")}
)
更新模型时,还需要注意以下差异:
- 模型行为: 不同模型的工具选择、输出风格和任务完成效果可能不同,切换前应使用测试会话验证关键任务。
- 请求参数: 模型请求由 AgentAPI 在智能体循环中发起。应用无需在每一轮请求中重复设置
max_tokens或推理参数。 - 会话输入: AgentAPI 使用事件驱动的会话模型,不支持通过预填充助手消息来约束下一段输出。
- 工具参数:
agent.custom_tool_use事件提供结构化的工具输入,应用无需自行解析原始 JSON 字符串。