diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml
index 3271e2ea0..40c23f981 100644
--- a/.github/workflows/python-package.yml
+++ b/.github/workflows/python-package.yml
@@ -76,14 +76,28 @@ jobs:
git fetch upstream
git remote -v
git status
- changed_files=$(git diff --name-only --diff-filter=ACMRT master -- '*.py' '*.sh')
- echo "发生更改的py/sh文件为:"
+
+ # 找到当前分支与 upstream/master 的共同祖先提交
+ merge_base=$(git merge-base HEAD upstream/master)
+ echo "merge_base=$merge_base"
+
+ # 比较当前分支与 merge_base 之间的差异
+ changed_files=$(git diff --name-only --diff-filter=ACMRT $merge_base)
+ changed_files_py_sh=$(git diff --name-only --diff-filter=ACMRT $merge_base -- '*.py' '*.sh')
+
+ echo "发生更改的文件为:"
echo "$changed_files"
- if [ -n "$changed_files" ]; then
+
+ echo "发生更改的py/sh文件为:"
+
+ if [ -n "$changed_files_py_sh" ]; then
export APPBUILDER_PYTHON_TESTS=True
+ echo "$changed_files_py_sh"
else
export APPBUILDER_PYTHON_TESTS=False
+ echo "没有检测到Python或Shell文件被更改"
fi
+
echo "APPBUILDER_PYTHON_TESTS=$APPBUILDER_PYTHON_TESTS" >> $GITHUB_ENV
pwd
- name: Install dependencies
diff --git a/README.md b/README.md
index 9add32a99..df505893a 100644
--- a/README.md
+++ b/README.md
@@ -76,7 +76,7 @@ AppBuilder-SDK不仅提供了百度智能云提供的基础能力组件,同时
## 如何安装AppBuilder-SDK
-#### 百度智能云千帆AppBuilder-SDK 最新版本 0.9.3 (2024-08-20)
+#### 百度智能云千帆AppBuilder-SDK 最新版本 0.9.4 (2024-09-10)
百度智能云千帆AppBuilder-SDK 更新记录&最新特性请查阅我们的[版本说明](/docs/quick_start/changelog.md)
@@ -263,6 +263,7 @@ Hook:
| 基础能力组件 | [基础组件服务化](/cookbooks/components/agent_runtime.ipynb) | 基础组件可通过flask实现服务化部署 或 通过chainlit实现可交互的前端部署,集成到您的系统中 |
| 流程编排 | [Assistant SDK](/cookbooks/pipeline/assistant_function_call.ipynb) | 学习如何纯代码态搭建一个Agent应用,并实现自定义工作流程及FunctionCall |
| 端到端应用 | [AppBuilder Client SDK](/cookbooks/agent_builder.ipynb) | 使用AppBuilder网页端创建并发布一个Agent应用后,通过AppBuilderClient SDK集成到你的系统中 |
+| 端到端应用 | [通过AppBuilder-ToolCall功能实现端云组件联动的Agent](/cookbooks/end2end_application/agent/tool_call.ipynb) | 学习Agent、FunctionCall的知识,并构造调用本地组件的Agent |
| 端到端应用 | [简历筛选小助手](/cookbooks/end2end_application/rag/rag.ipynb) | 通过对本地简历库的简历进行解析、切片、创建索引,实现基于JD进行简历筛选,并对筛选的Top1简历进行总结 |
| 端到端应用 | [企业级问答系统](/cookbooks/end2end_application/rag/qa_system_2_dialogue.ipynb) | 学习如何通过SDK与网页平台搭配,实现离线知识库生产与在线问答 |
| 进阶应用 | [使用appbuilder_bce_deploy部署公有云服务](/cookbooks/advanced_application/cloud_deploy.ipynb) | 一键将自己的服务部署到百度智能云,部署后可以自动生成公网ip,联动工作流的API节点 |
diff --git a/README_en.md b/README_en.md
index e776fce7c..3a2eccccd 100644
--- a/README_en.md
+++ b/README_en.md
@@ -47,7 +47,7 @@ Baidu AI Cloud Qianfan AppBuilder-SDK offers the following essential features fo
## How to install?
-#### The latest version of Baidu AI Cloud Qianfan AppBuilder SDK is 0.9.3 (2024-08-20)
+#### The latest version of Baidu AI Cloud Qianfan AppBuilder SDK is 0.9.4 (2024-09-10)
Baidu AI Cloud Qianfan AppBuilder SDK ReleaseNote please refer to our [version description](/docs/quick_start/changelog.md)
diff --git a/README_ja.md b/README_ja.md
index 87e5f6566..6a92fb442 100644
--- a/README_ja.md
+++ b/README_ja.md
@@ -44,7 +44,7 @@ Baidu AI Cloud Qianfan AppBuilder-SDKは、AIアプリケーション開発者
## どのようにインストールしますか?
-#### Baidu AI Cloud Qianfan AppBuilder SDKの最新バージョンは0.9.3(2024-08-20)です
+#### Baidu AI Cloud Qianfan AppBuilder SDKの最新バージョンは0.9.4(2024-09-10)です
Baidu AI Cloud Qianfan AppBuilder SDKのリリースノートについては、[バージョン説明](/docs/quick_start/changelog.md)をご覧ください。
diff --git a/appbuilder/__init__.py b/appbuilder/__init__.py
index 68619946b..e5e4e998c 100644
--- a/appbuilder/__init__.py
+++ b/appbuilder/__init__.py
@@ -13,7 +13,7 @@
# limitations under the License.
-__version__ = '0.9.3'
+__version__ = '0.9.4'
import os
import sys
@@ -90,6 +90,7 @@ def get_default_header():
from .core.components.retriever.baidu_vdb.baiduvdb_retriever import BaiduVDBVectorStoreIndex
from .core.components.retriever.baidu_vdb.baiduvdb_retriever import BaiduVDBRetriever
from .core.components.retriever.baidu_vdb.baiduvdb_retriever import TableParams
+from .core.components.retriever.reranker.rerank import Reranker
from .core.components.ppt_generation_from_instruction.component import PPTGenerationFromInstruction
from .core.components.ppt_generation_from_paper.component import PPTGenerationFromPaper
from .core.components.ppt_generation_from_file.component import PPTGenerationFromFile
@@ -184,6 +185,7 @@ def get_default_header():
"BaiduVDBVectorStoreIndex",
"BaiduVDBRetriever",
"TableParams",
+ "Reranker",
"HallucinationDetection",
'DishRecognition',
diff --git a/appbuilder/core/agent.py b/appbuilder/core/agent.py
index e5723409c..9732554ba 100644
--- a/appbuilder/core/agent.py
+++ b/appbuilder/core/agent.py
@@ -322,8 +322,9 @@ def warp():
user_id = request.headers.get("X-Appbuilder-User-Id", None)
init_context(session_id=session_id, request_id=request_id, user_id=user_id)
- logging.debug(
- f"[request_id={request_id}, session_id={session_id}] message={message}, stream={stream}, data={data}")
+ logging.info(
+ f"request_id={request_id}, session_id={session_id}] message={message},"
+ f" stream={stream}, data={data}, start run...")
def gen_sse_resp():
with app.app_context():
@@ -332,7 +333,16 @@ def gen_sse_resp():
while retry_count < MAX_RETRY_COUNT:
try:
answer = self.chat(message, stream, **data)
+ except Exception as e: # 调用chat方法报错,直接返回
+ code = 500 if not hasattr(e, "code") else e.code
+ err_resp = {"code": code, "message": "InternalServerError", "result": None}
+ logging.error(
+ f"request_id={request_id}, session_id={session_id}, err={e}, execute self.chat failed", exc_info=True)
+ yield "data: " + json.dumps(err_resp, ensure_ascii=False) + "\n\n"
+ return
+ else: # 调用chat方法成功,开始生成流式事件
content_iterator = iter(answer.content)
+ answer.content = None
result = None
try:
for sub_content in content_iterator:
@@ -349,10 +359,21 @@ def gen_sse_resp():
received_first_packet = True
except Exception as e:
retry_count += 1
- if not received_first_packet:
+ logging.error(
+ f"[request_id={request_id}, session_id={session_id}] err={e}, "
+ f"retry_count={retry_count}", exc_info=True)
+ # 如果未收到首包且重试次数小于最大重试次数,则尝试重新执行一次chat方法
+ if not received_first_packet and retry_count < MAX_RETRY_COUNT:
continue
- else:
- raise e
+ else: # 其它情况返回
+ logging.error(
+ f"[request_id={request_id}, session_id={session_id}] err={e}, "
+ f"retry_count={retry_count}, received_first_packet={received_first_packet}"
+ , exc_info=True)
+ code = 500 if not hasattr(e, "code") else e.code
+ err_resp = {"code": code, "message": "InternalServerError", "result": None}
+ yield "data: " + json.dumps(err_resp, ensure_ascii=False) + "\n\n"
+ return
result.content = ""
yield "data: " + json.dumps({
"code": 0, "message": "",
@@ -362,41 +383,30 @@ def gen_sse_resp():
"answer_message": json.loads(result.json(exclude_none=True))
}
}, ensure_ascii=False) + "\n\n"
+ logging.info(
+ f"request_id={request_id}, session_id={session_id}]"
+ f"retry_count={retry_count}, success response", exc_info=True)
self.user_session._post_append()
- break
- except Exception as e:
- code = 500 if not hasattr(
- e, "code") else e.code
- err_resp = {"code": code,
- "message": "InternalServerError", "result": None}
- logging.error(
- f"[request_id={request_id}, session_id={session_id}] err={e}", exc_info=True)
- yield "data: " + json.dumps(err_resp, ensure_ascii=False) + "\n\n"
+ return # 正常返回
- try:
- if stream:
- return Response(stream_with_context(gen_sse_resp()), 200,
- {'Content-Type': 'text/event-stream; charset=utf-8'},
- )
- else:
+ if stream: # 流式
+ return Response(stream_with_context(gen_sse_resp()), 200,
+ {'Content-Type': 'text/event-stream; charset=utf-8'})
+ if not stream: # 非流式
+ try:
answer = self.chat(message, stream, **data)
- blocking_result = json.loads(
- copy.deepcopy(answer).json(exclude_none=True))
- logging.info(
- f"[request_id={request_id}, session_id={session_id}] blocking_result={blocking_result}")
+ blocking_result = json.loads(copy.deepcopy(answer).json(exclude_none=True))
+ logging.debug(f"[request_id={request_id}, session_id={session_id}] blocking_result={blocking_result}")
self.user_session._post_append()
return {
"code": 0, "message": "",
"result": {"session_id": session_id, "answer_message": blocking_result}
}
- except Exception as e:
- logging.error(
- f"[request_id={request_id}, session_id={session_id}] err={e}", exc_info=True)
- raise e
- except Exception as e:
- logging.error(
- f"[request_id={request_id}, session_id={session_id}] err={e}", exc_info=True)
- raise e
+ except Exception as e:
+ logging.error(
+ f"[request_id={request_id}, session_id={session_id}] err={e}", exc_info=True)
+ code = 500 if not hasattr(e, "code") else e.code
+ return {"code": code, "message": "InternalServerError", "result": None}
app.add_url_rule(url_rule, 'chat', warp, methods=['POST'])
return app
diff --git a/appbuilder/core/components/doc_parser/doc_parser.py b/appbuilder/core/components/doc_parser/doc_parser.py
index a18cf0102..b26cdc6e0 100644
--- a/appbuilder/core/components/doc_parser/doc_parser.py
+++ b/appbuilder/core/components/doc_parser/doc_parser.py
@@ -26,7 +26,9 @@
from appbuilder.utils.logger_util import logger
from appbuilder.core._client import HTTPClient
from appbuilder.core.components.doc_parser.base import ParserConfig, ParseResult
-from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace
+from appbuilder.utils.trace.tracer_wrapper import (
+ components_run_trace,
+)
class DocParser(Component):
@@ -46,6 +48,7 @@ class DocParser(Component):
parse_result = parser(msg)
"""
+
name: str = "doc_parser"
tool_desc: Dict[str, Any] = {"description": "parse document content"}
base_url: str = "/v1/bce/xmind/parser"
@@ -61,16 +64,24 @@ def make_parse_result(self, response: Dict):
"""
将解析结果的内容转化成ParseResult的结构
"""
- para_nodes = response["para_nodes"] if response["para_nodes"] is not None else []
+ para_nodes = (
+ response["para_nodes"] if response["para_nodes"] is not None else []
+ )
catalog = response["catalog"] if response["catalog"] is not None else []
pdf_data = response["pdf_data"]
title_node_ids = [title["node_id"] for title in catalog] if catalog else []
page_contents = []
for content in response["file_content"]:
- page_content = {"page_num": content["page_num"], "page_width": int(content["page_size"]["width"]),
- "page_height": int(content["page_size"]["height"]), "page_angle": int(content["page_angle"]),
- "page_type": content["page_content"]["type"], "page_layouts": [], "page_titles": [],
- "page_tables": []}
+ page_content = {
+ "page_num": content["page_num"],
+ "page_width": int(content["page_size"]["width"]),
+ "page_height": int(content["page_size"]["height"]),
+ "page_angle": int(content["page_angle"]),
+ "page_type": content["page_content"]["type"],
+ "page_layouts": [],
+ "page_titles": [],
+ "page_tables": [],
+ }
for layout_item in content["page_content"]["layout"]:
if layout_item["node_id"] in title_node_ids:
continue
@@ -81,8 +92,16 @@ def make_parse_result(self, response: Dict):
table_row = []
for i in range(len(layout_item["matrix"])):
cell_index = layout_item["matrix"][i]
- row_markdown = "|" + "|".join(
- [layout_item["children"][index]["text"] for index in set(cell_index)]) + "|"
+ row_markdown = (
+ "|"
+ + "|".join(
+ [
+ layout_item["children"][index]["text"]
+ for index in set(cell_index)
+ ]
+ )
+ + "|"
+ )
if i != len(layout_item["matrix"]) - 1:
row_markdown += "\n"
table_row.append(row_markdown)
@@ -94,9 +113,18 @@ def make_parse_result(self, response: Dict):
for title in catalog:
page_num = title["position"][0]["pageno"]
page_contents[page_num]["page_titles"].append(
- {"text": title["text"], "type": title["level"], "box": title["position"][0]["box"],
- "node_id": title["node_id"]})
- parse_result = {"para_node_tree": para_nodes, "page_contents": page_contents, "pdf_data": pdf_data}
+ {
+ "text": title["text"],
+ "type": title["level"],
+ "box": title["position"][0]["box"],
+ "node_id": title["node_id"],
+ }
+ )
+ parse_result = {
+ "para_node_tree": para_nodes,
+ "page_contents": page_contents,
+ "pdf_data": pdf_data,
+ }
# parse_result = ParseResult.parse_obj(parse_result)
return parse_result
@@ -123,13 +151,26 @@ def run(self, input_message: Message, return_raw=False) -> Message:
payload = json.dumps({"file_list": [param]})
headers = self.http_client.auth_header()
headers["Content-Type"] = "application/json"
- response = self.http_client.session.post(url=self.http_client.service_url(self.base_url), headers=headers, data=payload)
+ response = self.http_client.session.post(
+ url=self.http_client.service_url(self.base_url),
+ headers=headers,
+ data=payload,
+ )
self.http_client.check_response_header(response)
self.http_client.check_response_json(response.json())
+ request_id = self.http_client.response_request_id(response)
response = response.json()
if response["error_code"] != 0:
- logger.error("doc parser service log_id {} err {}".format(response["log_id"], response["error_msg"]))
- raise AppBuilderServerException(response["error_msg"])
+ logger.error(
+ "doc parser service log_id {} err {}".format(
+ response["log_id"], response["error_msg"]
+ )
+ )
+ raise AppBuilderServerException(
+ request_id=request_id,
+ service_err_code=response["error_code"],
+ service_err_message=response["error_msg"],
+ )
parse_result = self.make_parse_result(response["result"]["result_list"][0])
if return_raw:
parse_result["raw"] = response
diff --git a/appbuilder/core/components/retriever/reranker/README.md b/appbuilder/core/components/retriever/reranker/README.md
new file mode 100644
index 000000000..3e6c4b03d
--- /dev/null
+++ b/appbuilder/core/components/retriever/reranker/README.md
@@ -0,0 +1,83 @@
+# 文本精排(Reranker)
+
+## 简介
+文本精排能力,将Query召回到的N个候选文本段落进行精排;保证与Query相关程度越高的文本段落排序越靠前,提升检索效果。
+
+### 功能介绍
+文本精排(Reranker)用于检索排序,输入为Query和Top K个段落,输出为每个段落的排序得分;Query相关程度越高的文本段落排序越靠前,用于提升检索效果。
+
+### 特色优势
+- 高效准确:基于开源模型[
+bce-reranker](https://huggingface.co/maidalun1020/bce-reranker-base_v1)的能力,提供高效且准确的内容检索功能。[百度云推理服务Api](https://cloud.baidu.com/doc/WENXINWORKSHOP/s/xlu216rqn)
+
+### 应用场景
+检索排序场景
+
+
+## 基本用法
+
+以下是有关如何开始使用BESRetriever的代码示例:
+
+```python
+import os
+import appbuilder
+from appbuilder import Message
+
+# 请前往千帆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"] = '...'
+
+reranker = appbuilder.Reranker()
+ranked_1 = reranker("你好", ["他也好", "hello?"])
+print(ranked_1)
+
+# 使用上游的Message作为输入的代码示例
+ranked_2 = reranker(appbuilder.Message("你好"), appbuilder.Message(["他也好", "hello?"]))
+print(ranked_2)
+```
+
+## 参数说明
+### 初始化参数说明:
+
+| 参数名称 | 参数类型 |是否必须 | 描述 | 示例值 |
+|---------|--------|--------|------------------|---------------|
+| model | str |是 | 指定底座模型的类型。当前仅支持 bce-reranker-base 作为可选值。若不指定,默认值为 bce-reranker-base。 | bce-reranker-base |
+
+
+### 调用参数:
+
+| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 |
+|---------|--------|--------|------------------|---------------|
+| query | str |是 | 精排Query,长度小于1600。 | "你好" |
+| texts | List[str] | 是 | 精排输入段落,会对列表里的所有内容排序,最大长度为50. | ["你好", "世界"] |
+
+
+### 响应示例
+#### 输入
+```python
+query="你好", text=["他也好", "hello?"]
+```
+
+#### 响应
+```json
+[
+ {
+ "document": "hello?",
+ "relevance_score": 0.5651187300682068,
+ "index": 1
+ },
+ {
+ "document": "他也好",
+ "relevance_score": 0.47729530930519104,
+ "index": 0
+ }
+]
+```
+
+
+### 错误码
+
+无
+
+## 更新记录和贡献
+
+* reranker-base (2024-08)
diff --git a/appbuilder/core/components/retriever/reranker/__init__.py b/appbuilder/core/components/retriever/reranker/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/appbuilder/core/components/retriever/reranker/rerank.py b/appbuilder/core/components/retriever/reranker/rerank.py
new file mode 100644
index 000000000..d2b45f4fd
--- /dev/null
+++ b/appbuilder/core/components/retriever/reranker/rerank.py
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+
+# 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.
+
+""" Reranker 文本精排
+"""
+from typing import Union, List
+
+from appbuilder.core.message import Message
+from appbuilder.core.component import ComponentArguments, Component, Message
+from appbuilder.core._exception import AppBuilderServerException, ModelNotSupportedException
+from appbuilder.utils.trace.tracer_wrapper import components_run_trace
+
+
+class RerankerArgs(ComponentArguments):
+ """配置"""
+
+ text: Union[Message[str], str]
+
+
+class Reranker(Component):
+ """ Reranker
+
+ Examples:
+
+ .. code-block:: python
+
+ import os
+ import appbuilder
+ from appbuilder import Message
+
+ os.environ["APPBUILDER_TOKEN"] = '...'
+
+ reranker = appbuilder.Reranker()
+ ranked_1 = reranker("你好", ["他也好", "hello?"])
+ print(ranked_1)
+ """
+ name: str = "reranker"
+ version: str = "v1"
+
+ meta = RerankerArgs
+ base_urls = {
+ 'bce-reranker-base' : "/api/v1/component/component/bce_reranker_base"
+ }
+ accepted_models = list(base_urls.keys())
+
+ def __init__(self, model="bce-reranker-base"):
+ """Reranker"""
+
+ if model not in self.accepted_models:
+ raise ModelNotSupportedException(f"Model {model} not supported, only support {self.accepted_models}")
+
+ if model in self.base_urls:
+ self.base_url = self.base_urls[model]
+ else:
+ raise ModelNotSupportedException(f"Model {model} is not yet supported, only support {self.base_urls.keys()}")
+
+ super().__init__(self.meta)
+
+ def _check_response_json(self, data: dict):
+ """
+ check_response_json
+ """
+
+ self.http_client.check_response_json(data)
+ if "error_code" in data and "error_msg" in data:
+ raise AppBuilderServerException(
+ service_err_code=data['error_code'],
+ service_err_message=data['error_msg'],
+ )
+
+ def _request(self, payload: dict) -> dict:
+ """
+ request to gateway
+ """
+ headers = self.http_client.auth_header()
+ headers["Content-Type"] = "application/json"
+ resp = self.http_client.session.post(
+ url=self.http_client.service_url(self.base_url, "/"),
+ headers=headers,
+ json=payload,
+ )
+ self.http_client.check_response_header(resp)
+ self._check_response_json(resp.json())
+
+ return resp.json()
+
+ def _batch(self, query, texts: List[str]) -> List[dict]:
+ """
+ batch run implement
+ """
+ if len(texts) > 50:
+ raise ValueError(f'Rerank texts max nums must be lower than 50, but got {len(texts)}')
+ for v in texts:
+ if not isinstance(v, str):
+ raise ValueError(f'Rerank texts must be str, but got {v}')
+
+ params = {
+ "inputs": {
+ "query": query,
+ "texts": texts
+ }
+ }
+ result = self._request(params)
+ result = result["result"]
+ return result
+
+ @components_run_trace
+ def run(self, query: Union[Message[str], str],
+ texts: Union[Message[List[str]], List[str]]) -> Message[List[dict]]:
+ """
+ run
+ """
+ _query = query if isinstance(query, str) else query.content
+ _texts = texts if isinstance(texts, List) else texts.content
+
+ return Message(self._batch(_query, _texts))
diff --git a/appbuilder/core/console/appbuilder_client/appbuilder_client.py b/appbuilder/core/console/appbuilder_client/appbuilder_client.py
index d781fc0bc..69f8cfc62 100644
--- a/appbuilder/core/console/appbuilder_client/appbuilder_client.py
+++ b/appbuilder/core/console/appbuilder_client/appbuilder_client.py
@@ -169,7 +169,7 @@ def upload_local_file(self, conversation_id, local_file_path: str) -> str:
if len(conversation_id) == 0:
raise ValueError("conversation_id is empty, you can run self.create_conversation to get a conversation_id")
-
+
filepath = os.path.abspath(local_file_path)
if not os.path.exists(filepath):
raise FileNotFoundError(f"{filepath} does not exist")
@@ -196,24 +196,28 @@ def run(self, conversation_id: str,
stream: bool = False,
tools: list[data_class.Tool] = None,
tool_outputs: list[data_class.ToolOutput] = None,
+ tool_choice: data_class.ToolChoice = None,
+ end_user_id: str = None,
**kwargs
) -> Message:
r"""
- 参数:
- query (str: 必须): query内容
- conversation_id (str, 必须): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话
- file_ids(list[str], 可选):
- stream (bool, 可选): 为True时,流式返回,需要将message.content.answer拼接起来才是完整的回答;为False时,对应非流式返回
- tools(list[data_class.Tools], 可选): 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None
- tool_outputs(list[data_class.ToolOutput], 可选): 工具输出列表,格式为list[ToolOutput], ToolOutputd内容为本地的工具执行结果,以自然语言/json dump str描述,默认为None
- 返回: message (obj: `Message`): 对话结果.
+ 参数:
+ query (str: 必须): query内容
+ conversation_id (str, 必须): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话
+ file_ids(list[str], 可选):
+ stream (bool, 可选): 为True时,流式返回,需要将message.content.answer拼接起来才是完整的回答;为False时,对应非流式返回
+ tools(list[data_class.Tools], 可选): 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None
+ tool_outputs(list[data_class.ToolOutput], 可选): 工具输出列表,格式为list[ToolOutput], ToolOutputd内容为本地的工具执行结果,以自然语言/json dump str描述,默认为None
+ tool_choice(data_class.ToolChoice, 可选): 控制大模型使用组件的方式,默认为None
+ end_user_id (str, 可选): 用户ID,用于区分不同用户
+ 返回: message (obj: `Message`): 对话结果.
"""
if len(conversation_id) == 0:
raise ValueError(
"conversation_id is empty, you can run self.create_conversation to get a conversation_id"
)
-
+
if query == "" and (tool_outputs is None or len(tool_outputs) == 0):
raise ValueError("AppBuilderClient Run API: query and tool_outputs cannot both be empty")
@@ -224,7 +228,9 @@ def run(self, conversation_id: str,
stream=True if stream else False,
file_ids=file_ids,
tools=tools,
- tool_outputs=tool_outputs
+ tool_outputs=tool_outputs,
+ tool_choice=tool_choice,
+ end_user_id=end_user_id,
)
headers = self.http_client.auth_header_v2()
@@ -244,7 +250,7 @@ def run(self, conversation_id: str,
out = data_class.AppBuilderClientAnswer()
_transform(resp, out)
return Message(content=out)
-
+
def run_with_handler(self,
conversation_id: str,
query: str = "",
@@ -263,7 +269,7 @@ def run_with_handler(self,
stream=stream,
**kwargs
)
-
+
return event_handler
@staticmethod
diff --git a/appbuilder/core/console/appbuilder_client/data_class.py b/appbuilder/core/console/appbuilder_client/data_class.py
index 4ed8e7a1b..a191c4918 100644
--- a/appbuilder/core/console/appbuilder_client/data_class.py
+++ b/appbuilder/core/console/appbuilder_client/data_class.py
@@ -22,7 +22,7 @@ class Function(BaseModel):
name: str = Field(..., description="工具名称")
description: str = Field(..., description="工具描述")
parameters: dict = Field(..., description="工具参数, json_schema格式")
-
+
class Tool(BaseModel):
type: str = "function"
function: Function = Field(..., description="工具信息")
@@ -40,6 +40,28 @@ class ToolCall(BaseModel):
type: str = Field("function", description="需要输出的工具调用的类型。就目前而言,这始终是function")
function: FunctionCallDetail = Field(..., description="函数定义")
+
+class ToolChoiceFunction(BaseModel):
+ name: str = Field(
+ ...,
+ description="组件的英文名称(唯一标识),用户通过工作流完成自定义组件后,可在个人空间-组件下查看组件英文名称",
+ )
+ input: dict = Field(
+ ...,
+ description="当组件没有入参或者必填的入参只有一个时可省略,必填的入参只有一个且省略时,使用query字段的值作为入参",
+ )
+
+
+class ToolChoice(BaseModel):
+ type: str = Field(
+ ...,
+ description="auto/function,auto表示由LLM自动判断调什么组件;function表示由用户指定调用哪个组件",
+ )
+ function: Optional[ToolChoiceFunction] = Field(
+ ..., description="当type为function时,需要指定调用哪个组件"
+ )
+
+
class AppBuilderClientRequest(BaseModel):
"""会话请求参数
属性:
@@ -56,6 +78,8 @@ class AppBuilderClientRequest(BaseModel):
app_id: str
tools: Optional[list[Tool]] = None
tool_outputs: Optional[list[ToolOutput]] = None
+ tool_choice: Optional[ToolChoice] = None
+ end_user_id: Optional[str] = None
class Usage(BaseModel):
@@ -107,7 +131,7 @@ class AppBuilderClientResponse(BaseModel):
message_id: str = ""
is_completion: Optional[bool] = False
content: list[OriginalEvent] = []
-
+
class TextDetail(BaseModel):
"""content_type=text,详情内容
@@ -286,4 +310,3 @@ class AppBuilderClientAppListResponse(BaseModel):
request_id: str = Field("", description="请求ID")
data: Optional[list[AppOverview]] = Field(
[], description="应用概览列表")
-
\ No newline at end of file
diff --git a/appbuilder/tests/test_core_agent.py b/appbuilder/tests/test_core_agent.py
index dccaf1a80..23de1a4a7 100644
--- a/appbuilder/tests/test_core_agent.py
+++ b/appbuilder/tests/test_core_agent.py
@@ -11,63 +11,139 @@
# 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.
+import json
import os
import unittest
-import json
-import subprocess
-import asyncio
+import random
+from appbuilder.core.components.llms.style_writing import StyleWriting
from appbuilder.core.agent import AgentRuntime
from appbuilder.core.component import Component
+from appbuilder.core.message import Message
+from appbuilder.utils.sse_util import SSEClient
+
+
+def generate_event(case):
+ # 模拟正常事件
+ if case == "normal":
+ yield "event1"
+ yield "event2"
+ yield "event3"
+ # 模拟首包一定概率出错
+ elif case == "head_may_failed":
+ num = random.randint(1, 100)
+ if num < 20:
+ raise Exception("事件生成报错")
+ else:
+ yield "event1"
+ # 模型中包出错
+ elif case == "middle_failed":
+ yield "event1"
+ raise Exception("事件生成报错")
+ # 模拟首包总是报错
+ elif case == "head_always_failed":
+ raise Exception("事件生成报错")
+
+
+# 模拟流式事件生成报错
+class FakeComponent1(Component):
+ def run(self, message, stream, **kwargs):
+ # 模拟流式调用
+ if stream:
+ case = kwargs["case"]
+ return Message(content=generate_event(case))
+ else:
+ return Message(content="result")
+
+
+# 模拟组件内部执行报错
+class FakeComponent2(Component):
+ def run(self, message, stream, **kwargs):
+ # 内部执行报错
+ raise Exception("内部执行报错")
+
-@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "")
class TestCoreAgent(unittest.TestCase):
def setUp(self):
- self.appbuilder_run_chainlit=os.getenv('APPBUILDER_RUN_CHAINLIT')
-
- def tearDown(self):
- if self.appbuilder_run_chainlit is None:
- os.unsetenv('APPBUILDER_RUN_CHAINLIT')
- else:
- os.environ['APPBUILDER_RUN_CHAINLIT'] = self.appbuilder_run_chainlit
-
- def test_core_agent_create_flask(self):
- # test_core_agent_create_flask_app
- component = Component()
+ pass
+
+ def test_core_agent_create_flask1(self):
+ component = FakeComponent1()
+ # agent = AgentRuntime(component=StyleWriting(model="eb"))
agent = AgentRuntime(component=component)
- subprocess.check_call(['pip', 'uninstall', 'flask', '-y'])
- with self.assertRaises(ImportError):
- agent.create_flask_app()
- subprocess.check_call(['pip', 'install', 'flask==2.3.2'])
- subprocess.check_call(['pip', 'install', 'flask-restful==0.3.9'])
- subprocess.check_call(['pip', 'install', 'werkzeug'])
+
app = agent.create_flask_app()
-
- # 附加测试core/component,相关代码还未编写
- def test_core_component(self):
- component = Component()
- component.http_client
- with self.assertRaises(NotImplementedError):
- component.run()
- component.batch()
- component._trace()
- component._debug()
- component.tool_desc()
- component.tool_name()
- component.tool_eval()
- # 运行asyncio
- asyncio.run(component.arun())
- asyncio.run(component.abatch())
- # test_tool_eval
- component.manifests=[1,2,3]
- with self.assertRaises(NotImplementedError):
- component.tool_eval()
- # test self._http_client is None
- component._http_client = None
- component.http_client
-
-
-
+ client = app.test_client()
+ payload = {
+ "stream": False,
+ "message": "message",
+ }
+ # 非流式请求
+ rsp = client.post("http://127.0.0.1:8080/chat", json=payload)
+ assert (rsp.json["code"] == 0)
+
+ # 流式请求
+ for case in ["normal", "head_may_failed", "middle_failed", "head_always_failed"]:
+ payload = {
+ "stream": True,
+ "message": "message",
+ "case": case
+ }
+ if case == "normal":
+ rsp = client.post("http://127.0.0.1:8080/chat", json=payload)
+ data_chunks = rsp.data.splitlines(keepends=True)
+ for event in SSEClient(data_chunks).events():
+ d = json.loads(event.data)
+ self.assertEqual(d["code"], 0)
+ if case == "head_may_failed":
+ for i in range(5):
+ rsp = client.post("http://127.0.0.1:8080/chat", json=payload)
+ data_chunks = rsp.data.splitlines(keepends=True)
+ for event in SSEClient(data_chunks).events():
+ d = json.loads(event.data)
+ self.assertEqual(d["code"], 0)
+ if case == "middle_failed":
+ rsp = client.post("http://127.0.0.1:8080/chat", json=payload)
+ data_chunks = rsp.data.splitlines(keepends=True)
+ i = 0
+ for event in SSEClient(data_chunks).events():
+ d = json.loads(event.data)
+ if i == 0:
+ self.assertEqual(d["code"], 0)
+ if i == 1:
+ self.assertNotEqual(d["code"], 0)
+ i += 1
+
+ if case == "head_always_failed":
+ rsp = client.post("http://127.0.0.1:8080/chat", json=payload)
+ data_chunks = rsp.data.splitlines(keepends=True)
+ for event in SSEClient(data_chunks).events():
+ self.assertNotEqual(d["code"], 0)
+
+ def test_core_agent_create_flask2(self):
+ component = FakeComponent2()
+ agent = AgentRuntime(component=component)
+ app = agent.create_flask_app()
+ client = app.test_client()
+ payload = {
+ "stream": False,
+ "message": "message",
+ }
+ # 非流式请求
+ rsp = client.post("http://127.0.0.1:8080/chat", json=payload)
+ assert (rsp.json["code"] != 0)
+
+ payload = {
+ "stream": True,
+ "message": "message",
+ }
+ # 流式请求
+ rsp = client.post("http://127.0.0.1:8080/chat", json=payload)
+ data_chunks = rsp.data.splitlines(keepends=True)
+ for event in SSEClient(data_chunks).events():
+ d = json.loads(event.data)
+ self.assertNotEqual(d["code"], 0)
+
+
if __name__ == '__main__':
unittest.main()
-
diff --git a/appbuilder/tests/test_rerank.py b/appbuilder/tests/test_rerank.py
new file mode 100644
index 000000000..f0045112e
--- /dev/null
+++ b/appbuilder/tests/test_rerank.py
@@ -0,0 +1,37 @@
+"""
+test rerank
+"""
+import os
+import time
+
+import unittest
+
+import appbuilder
+
+
+@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "")
+class TestReranker(unittest.TestCase):
+
+ def setUp(self):
+ self.reranker = appbuilder.Reranker()
+
+ def test_run(self):
+ ranked_1 = self.reranker("你好", ["他好", "hello?"])
+ time.sleep(1)
+ ranked_2 = self.reranker(appbuilder.Message("你好"), appbuilder.Message(["他好", "hello?"]))
+
+ self.assertEqual(ranked_1.content[0]["relevance_score"], ranked_2.content[0]["relevance_score"])
+ self.assertEqual(ranked_1.content[1]["relevance_score"], ranked_2.content[1]["relevance_score"])
+
+ def test_not_support_model(self):
+ try:
+ _ = appbuilder.Reranker(model="foo")
+ except Exception as e:
+ from appbuilder.core._exception import ModelNotSupportedException
+ assert isinstance(e, ModelNotSupportedException)
+ msg = str(e)
+ assert "Model foo not supported" in msg
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/appbuilder/utils/bce_deploy.py b/appbuilder/utils/bce_deploy.py
index bb7d4f2de..435cd4435 100644
--- a/appbuilder/utils/bce_deploy.py
+++ b/appbuilder/utils/bce_deploy.py
@@ -113,8 +113,8 @@ def build_user_data(self):
+ f"rm {self.tar_file_name}\\n"
+ f"chmod a+x {self.run_script_name}\\n"
+ "yum install -y docker\\n"
- + "docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-cloud:0.9.2\\n"
- + f"docker run -itd --net=host -v /root/test:{workspace} --name appbuilder-sdk registry.baidubce.com/appbuilder/appbuilder-sdk-cloud:0.9.2 {workspace}/{self.run_script_name}"
+ + "docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-cloud:0.9.4\\n"
+ + f"docker run -itd --net=host -v /root/test:{workspace} --name appbuilder-sdk registry.baidubce.com/appbuilder/appbuilder-sdk-cloud:0.9.4{workspace}/{self.run_script_name}"
)
return user_data
diff --git a/cookbooks/README.md b/cookbooks/README.md
index d799811e2..3fc0c2c10 100644
--- a/cookbooks/README.md
+++ b/cookbooks/README.md
@@ -28,7 +28,8 @@
- [RAG应用-问答助手](/cookbooks/end2end_application/rag/console_rag.ipynb)
- [RAG应用-企业问答系统-离线知识生产](/cookbooks/end2end_application/rag/qa_system_1_dataset.ipynb)
- [RAG应用-企业问答系统-在线问答流程](/cookbooks/end2end_application/rag/qa_system_2_dialogue.ipynb)
-- [知识库操作助手](/cookbooks/end2end_application/console_dataset.ipynb)
+- [知识库操作助手](/cookbooks/end2end_application/rag/console_dataset.ipynb)
+- [通过AppBuilder-ToolCall功能实现端云组件联动的Agent](/cookbooks/end2end_application/agent/tool_call.ipynb)
### 进阶应用
- [公有云部署](/cookbooks/advanced_application/cloud_deploy.ipynb)
diff --git a/cookbooks/end2end_application/agent/tool_call.ipynb b/cookbooks/end2end_application/agent/tool_call.ipynb
new file mode 100644
index 000000000..0946ed830
--- /dev/null
+++ b/cookbooks/end2end_application/agent/tool_call.ipynb
@@ -0,0 +1,782 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 前言 - 学习本项目你可以获得什么\n",
+ "- 理论学习:了解AIAgent的基础知识\n",
+ "- 上手实操:深入了解Agent中的FunctionCall运行流程\n",
+ "- 上手实操:入门百度智能云千帆AppBuilder,在十分钟内打造一个个性化AIAgent\n",
+ "- 上手实操:使用AppBuilder-SDK打造一个端云组件联动的进阶Agent"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 1. 项目背景\n",
+ "\n",
+ "### 1.1、 什么是AppBuilder\n",
+ "[百度智能云千帆AppBuilder](https://appbuilder.cloud.baidu.com/)(以下简称AppBuilder)是基于大模型搭建AI原生应用的工作台,旨在降低AI原生应用的开发门槛,赋能开发者和企业快速实现应用搭建。\n",
+ "\n",
+ "平台提供了RAG(检索增强生成)、Agent(智能体)等应用框架,内置了文档问答、表格问答、多轮对话、生成创作等多种应用组件,还包括百度搜索和百度地图等特色组件,以及文本处理、图像处理和语音处理等传统AI组件,支持零代码、低代码、全代码三种开发方式,满足不同开发能力的开发者和企业的场景需求。\n",
+ "\n",
+ "### 1.2、 什么是AppBuilder-SDK\n",
+ "\n",
+ "[百度智能云千帆AppBuilder-SDK](https://github.com/baidubce/app-builder)(以下简称AB-SDK),百度智能云千帆AppBuilder-SDK是百度智能云千帆AppBuilder面向AI原生应用开发者提供的一站式开发平台的客户端SDK。\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 2. 项目介绍 - 通过ToolCall实现端云组件联动的Agent\n",
+ "### 2.1、 什么是Agent\n",
+ "\n",
+ "AIAgent是能够感知环境,基于目标进行决策并执行动作的智能化应用。不同于传统人工智能应用(主要指以规则引擎、机器学习、深度学习等技术为核心)和RPA机器人,AIAgent能够基于目标和对现状能力的认知,在环境约束中,依赖特定资源和现有工具,找到行动规则并将行动拆解为必要的步骤,自主执行步骤,达成目标。\n",
+ "\n",
+ "AIAgent具备三个核心能力:独立思考、自主执行、持续迭代。\n",
+ "- 独立思考是指AlAgent能够根据给定任务目标和约束条件,进行任务规划和问题拆解,形成执行步骤(即工作流);\n",
+ "- 自主执行是指AlAgent能够调取各类组件和工具,按照执行步骤依次执行,实现任务目标;\n",
+ "- 持续选代是指AlAgent能够自动记录任务目标、工作流和执行结果,基于结果反馈,沉淀专家知识和案例。\n",
+ "\n",
+ "AICopilot、AIAgent、大模型等名词在各类文章上经常混淆,此处简要说明下三者的区别。大模型一般是指大模型技术,AlAgent和Al Copilot是基于大模型技术的智能化应用,AlAgent和AlCopilot在功能和场景上存在差别。\n",
+ "\n",
+ "自主性是AIAgent和AI Copilot之间最大的区别。AI Copilot是“副驾驶”,只是提供建议而非决策,AIAgent是“主驾驶”,需要真正做出决策并开展行动。\n",
+ "\n",
+ "
\n",
+ "\n",
+ "### 2.2、 什么是ToolCall\n",
+ "\n",
+ "解释该问题,需要了解以下的知识点:`Agent工具` -> `FunctionCall` - `ToolCall`\n",
+ "\n",
+ "AIAgent 有四大核心组件:记忆、规划、工具和执行。其中工具部分,与我们的开发关系最密切,在各类Agent开发平台/工具中,常被称为“组件”、\"插件\"、\"能力\"等.\n",
+ "\n",
+ "关于Agent的工具的定义与分类,如下图~\n",
+ "\n",
+ "
\n",
+ "\n",
+ "Agent使用工具的流程,一般称为`FunctionCall`,最早由OpenAI提出,并在[Assistant API](https://platform.openai.com/docs/assistants/overview)中广泛应用。\n",
+ "\n",
+ "\n",
+ "ToolCall,则是AppBuilder平台提出的一种进阶的FunctionCall,本质与OpenAI的FunctionCall一致,但具有以下两个特点:\n",
+ "\n",
+ "- **端云组件联动**: Agent 调用工具时,可以同时调用云端和本地组件。\n",
+ "\n",
+ "- **组件类型泛化**: AppBuilder在未来会支持多种类型组件,已经超出了Function的含义,例如数据库、记忆库、工作流等等\n",
+ "\n",
+ "\n",
+ "\n",
+ "### 2.3、 什么是端云组件联动,要解决什么问题\n",
+ "\n",
+ "我们首先从工具的执行位置出发展开~ 在使用如AppBuilder / Coze 等平台开发Agent时,我们可以使用很多平台组件广场中,官方提供的组件,这里组件开箱即用,非常方便。\n",
+ "\n",
+ "
\n",
+ "\n",
+ "但是存在一个问题,基于平台云端组件开发的应用,无法调用内网/局域网/私域的知识与能力,也无法与本地的工具进行联动,限制了Agent的灵活性。\n",
+ "\n",
+ "我们在解决实际业务问题时,常遇到需要访问内网链接API或本地/硬件功能的FunctionCall需求,AppBuilder ToolCall可以解决这个问题:\n",
+ "\n",
+ "* 1、用户可注册一个本地运行的组件到已发布的应用\n",
+ "* 2、由AppBuilder-Agent的云端思考模型进行规划和参数生成\n",
+ "* 3、用户基于生成的参数调用本地组件,并再上传运行结果\n",
+ "* 4、以此实现将本地组件能力嵌入到应用整体流程\n",
+ "\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 3、ToolCall(FunctionCall)基础知识介绍\n",
+ "\n",
+ "### 3.1、Agent是如何调用Tool的\n",
+ "\n",
+ "我们可以将Agent的黑箱拆解为以下几个部分:\n",
+ "1. Agent的背景信息\n",
+ "2. Agent的输入信息\n",
+ "3. Agent的思考过程\n",
+ "4. Agent触发组件调用\n",
+ "5. Agent基于组件输出反思总结\n",
+ "\n",
+ "#### Agent的背景信息包含以下几个部分\n",
+ "- 角色定义描述(Prompt):定义Agent的角色\n",
+ "- 能力描述(Prompt):定义Agent可以干什么\n",
+ "- 工具描述(JsonSchema/Str):将工具的输入和输出,按照规范,定义为一段字符串,作为最终大模型Prompt的一部分\n",
+ "\n",
+ "#### Agent的输入信息包含以下几个部分\n",
+ "- 用户输入(Query/Prompt):用户输入的文本\n",
+ "- 对话相关的文件(File/Url):与本地对话相关的文件路径\n",
+ "\n",
+ "#### Agent的思考过程\n",
+ "AppBuilder-Agent会将背景信息与输入信息,拼接为最终的Prompt,然后调用大模型推理。\n",
+ "\n",
+ "Prompt的一个简单且直观的例子是:\n",
+ "\n",
+ "你是`{角色定义描述}`,你可以做以下事情:`{能力描述}`,你可以使用这些工具:`{工具描述-description}`,工具依赖的输入是:`{工具描述-paramters-properties-name}`,这些输入的格式分别是`{工具描述-paramters-properties-type}`。现在用户的问题是`{用户输入}`,与该问题相关的文件是`{对话相关的文件}`,请你解决用户的这个问题。\n",
+ "\n",
+ "#### Agent触发组件调用\n",
+ "\n",
+ "如果用户的query和组件能够解决的问题匹配,那么大模型就会尝试根据prompt里给出的工具的描述,从query中提炼出该次调用工具所需的参数,生成一个ToolCall命令,交给执行组件的模块去执行。\n",
+ "\n",
+ "例如,我们给出的组件能力是\"查找公司内指定人员的信息\",函数的参数名为\"name\"。当用户输入\"查找张三的信息\",大模型会从query中提炼出参数\"name=张三\"这个信息。\n",
+ "\n",
+ "
\n",
+ "\n",
+ "#### Agent基于组件输出反思总结\n",
+ "\n",
+ "组件运行模块执行组件后,会给出字符串形式的结果给到Agent,Agent会再次将结果拼接为Prompt,然后调用大模型推理。判断用户的需求是否已经解决。如果解决了,则经过一个对话模块,总结用户的需求,并生成一个对话记录。如果未解决,则继续调用大模型推理,尝试调用更多的工具,直到用户的需求被解决。\n",
+ "\n",
+ "### 3.2、开发者如何命令Agent调用本地Tool\n",
+ "\n",
+ "我们以AppBuilder-SDK中的AppBuilder-Client的基础代码为例,介绍开发者应该如何使用ToolCall功能\n",
+ "\n",
+ "\n",
+ "```python\n",
+ "import appbuilder\n",
+ "\n",
+ "# 实例化AppBuilderClient\n",
+ "app_client = appbuilder.AppBuilderClient(app_id)\n",
+ "conversation_id = app_client.create_conversation()\n",
+ "\n",
+ "# 第一次对话,输入原始的query 和 工具描述\n",
+ "message_1 = app_client.run(\n",
+ " conversation_id=conversation_id,\n",
+ " query=\"请问张三同学的生日是哪天?\",\n",
+ " tools=tools\n",
+ ")\n",
+ "tool_call = message_1.content.events[-1].tool_calls[-1]\n",
+ "tool_call_id = tool_call.id\n",
+ "\n",
+ "# 第二次对话,在本地执行组件后,上传组件的运行结果\n",
+ "tool_call_result = \"张三同学的生日是2008年8月8日\"\n",
+ "message_2 = app_client.run(\n",
+ " conversation_id=conversation_id,\n",
+ " tool_outputs=[{\n",
+ " \"tool_call_id\": tool_call_id,\n",
+ " \"output\": tool_call_result\n",
+ " }]\n",
+ ")\n",
+ "print(message_2.content)\n",
+ "```\n",
+ "\n",
+ "其中`AppBuilderClient`的`run`方法是核心,我们展开该函数的定义和参数介绍:\n",
+ "\n",
+ "`AppBuilderClient().run() -> Message`\n",
+ "\n",
+ "```python\n",
+ "def run(self, conversation_id: str,\n",
+ " query: str = \"\",\n",
+ " file_ids: list = [],\n",
+ " stream: bool = False,\n",
+ " tools: list[data_class.Tool] = None,\n",
+ " tool_outputs: list[data_class.ToolOutput] = None,\n",
+ " **kwargs\n",
+ " ) -> Message:\n",
+ " r\"\"\"\n",
+ " 参数:\n",
+ " query (str: 必须): query内容\n",
+ " conversation_id (str, 必须): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话\n",
+ " file_ids(list[str], 可选):\n",
+ " stream (bool, 可选): 为True时,流式返回,需要将message.content.answer拼接起来才是完整的回答;为False时,对应非流式返回\n",
+ " tools(list[data_class.Tools], 可选): 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None\n",
+ " tool_outputs(list[data_class.ToolOutput], 可选): 工具输出列表,格式为list[ToolOutput], ToolOutputd内容为本地的工具执行结果,以自然语言/json dump str描述,默认为None\n",
+ " 返回: message (obj: `Message`): 对话结果.\n",
+ " \"\"\"\n",
+ " pass\n",
+ "```\n",
+ "\n",
+ "\n",
+ "| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 |\n",
+ "| --------------- | ---------------- | -------- | ------------------------------------------------------------ | ----------------- |\n",
+ "| conversation_id | String | 是 | 会话ID | |\n",
+ "| query | String | 否 | query问题内容 | \"今天天气怎么样?\" |\n",
+ "| file_ids | list[String] | 否 | 对话可引用的文档ID | |\n",
+ "| stream | Bool | 否 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | False |\n",
+ "| tools | List[Tool] | 否 | 一个列表,其中每个字典对应一个工具的配置 | |\n",
+ "| tools[0] | Tool | 否 | 工具配置 | |\n",
+ "| +type | String | 否 | 枚举:
**file_retrieval**: 知识库检索工具能够理解文档内容,支持用户针对文档内容的问答。
**code_interpreter**: 代码解释器, 代码解释器能够生成并执行代码,从而协助用户解决复杂问题,涵盖科学计算(包括普通数学计算题)、数据可视化、文件编辑处理(图片、PDF文档、视频、音频等)、文件格式转换(如WAV、MP3、text、SRT、PNG、jpg、MP4、GIF、MP3等)、数据分析&清洗&处理(文件以excel、csv格式为主)、机器学习&深度学习建模&自然语言处理等多个领域。
**function**: 支持fucntion call模式调用工具 | |\n",
+ "| +function | Function | 否 | Function工具描述
仅当**type为**`**function**` 时需要且必须填写 | |\n",
+ "| ++name | String | 否 | 函数名
只允许数字、大小写字母和中划线和下划线,最大长度为64个字符。一次运行中唯一。 | |\n",
+ "| ++description | String | 否 | 工具描述 | |\n",
+ "| ++parameters | Dict | 否 | 工具参数, json_schema格式 | |\n",
+ "| tool_outputs | List[ToolOutput] | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | |\n",
+ "| tool_outputs[0] | ToolOutput | 否 | 工具执行结果 | |\n",
+ "| +tool_call_id | String | 否 | 工具调用ID | |\n",
+ "| +output | String | 否 | 工具输出 | |\n",
+ "\n",
+ "`Tool`与`Function`是本地组件的描述,类型为object,其定义如下:\n",
+ "\n",
+ "```python\n",
+ "class Tool(BaseModel):\n",
+ " type: str = \"function\"\n",
+ " function: Function = Field(..., description=\"工具信息\")\n",
+ "\n",
+ "class Function(BaseModel):\n",
+ " name: str = Field(..., description=\"工具名称\")\n",
+ " description: str = Field(..., description=\"工具描述\")\n",
+ " parameters: dict = Field(..., description=\"工具参数, json_schema格式\")\n",
+ "```\n",
+ "\n",
+ "`ToolOutput`是本地组件的执行结果,需要再次上传到Agent,参与思考,类型为object,其定义如下:\n",
+ "```python\n",
+ "class ToolOutput(BaseModel):\n",
+ " tool_call_id: str = Field(..., description=\"工具调用ID\")\n",
+ " output: str = Field(..., description=\"工具输出\")\n",
+ "\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 4、ToolCall的第一个例子\n",
+ "\n",
+ "我们继续以上文中提到的查找张三生日为例,看一下完整的流程是怎么样的\n",
+ "\n",
+ "### 前置工作,在AppBuilder平台上创建一个白板应用(可以跳过)\n",
+ "\n",
+ "网页链接:https://appbuilder.cloud.baidu.com/\n",
+ "\n",
+ "注册后,进入控制台:https://console.bce.baidu.com/ai_apaas/dialogHome\n",
+ "\n",
+ "点击左上角的【创建应用】-> 【AI自动配置】,我们输入以下Prompt,自动生成Agent:`你是智能问题解决者,自动集成多种工具组件,解决用户各类问题`\n",
+ "\n",
+ "
\n",
+ "\n",
+ "最终生成的Agent长这个样子:\n",
+ "\n",
+ "
\n",
+ "\n",
+ "而后点击【发布】,分别在控制台的左侧【个人空间】获取`app_id`,在【我的密钥】获取`APPBUILDER_TOEN`后,就可以开始后续的操作了。\n",
+ "\n",
+ "当然,下面的示例代码中,我们已经提供了可以直接运行的试用Token与App,你可以直接上手运行"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "vscode": {
+ "languageId": "plaintext"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "import appbuilder\n",
+ "\n",
+ "# AppBuilder Token,此处为试用Token,速度Quota有限制,正式使用替换为您个人的Token\n",
+ "os.environ[\"APPBUILDER_TOKEN\"] = \"bce-v3/ALTAK-n5AYUIUJMarF7F7iFXVeK/1bf65eed7c8c7efef9b11388524fa1087f90ea58\"\n",
+ "\n",
+ "# 应用为:智能问题解决者\n",
+ "app_id = \"b9473e78-754b-463a-916b-f0a9097a8e5f\"\n",
+ "app_client = appbuilder.AppBuilderClient(app_id)\n",
+ "conversation_id = app_client.create_conversation()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "首次提问一个问题,应用不具备该能力,通过回答可以印证\n",
+ "\n",
+ "- 由于并没有关于张三同学的信息,所以Agent无法实现查询"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "vscode": {
+ "languageId": "plaintext"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "message_1 = app_client.run(\n",
+ " conversation_id=conversation_id,\n",
+ " query=\"请问本公司的张三同学的生日是哪天?\",\n",
+ ")\n",
+ "print(\"Agent第一次回答: {}\".format(message_1.content.answer))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**output**\n",
+ "```\n",
+ "Agent第一次回答: 为了回答这个问题,我们首先需要明确几个关键点:\n",
+ "\n",
+ "1. **问题理解**:\n",
+ " - 需要确定的是“张三同学的生日”。\n",
+ "\n",
+ "2. **工具选择**:\n",
+ " - 由于问题涉及的是特定个人的信息(张三的生日),这通常不是通过工具或系统查询能得到的,而是需要通过公司内部的人事记录或直接询问张三本人来获取。\n",
+ "\n",
+ "3. **解决方案生成**:\n",
+ " - **步骤一**:首先,尝试访问公司的人事系统或员工档案,看是否有张三的生日信息记录。\n",
+ " - **步骤二**:如果人事系统或员工档案中没有相关信息,或者你不具备访问权限,那么可以考虑直接询问张三本人或其同事,看是否有人知道他的生日。\n",
+ " - **步骤三**:如果以上方法都不可行,还可以尝试联系公司的人力资源部门,看他们是否能提供相关信息。\n",
+ "\n",
+ "4. **注意事项**:\n",
+ " - 在尝试获取张三的生日信息时,要确保遵守公司的隐私政策和相关法律法规,不要侵犯张三的隐私权。\n",
+ " - 如果张三不愿意透露他的生日信息,应尊重他的选择,并停止进一步询问。\n",
+ "\n",
+ "5. **可能遇到的问题**:\n",
+ " - 人事系统或员工档案中可能没有张三的生日信息。\n",
+ " - 张三或其同事可能不愿意透露生日信息。\n",
+ " - 人力资源部门可能因隐私政策而无法提供相关信息。\n",
+ "\n",
+ "综上所述,要确定张三的生日,最直接且尊重隐私的方法是直接询问张三本人,或者通过公司正式渠道(如人力资源部门)在遵守隐私政策的前提下进行查询。\n",
+ "```\n",
+ "\n",
+ "\n",
+ "##### 赋予应用一个本地查询组件能力\n",
+ "\n",
+ "- 这里我们使用info_dict模拟一个数据库查询的返回结果"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "vscode": {
+ "languageId": "plaintext"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def get_person_infomation(name: str):\n",
+ " info_dict = {\n",
+ " \"张三\": \"1980年1月1日\",\n",
+ " \"李四\": \"1975年12月31日\",\n",
+ " \"刘伟\": \"1990年12月30日\"\n",
+ " }\n",
+ "\n",
+ " if name in info_dict:\n",
+ " return f\"您要查找的{name}的生日是:{info_dict[name]}\"\n",
+ " else:\n",
+ " return f\"您要查找的{name}的信息我们暂未收录,请联系管理员添加。\"\n",
+ " \n",
+ "# 创建工具的描述:json_schema格式\n",
+ "tools = [\n",
+ " {\n",
+ " \"type\": \"function\",\n",
+ "\"function\": {\n",
+ " \"name\": \"get_person_infomation\",\n",
+ " \"description\": \"查找公司内指定人员的信息\",\n",
+ " \"parameters\": {\n",
+ " \"type\": \"object\",\n",
+ " \"properties\": {\n",
+ " \"name\": {\n",
+ " \"type\": \"string\",\n",
+ " \"description\": \"人员名称,例如:张三、李四\",\n",
+ " },\n",
+ " },\n",
+ " \"required\": [\"name\"],\n",
+ " },\n",
+ "},\n",
+ " }\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "- 现在我们已经完成了本地tool组件的设计,接下来我们将tool的功能赋予Client应用"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "vscode": {
+ "languageId": "plaintext"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "message_2 = app_client.run(\n",
+ " conversation_id=conversation_id,\n",
+ " query=\"请问本公司的张三同学的生日是哪天?\",\n",
+ " tools=tools\n",
+ ")\n",
+ "print(\"Agent的中间思考过程:\")\n",
+ "print(message_2.content.events[-1].model_dump_json(indent=4))\n",
+ "print(\"Agent思考结束,等待我们上传本地结果\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**output**\n",
+ "这部分输出为Client应用的思考过程\n",
+ "```\n",
+ "Agent的中间思考过程:\n",
+ "{\n",
+ " \"code\": 0,\n",
+ " \"message\": \"\",\n",
+ " \"status\": \"interrupt\",\n",
+ " \"event_type\": \"Interrupt\",\n",
+ " \"content_type\": \"contexts\",\n",
+ " \"detail\": {\n",
+ " \"text\": {\n",
+ " \"function_call\": {\n",
+ " \"thought\": \"用户想要查询公司内张三同学的生日信息,这个需求很明确,且背景信息也足够。我可以使用get_person_infomation工具来查找张三的生日信息。\",\n",
+ " \"name\": \"get_person_infomation\",\n",
+ " \"arguments\": {\n",
+ " \"name\": \"张三\"\n",
+ " },\n",
+ " \"usage\": {\n",
+ " \"prompt_tokens\": 697,\n",
+ " \"completion_tokens\": 87,\n",
+ " \"total_tokens\": 784,\n",
+ " \"name\": \"ERNIE-4.0-Turbo-8K\",\n",
+ " \"type\": \"plan\"\n",
+ " },\n",
+ " \"tool_call_id\": \"c23309f7-e24a-4476-85e2-3ef9cfd4f6ed\"\n",
+ " },\n",
+ " \"used_tool\": []\n",
+ "...\n",
+ " ]\n",
+ "}\n",
+ "Agent思考结束,等待我们上传本地结果\n",
+ "```\n",
+ "\n",
+ "- 大模型下发了调用本地函数的参数,我们使用这个参数调用本地函数"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "vscode": {
+ "languageId": "plaintext"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "tool_call = message_2.content.events[-1].tool_calls[-1]\n",
+ "tool_call_id = tool_call.id\n",
+ "tool_call_argument = tool_call.function.arguments\n",
+ "local_func_result = get_person_infomation(**tool_call_argument)\n",
+ "print(\"local_func_result: {}\\n\".format(local_func_result))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**output**\n",
+ "```\n",
+ "local_func_result: 您要查找的张三的生日是:1980年1月1日\n",
+ "```\n",
+ "\n",
+ "- 向应用返回本地运行的结果,完成本地函数toolcall调用"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "vscode": {
+ "languageId": "plaintext"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "message_3 = app_client.run(\n",
+ " conversation_id=conversation_id,\n",
+ " tool_outputs=[{\n",
+ " \"tool_call_id\": tool_call_id,\n",
+ " \"output\": local_func_result\n",
+ " }]\n",
+ ")\n",
+ "print(\"Agent 拥有了本地函数调用能力后,回答是: {}\".format(message_3.content.answer))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**output**\n",
+ "```\n",
+ "Agent的中间思考过程:\n",
+ "{\n",
+ " \"code\": 0,\n",
+ " \"message\": \"\",\n",
+ " \"status\": \"interrupt\",\n",
+ " \"event_type\": \"Interrupt\",\n",
+ " \"content_type\": \"contexts\",\n",
+ " \"detail\": {\n",
+ " \"text\": {\n",
+ " \"function_call\": {\n",
+ " \"thought\": \"用户想要查询公司内张三同学的生日信息,这个需求很明确,且背景信息也足够。我可以使用get_person_infomation工具来查找张三的生日信息。\",\n",
+ " \"name\": \"get_person_infomation\",\n",
+ " \"arguments\": {\n",
+ " \"name\": \"张三\"\n",
+ " },\n",
+ " \"usage\": {\n",
+ " \"prompt_tokens\": 697,\n",
+ " \"completion_tokens\": 87,\n",
+ " \"total_tokens\": 784,\n",
+ " \"name\": \"ERNIE-4.0-Turbo-8K\",\n",
+ " \"type\": \"plan\"\n",
+ " },\n",
+ " \"tool_call_id\": \"c23309f7-e24a-4476-85e2-3ef9cfd4f6ed\"\n",
+ " },\n",
+ " \"used_tool\": []\n",
+ "...\n",
+ " ]\n",
+ "}\n",
+ "Agent思考结束,等待我们上传本地结果\n",
+ "\n",
+ "Output is truncated. View as a scrollable element or open in a text editor. Adjust cell output settings...\n",
+ "\n",
+ "local_func_result: 您要查找的张三的生日是:1980年1月1日\n",
+ "\n",
+ "Agent 拥有了本地函数调用能力后,回答是: # 解决方案\n",
+ "\n",
+ "## 问题分析\n",
+ "\n",
+ "用户想要查询公司内张三同学的生日信息。这是一个明确且具体的需求,我们可以通过`get_person_infomation`工具来获取这一信息。\n",
+ "\n",
+ "## 工具运用\n",
+ "\n",
+ "1. **工具选择**:`get_person_infomation`\n",
+ "2. **参数设置**:\n",
+ "\n",
+ "\t* `name`:张三\n",
+ "\n",
+ "3. **执行结果**:张三的生日是1980年1月1日。\n",
+ "\n",
+ "## 解决方案步骤\n",
+ "\n",
+ "1. 使用`get_person_infomation`工具,并设置参数`name`为“张三”。\n",
+ "2. 等待工具执行,并获取张三的生日信息。\n",
+ "3. 将获取到的生日信息(1980年1月1日)告知用户。\n",
+ "\n",
+ "## 注意事项\n",
+ "\n",
+ "* 确保在使用`get_person_infomation`工具时,输入的姓名与公司内部记录的姓名完全一致,以避免查询错误。\n",
+ "* 如果工具返回“未找到”或类似结果,请检查姓名是否有误或联系公司人事部门确认信息。\n",
+ "\n",
+ "通过上述步骤,我们可以准确地回答用户的问题,并提供张三的生日信息。\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 5、ToolCal第二个例子-调用本地工具并且代码更简洁\n",
+ "\n",
+ "我们可以使用AppBuilderClient应用来执行tool_call操作,完成指定的命令,但是需要自己配置client的思考与运行流程,较为繁琐。SDK提供了使用AppBuilderEventHandler简化tool_call操作的功能\n",
+ "\n",
+ "##### 配置运行环境&导入Client应用"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "import appbuilder\n",
+ "\n",
+ "\n",
+ "# AppBuilder Token,此处为试用Token\n",
+ "os.environ[\"APPBUILDER_TOKEN\"] = \"bce-v3/ALTAK-n5AYUIUJMarF7F7iFXVeK/1bf65eed7c8c7efef9b11388524fa1087f90ea58\"\n",
+ "\n",
+ "# 应用为:智能问题解决者\n",
+ "app_id = \"b9473e78-754b-463a-916b-f0a9097a8e5f\"\n",
+ "app_client = appbuilder.AppBuilderClient(app_id)\n",
+ "conversation_id = app_client.create_conversation()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "##### 继承AppBuilderEventHandler类,并实现针对各类型event的处理方法"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from appbuilder.core.console.appbuilder_client.event_handler import AppBuilderEventHandler\n",
+ "class MyEventHandler(AppBuilderEventHandler):\n",
+ " def execute_local_command(self, cmd: str):\n",
+ " import subprocess\n",
+ " try:\n",
+ " result = subprocess.check_output(cmd, shell=True).decode(\"utf-8\")\n",
+ " if result.strip() == \"\":\n",
+ " return \"命令执行成功,无返回值\"\n",
+ " return result\n",
+ " except Exception as e:\n",
+ " return str(e)\n",
+ " \n",
+ " def interrupt(self, run_context, run_response):\n",
+ " thought = run_context.current_thought\n",
+ " # 绿色打印\n",
+ " print(\"\\033[1;32m\", \"-> Agent 中间思考: \", thought, \"\\033[0m\")\n",
+ "\n",
+ " tool_output = []\n",
+ " for tool_call in run_context.current_tool_calls:\n",
+ " tool_call_id = tool_call.id\n",
+ " tool_res = self.execute_local_command(\n",
+ " **tool_call.function.arguments)\n",
+ " # 蓝色打印\n",
+ " print(\"\\033[1;34m\", \"-> 本地ToolCall结果: \\n\", tool_res, \"\\033[0m\\n\")\n",
+ " tool_output.append(\n",
+ " {\n",
+ " \"tool_call_id\": tool_call_id,\n",
+ " \"output\": tool_res\n",
+ " }\n",
+ " )\n",
+ " return tool_output\n",
+ " \n",
+ " def success(self, run_context, run_response):\n",
+ " print(\"\\n\\033[1;31m\",\"-> Agent 非流式回答: \\n\", run_response.answer, \"\\033[0m\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "##### 定义本地的tools工具\n",
+ "\n",
+ "通过`subprocess.check_output`方法,可以在终端中执行命令,并返回执行结果"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "tools = [\n",
+ " {\n",
+ " \"type\": \"function\",\n",
+ " \"function\": {\n",
+ " \"name\": \"execute_local_command\",\n",
+ " \"description\": \"可以在bash环境中,执行输入的指令,注意,一次只能执行一个原子命令。例如:ls\",\n",
+ " \"parameters\": {\n",
+ " \"type\": \"object\",\n",
+ " \"properties\": {\n",
+ " \"cmd\": {\n",
+ " \"type\": \"string\",\n",
+ " \"description\": \"需要执行的指令\",\n",
+ " },\n",
+ " },\n",
+ " \"required\": [\"cmd\"],\n",
+ " },\n",
+ " },\n",
+ " }\n",
+ "]\n",
+ "\n",
+ "with app_client.run_with_handler(\n",
+ " conversation_id = conversation_id,\n",
+ " query = \"请问当前文件夹下有哪些文件?如果没有test.txt文件,请新建一个test.txt文件,内容为:Hello World!\",\n",
+ " tools = tools,\n",
+ " event_handler = MyEventHandler(),\n",
+ " ) as run:\n",
+ " run.until_done()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**output**\n",
+ "```\n",
+ " -> Agent 中间思考: 首先,我需要使用execute_local_command工具来执行'ls'命令,列出当前文件夹下的所有文件。然后,我需要检查输出中是否存在test.txt文件。如果不存在,我将再次使用execute_local_command工具来执行'echo \"Hello World\" > test.txt'命令,以创建并写入test.txt文件。 \n",
+ " -> 本地ToolCall结果: \n",
+ " multi_tool_call.ipynb\n",
+ "multi_tool_call.py\n",
+ "multi_tool_call_with_handler.ipynb\n",
+ "multi_tool_call_with_handler.py\n",
+ "sdk_ knowledgebase.ipynb\n",
+ "sdk_trace.ipynb\n",
+ "simple_tool_call.ipynb\n",
+ "simple_tool_call.py\n",
+ "tmp.log\n",
+ "黑神话(悟空).pdf\n",
+ " \n",
+ "\n",
+ " -> Agent 中间思考: 根据execute_local_command工具的返回结果,当前文件夹下并没有test.txt文件。因此,我需要使用execute_local_command工具来执行'echo \"Hello World\" > test.txt'命令,以创建并写入test.txt文件。 \n",
+ " -> 本地ToolCall结果: \n",
+ " 命令执行成功,无返回值 \n",
+ "\n",
+ "\n",
+ " -> Agent 非流式回答: \n",
+ " 当前文件夹下的文件包括:\n",
+ "\n",
+ "- multi_tool_call.ipynb\n",
+ "- multi_tool_call.py\n",
+ "- multi_tool_call_with_handler.ipynb\n",
+ "...\n",
+ "- tmp.log\n",
+ "- 黑神话(悟空).pdf\n",
+ "\n",
+ "经过检查,发现当前文件夹下**不存在**test.txt文件。因此,已经为您新建了一个test.txt文件,并写入了内容“Hello World!”。 \n",
+ "```\n",
+ "\n",
+ "- 使用AppBuilderEventHandler架构可以简化client的交互方式"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 6、项目总结\n",
+ "\n",
+ "本项目通过多个知识点的学习,以及两个使用AppBuilder-SDK的实操,最终完成了一个支持ToolCall AIAgent的构建。\n",
+ "\n",
+ "- 理论学习:了解AIAgent的基础知识\n",
+ "- 上手实操:深入了解Agent中的FunctionCall运行流程\n",
+ "- 上手实操:入门百度智能云千帆AppBuilder,在十分钟内打造一个个性化AIAgent\n",
+ "- 上手实操:使用AppBuilder-SDK打造一个端云组件联动的进阶Agent\n",
+ "\n",
+ "\n",
+ "希望您可以不吝`Star`,给`AppBuilder-SDK`一些鼓励,期待您的`PR`,一起共建AIAgent生态。\n",
+ "\n",
+ "Github地址:https://github.com/baidubce/app-builder\n",
+ "\n",
+ "
\n",
+ "\n",
+ "最后,您也可以进入`AppBuilder-SDK`的WX交流群,和大家一起交流AppBuilder使用及开发心得。\n",
+ "\n",
+ "
"
+ ]
+ }
+ ],
+ "metadata": {
+ "language_info": {
+ "name": "python"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/cookbooks/end2end_application/console_dataset.ipynb b/cookbooks/end2end_application/rag/console_dataset.ipynb
similarity index 100%
rename from cookbooks/end2end_application/console_dataset.ipynb
rename to cookbooks/end2end_application/rag/console_dataset.ipynb
diff --git "a/cookbooks/end2end_application/image/dataset\347\244\272\344\276\213.png" "b/cookbooks/end2end_application/rag/image/dataset\347\244\272\344\276\213.png"
similarity index 100%
rename from "cookbooks/end2end_application/image/dataset\347\244\272\344\276\213.png"
rename to "cookbooks/end2end_application/rag/image/dataset\347\244\272\344\276\213.png"
diff --git a/docs/develop_guide/README.md b/docs/develop_guide/README.md
index 71688691c..4a2bb8b32 100644
--- a/docs/develop_guide/README.md
+++ b/docs/develop_guide/README.md
@@ -10,7 +10,7 @@
当前已集成Python版本AppBuilder-SDK 0.7.1及相关依赖,方便开发者融入个人已有的大模型应用程序。此部分仍在不断建设中。
二次开发可以采用官方提供的开发镜像,便于快速安装各种依赖库。
``` shell
-docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-devel:0.9.2
+docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-devel:0.9.4
```
### 消息(Message)
diff --git a/docs/quick_start/changelog.md b/docs/quick_start/changelog.md
index f62c9d471..4b54b44d1 100644
--- a/docs/quick_start/changelog.md
+++ b/docs/quick_start/changelog.md
@@ -9,7 +9,7 @@
* 模型列表获取:与千帆大模型平台模型名打通,可动态获取当前账号模型名,并在组件中使用[获取模型列表](/docs/basic_module/get_model_list.md)
* 可通过官方镜像开发和运行实例代码[二次开发](/docs/develop_guide/README.md)
* **2024.02.27 v0.4.0版本发布** [Release Note](https://github.com/baidubce/app-builder/releases/tag/0.4.0)
- * AppBuilder Console SDK发布[知识集合Cookbook](/cookbooks/end2end_application/console_dataset.ipynb),[RAG调用Cookbook](/cookbooks/end2end_application/rag/rag.ipynb)
+ * AppBuilder Console SDK发布[知识集合Cookbook](/cookbooks/end2end_application/rag/console_dataset.ipynb),[RAG调用Cookbook](/cookbooks/end2end_application/rag/rag.ipynb)
* 大模型组件新增:Excel2Figure(基于Excel信息画图表)
* AI能力引擎组件新增&更新:植物识别、动物识别、表格文字识别V2、手写文字识别、二维码识别、身份证混贴识别、文档矫正识别、图像内容理解、流式TTS
* AgentRuntime:新增[Cookbook](/cookbooks/components/agent_runtime.ipynb)
@@ -51,4 +51,7 @@
* 新增`PPTGenerationFromFile`、`PPTGenerationFromInstruction`、`PPTGenerationFromPaper`三个组件
* **2024.08.20 v0.9.3版本发布** [ReleaseNote](https://github.com/baidubce/app-builder/releases/tag/0.9.3)
* Text2Image组件接口及效果更新
- * ImageUnderstand组件接口及效果更新
\ No newline at end of file
+ * ImageUnderstand组件接口及效果更新
+* **2024.09.04 v0.9.4版本发布** [ReleaseNote](https://github.com/baidubce/app-builder/releases/tag/0.9.4)
+ * AppBuilderClient新增tool_choice / end_user_id功能
+ * 增加VScode setting,优化开发者使用体验
\ No newline at end of file
diff --git a/docs/quick_start/install.md b/docs/quick_start/install.md
index 0c9ff55fc..fb1e0803a 100644
--- a/docs/quick_start/install.md
+++ b/docs/quick_start/install.md
@@ -16,20 +16,20 @@ pip install --upgrade appbuilder-sdk
com.baidubce
appbuilder
- 0.9.2
+ 0.9.4
```
#### Gradle
对于Kotlin DSL,在build.gradle.kts的dependencies中添加依赖
```kotlin
-implementation("com.baidubce:appbuilder:0.9.2")
+implementation("com.baidubce:appbuilder:0.9.4")
```
对于Groovy DSL,在build.gradle的dependencies中添加依赖
```groovy
-implementation 'com.baidubce:appbuilder:0.9.2'
+implementation 'com.baidubce:appbuilder:0.9.4'
```
#### 本地导入
-点击[链接](https://repo1.maven.org/maven2/com/baidubce/appbuilder/0.9.2/appbuilder-0.9.2.jar) 下载Jar包,将Jar包导入到项目目录下。
+点击[链接](https://repo1.maven.org/maven2/com/baidubce/appbuilder/0.9.4/appbuilder-0.9.4.jar) 下载Jar包,将Jar包导入到项目目录下。
### Go (仅支持调用端到端应用)
> 支持Go 1.18.1以上版本
@@ -40,5 +40,5 @@ go get github.com/baidubce/app-builder/go/appbuilder
### Docker (当前仅集成了Python版本AppBuilder-SDK)
``` shell
-docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-devel:0.9.2
+docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-devel:0.9.4
```
diff --git a/go/appbuilder/agent_builder.go b/go/appbuilder/agent_builder.go
index bd6a5012a..a78791f1d 100644
--- a/go/appbuilder/agent_builder.go
+++ b/go/appbuilder/agent_builder.go
@@ -26,7 +26,6 @@ import (
"os"
"path/filepath"
"time"
- "io/ioutil"
)
// Deprecated: 请使用AppBuilderClient 代替 AgentBuilder
@@ -74,11 +73,11 @@ func (t *AgentBuilder) CreateConversation() (string, error) {
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- data, err = ioutil.ReadAll(resp.Body)
+ data, err = io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- rsp := make(map[string]interface{})
+ rsp := make(map[string]any)
if err := json.Unmarshal(data, &rsp); err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -126,11 +125,11 @@ func (t *AgentBuilder) UploadLocalFile(conversationID string, filePath string) (
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- body, err := ioutil.ReadAll(resp.Body)
+ body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- rsp := make(map[string]interface{})
+ rsp := make(map[string]any)
if err := json.Unmarshal(body, &rsp); err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -145,7 +144,7 @@ func (t *AgentBuilder) Run(conversationID string, query string, fileIDS []string
if len(conversationID) == 0 {
return nil, errors.New("conversationID mustn't be empty")
}
- m := map[string]interface{}{"app_id": t.appID,
+ m := map[string]any{"app_id": t.appID,
"conversation_id": conversationID,
"query": query,
"file_ids": fileIDS,
diff --git a/go/appbuilder/agent_builder_data.go b/go/appbuilder/agent_builder_data.go
index fffe93eeb..e810154d1 100644
--- a/go/appbuilder/agent_builder_data.go
+++ b/go/appbuilder/agent_builder_data.go
@@ -20,7 +20,6 @@ import (
"io"
"reflect"
"strings"
- "io/ioutil"
)
func (t *AgentBuilderAnswer) transform(inp *AgentBuilderRawResponse) {
@@ -59,11 +58,11 @@ type AgentBuilderStreamIterator struct {
func (t *AgentBuilderStreamIterator) Next() (*AgentBuilderAnswer, error) {
data, err := t.r.ReadMessageLine()
- if err != nil && !(err ==io.EOF) {
+ if err != nil && !(err == io.EOF) {
t.body.Close()
return nil, fmt.Errorf("requestID=%s, err=%v", t.requestID, err)
}
- if err != nil && err ==io.EOF {
+ if err != nil && err == io.EOF {
t.body.Close()
return nil, err
}
@@ -89,7 +88,7 @@ type AgentBuilderOnceIterator struct {
}
func (t *AgentBuilderOnceIterator) Next() (*AgentBuilderAnswer, error) {
- data, err := ioutil.ReadAll(t.body)
+ data, err := io.ReadAll(t.body)
if err != nil {
return nil, fmt.Errorf("requestID=%s, err=%v", t.requestID, err)
}
diff --git a/go/appbuilder/app_builder_client.go b/go/appbuilder/app_builder_client.go
index 760a7c00f..93548dedc 100644
--- a/go/appbuilder/app_builder_client.go
+++ b/go/appbuilder/app_builder_client.go
@@ -28,7 +28,6 @@ import (
"path/filepath"
"strconv"
"time"
- "io/ioutil"
)
func GetAppList(req GetAppListRequest, config *SDKConfig) ([]App, error) {
@@ -44,7 +43,7 @@ func GetAppList(req GetAppListRequest, config *SDKConfig) ([]App, error) {
header.Set("Content-Type", "application/json")
request.Header = header
- reqMap := make(map[string]interface{})
+ reqMap := make(map[string]any)
reqJson, _ := json.Marshal(req)
json.Unmarshal(reqJson, &reqMap)
params := url.Values{}
@@ -75,7 +74,7 @@ func GetAppList(req GetAppListRequest, config *SDKConfig) ([]App, error) {
if err != nil {
return nil, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- data, err := ioutil.ReadAll(resp.Body)
+ data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -109,11 +108,11 @@ type AppBuilderClient struct {
// 在 AppBuilderClient 结构体中添加 Getter 方法
func (t *AppBuilderClient) GetSdkConfig() *SDKConfig {
- return t.sdkConfig
+ return t.sdkConfig
}
func (t *AppBuilderClient) GetClient() HTTPClient {
- return t.client
+ return t.client
}
type HTTPClient interface {
@@ -148,7 +147,7 @@ func (t *AppBuilderClient) CreateConversation() (string, error) {
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- rsp := make(map[string]interface{})
+ rsp := make(map[string]any)
if err := json.Unmarshal(data, &rsp); err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -159,7 +158,6 @@ func (t *AppBuilderClient) CreateConversation() (string, error) {
return val.(string), nil
}
-
func (t *AppBuilderClient) UploadLocalFile(conversationID string, filePath string) (string, error) {
var data bytes.Buffer
w := multipart.NewWriter(&data)
@@ -197,11 +195,11 @@ func (t *AppBuilderClient) UploadLocalFile(conversationID string, filePath strin
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- body, err := ioutil.ReadAll(resp.Body)
+ body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- rsp := make(map[string]interface{})
+ rsp := make(map[string]any)
if err := json.Unmarshal(body, &rsp); err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -216,12 +214,12 @@ func (t *AppBuilderClient) Run(conversationID string, query string, fileIDS []st
if len(conversationID) == 0 {
return nil, errors.New("conversationID mustn't be empty")
}
- m := map[string]interface{}{
- "app_id": t.appID,
- "conversation_id": conversationID,
- "query": query,
- "file_ids": fileIDS,
- "stream": stream,
+ m := map[string]any{
+ "app_id": t.appID,
+ "conversation_id": conversationID,
+ "query": query,
+ "file_ids": fileIDS,
+ "stream": stream,
}
request := http.Request{}
@@ -237,7 +235,7 @@ func (t *AppBuilderClient) Run(conversationID string, query string, fileIDS []st
request.Header = header
data, _ := json.Marshal(m)
request.Body = NopCloser(bytes.NewReader(data))
- request.ContentLength = int64(len(data)) //手动设置长度
+ request.ContentLength = int64(len(data)) // 手动设置长度
t.sdkConfig.BuildCurlCommand(&request)
@@ -276,8 +274,8 @@ func (t *AppBuilderClient) RunWithToolCall(req AppBuilderClientRunRequest) (AppB
request.Header = header
data, _ := json.Marshal(req)
request.Body = NopCloser(bytes.NewReader(data))
- request.ContentLength = int64(len(data)) //手动设置长度
-
+ request.ContentLength = int64(len(data)) // 手动设置长度
+
t.sdkConfig.BuildCurlCommand(&request)
resp, err := t.client.Do(&request)
if err != nil {
diff --git a/go/appbuilder/app_builder_client_data.go b/go/appbuilder/app_builder_client_data.go
index ceb5a7557..2b5724438 100644
--- a/go/appbuilder/app_builder_client_data.go
+++ b/go/appbuilder/app_builder_client_data.go
@@ -20,7 +20,6 @@ import (
"io"
"reflect"
"strings"
- "io/ioutil"
)
const (
@@ -49,9 +48,11 @@ type AppBuilderClientRunRequest struct {
AppID string `json:"app_id"`
Query string `json:"query"`
Stream bool `json:"stream"`
+ EndUserID *string `json:"end_user_id"`
ConversationID string `json:"conversation_id"`
Tools []Tool `json:"tools"`
ToolOutputs []ToolOutput `json:"tool_outputs"`
+ ToolChoice *ToolChoice `json:"tool_choice"`
}
type Tool struct {
@@ -60,9 +61,9 @@ type Tool struct {
}
type Function struct {
- Name string `json:"name"`
- Description string `json:"description"`
- Parameters map[string]interface{} `json:"parameters"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Parameters map[string]any `json:"parameters"`
}
type ToolOutput struct {
@@ -70,6 +71,16 @@ type ToolOutput struct {
Output string `json:"output" description:"工具输出"`
}
+type ToolChoice struct {
+ Type string `json:"type"`
+ Function ToolChoiceFunction `json:"function"`
+}
+
+type ToolChoiceFunction struct {
+ Name string `json:"name"`
+ Input map[string]interface{} `json:"input"`
+}
+
type AgentBuilderRawResponse struct {
RequestID string `json:"request_id"`
Date string `json:"date"`
@@ -111,7 +122,7 @@ type Event struct {
EventType string
ContentType string
Usage Usage
- Detail interface{} // 将any替换为interface{}
+ Detail any // 将any替换为interface{}
ToolCalls []ToolCall
}
@@ -123,7 +134,7 @@ type ToolCall struct {
type FunctionCallOption struct {
Name string `json:"name"`
- Arguments map[string]interface{} `json:"arguments"`
+ Arguments map[string]any `json:"arguments"`
}
type TextDetail struct {
@@ -153,7 +164,7 @@ type Reference struct {
}
type FunctionCallDetail struct {
- Text interface{} `json:"text"`
+ Text any `json:"text"`
Image string `json:"image"`
Audio string `json:"audio"`
Video string `json:"video"`
@@ -226,7 +237,7 @@ func (t *AppBuilderClientAnswer) transform(inp *AppBuilderClientRawResponse) {
Usage: c.Usage,
Detail: c.Outputs,
ToolCalls: c.ToolCalls}
- //这部分新改的
+ // 这部分新改的
tp, ok := TypeToStruct[ev.ContentType]
if !ok {
tp = reflect.TypeOf(DefaultDetail{})
@@ -234,7 +245,7 @@ func (t *AppBuilderClientAnswer) transform(inp *AppBuilderClientRawResponse) {
v := reflect.New(tp)
_ = json.Unmarshal(c.Outputs, v.Interface())
ev.Detail = v.Elem().Interface()
- //这部分新改的
+ // 这部分新改的
t.Events = append(t.Events, ev)
}
}
@@ -285,7 +296,7 @@ type AppBuilderClientOnceIterator struct {
}
func (t *AppBuilderClientOnceIterator) Next() (*AppBuilderClientAnswer, error) {
- data, err := ioutil.ReadAll(t.body)
+ data, err := io.ReadAll(t.body)
if err != nil {
return nil, fmt.Errorf("requestID=%s, err=%v", t.requestID, err)
}
diff --git a/go/appbuilder/app_builder_client_test.go b/go/appbuilder/app_builder_client_test.go
index a0062fe03..b70e43dc7 100644
--- a/go/appbuilder/app_builder_client_test.go
+++ b/go/appbuilder/app_builder_client_test.go
@@ -69,13 +69,12 @@ func TestNewAppBuilderClient(t *testing.T) {
func TestAppBuilderClientRunWithToolCall(t *testing.T) {
os.Setenv("APPBUILDER_LOGLEVEL", "DEBUG")
os.Setenv("APPBUILDER_LOGFILE", "")
- os.Setenv("GATEWAY_URL_V2", "https://apaas-api-sandbox.baidu-int.com/")
- config, err := NewSDKConfig("", "bce-v3/ALTAK-vGrDN4BvjP15rDrXBI9OC/6d435ece62ed09b396e1b051bd87869c11861332")
+ config, err := NewSDKConfig("", "")
if err != nil {
t.Fatalf("new http client config failed: %v", err)
}
- appID := "4d4b1b27-d607-4d2a-9002-206134217a9f"
+ appID := ""
client, err := NewAppBuilderClient(appID, config)
if err != nil {
t.Fatalf("new AgentBuidler instance failed")
@@ -86,17 +85,17 @@ func TestAppBuilderClientRunWithToolCall(t *testing.T) {
t.Fatalf("create conversation failed: %v", err)
}
- parameters := make(map[string]interface{})
+ parameters := make(map[string]any)
- location := make(map[string]interface{})
+ location := make(map[string]any)
location["type"] = "string"
location["description"] = "省,市名,例如:河北省"
- unit := make(map[string]interface{})
+ unit := make(map[string]any)
unit["type"] = "string"
unit["enum"] = []string{"摄氏度", "华氏度"}
- properties := make(map[string]interface{})
+ properties := make(map[string]any)
properties["location"] = location
properties["unit"] = unit
@@ -160,3 +159,52 @@ func TestAppBuilderClientRunWithToolCall(t *testing.T) {
fmt.Println("----------------answer-------------------")
fmt.Println(totalAnswer)
}
+
+func TestAppBuilderClientRunToolChoice(t *testing.T) {
+ os.Setenv("APPBUILDER_LOGLEVEL", "DEBUG")
+ os.Setenv("APPBUILDER_LOGFILE", "")
+ config, err := NewSDKConfig("", "")
+ if err != nil {
+ t.Fatalf("new http client config failed: %v", err)
+ }
+
+ appID := ""
+ client, err := NewAppBuilderClient(appID, config)
+ if err != nil {
+ t.Fatalf("new AgentBuidler instance failed")
+ }
+
+ conversationID, err := client.CreateConversation()
+ if err != nil {
+ t.Fatalf("create conversation failed: %v", err)
+ }
+
+ input := make(map[string]interface{})
+ input["city"] = "北京"
+ end_user_id := "go_user_id_0"
+ i, err := client.RunWithToolCall(AppBuilderClientRunRequest{
+ ConversationID: conversationID,
+ AppID: appID,
+ Query: "你能干什么",
+ EndUserID: &end_user_id,
+ Stream: false,
+ ToolChoice: &ToolChoice{
+ Type: "function",
+ Function: ToolChoiceFunction{
+ Name: "WeatherQuery",
+ Input: input,
+ },
+ },
+ })
+
+ if err != nil {
+ fmt.Println("run failed: ", err)
+ }
+
+ for answer, err := i.Next(); err == nil; answer, err = i.Next() {
+ for _, ev := range answer.Events {
+ evJSON, _ := json.Marshal(ev)
+ fmt.Println(string(evJSON))
+ }
+ }
+}
diff --git a/go/appbuilder/config.go b/go/appbuilder/config.go
index 12e717c92..c43cbe913 100644
--- a/go/appbuilder/config.go
+++ b/go/appbuilder/config.go
@@ -15,188 +15,187 @@
package appbuilder
import (
- "fmt"
- "io"
- "log"
- "net/http"
- "net/url"
- "os"
- "path"
- "strings"
- "io/ioutil"
- "github.com/google/uuid"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "net/url"
+ "os"
+ "path"
+ "strings"
+
+ "github.com/google/uuid"
)
const (
- GatewayURL = "GATEWAY_URL"
- GatewayURLV2 = "GATEWAY_URL_V2"
- SecretKey = "APPBUILDER_TOKEN"
- ConsoleOpenAPIVersion = "CONSOLE_OPENAPI_VERSION"
- ConsoleOpenAPIPrefix = "CONSOLE_OPENAPI_PREFIX"
- SecretKeyPrefix = "SECRET_KEY_PREFIX"
-
- DefaultSecretKeyPrefix = "Bearer"
- DefaultGatewayURL = "https://appbuilder.baidu.com"
- DefaultGatewayURLV2 = "https://qianfan.baidubce.com"
- DefaultConsoleOpenAPIVersion = "/v2"
- DefaultConsoleOpenAPIPrefix = ""
+ GatewayURL = "GATEWAY_URL"
+ GatewayURLV2 = "GATEWAY_URL_V2"
+ SecretKey = "APPBUILDER_TOKEN"
+ ConsoleOpenAPIVersion = "CONSOLE_OPENAPI_VERSION"
+ ConsoleOpenAPIPrefix = "CONSOLE_OPENAPI_PREFIX"
+ SecretKeyPrefix = "SECRET_KEY_PREFIX"
+
+ DefaultSecretKeyPrefix = "Bearer"
+ DefaultGatewayURL = "https://appbuilder.baidu.com"
+ DefaultGatewayURLV2 = "https://qianfan.baidubce.com"
+ DefaultConsoleOpenAPIVersion = "/v2"
+ DefaultConsoleOpenAPIPrefix = ""
)
type SDKConfig struct {
- GatewayURL string
- GatewayURLV2 string
- ConsoleOpenAPIVersion string
- ConsoleOpenAPIPrefix string
- SecretKey string
- HTTPClient HTTPClient // custom HTTP Client, optional
- logger *log.Logger
+ GatewayURL string
+ GatewayURLV2 string
+ ConsoleOpenAPIVersion string
+ ConsoleOpenAPIPrefix string
+ SecretKey string
+ HTTPClient HTTPClient // custom HTTP Client, optional
+ logger *log.Logger
}
func NewSDKConfig(gatewayURL, secretKey string) (*SDKConfig, error) {
- gatewayURL = getEnvWithDefault(GatewayURL, gatewayURL, DefaultGatewayURL)
- gatewayURLV2 := getEnvWithDefault(GatewayURLV2, "", DefaultGatewayURLV2)
- openAPIVersion := getEnvWithDefault(ConsoleOpenAPIVersion, "", DefaultConsoleOpenAPIVersion)
- openAPIPrefix := getEnvWithDefault(ConsoleOpenAPIPrefix, "", DefaultConsoleOpenAPIPrefix)
-
- secretKey = getEnvWithDefault(SecretKey, secretKey, "")
- if len(secretKey) == 0 {
- log.Println("Error: secret key is empty")
- }
- secretKeyPrefix := getEnvWithDefault(SecretKeyPrefix, "", DefaultSecretKeyPrefix)
- if !strings.HasPrefix(secretKey, secretKeyPrefix) {
- secretKey = secretKeyPrefix + " " + secretKey
- }
-
- sdkConfig := &SDKConfig{
- GatewayURL: gatewayURL,
- GatewayURLV2: gatewayURLV2,
- ConsoleOpenAPIVersion: openAPIVersion,
- ConsoleOpenAPIPrefix: openAPIPrefix,
- SecretKey: secretKey,
- }
-
- logFile := os.Getenv("APPBUILDER_LOGFILE")
- if len(logFile) > 0 {
- f, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
- if err != nil {
- sdkConfig.logger = log.New(os.Stdout, "", log.LstdFlags)
- } else {
- sdkConfig.logger = log.New(f, "", log.LstdFlags)
- }
- } else {
- sdkConfig.logger = log.New(os.Stdout, "", log.LstdFlags)
- }
-
- return sdkConfig, nil
+ gatewayURL = getEnvWithDefault(GatewayURL, gatewayURL, DefaultGatewayURL)
+ gatewayURLV2 := getEnvWithDefault(GatewayURLV2, "", DefaultGatewayURLV2)
+ openAPIVersion := getEnvWithDefault(ConsoleOpenAPIVersion, "", DefaultConsoleOpenAPIVersion)
+ openAPIPrefix := getEnvWithDefault(ConsoleOpenAPIPrefix, "", DefaultConsoleOpenAPIPrefix)
+
+ secretKey = getEnvWithDefault(SecretKey, secretKey, "")
+ if len(secretKey) == 0 {
+ log.Println("Error: secret key is empty")
+ }
+ secretKeyPrefix := getEnvWithDefault(SecretKeyPrefix, "", DefaultSecretKeyPrefix)
+ if !strings.HasPrefix(secretKey, secretKeyPrefix) {
+ secretKey = secretKeyPrefix + " " + secretKey
+ }
+
+ sdkConfig := &SDKConfig{
+ GatewayURL: gatewayURL,
+ GatewayURLV2: gatewayURLV2,
+ ConsoleOpenAPIVersion: openAPIVersion,
+ ConsoleOpenAPIPrefix: openAPIPrefix,
+ SecretKey: secretKey,
+ }
+
+ logFile := os.Getenv("APPBUILDER_LOGFILE")
+ if len(logFile) > 0 {
+ f, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
+ if err != nil {
+ sdkConfig.logger = log.New(os.Stdout, "", log.LstdFlags)
+ } else {
+ sdkConfig.logger = log.New(f, "", log.LstdFlags)
+ }
+ } else {
+ sdkConfig.logger = log.New(os.Stdout, "", log.LstdFlags)
+ }
+
+ return sdkConfig, nil
}
func getEnvWithDefault(key, paramValue, defaultValue string) string {
- if paramValue != "" {
- return paramValue
- }
-
- v := os.Getenv(key)
- if v == "" {
- return defaultValue
- }
- return v
+ if paramValue != "" {
+ return paramValue
+ }
+
+ v := os.Getenv(key)
+ if v == "" {
+ return defaultValue
+ }
+ return v
}
func (t *SDKConfig) AuthHeader() http.Header {
- header := t.authHeader()
- header.Set("X-Appbuilder-Authorization", t.SecretKey)
- t.logger.Printf("Auth Header %v", header)
- return header
+ header := t.authHeader()
+ header.Set("X-Appbuilder-Authorization", t.SecretKey)
+ t.logger.Printf("Auth Header %v", header)
+ return header
}
// AuthHeaderV2 适配OpenAPI,当前仅AgentBuilder使用
func (t *SDKConfig) AuthHeaderV2() http.Header {
- header := t.authHeader()
- header.Set("Authorization", t.SecretKey)
- t.logger.Printf("Auth Header %v", header)
- return header
+ header := t.authHeader()
+ header.Set("Authorization", t.SecretKey)
+ t.logger.Printf("Auth Header %v", header)
+ return header
}
func (t *SDKConfig) authHeader() http.Header {
- header := make(http.Header)
- platform := os.Getenv("APPBUILDER_SDK_PLATFORM")
- if platform == "" {
- platform = "unknown"
- }
- header.Set("X-Appbuilder-Origin", "appbuilder_sdk")
- header.Set("X-Appbuilder-Sdk-Config", "{\"appbuilder_sdk_version\":\"0.9.0\",\"appbuilder_sdk_language\":\"go\",\"appbuilder_sdk_platform\":\""+platform+"\"}")
- header.Set("X-Appbuilder-Request-Id", uuid.New().String())
- return header
+ header := make(http.Header)
+ platform := os.Getenv("APPBUILDER_SDK_PLATFORM")
+ if platform == "" {
+ platform = "unknown"
+ }
+ header.Set("X-Appbuilder-Origin", "appbuilder_sdk")
+ header.Set("X-Appbuilder-Sdk-Config", "{\"appbuilder_sdk_version\":\"0.9.4\",\"appbuilder_sdk_language\":\"go\",\"appbuilder_sdk_platform\":\""+platform+"\"}")
+ header.Set("X-Appbuilder-Request-Id", uuid.New().String())
+ return header
}
func (t *SDKConfig) ServiceURL(suffix string) (*url.URL, error) {
- absolutePath := t.GatewayURL
- if !strings.HasSuffix(absolutePath, "/") {
- absolutePath += "/"
- }
- if strings.HasPrefix(suffix, "/") {
- suffix = strings.TrimPrefix(suffix, "/")
- }
- absolutePath += suffix
-
- return t.formatURL(absolutePath, suffix)
+ absolutePath := t.GatewayURL
+ if !strings.HasSuffix(absolutePath, "/") {
+ absolutePath += "/"
+ }
+ if strings.HasPrefix(suffix, "/") {
+ suffix = strings.TrimPrefix(suffix, "/")
+ }
+ absolutePath += suffix
+
+ return t.formatURL(absolutePath, suffix)
}
-
// ServiceURLV2 适配OpenAPI,当前仅AppbuilderClient使用
func (t *SDKConfig) ServiceURLV2(suffix string) (*url.URL, error) {
- suffix = path.Join(t.ConsoleOpenAPIPrefix, t.ConsoleOpenAPIVersion, suffix)
- return t.formatURL(t.GatewayURLV2, suffix)
+ suffix = path.Join(t.ConsoleOpenAPIPrefix, t.ConsoleOpenAPIVersion, suffix)
+ return t.formatURL(t.GatewayURLV2, suffix)
}
func (t *SDKConfig) formatURL(absolutePath, suffix string) (*url.URL, error) {
- t.logger.Printf("Service URL %s", absolutePath)
- url, err := url.Parse(absolutePath)
- if err != nil {
- return nil, err
- }
+ t.logger.Printf("Service URL %s", absolutePath)
+ url, err := url.Parse(absolutePath)
+ if err != nil {
+ return nil, err
+ }
- endpoint, err := url.Parse(suffix)
- if err != nil {
- return nil, err
- }
+ endpoint, err := url.Parse(suffix)
+ if err != nil {
+ return nil, err
+ }
- url = url.ResolveReference(endpoint)
+ url = url.ResolveReference(endpoint)
- return url, nil
+ return url, nil
}
type nopCloser struct {
- io.Reader
+ io.Reader
}
func (nopCloser) Close() error { return nil }
func NopCloser(r io.Reader) io.ReadCloser {
- return nopCloser{r}
+ return nopCloser{Reader: r}
}
func (t *SDKConfig) BuildCurlCommand(req *http.Request) {
- curlCmd := fmt.Sprintf("curl -X %s -L '%v' \\\n", req.Method, req.URL.String())
-
- for k, v := range req.Header {
- header := fmt.Sprintf("-H '%v: %v' \\\n", k, v[0])
- curlCmd = fmt.Sprintf("%v %v", curlCmd, header)
- }
-
- if req.Method == "POST" {
- bodyBytes, err := ioutil.ReadAll(req.Body)
- if err != nil {
- t.logger.Println("Failed to read request body:", err)
- return
- }
- req.Body.Close()
- req.Body = NopCloser(strings.NewReader(string(bodyBytes)))
-
- body := fmt.Sprintf("-d '%v'", string(bodyBytes))
- curlCmd = fmt.Sprintf("%v %v", curlCmd, body)
- } else if req.Method == "GET" || req.Method == "DELETE" {
- curlCmd = strings.TrimSuffix(curlCmd, " \\\n")
- }
- fmt.Println("\n" + curlCmd + "\n")
+ curlCmd := fmt.Sprintf("curl -X %s -L '%v' \\\n", req.Method, req.URL.String())
+
+ for k, v := range req.Header {
+ header := fmt.Sprintf("-H '%v: %v' \\\n", k, v[0])
+ curlCmd = fmt.Sprintf("%v %v", curlCmd, header)
+ }
+
+ if req.Method == "POST" {
+ bodyBytes, err := io.ReadAll(req.Body)
+ if err != nil {
+ t.logger.Println("Failed to read request body:", err)
+ return
+ }
+ req.Body.Close()
+ req.Body = NopCloser(strings.NewReader(string(bodyBytes)))
+
+ body := fmt.Sprintf("-d '%v'", string(bodyBytes))
+ curlCmd = fmt.Sprintf("%v %v", curlCmd, body)
+ } else if req.Method == "GET" || req.Method == "DELETE" {
+ curlCmd = strings.TrimSuffix(curlCmd, " \\\n")
+ }
+ fmt.Println("\n" + curlCmd + "\n")
}
diff --git a/go/appbuilder/dataset.go b/go/appbuilder/dataset.go
index cbcbefda9..5c11010de 100644
--- a/go/appbuilder/dataset.go
+++ b/go/appbuilder/dataset.go
@@ -25,7 +25,6 @@ import (
"os"
"path/filepath"
"time"
- "io/ioutil"
)
// Deprecated: 已废弃,请使用 NewKnowledgeBase
@@ -69,7 +68,7 @@ func (t *Dataset) Create(name string) (string, error) {
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- data, err = ioutil.ReadAll(resp.Body)
+ data, err = io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -144,7 +143,7 @@ func (t *Dataset) uploadLocalFile(localFilePath string) (string, error) {
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- respData, err := ioutil.ReadAll(resp.Body)
+ respData, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -170,7 +169,7 @@ func (t *Dataset) addFileToDataset(datasetID string, fileID []string) ([]string,
request.Method = "POST"
header.Set("Content-Type", "application/json")
request.Header = header
- m := map[string]interface{}{
+ m := map[string]any{
"file_ids": fileID,
"dataset_id": datasetID}
data, _ := json.Marshal(m)
@@ -184,7 +183,7 @@ func (t *Dataset) addFileToDataset(datasetID string, fileID []string) ([]string,
if err != nil {
return nil, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- respData, err := ioutil.ReadAll(resp.Body)
+ respData, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -209,7 +208,7 @@ func (t *Dataset) ListDocument(datasetID string, page int, limit int, keyword st
request.Method = "POST"
header.Set("Content-Type", "application/json")
request.Header = header
- m := map[string]interface{}{
+ m := map[string]any{
"dataset_id": datasetID,
"page": page,
"limit": limit,
@@ -227,7 +226,7 @@ func (t *Dataset) ListDocument(datasetID string, page int, limit int, keyword st
if err != nil {
return nil, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- respData, err := ioutil.ReadAll(resp.Body)
+ respData, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
diff --git a/go/appbuilder/dataset_data.go b/go/appbuilder/dataset_data.go
index 25e4bf807..895d3f23e 100644
--- a/go/appbuilder/dataset_data.go
+++ b/go/appbuilder/dataset_data.go
@@ -17,7 +17,7 @@ package appbuilder
type DatasetResponse struct {
Code int `json:"code"`
Message string `json:"message"`
- Result map[string]interface{} `json:"result"`
+ Result map[string]any `json:"result"`
}
type DatasetBindResponse struct {
diff --git a/go/appbuilder/go.mod b/go/appbuilder/go.mod
index fa6ef46ca..998b916d1 100644
--- a/go/appbuilder/go.mod
+++ b/go/appbuilder/go.mod
@@ -1,14 +1,5 @@
module github.com/baidubce/app-builder/go/appbuilder
-go 1.19
+go 1.18
-require (
- github.com/google/uuid v1.6.0
- github.com/rs/zerolog v1.33.0
-)
-
-require (
- github.com/mattn/go-colorable v0.1.13 // indirect
- github.com/mattn/go-isatty v0.0.20 // indirect
- golang.org/x/sys v0.21.0 // indirect
-)
+require github.com/google/uuid v1.6.0
diff --git a/go/appbuilder/go.sum b/go/appbuilder/go.sum
index a79978bb9..7790d7c3e 100644
--- a/go/appbuilder/go.sum
+++ b/go/appbuilder/go.sum
@@ -1,19 +1,2 @@
-github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
-github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
-github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
-github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
-golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
diff --git a/go/appbuilder/knowledge_base.go b/go/appbuilder/knowledge_base.go
index 7828350c7..61ba25fb5 100644
--- a/go/appbuilder/knowledge_base.go
+++ b/go/appbuilder/knowledge_base.go
@@ -27,7 +27,6 @@ import (
"path/filepath"
"strconv"
"time"
- "io/ioutil"
"github.com/google/uuid"
)
@@ -75,7 +74,7 @@ func (t *KnowledgeBase) CreateDocument(req CreateDocumentRequest) (CreateDocumen
if err != nil {
return CreateDocumentResponse{}, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- data, err = ioutil.ReadAll(resp.Body)
+ data, err = io.ReadAll(resp.Body)
if err != nil {
return CreateDocumentResponse{}, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -99,7 +98,7 @@ func (t *KnowledgeBase) DeleteDocument(req DeleteDocumentRequest) error {
return err
}
- reqMap := make(map[string]interface{})
+ reqMap := make(map[string]any)
reqJson, _ := json.Marshal(req)
json.Unmarshal(reqJson, &reqMap)
params := url.Values{}
@@ -141,7 +140,7 @@ func (t *KnowledgeBase) GetDocumentList(req GetDocumentListRequest) (*GetDocumen
return nil, err
}
- reqMap := make(map[string]interface{})
+ reqMap := make(map[string]any)
reqJson, _ := json.Marshal(req)
json.Unmarshal(reqJson, &reqMap)
params := url.Values{}
@@ -175,7 +174,7 @@ func (t *KnowledgeBase) GetDocumentList(req GetDocumentListRequest) (*GetDocumen
if err != nil {
return nil, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- respData, err := ioutil.ReadAll(resp.Body)
+ respData, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -222,7 +221,7 @@ func (t *KnowledgeBase) UploadFile(localFilePath string) (string, error) {
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- respData, err := ioutil.ReadAll(resp.Body)
+ respData, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -263,7 +262,7 @@ func (t *KnowledgeBase) CreateKnowledgeBase(req KnowledgeBaseDetail) (KnowledgeB
if err != nil {
return KnowledgeBaseDetail{}, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- data, err = ioutil.ReadAll(resp.Body)
+ data, err = io.ReadAll(resp.Body)
if err != nil {
return KnowledgeBaseDetail{}, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -300,7 +299,7 @@ func (t *KnowledgeBase) GetKnowledgeBaseDetail(knowledgeBaseID string) (Knowledg
if err != nil {
return KnowledgeBaseDetail{}, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- data, err = ioutil.ReadAll(resp.Body)
+ data, err = io.ReadAll(resp.Body)
if err != nil {
return KnowledgeBaseDetail{}, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -335,7 +334,7 @@ func (t *KnowledgeBase) GetKnowledgeBaseList(req GetKnowledgeBaseListRequest) (G
if err != nil {
return GetKnowledgeBaseListResponse{}, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- data, err = ioutil.ReadAll(resp.Body)
+ data, err = io.ReadAll(resp.Body)
if err != nil {
return GetKnowledgeBaseListResponse{}, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -373,7 +372,7 @@ func (t *KnowledgeBase) ModifyKnowledgeBase(req ModifyKnowlegeBaseRequest) error
if err != nil {
return fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- _, err = ioutil.ReadAll(resp.Body)
+ _, err = io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -417,7 +416,7 @@ func (t *KnowledgeBase) deleteKnowledgeBase(knowledgeBaseID string, clientToken
if err != nil {
return fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- _, err = ioutil.ReadAll(resp.Body)
+ _, err = io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -451,7 +450,7 @@ func (t *KnowledgeBase) CreateDocuments(req CreateDocumentsRequest) error {
if err != nil {
return fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- _, err = ioutil.ReadAll(resp.Body)
+ _, err = io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -508,7 +507,7 @@ func (t *KnowledgeBase) UploadDocuments(localFilePath string, req CreateDocument
if err != nil {
return fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- _, err = ioutil.ReadAll(resp.Body)
+ _, err = io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -542,7 +541,7 @@ func (t *KnowledgeBase) CreateChunk(req CreateChunkRequest) (string, error) {
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- data, err = ioutil.ReadAll(resp.Body)
+ data, err = io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -581,7 +580,7 @@ func (t *KnowledgeBase) ModifyChunk(req ModifyChunkRequest) error {
if err != nil {
return fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- data, err = ioutil.ReadAll(resp.Body)
+ data, err = io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -631,7 +630,7 @@ func (t *KnowledgeBase) deleteChunk(chunkID string, clientToken string) error {
if err != nil {
return fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- data, err = ioutil.ReadAll(resp.Body)
+ data, err = io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -670,7 +669,7 @@ func (t *KnowledgeBase) DescribeChunk(chunkID string) (DescribeChunkResponse, er
if err != nil {
return DescribeChunkResponse{}, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- data, err = ioutil.ReadAll(resp.Body)
+ data, err = io.ReadAll(resp.Body)
if err != nil {
return DescribeChunkResponse{}, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
@@ -706,7 +705,7 @@ func (t *KnowledgeBase) DescribeChunks(req DescribeChunksRequest) (DescribeChunk
if err != nil {
return DescribeChunksResponse{}, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
- data, err = ioutil.ReadAll(resp.Body)
+ data, err = io.ReadAll(resp.Body)
if err != nil {
return DescribeChunksResponse{}, fmt.Errorf("requestID=%s, err=%v", requestID, err)
}
diff --git a/go/appbuilder/knowledge_base_data.go b/go/appbuilder/knowledge_base_data.go
index b97eb13da..518499d0a 100644
--- a/go/appbuilder/knowledge_base_data.go
+++ b/go/appbuilder/knowledge_base_data.go
@@ -65,7 +65,7 @@ type GetDocumentListResponse struct {
type Document struct {
ID string `json:"id"`
Name string `json:"name"`
- CreatedAt interface{} `json:"created_at"`
+ CreatedAt any `json:"created_at"`
WordCount int64 `json:"word_count"`
Enabled bool `json:"enabled"`
Meta DocumentMeta `json:"meta"`
diff --git a/go/appbuilder/rag_data.go b/go/appbuilder/rag_data.go
index 7a5780a4c..d76416d34 100644
--- a/go/appbuilder/rag_data.go
+++ b/go/appbuilder/rag_data.go
@@ -19,7 +19,6 @@ import (
"fmt"
"io"
"strings"
- "io/ioutil"
)
type RAGRunResponse struct {
@@ -109,7 +108,7 @@ func (t *RAGOnceIterator) Next() (*RAGAnswer, error) {
if t.eoi {
return nil, io.EOF
}
- data, err := ioutil.ReadAll(t.body)
+ data, err := io.ReadAll(t.body)
if err != nil {
return nil, fmt.Errorf("requestID=%s, err=%v", t.requestID, err)
}
diff --git a/go/appbuilder/util.go b/go/appbuilder/util.go
index 1d1fa6d47..26d6f2c22 100644
--- a/go/appbuilder/util.go
+++ b/go/appbuilder/util.go
@@ -18,8 +18,8 @@ import (
"bufio"
"errors"
"fmt"
+ "io"
"net/http"
- "io/ioutil"
)
type SSEEvent struct {
@@ -34,7 +34,7 @@ func checkHTTPResponse(rsp *http.Response) (string, error) {
return requestID, nil
}
- data, err := ioutil.ReadAll(rsp.Body)
+ data, err := io.ReadAll(rsp.Body)
if err != nil {
return requestID, err
}
diff --git a/java/pom.xml b/java/pom.xml
index 0689b3c6c..9ba6f8e69 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -6,7 +6,7 @@
com.baidubce
appbuilder
- 0.9.2
+ 0.9.4
jar
app-builder
diff --git a/java/src/main/java/com/baidubce/appbuilder/base/utils/http/HttpClient.java b/java/src/main/java/com/baidubce/appbuilder/base/utils/http/HttpClient.java
index 73f4f188a..34880ca3f 100644
--- a/java/src/main/java/com/baidubce/appbuilder/base/utils/http/HttpClient.java
+++ b/java/src/main/java/com/baidubce/appbuilder/base/utils/http/HttpClient.java
@@ -105,7 +105,7 @@ public ClassicHttpRequest createPostRequest(String url, HttpEntity entity) {
? System.getenv("APPBUILDER_SDK_PLATFORM")
: "unknown";
httpPost.setHeader("X-Appbuilder-Sdk-Config",
- "{\"appbuilder_sdk_version\":\"0.9.2\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\""
+ "{\"appbuilder_sdk_version\":\"0.9.4\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\""
+ platform + "\"}");
httpPost.setHeader("X-Appbuilder-Request-Id", java.util.UUID.randomUUID().toString());
httpPost.setEntity(entity);
@@ -134,7 +134,7 @@ public ClassicHttpRequest createPostRequestV2(String url, HttpEntity entity) {
? System.getenv("APPBUILDER_SDK_PLATFORM")
: "unknown";
httpPost.setHeader("X-Appbuilder-Sdk-Config",
- "{\"appbuilder_sdk_version\":\"0.9.2\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\""
+ "{\"appbuilder_sdk_version\":\"0.9.4\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\""
+ platform + "\"}");
httpPost.setHeader("X-Appbuilder-Request-Id", java.util.UUID.randomUUID().toString());
httpPost.setEntity(entity);
@@ -158,7 +158,7 @@ public ClassicHttpRequest createGetRequestV2(String url, Map map
? System.getenv("APPBUILDER_SDK_PLATFORM")
: "unknown";
httpGet.setHeader("X-Appbuilder-Sdk-Config",
- "{\"appbuilder_sdk_version\":\"0.9.2\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\""
+ "{\"appbuilder_sdk_version\":\"0.9.4\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\""
+ platform + "\"}");
httpGet.setHeader("X-Appbuilder-Request-Id", java.util.UUID.randomUUID().toString());
String headers = "headers: \n";
@@ -181,7 +181,7 @@ public ClassicHttpRequest createDeleteRequestV2(String url, Map
? System.getenv("APPBUILDER_SDK_PLATFORM")
: "unknown";
httpDelete.setHeader("X-Appbuilder-Sdk-Config",
- "{\"appbuilder_sdk_version\":\"0.9.2\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\""
+ "{\"appbuilder_sdk_version\":\"0.9.4\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\""
+ platform + "\"}");
httpDelete.setHeader("X-Appbuilder-Request-Id", java.util.UUID.randomUUID().toString());
String headers = "headers: \n";
diff --git a/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/AppBuilderClientRunRequest.java b/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/AppBuilderClientRunRequest.java
index 7706fc746..54d0e1efe 100644
--- a/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/AppBuilderClientRunRequest.java
+++ b/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/AppBuilderClientRunRequest.java
@@ -10,9 +10,13 @@ public class AppBuilderClientRunRequest {
private boolean stream;
@SerializedName("conversation_id")
private String conversationID;
+ @SerializedName("end_user_id")
+ private String endUserId;
private Tool[] tools;
@SerializedName("tool_outputs")
private ToolOutput[] ToolOutputs;
+ @SerializedName("tool_choice")
+ private ToolChoice ToolChoice;
public String getAppId() {
return appId;
@@ -46,6 +50,14 @@ public void setConversationID(String conversationID) {
this.conversationID = conversationID;
}
+ public String getEndUserId() {
+ return endUserId;
+ }
+
+ public void setEndUserId(String endUserId) {
+ this.endUserId = endUserId;
+ }
+
public Tool[] getTools() {
return tools;
}
@@ -62,6 +74,14 @@ public void setToolOutputs(ToolOutput[] toolOutputs) {
this.ToolOutputs = toolOutputs;
}
+ public ToolChoice getToolChoice() {
+ return ToolChoice;
+ }
+
+ public void setToolChoice(ToolChoice toolChoice) {
+ this.ToolChoice = toolChoice;
+ }
+
public static class Tool {
private String type;
private Function function;
@@ -79,6 +99,7 @@ public Function getFunction() {
return function;
}
+
public static class Function {
private String name;
private String description;
@@ -122,4 +143,40 @@ public String getOutput() {
return output;
}
}
+
+ public static class ToolChoice {
+ private String type;
+ private Function function;
+
+ public ToolChoice(String type, Function function) {
+ this.type=type;
+ this.function=function;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public Function getFunction() {
+ return function;
+ }
+
+ public static class Function {
+ private String name;
+ private Map input;
+
+ public Function(String name, Map input) {
+ this.name = name;
+ this.input = input;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Map getInput() {
+ return input;
+ }
+ }
+ }
}
diff --git a/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java b/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java
index 409c48845..df96628ee 100644
--- a/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java
+++ b/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java
@@ -9,7 +9,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import javax.tools.Tool;
import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientIterator;
import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientResult;
import com.baidubce.appbuilder.model.appbuilderclient.AppListRequest;
@@ -100,11 +99,10 @@ public void AppBuilderClientRunFuncTest() throws IOException, AppBuilderServerEx
required.add("location");
parameters.put("required", required);
- AppBuilderClientRunRequest.Tool.Function func =
- new AppBuilderClientRunRequest.Tool.Function(name, desc, parameters);
- AppBuilderClientRunRequest.Tool tool =
- new AppBuilderClientRunRequest.Tool("function", func);
- request.setTools(new AppBuilderClientRunRequest.Tool[] {tool});
+ AppBuilderClientRunRequest.Tool.Function func = new AppBuilderClientRunRequest.Tool.Function(name, desc,
+ parameters);
+ AppBuilderClientRunRequest.Tool tool = new AppBuilderClientRunRequest.Tool("function", func);
+ request.setTools(new AppBuilderClientRunRequest.Tool[] { tool });
AppBuilderClientIterator itor = builder.run(request);
assertTrue(itor.hasNext());
@@ -119,9 +117,8 @@ public void AppBuilderClientRunFuncTest() throws IOException, AppBuilderServerEx
request2.setAppId(appId);
request2.setConversationID(conversationId);
- AppBuilderClientRunRequest.ToolOutput output =
- new AppBuilderClientRunRequest.ToolOutput(ToolCallID, "北京今天35度");
- request2.setToolOutputs(new AppBuilderClientRunRequest.ToolOutput[] {output});
+ AppBuilderClientRunRequest.ToolOutput output = new AppBuilderClientRunRequest.ToolOutput(ToolCallID, "北京今天35度");
+ request2.setToolOutputs(new AppBuilderClientRunRequest.ToolOutput[] { output });
AppBuilderClientIterator itor2 = builder.run(request2);
assertTrue(itor2.hasNext());
while (itor2.hasNext()) {
@@ -129,4 +126,31 @@ public void AppBuilderClientRunFuncTest() throws IOException, AppBuilderServerEx
System.out.println(result);
}
}
+
+ @Test
+ public void AppBuilderClientRunToolChoiceTest() throws IOException, AppBuilderServerException {
+ AppBuilderClient builder = new AppBuilderClient(appId);
+ String conversationId = builder.createConversation();
+ assertNotNull(conversationId);
+
+ AppBuilderClientRunRequest request = new AppBuilderClientRunRequest();
+ request.setAppId(appId);
+ request.setConversationID(conversationId);
+ request.setQuery("你能干什么");
+ request.setStream(false);
+ request.setEndUserId("java_test_user_0");
+ Map input = new HashMap<>();
+ input.put("city", "北京");
+ AppBuilderClientRunRequest.ToolChoice.Function func = new AppBuilderClientRunRequest.ToolChoice.Function(
+ "WeatherQuery", input);
+ AppBuilderClientRunRequest.ToolChoice choice = new AppBuilderClientRunRequest.ToolChoice("function", func);
+ request.setToolChoice(choice);
+
+ AppBuilderClientIterator itor = builder.run(request);
+ assertTrue(itor.hasNext());
+ while (itor.hasNext()) {
+ AppBuilderClientResult result = itor.next();
+ System.out.println(result);
+ }
+ }
}
diff --git a/setup.py b/setup.py
index 5d72a0360..85f7e550c 100755
--- a/setup.py
+++ b/setup.py
@@ -39,7 +39,7 @@
setup(
name="appbuilder-sdk",
# NOTE(chengmo): 修改此版本号时,请注意同时修改 __init__.py 中的 __version__
- version="0.9.3",
+ version="0.9.4",
author="dongdaxiang",
author_email="dongdaxiang@baidu.com",
packages=packages,