Enables web search and contextual conversation using the Qwen web search agent's agent_id and agent_version.
Request syntax
POST /web-search-agent/chat/completions HTTP/1.1Request parameters
Note: If you provide dynamic parameters in the request, they override the corresponding status values in the application configuration.
Parameter | Type | Required | Description |
stream | bool | Yes | Must be set to |
input | object | Yes | Input fields. |
input.request_id | str | No | A user-defined request ID. |
input.messages | array[object] | Yes | A list of conversation messages. |
input.messages.[].role | str | Yes | The role of the message sender. Valid values: |
input.messages.[].content | str | Yes | The content of the message. |
parameters | object | Yes | Configuration parameters. |
parameters.agent_options | object | Yes | Agent-specific parameters. |
parameters.agent_options.agent_id | string | Yes | The application ID. |
parameters.agent_options.agent_version | string | Yes | The application version. |
parameters.agent_options.session_knowledge | string | No | Specifies the session-level knowledge to use. |
parameters.agent_options.system_prompt | string | No | Specifies the system prompt. |
parameters.agent_options.agent_policy | string | No | The execution policy.
|
parameters.agent_options.forced_search | bool | No | Specifies whether to force a search.
|
parameters.agent_options.enable_citation | bool | No | Specifies whether to include citations in the response. |
parameters.agent_options.enable_text_image_mixed | bool | No | Specifies whether to enable text-image mixed generation. |
parameters.agent_options.enable_lemma | bool | No | Specifies whether to include encyclopedia entries in the response. |
parameters.agent_options.related_video | bool | No | Specifies whether to include related videos in the response. |
parameters.agent_options.enable_rec_question | bool | No | Specifies whether to include related questions in the response. |
parameters.agent_options.show_step_info | bool | No | Specifies whether to include tool call status information in the response. The default value is |
parameters.agent_options.location | object | No | Location information for the request. |
parameters.agent_options.location.address | string | No | The specific address. |
parameters.agent_options.location.province | string | No | The province. |
parameters.agent_options.location.city | string | No | The city. |
parameters.agent_options.location.district | string | No | The district or county. |
parameters.agent_options.location.longitude | string | No | The longitude, accurate to 6 decimal places. |
parameters.agent_options.location.latitude | string | No | The latitude, accurate to 6 decimal places. |
Response parameters
Parameter | Type | Required | Description |
request_id | str | Yes | The request ID from the DashScope platform. |
code | str | Yes | The status code. |
message | str | Yes | The status message. |
output | object | Yes | The output object. |
output.request_id | str | No | A custom request ID defined by your application. |
output.choices | array[object] | Yes | A list of completion choices generated by the model. |
output.choices.[].finish_reason | str | Yes | The finish reason. A value of |
output.choices.[].message | object | Yes | The message object. |
output.choices.[].message.role | str | Yes | The role of the message author. Valid values: |
output.choices.[].message.content | str | array[object] | Yes | The text generated by the model, or the output from a tool. |
output.choices.[].message.reasoning_content | str | No | The model's thought process or reasoning steps. |
output.choices.[].message.tool_calls | array[object] | No | The tool calls requested by the model. |
output.choices.[].message.tool_calls[0].arguments | dict[str,object] | No | The arguments to pass to the tool function. |
output.choices.[].message.tool_calls[0].name | str | No | The name of the tool to call. |
output.choices.[].message.additional_kwargs.extra_json | Any | No | Additional structured data associated with a tool call. |
output.choices.[].message.extra | dict | No | Contains details about the current execution step. |
output.choices.[].message.extra.group | str | No | The execution phase. |
output.choices.[].message.extra.step_change | str | No | The step change event. |
output.choices.[].message.extra.step | str | No | The current step. |
output.choices.[].message.response_metadata | dict | No | Details about the model invocation for the request. |
output.usage | object | No | Usage statistics. |
output.usage.input_tokens | int | No | The number of input tokens. |
output.usage.output_tokens | int | No | The number of output tokens. |
output.usage.total_tokens | int | No | The total number of tokens. |
Execution stage enumeration
Execution stage ( | Description | Description |
planning | planning | Associated with the |
generating | generating | Associated with the |
Current step ( | Description |
planning | planning |
generating | generating |
tool_calling | tool calling |
tool_calling_{tool_name} | A tool call is in progress. The specific tool is identified by its name. |
Due to model limitations, the
step_changevalue may be absent. Use the persistentstepflag whenever possible.For an empty packet, the
step,step_change, andgroupfields may be absent.The
planandgenerationprocesses each consist of a*_startevent and a*_endevent.A
tool_callconsists of three events:tool_call_start,tool_calling, andtool_return.tool_call_startindicates the beginning of a tool call. The system emits thetool_callingevent after gathering the complete tool call parameters; this event contains the parameters.tool_returncontains the structured result from the tool.
Value of | Step change event ( | Event name | Description |
| plan_start | planning start | The |
| (empty) | planning | Indicates the system is reasoning or performing tool calls. |
| plan_end | planning end | When the |
| generation_start | generation start | Similar to the |
| (empty) | generating | Indicates the system is generating output. |
| generation_end | generation end | Similar to the |
| tool_call_start | tool call start | Indicates the start of a tool call. |
| tool_calling | tool calling | Contains the specific parameters and name for the tool call. The |
| tool_return | tool return | The response contains the tool return information, and the |
Step value and tool name mapping
When a tool is called, the ["extra"]["step"] field contains a value, such as tool_calling_xx, that identifies the tool used. The following tables map each step value to its corresponding tool name.
Text QA
Step value | Tool name |
tool_calling_search | web search |
tool_calling_visit | web page reading |
tool_calling_video_search | video search |
tool_calling_lemma_search | Baidu entry |
tool_calling_query_suggesting | follow-up query |
Multimodal QA
In addition to the text QA tools, this mode adds text-to-image search and reverse image search.
Step value | Tool name |
tool_calling_image_search | text-to-image search |
tool_calling_image_to_image_search | reverse image search |
Local service POI tools
Step value | Tool name |
tool_calling_poi_search | keyword search |
tool_calling_around_search | nearby search |
Tool call status
Parameters
The status of a tool call is in the ["extra"]["step_change"] field of the message. Detailed information about the call is in the ["extra"]["step_info"] field. The following table maps tool call statuses to their corresponding status information.
Tool call status | Description | Status information ( |
tool_call_start | Start tool call | Starting to call the [tool_name] tool. |
tool_calling | Calling tool | Calling the [tool_name] tool. |
tool_return | Tool call completed | Call to the [tool_name] tool completed: [result_details]. |
For example, a call to the web search tool progresses through the following states:
"step_change": "tool_call_start", "step": "tool_calling_search","step_info": "Starting to call the web search tool"
"step_change": "tool_calling", "step": "tool_calling_search","step_info": "Calling the web search tool"
"step_change": "tool_return", "step": "tool_calling_search", "step_info": "Call to the web search tool completed: retrieved 10 web pages"Rich media message protocol
When rich media is enabled, the model interleaves text with images by embedding them in the message body with the HTML <img> tag, as shown in the example below:
<img src=\"xxx\" data-type=\"image\" data-url=\"xxx\" data-title=\"Hangzhou after the typhoon: Blue sky and white clouds as refreshing as a comic book️\" alt=\"Hangzhou clear day blue sky\" width=\"1080\" height=\"1410\">The attributes in this tag are defined as follows:
data-type: Specifies the data type. A value ofimageindicates image data.src: The URL of the image.data-url: The URL of the image's source content page.data-title: The image title.width/height: The image's width and height.
For messages containing an image, the additional_kwargs field contains the following two fields:
data_type: For a rich media message containing an image, this value is set toimage.data_json: A JSON array of all images related to the content. Only one of these images is displayed in the message body.In
data_json, each image object contains the following fields:idx: The image ID.url: The source URL of the image's content page.title: The image title.published: The publication date of the source content.image_info["url"]: The URL of the image file.image_info["width"]: The image width.image_info["height"]: The image height.
{"idx": 15, "query": "Hangzhou clear day blue sky", "url": "xxx", "title": "xx", "published": "2024-11-04 03:04:04", "image_info": {"url": "xxx", "width": 1080, "height": 1410}}The following example shows a rich media message delivered in three message packets:
data: {
"status_code": 200,
"code": "",
"message": "",
"output": {
"choices": [
{
"finish_reason": "",
"message": {
"content": "Sightseeing.\n\n",
"additional_kwargs": {},
"response_metadata": {},
"type": "ai",
"name": null,
"id": "run--019e63e4-e728-7421-b3e5-57b408d6d4b0",
"example": false,
"tool_calls": [],
"invalid_tool_calls": [],
"usage_metadata": null,
"tool_call_chunks": [],
"reasoning_content": "",
"role": "assistant",
"extra": {
"group": "generating",
"step_change": "",
"step": "generating"
}
}
}
]
},
"usage": null,
"request_id": "chenwen-test-web-search-01"
}
data: {
"status_code": 200,
"code": "",
"message": "",
"output": {
"choices": [
{
"finish_reason": "",
"message": {
"content": "<img src=\"http://miaobi-lite.bj.bcebos.com/miaobi/5mao/b%275Y%2Bw6aOO6L%2BH5ZCO55qE5Zu%2B54mHXzE3MzA2NTY4MTMuMTQ0MDA1NQ%3D%3D%27/0.png\" data-type=\"image\" data-url=\"http://mbd.baidu.com/newspage/data/dtlandingsuper?nid=dt_4158884509151638702\" data-title=\"Hangzhou after the typhoon: Blue sky and white clouds as refreshing as a comic book\" alt=\"Hangzhou clear day blue sky\" width=\"1080\" height=\"1410\">\n\n\n",
"additional_kwargs": {
"data_type": "image",
"data_json": [
{
"idx": 15,
"query": "Hangzhou clear day blue sky",
"url": "http://mbd.baidu.com/newspage/data/dtlandingsuper?nid=dt_4158884509151638702",
"title": "Hangzhou after the typhoon: Blue sky and white clouds as refreshing as a comic book",
"published": "2024-11-04 03:04:04",
"image_info": {
"url": "http://miaobi-lite.bj.bcebos.com/miaobi/5mao/b%275Y%2Bw6aOO6L%2BH5ZCO55qE5Zu%2B54mHXzE3MzA2NTY4MTMuMTQ0MDA1NQ%3D%3D%27/0.png",
"width": 1080,
"height": 1410
}
},
{
"idx": 16,
"query": "Hangzhou clear day blue sky",
"url": "http://m.dianping.com/ugcdetail/175828244?sceneType=0&bizType=29&msource=baiduappugc",
"title": "The sky in Hangzhou is so beautiful!",
"published": "2023-07-26 00:00:00",
"image_info": {
"url": "http://qcloud.dpfile.com/pc/VWohABccM5j2sRMTT4YdH7qapT46U8bLlM3Wp-xKeMm3OVoihIkmurvV3052Y9Xt.jpg",
"width": 2048,
"height": 2731
}
},
{
"idx": 17,
"query": "Hangzhou clear day blue sky",
"url": "http://m.dianping.com/ugcdetail/175964222?sceneType=0&bizType=29&msource=baiduappugc",
"title": "The blue sky and white clouds in Hangzhou are beautiful, but you always get a tan",
"published": "2023-07-27 00:00:00",
"image_info": {
"url": "http://qcloud.dpfile.com/pc/6ScKLghe1ZcE15OpzbomPigY1VkO6o_UTx9z6UB_BoNtf5OGAsnbF-AFIAJjozRl.jpg",
"width": 2048,
"height": 2731
}
}
]
},
"response_metadata": {},
"type": "ai",
"name": null,
"id": "run--019e63e4-e728-7421-b3e5-57b408d6d4b0",
"example": false,
"tool_calls": [],
"invalid_tool_calls": [],
"usage_metadata": null,
"tool_call_chunks": [],
"reasoning_content": "",
"role": "assistant",
"extra": {
"group": "generating",
"step_change": "",
"step": "generating"
}
}
}
]
},
"usage": null,
"request_id": "chenwen-test-web-search-01"
}
data: {
"status_code": 200,
"code": "",
"message": "",
"output": {
"choices": [
{
"finish_reason": "",
"message": {
"content": "**Summary**:",
"additional_kwargs": {},
"response_metadata": {},
"type": "ai",
"name": null,
"id": "run--019e63e4-e728-7421-b3e5-57b408d6d4b0",
"example": false,
"tool_calls": [],
"invalid_tool_calls": [],
"usage_metadata": null,
"tool_call_chunks": [],
"reasoning_content": "",
"role": "assistant",
"extra": {
"group": "generating",
"step_change": "",
"step": "generating"
}
}
}
]
},
"usage": null,
"request_id": "chenwen-test-web-search-01"
}Render local life POI cards
When local life is enabled, the model returns location-based Point of Interest (POI) search results. If rich text and image display is also enabled, the POI search results include images. Images in the message body use the standard HTML image format, as shown in the following example:
<img src=\"https://aos-comment.amap.com/B0FFGD1PB5/comment/content_media_external_file_9739_ss__1749454867961_64014666.jpg\" data-type=\"poi\" data-url=\"https://www.amap.com/detail/B0FFGD1PB5\" data-title=\"Wu山景区吴山广场-吴山文化公园\" alt=\"Wu山景区吴山广场-吴山文化公园\" width=\"400\" height=\"auto\"/>\nThe attributes in this tag are defined as follows:
data-type: The data type. A value ofpoiindicates a map card.src: The URL of the first image for the card.data-url: The Amap URL that corresponds to the card.data-title: The card title.width/height: The width and height of the image.
In model messages that contain images, the additional_kwargs field includes the following fields:
data_type: For map card images, the value ispoi.data_json: A JSON object of all related map cards. Although the message body displays only one card, this field contains all relevant cards.
In data_json, each map card uses the following fields:
Field | Type | Description |
name | string | The place name. |
id | string | The unique identifier for the place. |
distance | string | The distance from the center point, in meters. This field is returned only for nearby searches. |
location | string | The longitude and latitude of the POI. |
type | string | The POI category. |
typecode | string | The category code of the POI. |
pname | string | The province where the POI is located. |
cityname | string | The city where the POI is located. |
adname | string | The district where the POI is located. |
address | string | The detailed address of the POI. |
photos | array | Contains information about the POI images. |
photos[].title | string | A description of the POI image. |
photos[].url | string | The download URL for the POI image. |
business | object | If available, contains the business information for the POI. |
Custom fields in business
Field | Type | Description |
business_area | string | The business area where the POI is located. |
opentime_today | string | Today's business hours for the POI, such as 08:30-17:30, 08:30-09:00, 12:00-13:30, or 09:00-13:00. |
opentime_week | string | A text description of the POI's weekly business hours. For example: "Monday to Friday: 08:30-17:30 (extended service hours: 08:30-09:00; 12:00-13:30); Saturday extended service hours: 09:00-13:00 (excluding legal holidays)". |
tel | string | The contact number for the POI. |
tag | string | Highlights of the POI. This field is currently returned only for dining POIs. |
rating | string | The rating of the POI. This field is currently returned only for POIs in the dining, hotel, attraction, and cinema categories. |
cost | string | The average spend at the POI. This field is currently returned only for POIs in the dining, hotel, attraction, and cinema categories. |
parking_type | string | The parking type (underground, ground-level, or roadside). This field is currently returned only for parking lot POIs. |
The following example shows the response when local life and rich text and image display are enabled.
"additional_kwargs": {
"data_type": "poi",
"data_json": [
{
"address": "Siyi Pavilion, Wushan Scenic Area (150m walk from Exit D of Wushan Square Metro Station)",
"distance": "352",
"business": {
"opentime_today": "Open 24 hours",
"keytag": "City Park",
"rating": "3.8",
"business_area": "Wushan",
"tel": "0571-8703****",
"rectag": "City Park",
"opentime_week": "Monday to Sunday 00:00-24:00"
},
"pcode": "330000",
"adcode": "330102",
"pname": "Zhejiang Province",
"cityname": "Hangzhou",
"type": "Scenic Spot; Park and Square; Park",
"photos": [
{
"title": "",
"url": "https://aos-comment.amap.com/B0FFGD1PB5/comment/content_media_external_file_9739_ss__1749454867961_64014666.jpg"
},
{
"title": "",
"url": "https://aos-comment.amap.com/B0FFGD1PB5/comment/content_media_external_images_media_1000008441_ss__1751303656358_97699056.jpg"
},
{
"title": "",
"url": "https://aos-comment.amap.com/B0FFGD1PB5/comment/content_media_external_file_9746_ss__1749454867953_49866303.jpg"
}
],
"typecode": "110101",
"adname": "Shangcheng District",
"citycode": "0571",
"navi": {
"navi_poiid": "H51F022002_375744",
"entr_location": "120.163399,30.239046",
"gridcode": "4520218310"
},
"name": "Wushan Scenic Area Wushan Square - Wushan Cultural Park",
"indoor": {
"indoor_map": "0"
},
"location": "120.163789,30.238795",
"id": "B0FFGD1PB5",
"distance_text": "352m from Wushan Xiaoputuo Wushan Scenic Area, Ziyang Subdistrict",
"from": "Wushan Xiaoputuo Wushan Scenic Area, Ziyang Subdistrict, Shangcheng District, Hangzhou, Zhejiang Province"
}
]
}Multimodal image Q&A
The Web Search Agent multimodal API lets you include images in your conversations. Provide an image URL with your question, and the Agent will interpret the image, call the necessary tools, and generate a response.
Request parameters
Parameter | Type | Required | Description |
input | object | Yes | The request's input object. |
input.messages | array | Yes | The list of messages in the conversation. |
input.messages[].role | string | Yes | The message sender's role. The value must be |
input.messages[].content | array | Yes | An array of content parts, which can be text or images. |
input.messages[].content[].type | string | No | The type of the content part. For images, set this to 1. URL format (Recommended): https://example.com/image.jpg 2. Base64 format: |
input.messages[].content[].image_url | object | Conditional | An object containing the image URL. Required if |
Examples
Sample requests
Text request
{
"input": {
"messages": [
{
"role": "user",
"content": "What is the current date?"
}
]
},
"stream": true,
"parameters": {
"agent_options": {
"agent_id": "aid-xxx",
"agent_version": "beta"
}
}
}Multimodal request
{
"input": {
"messages": [
{
"role": "user",
"content": [
{
"image_url": {
"url": "http://other-general-huabei2.oss-cn-beijing.aliyuncs.com/upload/36e553b350e98ba81f6b33b08833a784.png"
},
"type": "image_url"
},
{
"text": "What animal is this, and in which regions does it typically live?",
"type": "text"
}
]
}
]
},
"stream": true,
"parameters": {
"agent_options": {
"agent_version": "beta",
"agent_id": "aid-xxx"
}
}
}Sample response
data: {
"status_code": 200,
"code": "",
"message": "",
"output": {
"choices": [
{
"finish_reason": "",
"message": {
"content": "xxx",
"additional_kwargs": {},
"response_metadata": {
"headers": {
"vary": "Origin",
"x-request-id": "ca7a41ad-3994-9dcf-adf6-f7aa56bec62b",
"content-type": "text/event-stream;charset=UTF-8",
"x-dashscope-call-gateway": "true",
"req-cost-time": "508",
"req-arrive-time": "1773800485527",
"resp-start-time": "1773800486035",
"x-envoy-upstream-service-time": "506",
"date": "Wed, 18 Mar 2026 02:21:25 GMT",
"server": "istio-envoy",
"transfer-encoding": "chunked"
}
},
"type": "ai",
"name": null,
"id": "run--019cfebf-7194-78b3-ad12-c3df6b6423b1",
"example": false,
"tool_calls": [],
"invalid_tool_calls": [],
"usage_metadata": null,
"tool_call_chunks": [],
"reasoning_content": "",
"role": "assistant",
"extra": {
"group": "generating",
"step_change": "generation_start",
"step": "generating"
}
}
}
]
},
"usage": null,
"request_id": "xxxx-xxxx"
}Code sample
# coding=utf-8
import os
import json
import requests
chat_completions_url = 'https://dashscope.aliyuncs.com/api/v2/apps/web-search-agent/chat/completions'
headers = {
'Authorization': f'Bearer {os.getenv("DASHSCOPE_API_KEY", "")}', # Configure the API key.
'Content-Type': 'application/json'
}
if __name__ == "__main__":
params = {
"input": {
"messages": [{"role": "user", "content": "What are the mainstream multimodal models in China? Please analyze their performance and effectiveness."}] # Set the request message.
},
"parameters": {
"agent_options": { # Set agent options.
"agent_id": "${agent_id}", # The application ID, found on the Application Management page (e.g., aid-8fd***e00).
"agent_version": "${agent_version}" # The application version. Use 'beta' for the beta version or 'release' for the release version.
}
},
"stream": True
}
response = requests.post(chat_completions_url, headers=headers, json=params, stream=True)
resultlist = []
stage = ''
action = ''
content = ''
reasoning_content = ''
for chunk in response.iter_lines():
if chunk:
chunk_str = chunk.decode('utf-8').strip()
if chunk_str.startswith('data:'):
json_str = chunk_str[len('data:'):].strip()
try:
obj = json.loads(json_str)
# Check for a service error.
if obj.get('code') != '200':
print("Service error:", obj)
# Extract the message body.
msg = obj.get('output', {}).get('choices', [{}])[0].get('message', {})
extra_flags = msg.get('extra', {}) # Get the 'extra' field, which contains model state flags.
if stage != extra_flags.get('group', ''): # Get the current model stage.
print(f"agent stage: {extra_flags.get('group', '')}")
stage = extra_flags.get('group', '')
if action != extra_flags.get('step', '') and extra_flags.get('step', ''): # Get the current model step.
print(f"agent action: {extra_flags.get('step', '')}")
action = extra_flags.get('step', '')
role = msg.get('role', '') # Get the role, e.g., 'assistant' or 'tool'.
content = msg.get('content') # Get the generated content.
toolcalls = msg.get('tool_calls', []) # Get the tool calls.
if toolcalls:
print(f'{toolcalls}')
if role == "tool":
print("\\n" + content + "\\n", end='') # Wrap the tool output in newlines for readability.
else:
print(content, end='') # Streaming output.
# Save the results as needed.
resultlist.append(obj)
except Exception as e:
print("Parse error:", e)import java.io.*;
import java.net.*;
import java.util.*;
import com.alibaba.fastjson.*;
import java.nio.charset.StandardCharsets;
public class WebSearchStreamDemo {
// Configure the API key.
public final static String CHAT_COMPLETIONS_URL = "https://dashscope.aliyuncs.com/api/v2/apps/web-search-agent/chat/completions";
public final static String API_KEY = System.getenv("DASHSCOPE_API_KEY");
public static void main(String[] args) throws Exception {
// Build the parameters.
Map<String, Object> params = new HashMap<>();
// input.messages
List<Map<String, Object>> messages = new ArrayList<>();
Map<String, Object> msgObj = new HashMap<>();
msgObj.put("role", "user");
msgObj.put("content", "${prompt}");
messages.add(msgObj);
// input
Map<String, Object> input = new HashMap<>();
input.put("messages", messages);
// parameters.agent_options
Map<String, Object> agentOptions = new HashMap<>();
agentOptions.put("agent_id", "${agent_id}");// The application ID, found on the Application Management page (e.g., aid-8fd***e00).
agentOptions.put("agent_version", "${agent_version}"); // The application version. Use 'beta' for the beta version or 'release' for the release version.
// parameters
Map<String, Object> parameters = new HashMap<>();
parameters.put("agent_options", agentOptions);
params.put("input", input);
params.put("parameters", parameters);
params.put("stream", true);
String body = JSON.toJSONString(params);
// Create the HTTP request.
URL apiUrl = new URL(CHAT_COMPLETIONS_URL);
HttpURLConnection conn = (HttpURLConnection) apiUrl.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Authorization", "Bearer " + API_KEY);
conn.setRequestProperty("Content-Type", "application/json");
// Send the request body.
try (OutputStream os = conn.getOutputStream()) {
os.write(body.getBytes(StandardCharsets.UTF_8));
}
// Handle the streaming response.
InputStream inputStream = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
String line;
String stage = "";
String action = "";
List<JSONObject> resultList = new ArrayList<>();
while ((line = reader.readLine()) != null) {
if (!line.trim().isEmpty()) {
String chunkStr = line.trim();
if (chunkStr.startsWith("data:")) {
String jsonStr = chunkStr.substring(5).trim();
try {
JSONObject obj = JSON.parseObject(jsonStr);
// Check for a service error.
if (!"200".equals(obj.getString("code"))) {
System.out.print("Service error: " + obj);
}
// Extract the 'message' object from the response.
JSONObject msg = null;
if (obj.containsKey("output")) {
JSONObject output = obj.getJSONObject("output");
if (output != null && output.containsKey("choices")) {
JSONArray choices = output.getJSONArray("choices");
if (choices != null && !choices.isEmpty()) {
JSONObject firstChoice = choices.getJSONObject(0);
if (firstChoice.containsKey("message")) {
msg = firstChoice.getJSONObject("message");
}
}
}
}
if (msg == null) {
continue;
}
// Get the 'extra' field, which contains model state flags.
JSONObject extraFlags = msg.containsKey("extra") && msg.get("extra") != null
? msg.getJSONObject("extra") : new JSONObject();
// Get the agent stage.
String stageNew = extraFlags.containsKey("group") && extraFlags.get("group") != null
? extraFlags.getString("group") : "";
if (!stage.equals(stageNew)) {
System.out.println("agent stage: " + stageNew);
}
stage = stageNew;
// Get the agent action.
String actionNew = extraFlags.containsKey("step") && extraFlags.get("step") != null
? extraFlags.getString("step") : "";
if (!action.equals(actionNew) && !actionNew.isEmpty()) {
System.out.println("agent action: " + actionNew);
}
action = actionNew;
String role = msg.containsKey("role") && msg.get("role") != null
? msg.getString("role") : "";
Object contentObj = msg.get("content");
String content = null;
boolean isContentString = false;
// Check if the content is a string.
if (contentObj instanceof String) {
content = contentObj.toString();
isContentString = true;
}
// If 'content' is empty, use 'reasoning_content' as a fallback.
if (isContentString && content.isEmpty()) {
Object reasoningContentObj = msg.get("reasoning_content");
if (reasoningContentObj instanceof String) {
content = reasoningContentObj.toString();
}
}
// Process tool calls.
if (msg.containsKey("tool_calls") && msg.get("tool_calls") instanceof List) {
JSONArray toolCalls = msg.getJSONArray("tool_calls");
if (!toolCalls.isEmpty()) {
System.out.println(toolCalls);
}
}
if ("tool".equals(role)) {
System.out.print("\\n" + content + "\\n");
} else {
System.out.print(content);
}
// Save the results as needed.
resultList.add(obj);
} catch (Exception e) {
System.out.println("Parse error: " + e);
}
}
}
}
reader.close();
}
}