-
Notifications
You must be signed in to change notification settings - Fork 118
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #203 from ide-rea/master
新增Console AgentBuilder对话能力
- Loading branch information
Showing
16 changed files
with
796 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
# AgentBuilder组件 | ||
|
||
## 简介 | ||
|
||
AgentBuilder组件支持调用在[百度智能云千帆AppBuilder](https://cloud.baidu.com/product/AppBuilder)平台上通过AgentBuilder构建并发布的智能体应用。 | ||
|
||
### 功能介绍 | ||
|
||
具体包括创建会话、上传文档、运行对话等 | ||
|
||
### 特色优势 | ||
|
||
与云端Console AgentBuilder能力打通,实现低代码会话 | ||
|
||
### 应用场景 | ||
|
||
快速、高效集成云端已发布智能体应用能力 | ||
|
||
## 基本用法 | ||
|
||
以下是使用SDK进行问答的示例代码 | ||
|
||
```python | ||
import appbuilder | ||
import os | ||
|
||
# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 | ||
# 设置环境变量 | ||
os.environ["APPBUILDER_TOKEN"] = '...' | ||
app_id = '...' # 已发布AgentBuilder应用ID,可在console端查看 | ||
# 初始化智能体 | ||
agent = appbuilder.AgentBuilder(app_id) | ||
# 创建会话 | ||
conversation_id = agent.create_conversation() | ||
# 运行对话 | ||
out = agent.run("北京天气怎么样", conversation_id) | ||
# 打印会话结果 | ||
print(out.content.answer) | ||
``` | ||
|
||
## 参数说明 | ||
|
||
### 鉴权说明 | ||
|
||
使用组件之前,请首先申请并设置鉴权参数,可参考[使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 | ||
|
||
```python | ||
# 设置环境中的TOKEN,以下示例略 | ||
os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" | ||
``` | ||
|
||
### 初始化参数 | ||
|
||
- `app_id`: 线上AgentBuilder应用ID,可在[百度智能云千帆AppBuilder](https://cloud.baidu.com/product/AppBuilder)我的应用中查看应用ID,示例如图 | ||
|
||
<img width="768" alt="image" src="./image/agentbuilder.png"> | ||
|
||
### 调用参数 | ||
|
||
| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | | ||
|-----------------|--------------|------|------------|------------| | ||
| conversation_id | String | 是 | 会话ID | | | ||
| query | String | 否 | query问题内容 | "今天天气怎么样?" | | ||
| file_ids | list[String] | 否 | 对话可引用的文档ID | False | | ||
| stream | Bool | 否 | 是否流式返回 | False | | ||
|
||
### 非流式返回 | ||
|
||
| 参数名称 | 参数类型 | 描述 | 示例值 | | ||
|----------------|--------------------|------------------|------------------------------------------------------------------------| | ||
| content | AgentBuilderAnswer | 对话返回结果 | | | ||
| +code | Int | 错误码,0代码成功,非0表示失败 | 0 | | ||
| +message | String | 错误具体消息 | | | ||
| +answer | String | 智能体应用返回的回答 | | | ||
| +events | List[Event] | 事件列表 | | | ||
| +events[0] | Event | 具体事件内容 | | | ||
| ++code | String | 错误码 | | | ||
| ++message | String | 错误具体消息 | | | ||
| ++status | String | 事件状态 | 状态描述,preparing(准备运行)running(运行中)error(执行错误) done(执行完成) | | ||
| ++event_type | String | 事件类型 | | | ||
| ++content_type | String | 内容类型 | 可选值包括:code text, image, status,image, function_call, rag, audio、video等 | | ||
| ++detail | Dict | 事件输出详情 | 代码解释器、文生图、工具组件等的详细输出内容 | | ||
|
||
### 流式返回 | ||
|
||
| 参数名称 | 参数类型 | 描述 | 示例值 | | ||
|---------|------------------|--------------------------------|-----| | ||
| content | Python Generator | 可迭代,每次迭代返回AgentBuilderAnswer类型 | 无 | | ||
|
||
### 响应示例 | ||
|
||
``` | ||
Message(name=msg, content=code=0 message='' answer='模型识别结果为:\n类别: 黑松 置信度: 0.599807\n根据植物识别工具的识别结果,图中的植物很可能是黑松,置信度为0.599807。需要注意的是,置信度并不是特别高,因此这个结果仅供参考。如果你需要更准确的识别结果,可以尝试提供更多的图片信息或者使用更专业的植物识别工具。如果你还有其他问题或者需要进一步的帮助,请随时告诉我。' events=[Event(code=0, message='', status='done', event_type='function_call', content_type='function_call', detail={'text': {'thought': '', 'name': 'plant_rec', 'arguments': {'img_path': 'tree.png'}, 'component': 'PlantRecognition', 'name_cn': '植物识别'}}), Event(code=0, message='', status='preparing', event_type='PlantRecognition', content_type='status', detail={}), Event(code=0, message='', status='done', event_type='PlantRecognition', content_type='text', detail={'text': '模型识别结果为:\n类别: 黑松 置信度: 0.599807\n'}), Event(code=0, message='', status='success', event_type='PlantRecognition', content_type='status', detail={}), Event(code=0, message='', status='done', event_type='function_call', content_type='function_call', detail={'text': {'thought': '', 'name': 'chat_agent', 'arguments': {}, 'component': 'ChatAgent', 'name_cn': '聊天助手'}}), Event(code=0, message='', status='preparing', event_type='ChatAgent', content_type='status', detail={}), Event(code=0, message='', status='done', event_type='ChatAgent', content_type='text', detail={'text': '根据植物识别工具的识别结果,图中的植物很可能是黑松,置信度为0.599807。需要注意的是,置信度并不是特别高,因此这个结果仅供参考。如果你需要更准确的识别结果,可以尝试提供更多的图片信息或者使用更专业的植物识别工具。如果你还有其他问题或者需要进一步的帮助,请随时告诉我。'}), Event(code=0, message='', status='success', event_type='ChatAgent', content_type='status', detail={})], mtype=AgentBuilderAnswer) | ||
``` | ||
|
||
## 高级用法 | ||
|
||
```python | ||
|
||
import appbuilder | ||
from appbuilder.core.console.agent_builder import data_class | ||
import os | ||
|
||
# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 | ||
# 设置环境变量 | ||
os.environ["APPBUILDER_TOKEN"] = '...' | ||
app_id = '...' # 已发布AgentBuilder应用的ID | ||
# 初始化智能体 | ||
agent = appbuilder.AgentBuilder(app_id) | ||
# 创建会话 | ||
conversation_id = agent.create_conversation() | ||
|
||
# 上传一个介绍介绍北京旅游景点的文档 | ||
file_id = agent.upload_local_file(conversation_id, "/path/to/pdf/file") | ||
# 开始对话,引用上传的文档 | ||
message = agent.run(conversation_id, "北京天气怎么样", file_ids=[file_id, ], stream=True) | ||
|
||
|
||
answer = "" | ||
|
||
# 每次迭代返回AgentBuilderAnswer结构,内可能包括多个事件内容 | ||
for content in message.content: | ||
# stream=True时,将answer拼接起来才是完整的的对话结果 | ||
answer += content.answer | ||
for event in content.events: | ||
content_type = event.content_type | ||
detail = event.detail | ||
# 根据content类型对事件详情进行解析 | ||
if content_type == "code": | ||
code_detail = data_class.CodeDetail(**detail) | ||
print(code_detail.code) | ||
elif content_type == "text": | ||
text_detail = data_class.TextDetail(**detail) | ||
print(text_detail.text) | ||
elif content_type == "image": | ||
image_detail = data_class.ImageDetail(**detail) | ||
print(image_detail.url) | ||
elif content_type == "rag": | ||
rag_detail = data_class.RAGDetail(**detail) | ||
print(rag_detail.references) | ||
elif content_type == "function_call": | ||
function_call_detail = data_class.FunctionCallDetail(**detail) | ||
print(function_call_detail.video) | ||
elif content_type == "audio": | ||
audio_detail = data_class.AudioDetail(**detail) | ||
print(audio_detail) | ||
elif content_type == "video": | ||
video_detail = data_class.VideoDetail(**detail) | ||
print(video_detail) | ||
elif content_type == "status": | ||
data_class.StatusDetail(**detail) | ||
else: | ||
print(detail) | ||
|
||
# 打印完整的answer结果 | ||
print(answer) | ||
``` | ||
|
||
## 更新记录和贡献 | ||
* 集成Console AgentBuilder能力(2024-03) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Copyright (c) 2024 Baidu, Inc. All Rights Reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
# Copyright (c) 2024 Baidu, Inc. All Rights Reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
"""AgentBuilder组件""" | ||
|
||
import json | ||
import os | ||
|
||
from appbuilder.core.component import Message, Component | ||
from appbuilder.core.console.agent_builder import data_class | ||
from appbuilder.core._exception import AppBuilderServerException | ||
from appbuilder.utils.sse_util import SSEClient | ||
|
||
|
||
class AgentBuilder(Component): | ||
r""" | ||
AgentBuilder组件支持调用在[百度智能云千帆AppBuilder](https://cloud.baidu.com/product/AppBuilder)平台上通过AgentBuilder | ||
构建并发布的智能体应用,具体包括创建会话、上传文档、运行对话等。 | ||
Examples: | ||
... code-block:: python | ||
import appbuilder | ||
# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 | ||
os.environ["APPBUILDER_TOKEN"] = '...' | ||
# 可在Console AgentBuilder应用页面获取 | ||
app_id = "app_id" | ||
agent_builder = appbuilder.AgentBuilder("app_id") | ||
conversation_id = agent_builder.create_conversation() | ||
file_id = agent_builder.upload_local_file(conversation_id, "/path/to/file") | ||
message = agent_builder.run(conversation_id, "今天你好吗?") | ||
# 打印对话结果 | ||
print(message.content) | ||
""" | ||
|
||
def __init__(self, app_id: str, **kwargs): | ||
r"""初始化智能体应用 | ||
参数: | ||
app_id (str: 必须) : 应用唯一ID | ||
返回: | ||
response (obj: `AgentBuilder`): 智能体实例 | ||
""" | ||
super().__init__(**kwargs) | ||
if (not isinstance(app_id, str)) or len(app_id) == 0: | ||
raise ValueError("app_id must be a str, and length is bigger then zero," | ||
"please go to official website which is 'https://cloud.baidu.com/product/AppBuilder'" | ||
" to get a valid app_id after your application is published.") | ||
self.app_id = app_id | ||
|
||
def create_conversation(self) -> str: | ||
r"""创建会话并返回会话ID,会话ID在服务端用于上下文管理、绑定会话文档等,如需开始新的会话,请创建并使用新的会话ID | ||
参数: | ||
无 | ||
返回: | ||
response (str: ): 唯一会话ID | ||
""" | ||
headers = self.http_client.auth_header() | ||
headers["Content-Type"] = "application/json" | ||
url = self.http_client.service_url("/v1/ai_engine/agi_platform/v1/conversation/create", "/api") | ||
response = self.http_client.session.post(url, headers=headers, json={"app_id": self.app_id}, timeout=None) | ||
self.http_client.check_response_header(response) | ||
request_id = self.http_client.response_request_id(response) | ||
data = response.json() | ||
self._check_console_response(request_id, data) | ||
resp = data_class.CreateConversationResponse(**data) | ||
return resp.result.conversation_id | ||
|
||
def upload_local_file(self, conversation_id, local_file_path: str) -> str: | ||
r"""上传文件并将文件与会话ID进行绑定,后续可使用该文件ID进行对话,目前仅支持上传xlsx、jsonl、pdf、png等文件格式 | ||
参数: | ||
conversation_id (str: 必须) : 会话ID | ||
local_file_path (str: 必须) : 本地文件路径 | ||
返回: | ||
response (str: ): 唯一文件ID | ||
""" | ||
|
||
if len(conversation_id) == 0: | ||
raise ValueError("conversation_id is empty") | ||
multipart_form_data = { | ||
'file': (os.path.basename(local_file_path), open(local_file_path, 'rb')), | ||
'app_id': (None, self.app_id), | ||
'conversation_id': (None, conversation_id), | ||
'scenario': (None, "assistant") | ||
} | ||
headers = self.http_client.auth_header() | ||
url = self.http_client.service_url("/v1/ai_engine/agi_platform/v1/instance/upload", "/api") | ||
response = self.http_client.session.post(url, files=multipart_form_data, headers=headers) | ||
self.http_client.check_response_header(response) | ||
request_id = self.http_client.response_request_id(response) | ||
data = response.json() | ||
self._check_console_response(request_id, data) | ||
resp = data_class.FileUploadResponse(**data) | ||
return resp.result.id | ||
|
||
def run(self, conversation_id: str, | ||
query: str, | ||
file_ids: list[str] = [], | ||
stream: bool = False, | ||
) -> Message: | ||
|
||
r""" 动物识别 | ||
参数: | ||
query (str: 必须): query内容 | ||
conversation_id (str, 必须): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 | ||
file_ids(list[str], 可选): | ||
stream (bool, 可选): 为True时,流式返回,需要将message.content.answer拼接起来才是完整的回答;为False时,对应非流式返回 | ||
返回: message (obj: `Message`): 对话结果. | ||
""" | ||
|
||
if len(conversation_id) == 0: | ||
raise ValueError("conversation_id is empty") | ||
|
||
req = data_class.HTTPRequest( | ||
app_id=self.app_id, | ||
conversation_id=conversation_id, | ||
query=query, | ||
response_mode="streaming" if stream else "blocking", | ||
file_ids=file_ids, | ||
) | ||
|
||
headers = self.http_client.auth_header() | ||
headers["Content-Type"] = "application/json" | ||
url = self.http_client.service_url("/v1/ai_engine/agi_platform/v1/instance/integrated", '/api') | ||
response = self.http_client.session.post(url, headers=headers, json=req.model_dump(), timeout=None, stream=True) | ||
self.http_client.check_response_header(response) | ||
request_id = self.http_client.response_request_id(response) | ||
if stream: | ||
client = SSEClient(response) | ||
return Message(content=self._iterate_events(request_id, client.events())) | ||
else: | ||
data = response.json() | ||
self._check_console_response(request_id, data) | ||
resp = data_class.HTTPResponse(**data) | ||
out = data_class.AgentBuilderAnswer() | ||
_transform(resp, out) | ||
return Message(content=out) | ||
|
||
def _iterate_events(self, request_id, events) -> data_class.AgentBuilderAnswer: | ||
for event in events: | ||
try: | ||
data = event.data | ||
if len(data) == 0: | ||
data = event.raw | ||
data = json.loads(data) | ||
self._check_console_response(request_id, data) | ||
except json.JSONDecodeError as e: | ||
raise AppBuilderServerException(request_id=request_id, message="json decoder failed {}".format(str(e))) | ||
inp = data_class.HTTPResponse(**data) | ||
out = data_class.AgentBuilderAnswer() | ||
_transform(inp, out) | ||
yield out | ||
|
||
@staticmethod | ||
def _check_console_response(request_id: str, data): | ||
if data["code"] != 0: | ||
raise AppBuilderServerException( | ||
request_id=request_id, | ||
service_err_code=data["code"], | ||
service_err_message="message={}". | ||
format(data["message"]) | ||
) | ||
|
||
|
||
def _transform(inp: data_class.HTTPResponse, out: data_class.AgentBuilderAnswer): | ||
out.code = inp.code | ||
out.message = inp.message | ||
out.answer = inp.result.answer | ||
for ev in inp.result.content: | ||
out.events.append(data_class.Event(code=ev.event_code, message=ev.event_message, | ||
status=ev.event_status, event_type=ev.event_type, | ||
content_type=ev.content_type, detail=ev.outputs)) |
Oops, something went wrong.