diff --git a/.github/workflows/sphinx_docs.yml b/.github/workflows/sphinx_docs.yml index e95d8403c..97f99ced4 100644 --- a/.github/workflows/sphinx_docs.yml +++ b/.github/workflows/sphinx_docs.yml @@ -31,8 +31,8 @@ jobs: - name: Add execute permission to build.sh run: | chmod +x docs/tutorial/en/build.sh - - id: build - name: Build Documentation + - id: build_en + name: Build English Documentation env: DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }} run: | @@ -47,5 +47,5 @@ jobs: if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: 'docs/tutorial/build/html' + publish_dir: 'docs/tutorial/en/build/html' cname: doc.agentscope.io diff --git a/docs/tutorial/en/source/tutorial/agent.py b/docs/tutorial/en/source/tutorial/agent.py index 9be78d529..db0198c4f 100644 --- a/docs/tutorial/en/source/tutorial/agent.py +++ b/docs/tutorial/en/source/tutorial/agent.py @@ -175,5 +175,5 @@ def reply(self, msg): # # Further Reading # --------------------- -# - :ref:`builtin-agent` +# - :ref:`builtin_agent` # - :ref:`model_api` diff --git a/docs/tutorial/en/source/tutorial/monitor.py b/docs/tutorial/en/source/tutorial/monitor.py index 54a1cd9d8..b9a9d6d4c 100644 --- a/docs/tutorial/en/source/tutorial/monitor.py +++ b/docs/tutorial/en/source/tutorial/monitor.py @@ -38,7 +38,7 @@ import json -print(json.dumps(agentscope.state_dict(), indent=2)) +print(json.dumps(agentscope.state_dict(), indent=2, ensure_ascii=False)) # %% # Monitoring the Runtime @@ -70,4 +70,4 @@ usage = agentscope.print_llm_usage() -print(json.dumps(usage, indent=2)) +print(json.dumps(usage, indent=2, ensure_ascii=False)) diff --git a/docs/tutorial/zh/Makefile b/docs/tutorial/zh/Makefile new file mode 100644 index 000000000..92dd33a1a --- /dev/null +++ b/docs/tutorial/zh/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/tutorial/zh/build.sh b/docs/tutorial/zh/build.sh new file mode 100644 index 000000000..358d1bfa6 --- /dev/null +++ b/docs/tutorial/zh/build.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -e + +# Generate the API rst files +sphinx-apidoc -o source/build_api ../../../src/agentscope -t source/_templates -e + +# Build the html +sphinx-build -M html source build \ No newline at end of file diff --git a/docs/tutorial/zh/make.bat b/docs/tutorial/zh/make.bat new file mode 100644 index 000000000..dc1312ab0 --- /dev/null +++ b/docs/tutorial/zh/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/tutorial/zh/source/_static/css/gallery.css b/docs/tutorial/zh/source/_static/css/gallery.css new file mode 100644 index 000000000..78892dbff --- /dev/null +++ b/docs/tutorial/zh/source/_static/css/gallery.css @@ -0,0 +1,83 @@ +.sphx-glr-download-link-note.admonition.note { + display: none; +} + +.sphx-glr-download { + display: none; +} + +.bordered-image { + border: 1px solid gray; +} + +:root { + --item-card-width: 200px; + --item-card-margin: 10px; + --item-card-title-height: 50px; + + --item-card-img-length: calc(var(--item-card-width) - 2*var(--item-card-margin)); + --item-card-title-width: calc(var(--item-card-width) - 2*var(--item-card-margin)); + --item-card-title-margin-top: var(--item-card-margin); + + --item-card-height: calc(var(--item-card-margin) * 3 + var(--item-card-img-length) + var(--item-card-title-height)); +} + +.gallery-item { + position: relative; + display: inline-block; + width: var(--item-card-width); + height: var(--item-card-height); + box-shadow: 0 0 8px rgba(0, 0, 0, 0.2); + margin: 7px; +} + +.gallery-item-card { + position: absolute; + top: 0; + left: 0; + width: var(--item-card-width); + height: var(--item-card-height); + display: flex; + flex-direction: column; + margin: var(--item-card-margin); +} + +.gallery-item-card-img { + height: var(--item-card-img-length); + width: var(--item-card-img-length); + min-width: var(--item-card-img-length); + min-height: var(--item-card-img-length); + display: block; +} + +.gallery-item-card-title { + text-align: center; + margin-top: var(--item-card-margin); + font-weight: bold; + min-height: var(--item-card-title-height); + height: var(--item-card-title-height); + width: var(--item-card-title-width); + display: flex; + align-items: center; + justify-content: center; +} + +.gallery-item-description { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(255, 255, 255, 0.9); + /*background-color: #1e8449;*/ + color: black; + display: none; + justify-content: center; + align-items: flex-start; +} + +.gallery-item:hover .gallery-item-description { + display: flex; + padding: 10px; + border: 1px solid rgba(0, 0, 0, 0.22); +} \ No newline at end of file diff --git a/docs/tutorial/zh/source/_templates/module.rst_t b/docs/tutorial/zh/source/_templates/module.rst_t new file mode 100644 index 000000000..74d73a4c5 --- /dev/null +++ b/docs/tutorial/zh/source/_templates/module.rst_t @@ -0,0 +1,5 @@ +{{ basename | heading }} +.. automodule:: {{ qualname }} +{%- for option in automodule_options %} + :{{ option }}: +{%- endfor %} \ No newline at end of file diff --git a/docs/tutorial/zh/source/_templates/package.rst_t b/docs/tutorial/zh/source/_templates/package.rst_t new file mode 100644 index 000000000..0c0f707da --- /dev/null +++ b/docs/tutorial/zh/source/_templates/package.rst_t @@ -0,0 +1,10 @@ +{%- macro automodule(modname, options) -%} +.. automodule:: {{ modname }} +{%- for option in options %} + :{{ option }}: +{%- endfor %} +{%- endmacro %} + +{{- pkgname | heading }} + +{{ automodule(pkgname, automodule_options) }} \ No newline at end of file diff --git a/docs/tutorial/zh/source/conf.py b/docs/tutorial/zh/source/conf.py new file mode 100644 index 000000000..eaf3d09bf --- /dev/null +++ b/docs/tutorial/zh/source/conf.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = "AgentScope Doc" +copyright = "2025, Alibaba" +author = "Alibaba Tongyi Lab" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "myst_parser", + "sphinx_gallery.gen_gallery", + "sphinx.ext.autodoc", + "sphinx.ext.viewcode", + "sphinx.ext.napoleon", +] + +myst_enable_extensions = [ + "colon_fence", +] + +sphinx_gallery_conf = { + "download_all_examples": False, + "examples_dirs": [ + "tutorial", + ], + "gallery_dirs": [ + "build_tutorial", + ], + "filename_pattern": "tutorial/.*\.py", + "example_extensions": [".py"], +} + +templates_path = ["_templates"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +languages = ["en", "zh_CN"] +language = "en" + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "sphinx_rtd_theme" +html_static_path = ["_static"] + +html_css_files = [ + "css/gallery.css", +] + +source_suffix = [".md", ".rst"] + + +# -- Options for API documentation ------------------------------------------- + +autodoc_member_order = "groupwise" +add_module_names = False +python_display_short_literal_types = True + + +def skip_member(app, what, name, obj, skip, options): + if name in [ + "Operator", + "ServiceFactory", + "", + ]: + return True + + return skip + + +def setup(app): + app.connect("autodoc-skip-member", skip_member) diff --git a/docs/tutorial/zh/source/index.rst b/docs/tutorial/zh/source/index.rst new file mode 100644 index 000000000..69a63ce36 --- /dev/null +++ b/docs/tutorial/zh/source/index.rst @@ -0,0 +1,64 @@ +.. AgentScope Doc documentation master file, created by + sphinx-quickstart on Thu Aug 8 15:07:21 2024. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to AgentScope's documentation! +========================================== + +.. toctree:: + :maxdepth: 1 + :caption: Tutorial + + build_tutorial/quickstart + build_tutorial/message + build_tutorial/agent + build_tutorial/conversation + +.. toctree:: + :maxdepth: 1 + :caption: FQA + + tutorial/faq + +.. toctree:: + :maxdepth: 1 + :caption: Task Guides + + build_tutorial/model + build_tutorial/prompt + build_tutorial/structured_output + build_tutorial/streaming + build_tutorial/builtin_agent + build_tutorial/multimodality + build_tutorial/visual + build_tutorial/monitor + build_tutorial/tool + tutorial/rag + build_tutorial/distribution + build_tutorial/prompt_optimization + build_tutorial/web_browser + build_tutorial/low_code + + +.. toctree:: + :maxdepth: 1 + :caption: Examples + + build_tutorial/examples + +.. toctree:: + :maxdepth: 1 + :caption: API Docs + + build_api/agentscope + build_api/agentscope.message + build_api/agentscope.models + build_api/agentscope.agents + build_api/agentscope.memory + build_api/agentscope.parsers + build_api/agentscope.rag + build_api/agentscope.service + build_api/agentscope.prompt + build_api/agentscope.tokens + build_api/agentscope.exception diff --git a/docs/tutorial/zh/source/tutorial/README.md b/docs/tutorial/zh/source/tutorial/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/tutorial/zh/source/tutorial/agent.py b/docs/tutorial/zh/source/tutorial/agent.py new file mode 100644 index 000000000..611e88fd9 --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/agent.py @@ -0,0 +1,177 @@ +# -*- coding: utf-8 -*- +""" +.. _build-agent: + +构建智能体 +==================== + +AgentScope 中,可以通过继承基类`agentscope.agents.AgentBase`来构建智能体 + +在下面,我们将构建一个简单的,可以和其他人互动的智能体。 + +""" + +from agentscope.agents import AgentBase +from agentscope.memory import TemporaryMemory +from agentscope.message import Msg +from agentscope.models import DashScopeChatWrapper +import json + + +# %% +# 定义智能体 +# -------------------------------- +# 继承 `agentscope.agents.AgentBase` 类并实现其构造函数和 `reply` 方法。 +# +# 在构造函数中,我们初始化智能体的名字、系统提示、记忆模块和模型。 +# 在本例中,我们采用 DashScope Chat API 中的 `qwen-max` 作为模型服务。 +# 当然,你可以将其替换为 `agentscope.models` 下的其它模型。 +# +# `reply`方法是智能体的核心,它接受消息作为输入并返回回复消息。 +# 在该方法中,我们实现了智能体的基本逻辑: +# +# - 在记忆中记录输入消息, +# - 使用系统提示和记忆构建提示词, +# - 调用模型获取返回值, +# - 在记忆中记录返回值并返回一个消息。 +# + + +class JarvisAgent(AgentBase): + def __init__(self): + super().__init__("Jarvis") + + self.name = "Jarvis" + self.sys_prompt = "你是一个名为Jarvis的助手。" + self.memory = TemporaryMemory() + self.model = DashScopeChatWrapper( + config_name="_", + model_name="qwen-max", + ) + + def reply(self, msg): + # 在记忆中记录消息 + self.memory.add(msg) + + # 使用系统提示和记忆构建上下文 + prompt = self.model.format( + Msg( + name="system", + content=self.sys_prompt, + role="system", + ), + self.memory.get_memory(), + ) + + # 调用模型获取响应 + response = self.model(prompt) + + # 在记忆中记录响应消息并返回 + msg = Msg( + name=self.name, + content=response.text, + role="assistant", + ) + self.memory.add(msg) + + self.speak(msg) + return msg + + +# %% +# 创建智能体类后,我们实例化它,并通过发送消息与之交互。 +# + +jarvis = JarvisAgent() + +msg = Msg( + name="user", + content="嗨!Jarvis。", + role="user", +) + +msg_reply = jarvis(msg) + +print(f"消息的发送者: {msg_reply.name}") +print(f"发送者的角色: {msg_reply.role}") +print(f"消息的内容: {msg_reply.content}") + + +# %% +# ====================== +# +# 组件 +# ---------- +# 现在我们简要介绍上述智能体中使用到的基本组件,包括 +# +# * 记忆 +# * 模型 +# +# 记忆 +# ^^^^^^^ +# 记忆模块提供了记忆管理的基本操作。 +# + +memory = TemporaryMemory() + +# 添加一条消息 +memory.add(Msg("system", "你是一个名为Jarvis的助手。", "system")) + +# 一次添加多条消息 +memory.add( + [ + Msg("Stank", "嗨!", "user"), + Msg("Jarvis", "我能为您做些什么吗?", "assistant"), + ], +) + +print(f"当前记忆: {memory.get_memory()}") +print(f"当前大小: {memory.size()}") + +# %% +# 使用参数 `recent_n` 获取最后两条消息。 +# + +recent_two_msgs = memory.get_memory(recent_n=2) + +for i, msg in enumerate(recent_two_msgs): + print( + f"MSG{i}: 发送者: {msg.name}, 角色: {msg.role}, 内容: {msg.content}", + ) + +# %% +# 删除记忆中的第一条消息。 +# + +memory.delete(0) + +print(f"删除后的记忆: {memory.get_memory()}") +print(f"删除后的大小: {memory.size()}") + +# %% +# 模型 +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# `agentscope.models` 封装了不同的模型 API,并在其 `format` 函数中为不同的 API 提供了基本的提示词构建策略。 +# +# 以 DashScope Chat API 为例: +# + +messages = [ + Msg("system", "你是一个名为Jarvis的助手。", "system"), + Msg("Stank", "嗨!", "user"), + Msg("Jarvis", "我能为您做些什么吗?", "assistant"), +] + +model = DashScopeChatWrapper( + config_name="api", + model_name="qwen-max", +) +prompt = model.format(messages) +print(json.dumps(prompt, indent=4, ensure_ascii=False)) + +# %% +# +# 进一步阅读 +# --------------------- +# - :ref:`builtin-agent` +# - :ref:`model_api` diff --git a/docs/tutorial/zh/source/tutorial/builtin_agent.py b/docs/tutorial/zh/source/tutorial/builtin_agent.py new file mode 100644 index 000000000..2f3ba443a --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/builtin_agent.py @@ -0,0 +1,222 @@ +# -*- coding: utf-8 -*- +""" +.. _builtin_agent: + +内置智能体 +============================= + +AgentScope 内置若干智能体类,以支持不同使用场景,同时展示如何使用 AgentScope 构建智能体。 + +.. list-table:: + :header-rows: 1 + + * - 类 + - 描述 + * - `UserAgent` + - 允许用户参与对话的智能体。 + * - `DialogAgent` + - 使用自然语言交谈的智能体。 + * - `DictDialogAgent` + - 支持结构化输出的智能体。 + * - `ReActAgent` + - 以 reasoning-acting 循环的方式使用工具的智能体。 + * - `LlamaIndexAgent` + - 检索增强型生成 (RAG) 智能体。 + +""" + +import agentscope + +for module in agentscope.agents.__all__: + if module.endswith("Agent"): + print(module) + +# %% +# .. note:: 为了使同一个智能体类能够支持不同的大语言模型 API,所有内置智能体类都通过模型配置名 `model_config_name` 来进行初始化。如果你构建的智能体不打算多个不同的模型,推荐可以显式地进行模型初始化,而不是使用模型配置名。 +# + +import agentscope + +agentscope.init( + model_configs={ + "config_name": "my-qwen-max", + "model_name": "qwen-max", + "model_type": "dashscope_chat", + }, +) + +# %% +# DialogAgent +# ---------------------------- +# DialogAgent 是 AgentScope 中最基本的智能体,可以以对话的方式与用户交互。 +# 开发者可以通过提供不同的系统提示和模型配置来自定义它。 +# + +from agentscope.agents import DialogAgent +from agentscope.message import Msg + +# 初始化一个对话智能体 +alice = DialogAgent( + name="Alice", + model_config_name="my-qwen-max", + sys_prompt="你是一个名叫 Alice 的助手。", +) + +# 向智能体发送一条消息 +msg = Msg("Bob", "嗨!你叫什么名字?", "user") +response = alice(msg) + +# %% +# UserAgent +# ---------------------------- +# `UserAgent` 类允许用户与其他智能体交互。 +# 当调用 `UserAgent` 对象时,它会要求用户输入,并将其格式化为 `Msg` 对象。 +# +# 这里我们展示如何初始化一个 `UserAgent` 对象,并与对话智能体 `alice` 进行交互。 +# + +from agentscope.agents import UserAgent +from io import StringIO +import sys + +user = UserAgent( + name="Bob", + input_hint="用户输入: \n", +) + +# 模拟用户输入 +sys.stdin = StringIO("你认识我吗?\n") + +msg = user() +msg = alice(msg) + +# %% +# DictDialogAgent +# ---------------------------- +# `DictDialogAgent` 支持结构化输出,并可通过 `set_parser` 方法指定解析器来实现自动后处理。 +# +# 我们首先初始化一个 `DictDialogAgent` 对象,然后通过更换解析器,实现不同结构化的输出。 +# + +from agentscope.agents import DictDialogAgent +from agentscope.parsers import MarkdownJsonDictParser + + +charles = DictDialogAgent( + name="Charles", + model_config_name="my-qwen-max", + sys_prompt="你是一个名叫 Charles 的助手。", + max_retries=3, # 获取所需结构化输出失败时的最大重试次数 +) + +# 要求智能体生成包含 `thought`、`speak` 和 `decision` 的结构化输出 +parser1 = MarkdownJsonDictParser( + content_hint={ + "thought": "你的想法", + "speak": "你要说的话", + "decision": "你的最终决定,true/false", + }, + required_keys=["thought", "speak", "decision"], +) + +charles.set_parser(parser1) +msg1 = charles(Msg("Bob", "在下雨天外出是个好主意吗?", "user")) + +print(f"内容字段: {msg1.content}") +print(f"内容字段的类型: {type(msg1.content)}") + +# %% +# 然后,我们要求智能体从 1 到 10 中选择一个数字。 +# + +parser2 = MarkdownJsonDictParser( + content_hint={ + "thought": "你的想法", + "speak": "你要说的话", + "number": "你选择的数字", + }, +) + +charles.set_parser(parser2) +msg2 = charles(Msg("Bob", "从 1 到 10 中选择一个数字。", "user")) + +print(f"响应消息的内容: {msg2.content}") + +# %% +# 下一个问题是如何对结构化输出进行后处理。 +# 例如,`thought` 字段应该存储在记忆中而不暴露给其他人, +# 而 `speak` 字段应该显示给用户,`decision` 字段应该能够在响应消息对象中轻松访问。 +# + +parser3 = MarkdownJsonDictParser( + content_hint={ + "thought": "你的想法", + "speak": "你要说的话", + "number": "你选择的数字", + }, + required_keys=["thought", "speak", "number"], + keys_to_memory=["thought", "speak", "number"], # 需要存储在记忆中 + keys_to_content="speak", # 需要显示给用户 + keys_to_metadata="number", # 需要存储在响应消息的元数据中 +) + +charles.set_parser(parser3) + +msg3 = charles(Msg("Bob", "从 20 到 30 中选择一个数字。", "user")) + +print(f"内容字段: {msg3.content}") +print(f"内容字段的类型: {type(msg3.content)}\n") + +print(f"元数据字段: {msg3.metadata}") +print(f"元数据字段的类型: {type(msg3.metadata)}") + + +# %% +# .. hint:: 有关结构化输出的高级用法和更多不同解析器,请参阅 :ref:`structured-output` 章节。 +# +# ReActAgent +# ---------------------------- +# `ReActAgent` 以 reasoning-acting 循环的方式使用工具来解决给定的问题。 +# +# 首先我们为智能体准备一个工具函数。 +# + +from agentscope.service import ServiceToolkit, execute_python_code + + +toolkit = ServiceToolkit() + +# 通过指定部分参数将 execute_python_code 设置为工具,这里用户需要在 add 方法里面配置部分 +# 参数,通常是一些应该由开发者提供的参数,例如 API Key 等,剩余参数由智能体自己填写。 +toolkit.add( + execute_python_code, + timeout=300, + use_docker=False, + maximum_memory_bytes=None, +) + +# %% +# 然后我们初始化一个 `ReActAgent` 来解决给定的问题。 +# + +from agentscope.agents import ReActAgent + +david = ReActAgent( + name="David", + model_config_name="my-qwen-max", + sys_prompt="你是一个名叫 David 的助手。", + service_toolkit=toolkit, + max_iters=10, + verbose=True, +) + +task = Msg("Bob", "请帮我计算 151513434*54353453453。", "user") + +response = david(task) + + +# %% +# LlamaIndexAgent +# ---------------------------- +# 有关更多详细信息,请参阅检索增强型生成 (RAG) 章节。 +# diff --git a/docs/tutorial/zh/source/tutorial/conversation.py b/docs/tutorial/zh/source/tutorial/conversation.py new file mode 100644 index 000000000..e3b15d245 --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/conversation.py @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- +""" +.. _build-conversation: + +构建对话 +====================== + +AgentScope 中,不同智能体之间通过“显式的消息交换”来构建对话。 + +""" + +from agentscope.agents import DialogAgent, UserAgent +from agentscope.message import Msg +from agentscope import msghub +import agentscope + +# 为简单起见,通过模型配置进行初始化 +agentscope.init( + model_configs={ + "config_name": "my-qwen-max", + "model_name": "qwen-max", + "model_type": "dashscope_chat", + }, +) + +# %% +# 两个智能体 +# ----------------------------- +# 这里我们构建一个简单的对话,对话双方是两个智能体 `Angel` 和 `Monster`。 +# + +angel = DialogAgent( + name="Angel", + sys_prompt="你是一个名叫 Angel 的歌手,说话风格简单凝练。", + model_config_name="my-qwen-max", +) + +monster = DialogAgent( + name="Monster", + sys_prompt="你是一个名叫 Monster 的运动员,说话风格简单凝练。", + model_config_name="my-qwen-max", +) + +# %% +# 现在,我们让这两个智能体对话三轮。 +# + +msg = None +for _ in range(3): + msg = angel(msg) + msg = monster(msg) + +# %% +# 如果你想参与到对话中,只需实例化一个内置的 `UserAgent` 来向智能体输入消息。 +# + +user = UserAgent(name="User") + +# %% +# 多于两个智能体 +# --------------------- +# 当一个对话中有多于两个智能体时,来自一个智能体的消息应该广播给所有其他智能体。 +# +# 为了简化广播消息的操作,AgentScope 提供了一个 `msghub` 模块。 +# 具体来说,在同一个 `msghub` 中的智能体会自动接收其它参与者发送的消息。 +# 我们只需要组织发言的顺序,而不需要显式地将消息传递给其它智能体。 +# +# 当然,你也可以显式传递消息,但是记忆模块中的查重操作会自动跳过添加重复的消息。因此不会造成记忆中的消息重复。 +# +# 这里是一个 `msghub` 的示例,我们首先创建三个智能体:Alice、Bob 和 Charlie,它们都使用` qwen-max` 模型。 + +alice = DialogAgent( + name="Alice", + sys_prompt="你是一个名叫Alice的助手。", + model_config_name="my-qwen-max", +) + +bob = DialogAgent( + name="Bob", + sys_prompt="你是一个名叫Bob的助手。", + model_config_name="my-qwen-max", +) + +charlie = DialogAgent( + name="Charlie", + sys_prompt="你是一个名叫Charlie的助手。", + model_config_name="my-qwen-max", +) + +# %% +# 三个智能体将在对话中轮流报数。 +# + +# 介绍对话规则 +greeting = Msg( + name="user", + content="现在开始从1开始逐个报数,每次只产生一个数字,绝对不能产生其他任何信息。", + role="user", +) + +with msghub( + participants=[alice, bob, charlie], + announcement=greeting, # 开始时,通知消息将广播给所有参与者。 +) as hub: + # 对话的第一轮 + alice() + bob() + charlie() + + # 我们可以动态管理参与者,例如从对话中删除一个智能体 + hub.delete(charlie) + + # 向所有参与者广播一条消息 + hub.broadcast( + Msg( + "user", + "Charlie已离开对话。", + "user", + ), + ) + + # 对话的第二轮 + alice() + bob() + charlie() + +# %% +# 现在我们打印Alice和Bob的记忆,以检查操作是否成功。 + +print("Alice的记忆:") +for msg in alice.memory.get_memory(): + print(f"{msg.name}:{msg.content}") + +print("\nCharlie的记忆:") +for msg in charlie.memory.get_memory(): + print(f"{msg.name}:{msg.content}") + +# %% +# 在上面的示例中,Charlie 在第一轮结束后离开了对话,因此没有收到 Alice 和 Bob 的"4"和"5"。 +# 所以第二轮时它报了"4"。 +# 另一方面,Alice 和 Bob 继续了对话,没有 Charlie 的参与。 +# diff --git a/docs/tutorial/zh/source/tutorial/distribution.py b/docs/tutorial/zh/source/tutorial/distribution.py new file mode 100644 index 000000000..cea79e50e --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/distribution.py @@ -0,0 +1,222 @@ +# -*- coding: utf-8 -*- +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: sphinx +# format_version: '1.1' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 (ipykernel) +# language: python +# name: python3 +# --- + +""" +.. _distribution: + +分布式 +============ + +本节介绍 AgentScope 分布式的使用方法。AgentScope 原生提供了基于 gRPC 的分布式模式, +在这种模式下,一个应用程序中的多个智能体可以部署到不同的进程或者甚至不同的机器上,从而充分利用计算资源,提高效率。 + +基本使用 +~~~~~~~~~~~ + +与传统模式相比,AgentScope 的分布式模式不需要修改主进程代码。只需在初始化智能体时调用 `to_dist` 函数。 + +本节将展示如何使用 AgentScope 的分布式模式进行网页搜索。 +为了展示 AgentScope 分布式模式带来的加速效果,示例中将自定义一个 `WebAgent` 类,在该类型将等待5秒来模拟抓取网页和从中寻找答案的过程。 + +执行搜索的过程是 `run` 函数。传统模式和分布式模式之间唯一的区别在于初始化阶段,即 `init_without_dist` 和 `init_with_dist`。 +在分布式模式下,您需要调用 `to_dist` 函数,将原始智能体转换为对应的分布式版本。 + +.. code-block:: python + + # 请勿在jupyter notebook中运行此代码。 + # 请将代码复制到 `dist_main.py` 文件中,并使用 `python dist_main.py` 命令运行此代码。 + # 在运行此代码之前,请安装分布式版本的 agentscope。 + + import time + import agentscope + from agentscope.agents import AgentBase + from agentscope.message import Msg + + class WebAgent(AgentBase): + + def __init__(self, name): + super().__init__(name) + + def get_answer(self, url: str, query: str): + time.sleep(5) + return f"来自 {self.name} 的答案" + + def reply(self, x: dict = None) -> dict: + return Msg( + name=self.name, + content=self.get_answer(x.content["url"], x.content["query"]) + ) + + + QUERY = "示例查询" + URLS = ["页面_1", "页面_2", "页面_3", "页面_4", "页面_5"] + + def init_without_dist(): + return [WebAgent(f"W{i}") for i in range(len(URLS))] + + + def init_with_dist(): + return [WebAgent(f"W{i}").to_dist() for i in range(len(URLS))] + + + def run(agents): + start = time.time() + results = [] + for i, url in enumerate(URLS): + results.append(agents[i].reply( + Msg( + name="system", + role="system", + content={ + "url": url, + "query": QUERY + } + ) + )) + for result in results: + print(result.content) + end = time.time() + return end - start + + + if __name__ == "__main__": + agentscope.init() + start = time.time() + simple_agents = init_without_dist() + dist_agents = init_with_dist() + end = time.time() + print(f"初始化时间:{end - start}") + print(f"无分布式模式下的运行时间:{run(simple_agents)}") + print(f"分布式模式下的运行时间:{run(dist_agents)}") + + +运行此示例的输出如下: + +.. code-block:: text + + 初始化时间:16.50428819656372 + 来自 W0 的答案 + 来自 W1 的答案 + 来自 W2 的答案 + 来自 W3 的答案 + 来自 W4 的答案 + 无分布式模式下的运行时间:25.034368991851807 + 来自 W0 的答案 + 来自 W1 的答案 + 来自 W3 的答案 + 来自 W2 的答案 + 来自 W4 的答案 + 分布式模式下的运行时间:5.0517587661743164 + +从上面的示例输出中,我们可以观察到在采用分布式模式后(25秒->5秒),运行速度显著提高。 + +上面的示例是AgentScope分布式模式最常见的使用场景。当不追求极端性能时,建议直接使用这种方法。 +如果您需要进一步优化性能,则需要对AgentScope分布式模式有更深入的了解。 +下面将介绍AgentScope分布式模式的高级用法。 +""" + +############################################################################### +# 高级用法 +# ~~~~~~~~~~~~~~~ +# +# 本节将介绍 AgentScope 分布式模式的高级使用方法,以进一步提高操作效率。 +# +# 基本概念 +# -------------- +# +# +# 在深入学习高级用法之前,我们必须先了解AgentScope分布式模式的一些基本概念。 +# +# - **主进程**:AgentScope应用程序所在的进程被称为主进程。例如,上一节中的 `run` 函数就是在主进程中运行的。每个 AgentScope 应用程序只有一个主进程。 +# - **智能体服务器进程**:AgentScope智能体服务器进程是智能体在分布式模式下运行的进程。例如,在上一节中,`dist_agents` 中的所有智能体都在智能体服务器进程中运行。可以有多个AgentScope智能体服务器进程。这些进程可以运行在任何可网络访问的机器上,每个智能体服务器进程中可以同时运行多个智能体。 +# - **子进程模式**:在子进程模式下,智能体服务器进程由主进程启动为子进程。例如,在上一节中,`dist_agents` 中的每个智能体实际上都是主进程的一个子进程。这是默认模式,也就是说,如果您直接调用 `to_dist` 函数而不传入任何参数,它将使用此模式。 +# - **独立进程模式**:在独立进程模式下,智能体服务器与主进程无关,需要预先在机器上启动。需要向 `to_dist` 函数传递特定参数,具体用法将在下一节中介绍。 +# +# 使用独立进程模式 +# ---------------------- +# +# 与子进程模式相比,独立进程模式可以避免初始化子进程的开销,从而减少执行开始时的延迟。这可以有效地提高多次调用 `to_dist` 的程序的效率。 +# +# 在独立进程模式下,您需要预先在机器上启动智能体服务器进程,并向 `to_dist` 函数传递特定参数。这里,我们将继续使用基本用法一节中的示例,假设基本用法的代码文件为 `dist_main.py`。然后,创建并单独运行以下脚本。 +# +# .. code-block:: python +# +# # 请勿在jupyter notebook中运行此代码。 +# # 将此代码复制到名为 `dist_server.py` 的文件中,并使用命令 `python dist_server.py` 运行。 +# # 在运行此代码之前,请安装分布式版本的 agentscope。 +# # pip install agentscope[distributed] +# +# from dist_main import WebAgent +# import agentscope +# +# if __name__ == "__main__": +# agentscope.init() +# assistant_server_launcher = RpcAgentServerLauncher( +# host="localhost", +# port=12345, +# custom_agent_classes=[WebAgent], +# ) +# assistant_server_launcher.launch(in_subprocess=False) +# assistant_server_launcher.wait_until_terminate() +# +# +# 该脚本在 `dist_server.py` 文件中启动AgentScope智能体服务器进程,该文件位于与基本用法中的 `dist_main.py` 文件相同的目录下。此外,我们还需要对 `dist_main.py` 文件做一些小的修改,添加一个新的 `init_with_dist_independent` 函数,并用这个新函数替换对 `init_with_dist` 的调用。 +# +# .. code-block:: python +# +# def init_with_dist_independent(): +# return [WebAgent(f"W{i}").to_dist(host="localhost", port=12345) for i in range(len(URLS))] +# +# if __name__ == "__main__": +# agentscope.init() +# start = time.time() +# simple_agents = init_without_dist() +# dist_agents = init_with_dist_independent() +# end = time.time() +# print(f"初始化所需时间:{end - start}") +# print(f"无分布式模式下的运行时间:{run(simple_agents)}") +# print(f"分布式模式下的运行时间:{run(dist_agents)}") +# +# +# 完成修改后,打开一个命令提示符并运行 `dist_server.py` 文件。一旦成功启动,再打开另一个命令提示符并运行 `dist_main.py` 文件。 +# +# 此时,`dist_main.py` 的输出中初始化时间将显著减少。例如,这里的初始化时间仅为0.02秒。 +# +# .. code-block:: text +# +# 初始化所需时间:0.018129825592041016 +# ... +# +# +# 需要注意的是,上面的示例中使用了 `host="localhost"` 和 `port=12345` ,并且 `dist_main.py` 和 `dist_server.py` 都在同一台机器上运行。在实际使用时,`dist_server.py`可以运行在不同的机器上。此时,`host` 应该设置为运行 `dist_server.py` 的机器的 IP 地址,而 `port` 应该设置为任何可用端口,确保不同机器可以通过网络进行通信。 +# +# 避免重复初始化 +# ------------------------------ +# +# 在上面的代码中,`to_dist` 函数是在已经初始化过的智能体上调用的。`to_dist` 的本质是将原始智能体克隆到智能体服务器进程中,同时在主进程中保留一个 `RpcAgent` 作为原始智能体的代理。对这个 `RpcAgent` 的调用将被转发到智能体服务器进程中对应的智能体。 +# +# 这种做法存在一个潜在问题:原始智能体会被初始化两次——一次在主进程中,一次在智能体服务器进程中,而且这两次初始化是按顺序执行的,无法通过并行来加速。对于初始化成本较低的智能体,直接调用 `to_dist` 函数不会对性能造成显著影响。但是对于初始化成本较高的智能体,避免冗余初始化就很重要。因此,AgentScope分布式模式提供了一种分布式模式初始化的替代方法,允许直接在任何智能体的初始化函数中传递 `to_dist` 参数,如下面修改后的示例所示: +# +# .. code-block:: python +# +# def init_with_dist(): +# return [WebAgent(f"W{i}", to_dist=True) for i in range(len(URLS))] +# +# +# def init_with_dist_independent(): +# return [WebAgent(f"W{i}", to_dist={"host": "localhost", "port": "12345"}) for i in range(len(URLS))] +# +# +# 对于子进程模式,您只需在初始化函数中传递 `to_dist=True` 即可。对于独立进程模式,则需要将原本传递给 `to_dist` 函数的参数以字典形式传递给 `to_dist` 字段。 diff --git a/docs/tutorial/zh/source/tutorial/examples.py b/docs/tutorial/zh/source/tutorial/examples.py new file mode 100644 index 000000000..ef3974b8c --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/examples.py @@ -0,0 +1,178 @@ +# -*- coding: utf-8 -*- +""" +样例 +======================== + +配置类 +------------------------ + +.. raw:: html + + + + + + + + + + +------------------------ + +智能体 +------------------------ + +.. raw:: html + + + + + + + + + + + + + + + + +------------------------ + +游戏 +------------------------ + +.. raw:: html + + + + + + + + + + + +------------------------ + +对话 +------------------------ + +.. raw:: html + + + + + + + + + + + +""" diff --git a/docs/tutorial/zh/source/tutorial/faq.md b/docs/tutorial/zh/source/tutorial/faq.md new file mode 100644 index 000000000..ab5dae86e --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/faq.md @@ -0,0 +1,48 @@ +# 常见问题解答 + +## 关于 AgentScope +_**Q**:AgentScope 与其他代理平台/框架有什么区别?_ +
+**A**:AgentScope 是一个面向开发者的多智能体平台,旨在简化**多智能体应用程序**的开发、部署和监控。 + +## 关于模型 + +_**Q**:如何在 AgentScope 中集成/使用新的模型 API?_ +
+**A**:请参考 [集成新的 LLM API](integrating_new_api) 。 + +_**Q**:AgentScope 支持哪些 LLM?_ +
+**A**:AgentScope 支持大多数现有的 LLM API,包括 OpenAI、Claude、Gemini、DashScope 等。支持列表请参考 [模型 API](model_api) 。 + +_**Q**:如何在 AgentScope 中监控 token 使用情况?_ +
+**A**:详情请参考 [监控 Token 使用情况](token_usage)。 + +## 关于工具 + +_**Q**:AgentScope 提供了哪些工具?_ +
+**A**:请参考 [工具](tools)。 + +_**Q**:如何在 AgentScope 中使用这些工具?_ +
+**A**:AgentScope 提供了 `ServiceToolkit` 模块用于工具使用。详细用法请参考 [工具](tools)。 + +## 关于智能体 + +_**Q**:如何在 AgentScope 中使用智能体?_ +
+**A**:您可以使用 AgentScope 中内置的智能体,或开发自己的智能体。详情请参考 [内置智能体](builtin_agent) 一节。 + +## 关于 GUI + +_**Q**:AgentScope 提供了哪些 GUI?_ +
+**A**:AgentScope 支持在 Gradio 中运行您的应用程序,并且还提供了一个名为 AgentScope Studio 的 GUI,供您监控和管理应用程序。 + +## 关于低代码开发 + +_**Q**:AgentScope 中的低代码开发是什么?_ +
+**A**:它意味着您可以通过拖拽组件来开发应用程序。详情请参考 [低代码开发](low_code)。 diff --git a/docs/tutorial/zh/source/tutorial/low_code.py b/docs/tutorial/zh/source/tutorial/low_code.py new file mode 100644 index 000000000..ab1402f1e --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/low_code.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +""" +.. _low_code: + +低代码开发 +=========================== +本教程介绍如何在AgentScope Workstation中通过拖拽界面构建多智能体应用程序。 + +Workstation +------------------ + +Workstation现已集成在 :ref:`agentscope-studio` 中。 +它为零代码用户提供了一种更简单的方式来构建多智能体应用程序。 + +.. note:: Workstation 正处于快速迭代开发中,将会频繁更新。 + +启动 Workstation +--------------------- + +首先确保您已安装最新版本的 AgentScope。 + +执行以下Python代码来启动 AgentScope Studio: + +.. code-block:: python + + import agentscope + agentscope.studio.init() + +或在终端中运行以下 bash 命令: + +.. code-block:: bash + + as_studio + +然后访问 `https://127.0.0.1:5000` 进入 AgentScope Studio,并点击侧边栏中的 Workstation 图标进入。 + + +* **中央工作区**:您可以在这个主要区域拖拽组件来构建应用程序。 + +* **顶部工具箱**:用于导入、导出、检查和运行您的应用程序。 + +.. image:: https://img.alicdn.com/imgextra/i1/O1CN01RXAVVn1zUtjXVvuqS_!!6000000006718-1-tps-3116-1852.gif + +内置示例 +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +对于初学者,我们强烈建议从预构建的示例开始。您可以直接单击示例将其导入到中央工作区。或者,为了获得更加结构化的学习体验,您也可以选择跟随每个示例链接的教程。这些教程将一步步指导如何在 AgentScope Workstation 上构建多智能体应用。 + +构建应用 +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +要构建应用程序,请执行以下步骤: + +* 选择并拖拽组件:从侧边栏中单击并拖拽所选组件到中央工作区。 + +* 连接节点:大多数节点都有输入和输出点。单击一个组件的输出点并拖拽到另一个组件的输入点,以创建消息流管道。这样不同的节点就可以传递消息。 + +* 配置节点:将节点放入工作区后,单击任意一个节点来填写其配置设置。您可以自定义提示、参数和其他属性。 + +运行应用 +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +构建完应用程序后,单击"运行"按钮。在运行之前,Workstation会检查应用程序中是否有任何错误。如果有错误,系统会提示您在继续之前进行修正。之后,您的应用程序将在与AgentScope Studio相同的Python环境中执行,您可以在Dashboard中找到它。 + +导入/导出应用 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Workstation支持导入和导出您的应用程序。单击"导出HTML"或"导出Python"按钮可生成代码,您可以将其分发给社区或本地保存。如果要将导出的代码转换为Python代码,可以使用以下命令将JSON配置编译为Python代码: + +.. code-block:: bash + + # 编译 + as_workflow config.json --compile ${YOUR_PYTHON_SCRIPT_NAME}.py + +如果您想直接运行本地配置,可以使用以下命令: + +.. code-block:: bash + + # 运行 + as_gradio config.json + + +想要进一步编辑您的应用程序吗?只需单击"导入HTML"按钮,将以前导出的HTML代码重新上传到AgentScope Workstation即可。 + +检查应用 +^^^^^^^^^^^^^^^^^^^^^^^^^ + +构建应用程序后,您可以单击"检查"按钮来验证应用程序结构的正确性。将执行以下检查规则: + +* 模型和智能体存在检查:每个应用程序必须至少包含一个模型节点和一个智能体节点。 + +* 单连接策略:每个组件的每个输入不应该有多于一个连接。 + +* 必填字段验证:所有必填输入字段都必须填写,以确保每个节点都有正确运行所需的参数。 + +* 配置命名一致性:智能体节点使用的"模型配置名称"必须与模型节点中定义的"配置名称"相对应。 + +* 适当的节点嵌套:像ReActAgent这样的节点应该只包含工具节点。同样,像IfElsePipeline这样的管道节点应该包含正确数量的元素(不超过2个),而ForLoopPipeline、WhileLoopPipeline和MsgHub应该遵守只有一个元素的规则(必须是SequentialPipeline作为子节点)。 + +""" diff --git a/docs/tutorial/zh/source/tutorial/message.py b/docs/tutorial/zh/source/tutorial/message.py new file mode 100644 index 000000000..a3fffb327 --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/message.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +""" +.. _message: + +消息 +==================== + +消息是一种专用的数据结构,用于信息交换。 +在 AgentScope 中,我们使用消息在智能体之间进行通信。 + +消息的最重要字段是:name、role 和 content。 +name 和 role 字段标识消息的发送者,content 字段包含实际信息。 + +.. Note:: role 字段必须选择 `"system"`、`"assistant"`、 `"user"` 其中之一。 +""" + +from agentscope.message import Msg +import json + +# %% +# 创建消息 +# ---------------- +# 可以通过指定 name、role 和 content 字段来创建消息。 +# + +msg = Msg( + name="Jarvis", + role="assistant", + content="嗨!我能为您效劳吗?", +) + +print(f'消息发送者:"{msg.name}"') +print(f'发送者角色:"{msg.role}"') +print(f'消息内容:"{msg.content}"') + +# %% +# 序列化 +# ---------------- +# 消息可以序列化为 JSON 格式的字符串。 +# + +serialized_msg = msg.to_dict() + +print(type(serialized_msg)) +print(json.dumps(serialized_msg, indent=4)) + +# %% +# 反序列化 +# ---------------- +# 从 JSON 格式的字典反序列化消息。 +# + +new_msg = Msg.from_dict(serialized_msg) + +print(new_msg) +print(f'消息发送者:"{new_msg.name}"') +print(f'发送者角色:"{new_msg.role}"') +print(f'消息内容:"{new_msg.content}"') diff --git a/docs/tutorial/zh/source/tutorial/model.py b/docs/tutorial/zh/source/tutorial/model.py new file mode 100644 index 000000000..e91b4c626 --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/model.py @@ -0,0 +1,347 @@ +# -*- coding: utf-8 -*- +""" +.. _model_api: + +模型 API +==================== + +AgentScope 已集成了许多不同模态的模型 API 。 + +.. note:: 1. 本列表不包括文本到语音(TTS)和语音到文本(STT)API。您可以参考 :ref:`tools` 一节。 + 2. 本节仅介绍如何在AgentScope中使用或集成不同的模型API。关于提示要求和提示工程策略的内容将在 :ref:`prompt-engineering` 一节中介绍。 + + +.. list-table:: + :header-rows: 1 + + * - API + - 对话 + - 文本生成 + - 视觉 + - Embedding + * - OpenAI + - ✓ + - ✗ + - ✓ + - ✓ + * - DashScope + - ✓ + - ✗ + - ✓ + - ✓ + * - Gemini + - ✓ + - ✗ + - ✗ + - ✓ + * - Ollama + - ✓ + - ✓ + - ✓ + - ✓ + * - Yi + - ✓ + - ✗ + - ✗ + - ✗ + * - LiteLLM + - ✓ + - ✗ + - ✗ + - ✗ + * - ZhipuAI + - ✓ + - ✗ + - ✗ + - ✓ + * - Anthropic + - ✓ + - ✗ + - ✗ + - ✗ + +在 AgentScope 中使用模型 API 有两种方式。可以根据自己的需求进行选择: + +- **使用模型配置**:这是构建与模型 API 无关的应用程序的推荐方式。可以通过修改配置来更改模型 API,而无需更改智能体代码。 +- **显式初始化模型**:如果只想使用特定的模型 API,显式初始化模型会更加方便和透明。API 文档字符串提供了参数和用法的详细信息。 + +.. tip:: 实际上,使用配置和显式初始化模型是等效的。使用模型配置时,AgentScope 只是将配置中的键值对传递给模型的构造函数。 +""" + +import os + +from agentscope.models import ( + DashScopeChatWrapper, + ModelWrapperBase, + ModelResponse, +) +import agentscope + +# %% +# 使用配置 +# ------------------------------ +# 在模型配置中,需要提供以下三个字段: +# +# - config_name:配置的名称。 +# - model_type:模型 API 的类型,例如 "dashscope_chat"、"openai_chat" 等。 +# - model_name:模型的名称,例如 "qwen-max"、"gpt-4o" 等。 +# +# 在使用模型 API 之前通过调用 `agentscope.init()` 来加载配置,如下所示: +# + +agentscope.init( + model_configs=[ + { + "config_name": "gpt-4o_temperature-0.5", + "model_type": "openai_chat", + "model_name": "gpt-4o", + "api_key": "xxx", + "temperature": 0.5, + }, + { + "config_name": "my-qwen-max", + "model_type": "dashscope_chat", + "model_name": "qwen-max", + }, + ], +) + +# %% +# 对于其它可选参数,可以查看对应模型 API 的构造函数的说明。 + +# %% +# 显式初始化模型 +# -------------------------------- +# `agentscope.models` 模块提供了所有的内置模型 API。 +# 您可以通过调用相应的模型类来显式初始化模型。 +# + +# 打印 agentscope.models 下的模块 +for module_name in agentscope.models.__all__: + if module_name.endswith("Wrapper"): + print(module_name) + +# %% +# 以 DashScope Chat API 为例: +# + +model = DashScopeChatWrapper( + config_name="_", + model_name="qwen-max", + api_key=os.environ["DASHSCOPE_API_KEY"], + stream=False, +) + +response = model( + messages=[ + {"role": "user", "content": "嗨!"}, + ], +) + +# %% +# `response` 是 `agentscope.models.ModelResponse` 的一个对象,它包含以下字段: +# +# - text:生成的文本 +# - embedding:生成的嵌入 +# - image_urls:引用生成的图像 +# - raw:来自 API 的原始响应 +# - parsed:解析后的响应,例如将字符串解析成 JSON 对象 +# - stream:用来挂载流式响应的生成器,更多详情请参考 :ref:`streaming` 一节。 + +print(f"文本:{response.text}") +print(f"嵌入:{response.embedding}") +print(f"图像URL:{response.image_urls}") +print(f"原始响应:{response.raw}") +print(f"解析后响应:{response.parsed}") +print(f"流响应:{response.stream}") + +# %% +# .. _integrating_new_api: +# +# 集成新的 LLM API +# ---------------------------- +# 将新的 LLM API 集成到 AgentScope 有两种方式。 +# +# OpenAI 兼容 API +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# 如果您的模型与 OpenAI Python API 兼容,建议重用 `OpenAIChatWrapper` 类并提供特定参数。 +# +# .. note:: 需要确保 API 的回复同样兼容 OpenAI Python API。 +# +# 以 vLLM 为例, +# 其 `官方文档 `_ 提供了以下使用 OpenAI Python 库的示例: +# +# .. code-block:: python +# +# from openai import OpenAI +# client = OpenAI( +# base_url="http://localhost:8000/v1", +# api_key="token-abc123", +# ) +# +# completion = client.chat.completions.create( +# model="NousResearch/Meta-Llama-3-8B-Instruct", +# messages=[ +# {"role": "user", "content": "Hello!"} +# ], +# temperature=0.5, +# ) +# +# print(completion.choices[0].message) +# +# +# 将 vLLM 集成到 AgentScope 非常简单,如下: +# +# - 将初始化 OpenAI 客户端的参数(除了 `api_key`)放入 `client_args`, +# - 将生成完成的参数(除了 `model`)放入 `generate_args`。 +# + +vllm_model_config = { + "model_type": "openai_chat", + "config_name": "vllm_llama2-7b-chat-hf", + "model_name": "meta-llama/Llama-2-7b-chat-hf", + "api_key": "token-abc123", # API 密钥 + "client_args": { + "base_url": "http://localhost:8000/v1/", # 用于指定 API 的基础 URL + }, + "generate_args": { + "temperature": 0.5, # 生成参数,如 temperature、seed + }, +} + +# %% +# 或者,直接用参数初始化 OpenAI Chat API 的模型类: +# + +from agentscope.models import OpenAIChatWrapper + +model = OpenAIChatWrapper( + config_name="", + model_name="meta-llama/Llama-2-7b-chat-hf", + api_key="token-abc123", + client_args={"base_url": "http://localhost:8000/v1/"}, + generate_args={"temperature": 0.5}, +) + +# %% +# RESTful API +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# 如果您的模型通过 RESTful post API 访问,并且响应格式与 OpenAI API 兼容,可以考虑使用 `PostAPIChatWrapper`。 +# +# 以下面的 curl 命令为例,只需将 header、API URL 和 data(除了 `messages`,它将自动传递)提取成模型类的初始化参数即可。 +# +# 一个示例 post 请求: +# +# .. code-block:: bash +# +# curl https://api.openai.com/v1/chat/completions +# -H "Content-Type: application/json" +# -H "Authorization: Bearer $OPENAI_API_KEY" +# -d '{ +# "model": "gpt-4o", +# "messages": [ +# {"role": "user", "content": "write a haiku about ai"} +# ] +# }' +# +# 相应的模型类初始化如下: +# + +from agentscope.models import PostAPIChatWrapper + +post_api_model = PostAPIChatWrapper( + config_name="", + api_url="https://api.openai.com/v1/chat/completions", # 目标 URL + headers={ + "Content-Type": "application/json", # 来自头部 + "Authorization": "Bearer $OPENAI_API_KEY", + }, + json_args={ + "model": "gpt-4o", # 来自数据 + }, +) + +# %% +# 它的模型配置如下: +# + +post_api_config = { + "config_name": "{my_post_model_config_name}", + "model_type": "post_api_chat", + "api_url": "https://api.openai.com/v1/chat/completions", + "headers": { + "Authorization": "Bearer {YOUR_API_TOKEN}", + }, + "json_args": { + "model": "gpt-4o", + }, +} + +# %% +# 如果你的模型 API 返回格式与 OpenAI 不同,可以继承 `PostAPIChatWrapper` 并重写 `_parse_response` 方法。 +# +# .. note:: 需要在子类中定义一个新的 `model_type` 字段,以区分它与现有的模型类。 +# +# + + +class MyNewModelWrapper(PostAPIChatWrapper): + model_type: str = "{my_new_model_type}" + + def _parse_response(self, response: dict) -> ModelResponse: + """解析来自 API 服务器的响应。 + + Args: + response (`dict`): + 从 API 服务器获取的响应,并通过 + `response.json()` 解析为统一的格式。 + + Returns (`ModelResponse`): + 解析后的响应。 + """ + # TODO: 将以下代码替换为您自己的解析逻辑 + return ModelResponse( + text=response["data"]["response"]["choices"][0]["message"][ + "content" + ], + ) + + +# %% +# 自定义模型类 +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# 如果需要从头开始实现新的模型类,首先需要了解 AgentScope 中的以下概念: +# +# - **model_type**:在使用模型配置时,AgentScope 使用 `model_type` 字段来区分不同的模型 API。因此,请确保您的新模型类具有唯一的 `model_type`。 +# - **__init__**:从模型配置初始化时,AgentScope 会将配置中的所有键值对传递给模型类的 `__init__` 方法。因此,请确保您的 `__init__` 方法可以处理配置中的所有参数。 +# - **__call__**:模型类的核心方法是 `__call__`,它接收输入消息并返回响应。其返回值应该是 `ModelResponse` 对象。 +# + + +class MyNewModelWrapper(ModelWrapperBase): + model_type: str = "{my_new_model_type}" + + def __init__(self, config_name, model_name, **kwargs) -> None: + super().__init__(config_name, model_name=model_name) + + # TODO: 在这里初始化您的模型 + + def __call__(self, *args, **kwargs) -> ModelResponse: + # TODO: 在这里实现您的模型的核心逻辑 + + return ModelResponse( + text="Hello, World!", + ) + + +# %% +# .. tip:: 可选地,可以在模型中实现一个 `format` 方法,从而将 AgentScope 中的 `Msg` 类 转化为目标模型 API 要求的格式。更多详情请参考 :ref:`prompt-engineering`。 +# +# 进一步阅读 +# --------------------- +# - :ref:`prompt-engineering` +# - :ref:`streaming` +# - :ref:`structured-output` diff --git a/docs/tutorial/zh/source/tutorial/monitor.py b/docs/tutorial/zh/source/tutorial/monitor.py new file mode 100644 index 000000000..0f7941687 --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/monitor.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +""" +.. _configuring_and_monitoring: + +配置和监控 +================================== + +AgentScope 的主入口是 `agentscope.init`,在这里您可以配置应用程序。 +""" + +import agentscope + + +agentscope.init( + model_configs=[ # 模型配置 + { + "config_name": "my-qwen-max", + "model_type": "dashscope_chat", + "model_name": "qwen-max", + }, + ], + project="项目 Alpha", # 项目名称 + name="测试-1", # 运行时名称 + disable_saving=False, # 是否禁用文件保存,推荐开启 + save_dir="./runs", # 保存目录 + save_log=True, # 是否保存日志 + save_code=False, # 是否保存此次运行的代码 + save_api_invoke=False, # 保存 API 调用 + cache_dir="~/.cache", # 缓存目录,用于缓存 Embedding 和其它 + use_monitor=True, # 是否监控 token 使用情况 + logger_level="INFO", # 日志级别 +) + +# %% +# 导出配置 +# -------------------------------- +# `state_dict` 方法可用于导出正在运行的应用程序的配置。 +# + +import json + +print(json.dumps(agentscope.state_dict(), indent=2)) + +# %% +# 运行监控 +# -------------------------- +# AgentScope 提供了 AgentScope Studio,这是一个 Web 可视化界面,用于监控和管理正在运行的应用程序和历史记录。 +# 有关更多详细信息,请参阅 :ref:`visual` 部分。 +# + +# %% +# .. _token_usage: +# +# 监控 Token 使用情况 +# ------------------------ +# `print_llm_usage` 将打印并返回当前运行应用程序的 token 使用情况。 +# + +from agentscope.models import DashScopeChatWrapper + +qwen_max = DashScopeChatWrapper( + config_name="-", + model_name="qwen-max", +) +qwen_plus = DashScopeChatWrapper( + config_name="-", + model_name="qwen-plus", +) + +# 调用 qwen-max 和 qwen-plus 来模拟 token 使用情况 +_ = qwen_max([{"role": "user", "content": "Hi!"}]) +_ = qwen_plus([{"role": "user", "content": "Who are you?"}]) + +usage = agentscope.print_llm_usage() + +print(json.dumps(usage, indent=2)) diff --git a/docs/tutorial/zh/source/tutorial/multimodality.py b/docs/tutorial/zh/source/tutorial/multimodality.py new file mode 100644 index 000000000..e11a6f2fe --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/multimodality.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +""" +.. _multimodality: + +多模态 +============================ + +在本节中,我们将展示如何在 AgentScope 中构建多模态应用程序。 + +构建视觉智能体 +------------------------------ + +对于大多数大语言模型 API,视觉和非视觉模型共享相同的 API,只是输入格式有所不同。 +在 AgentScope 中,模型包装器的 `format` 函数负责将输入的 `Msg` 对象转换为视觉模型所需的格式。 + +也就是说,我们只需指定视觉大语言模型而无需更改智能体的代码。 +有关 AgentScope 支持的视觉大语言模型 API,请参阅 :ref:`model_api` 部分。 + +以 "qwen-vl-max" 为例,我们将使用视觉大语言模型构建一个智能体。 +""" + +model_config = { + "config_name": "my-qwen-vl", + "model_type": "dashscope_multimodal", + "model_name": "qwen-vl-max", +} + +# %% +# +# 如往常一样,我们使用上述配置初始化 AgentScope,并使用视觉大语言模型创建一个新的智能体。 +# + +from agentscope.agents import DialogAgent +import agentscope + +agentscope.init(model_configs=model_config) + +agent = DialogAgent( + name="Monday", + sys_prompt="你是一个名为Monday的助手。", + model_config_name="my-qwen-vl", +) + +# %% +# 为了与智能体进行多模态数据的交互,`Msg` 类提供了一个 `url` 字段。 +# 你可以在 `url` 字段中放置本地或在线的图片 URL。 +# +# 这里让我们首先使用 matplotlib 创建一个图片 +# + +import matplotlib.pyplot as plt + +plt.figure(figsize=(6, 6)) +plt.bar(range(3), [2, 1, 4]) +plt.xticks(range(3), ["Alice", "Bob", "Charlie"]) +plt.title("The Apples Each Person Has in 2023") +plt.xlabel("Number of Apples") + +plt.show() +plt.savefig("./bar.png") + +# %% +# 然后,我们创建一个包含图像 URL 的 `Msg` 对象 +# + +from agentscope.message import Msg + +msg = Msg( + name="用户", + content="为我详细描述一下这个图片。", + role="user", + url="./bar.png", +) + +# %% +# 之后,我们可以将消息发送给视觉智能体并获取响应。 + +response = agent(msg) diff --git a/docs/tutorial/zh/source/tutorial/prompt.py b/docs/tutorial/zh/source/tutorial/prompt.py new file mode 100644 index 000000000..e862d288c --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/prompt.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +""" +.. _prompt-engineering: + +提示工程 +================================ + +提示工程是构建大语言模型应用的关键步骤,尤其是针对多智能体的应用。 +然而,目前市面上大多数 API 服务只专注于 Chat 场景,即对话只有两个参与者:用户(user)和 +助手(assistant),并且两者必须交替发送消息。 + +为了支持多智能体应用,AgentScope 构建了不同的提示策略,从而将一组 `Msg` 对象转换为模型 +API 需要的格式。 + +.. note:: 目前还没有一种提示工程可以做到一劳永逸。AgentScope 内置提示构建策略的目标 + 是让初学者可以顺利调用模型 API,而不是达到最佳性能。 + 对于高级用户,我们建议开发人员根据需求和模型 API 要求来自定义提示构建策略。 + +提示构建策略 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +内置提示策略在模型类的 `format` 方法中实现。以 DashScope Chat API 为例: + +""" + +from agentscope.models import DashScopeChatWrapper +from agentscope.message import Msg +import json + + +model = DashScopeChatWrapper( + config_name="_", + model_name="qwen-max", +) + +# 可以将 `Msg` 对象或 `Msg` 对象列表传递给 `format` 方法 +prompt = model.format( + Msg("system", "You're a helpful assistant.", "system"), + [ + Msg("assistant", "Hi!", "assistant"), + Msg("user", "Nice to meet you!", "user"), + ], +) + +print(json.dumps(prompt, indent=4, ensure_ascii=False)) + +# %% +# 格式化输入消息后,我们可以将其传给 `model` 对象,进行实际的 API 调用。 +# + +response = model(prompt) + +print(response.text) + +# %% +# 非视觉模型 +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# 在下表中,我们列出了内置的提示策略,以及支持的大语言模型的前缀。 +# +# 以下面的消息为例: +# +# .. code-block:: python +# +# Msg("system", "You're a helpful assistant named Alice.", "system"), +# Msg("Alice", "Hi!", "assistant"), +# Msg("Bob", "Nice to meet you!", "user") +# +# +# .. list-table:: +# :header-rows: 1 +# +# * - LLMs +# - `model_name` +# - Constructed Prompt +# * - OpenAI LLMs +# - `gpt-` +# - .. code-block:: python +# +# [ +# { +# "role": "system", +# "name": "system", +# "content": "You're a helpful assistant named Alice." +# }, +# { +# "role": "user", +# "name": "Alice", +# "content": "Hi!" +# }, +# { +# "role": "user", +# "name": "Bob", +# "content": "Nice to meet you!" +# } +# ] +# * - Gemini LLMs +# - `gemini-` +# - .. code-block:: python +# +# [ +# { +# "role": "user", +# "parts": [ +# "You're a helpful assistant named Alice.\\n## Conversation History\\nAlice: Hi!\\nBob: Nice to meet you!" +# ] +# } +# ] +# * - All other LLMs +# +# (e.g. DashScope, ZhipuAI ...) +# - +# - .. code-block:: python +# +# [ +# { +# "role": "system", +# "content": "You're a helpful assistant named Alice." +# }, +# { +# "role": "user", +# "content": "## Conversation History\\nAlice: Hi!\\nBob: Nice to meet you!" +# } +# ] +# +# .. tip:: 考虑到一些 API 兼容不同的大语言模型(例如 OpenAI Python 库),AgentScope 使用 `model_name` 字段来区分不同的模型并决定最终使用的策略。 +# +# 视觉模型 +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# 对于视觉模型,AgentScope 目前支持 OpenAI 视觉模型和 Dashscope 多模态 API。 +# 未来将假如更多的视觉模型的支持。 +# diff --git a/docs/tutorial/zh/source/tutorial/prompt_optimization.py b/docs/tutorial/zh/source/tutorial/prompt_optimization.py new file mode 100644 index 000000000..30f157d73 --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/prompt_optimization.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +""" +.. _system-prompt-optimization: + +系统提示优化 +============================ + +AgentScope 实现了一个用于优化智能体系统提示的模块。 + +.. _system-prompt-generator: + +系统提示生成器 +^^^^^^^^^^^^^^^^^^^^^^^^ + +系统提示生成器使用元提示(Meta prompt)来指导模型根据用户的要求生成系统提示,并允许开发人员使用内置示例或提供自己的示例作为上下文学习(ICL)。 + +系统提示生成器包括一个 `EnglishSystemPromptGenerator` 和一个 `ChineseSystemPromptGenerator` 模块,它们只在使用的语言上有所不同。 + +我们以 `ChineseSystemPromptGenerator` 为例,说明如何使用系统提示生成器。 + +初始化 +^^^^^^^^^^^^^^^^^^^^^^^^ + +要初始化生成器,你需要首先在 `agentscope.init` 函数中注册你的模型配置。 +""" + +from agentscope.prompt import ChineseSystemPromptGenerator +import agentscope + +model_config = { + "model_type": "dashscope_chat", + "config_name": "qwen_config", + "model_name": "qwen-max", + # 通过环境变量导出你的 api 密钥 +} + +# %% +# 生成器将使用内置的默认元提示来指导大语言模型生成系统提示。 +# + +agentscope.init( + model_configs=model_config, +) + +prompt_generator = ChineseSystemPromptGenerator( + model_config_name="qwen_config", +) + + +# %% +# 我们欢迎用户自由尝试不同的优化方法。我们提供了相应的 `SystemPromptGeneratorBase` 模块,可以通过继承来实现自定义的系统提示生成器。 +# +# 生成系统提示 +# ^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# 调用生成器的 `generate` 函数来生成系统提示,如下所示。 +# +# 可以输入一个需求,或者要优化的系统提示。 + +generated_system_prompt = prompt_generator.generate( + user_input="为一位小红书营销专家生成系统提示,他负责推广书籍。", +) + +print(generated_system_prompt) + +# %% +# 上下文学习(ICL) +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# AgentScope 在系统提示生成中支持上下文学习。 +# +# 要使用示例,AgentScope 提供了以下参数: +# +# - `example_num`: 附加到元提示的示例数量,默认为 0 +# - `example_selection_strategy`: 选择示例的策略,可选 "random" 和 "similarity"。 +# - `example_list`: 一个示例列表,其中每个示例必须是一个带有键 "user_prompt" 和 "opt_prompt" 的字典。如果未指定,将使用内置的示例列表。 +# +# 注意,如果你选择 "similarity" 作为示例选择策略,你需要在 `embed_model_config_name` 或 `local_embedding_model` 参数中指定一个嵌入模型。 +# +# 它们的区别如下: +# +# - `embed_model_config_name`: 你必须先在 `agentscope.init` 中注册嵌入模型,并在此参数中指定模型配置名称。 +# - `local_embedding_model`: 或者,你可以使用 `sentence_transformers.SentenceTransformer` 库支持的本地小型嵌入模型。 +# +# 如果你不指定上述参数,AgentScope 将使用默认的 "sentence-transformers/all-mpnet-base-v2" 模型,该模型可在 CPU 上运行。 + +icl_generator = ChineseSystemPromptGenerator( + model_config_name="qwen_config", + example_num=3, + example_selection_strategy="random", +) + +icl_generated_system_prompt = icl_generator.generate( + user_input="为一位小红书营销专家生成系统提示,他负责推广书籍。", +) + +print(icl_generated_system_prompt) + +# %% +# .. note:: 1. 样例的 Embedding 将被缓存在 `~/.cache/agentscope/` 中,以避免重复计算。 +# 2. `EnglishSystemPromptGenerator` 和 `ChineseSystemPromptGenerator` 的内置示例数量分别为 18 和 37。请注意 Embedding API 服务的成本。 +# diff --git a/docs/tutorial/zh/source/tutorial/quickstart.py b/docs/tutorial/zh/source/tutorial/quickstart.py new file mode 100644 index 000000000..f1d263e50 --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/quickstart.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +""" +.. _quickstart: + +快速入门 +============================ + +AgentScope 需要 Python 3.9 或更高版本。你可以从源码或 pypi 安装。 + +从 PyPI 安装 +---------------- +.. code-block:: bash + + pip install agentscope + +从源码安装 +---------------- +要从源码安装 AgentScope,你需要从 GitHub 克隆仓库,然后通过以下命令安装 + +.. code-block:: bash + + git clone https://github.com/modelscope/agentscope + cd agentscope + pip install -e . + +要确保 AgentScope 安装正常。可以执行以下代码: +""" + +import agentscope + +print(agentscope.__version__) + +# %% +# 额外依赖 +# ---------------------------- +# +# AgentScope 提供了针对不同需求的额外依赖。你可以根据需求安装它们。 +# +# - ollama: Ollama API +# - litellm: Litellm API +# - zhipuai: Zhipuai API +# - gemini: Gemini API +# - anthropic: Anthropic API +# - service: 用于不同工具函数的依赖 +# - distribute: 用于分布式模式的依赖 +# - full: 一次性安装所有依赖 +# +# 以分布式模式为例,安装命令因操作系统而异。 +# +# 对于 Windows 用户: +# +# .. code-block:: bash +# +# pip install agentscope[gemini] +# # 或 +# pip install agentscope[ollama,distribute] +# +# 对于 Mac 和 Linux 用户: +# +# .. code-block:: bash +# +# pip install agentscope\[gemini\] +# # 或 +# pip install agentscope\[ollama,distribute\] +# diff --git a/docs/tutorial/zh/source/tutorial/rag.md b/docs/tutorial/zh/source/tutorial/rag.md new file mode 100644 index 000000000..8199f5361 --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/rag.md @@ -0,0 +1,280 @@ +# RAG + +我们在此介绍AgentScope与RAG相关的三个概念:知识(Knowledge),知识库(Knowledge Bank)和RAG 智能体。 + +### Knowledge +知识模块(目前仅有“LlamaIndexKnowledge”;即将提供对LangChain的支持)负责处理所有与RAG相关的操作。 + +#### 如何初始化一个Knowledge对象 + 用户可以使用JSON配置来创建一个Knowledge模块,以指定1)数据路径,2)数据加载器,3)数据预处理方法,以及4)嵌入模型(模型配置名称)。 +一个详细的示例可以参考以下内容: +
+ 详细的配置示例 + + ```json + [ + { + "knowledge_id": "{your_knowledge_id}", + "emb_model_config_name": "{your_embed_model_config_name}", + "data_processing": [ + { + "load_data": { + "loader": { + "create_object": true, + "module": "llama_index.core", + "class": "SimpleDirectoryReader", + "init_args": { + "input_dir": "{path_to_your_data_dir_1}", + "required_exts": [".md"] + } + } + } + }, + { + "load_data": { + "loader": { + "create_object": true, + "module": "llama_index.core", + "class": "SimpleDirectoryReader", + "init_args": { + "input_dir": "{path_to_your_python_code_data_dir}", + "recursive": true, + "required_exts": [".py"] + } + } + }, + "store_and_index": { + "transformations": [ + { + "create_object": true, + "module": "llama_index.core.node_parser", + "class": "CodeSplitter", + "init_args": { + "language": "python", + "chunk_lines": 100 + } + } + ] + } + } + ] + } + ] + ``` + +
+ +#### 更多关于 knowledge 配置 +以上提到的配置通常保存为一个JSON文件,它必须包含以下关键属性 +* `knowledge_id`: 每个knowledge模块的唯一标识符; +* `emb_model_config_name`: embedding模型的名称; +* `chunk_size`: 对文件分块的默认大小; +* `chunk_overlap`: 文件分块之间的默认重叠大小; +* `data_processing`: 一个list型的数据处理方法集合。 + +##### 以配置 LlamaIndexKnowledge 为例 + +当使用`llama_index_knowledge`是,对于上述的最后一项`data_processing` ,这个`list`型的参数中的每个条目(为`dict`型)都对应配置一个data loader对象,其功能包括用来加载所需的数据(即字段`load_data`中包含的信息),以及处理加载数据的转换对象(`store_and_index`)。换而言之,在一次载入数据时,可以同时从多个数据源中加载数据,并处理后合并在同一个索引下以供后面的数据提取使用(retrieve)。有关该组件的更多信息,请参阅 [LlamaIndex-Loading](https://docs.llamaindex.ai/en/stable/module_guides/loading/)。 + +在这里,无论是针对数据加载还是数据处理,我们都需要配置以下属性 +* `create_object`:指示是否创建新对象,在此情况下必须为true; +* `module`:对象对应的类所在的位置; +* `class`:这个类的名称。 + +更具体得说,当对`load_data`进行配置时候,您可以选择使用多种多样的的加载器,例如使用`SimpleDirectoryReader`(在`class`字段里配置)来读取各种类型的数据(例如txt、pdf、html、py、md等)。关于这个数据加载器,您还需要配置以下关键属性 +* `input_dir`:数据加载的路径; +* `required_exts`:将加载的数据的文件扩展名。 + +有关数据加载器的更多信息,请参阅[这里](https://docs.llamaindex.ai/en/stable/module_guides/loading/simpledirectoryreader/)。 + +对于`store_and_index`而言,这个配置是可选的,如果用户未指定特定的转换方式,系统将使用默认的transformation(也称为node parser)方法,名称为`SentenceSplitter`。对于某些特定需求下也可以使用不同的转换方式,例如对于代码解析可以使用`CodeSplitter`,针对这种特殊的node parser,用户可以设置以下属性: +* `language`:希望处理代码的语言名; +* `chunk_lines`:分割后每个代码块的行数。 + +有关节点解析器的更多信息,请参阅[这里](https://docs.llamaindex.ai/en/stable/module_guides/loading/node_parsers/)。 + +如果用户想要避免详细的配置,我们也在`KnowledgeBank`中提供了一种快速的方式(请参阅以下内容)。 + +#### 如何使用一个 Knowledge 对象 +当我们成功创建了一个knowledge后,用户可以通过`.retrieve`从`Knowledge` 对象中提取信息。`.retrieve`函数一下三个参数: +* `query`: 输入参数,用户希望提取与之相关的内容; +* `similarity_top_k`: 提取的“数据块”数量; +* `to_list_strs`: 是否只返回字符串(str)的列表(list)。 + +*高阶:* 对于 `LlamaIndexKnowledge`, 它的`.retrieve`函数也支持熟悉LlamaIndex的用户直接传入一个建好的retriever。 + +#### 关于`LlamaIndexKnowledge`的细节 +在这里,我们将使用`LlamaIndexKnowledge`作为示例,以说明在`Knowledge`模块内的操作。 +当初始化`LlamaIndexKnowledge`对象时,`LlamaIndexKnowledge.__init__`将执行以下步骤: + * 它处理数据并生成检索索引 (`LlamaIndexKnowledge._data_to_index(...)`中完成) 其中包括 + * 加载数据 `LlamaIndexKnowledge._data_to_docs(...)`; + * 对数据进行预处理,使用预处理方法(比如分割)和向量模型生成向量 `LlamaIndexKnowledge._docs_to_nodes(...)`; + * 基于生成的向量做好被查询的准备, 即生成索引。 + * 如果索引已经存在,则会调用 `LlamaIndexKnowledge._load_index(...)` 来加载索引,并避免重复的嵌入调用。 +
+ +### Knowledge Bank +知识库将一组Knowledge模块(例如,来自不同数据集的知识)作为知识的集合进行维护。因此,不同的智能体可以在没有不必要的重新初始化的情况下重复使用知识模块。考虑到配置Knowledge模块可能对大多数用户来说过于复杂,知识库还提供了一个简单的函数调用来创建Knowledge模块。 + +* `KnowledgeBank.add_data_as_knowledge`: 创建Knowledge模块。一种简单的方式只需要提供knowledge_id、emb_model_name和data_dirs_and_types。 + 因为`KnowledgeBank`默认生成的是 `LlamaIndexKnowledge`, 所以所有文本类文件都可以支持,包括`.txt`, `.html`, `.md` ,`.csv`,`.pdf`和 所有代码文件(如`.py`). 其他支持的文件类型可以参考 [LlamaIndex document](https://docs.llamaindex.ai/en/stable/module_guides/loading/simpledirectoryreader/). + ```python + knowledge_bank.add_data_as_knowledge( + knowledge_id="agentscope_tutorial_rag", + emb_model_name="qwen_emb_config", + data_dirs_and_types={ + "../../docs/sphinx_doc/en/source/tutorial": [".md"], + }, + ) + ``` + 对于更高级的初始化,用户仍然可以将一个知识模块配置作为参数knowledge_config传递: + ```python + # load knowledge_config as dict + knowledge_bank.add_data_as_knowledge( + knowledge_id=knowledge_config["knowledge_id"], + emb_model_name=knowledge_config["emb_model_config_name"], + knowledge_config=knowledge_config, + ) + ``` +* `KnowledgeBank.get_knowledge`: 它接受两个参数,knowledge_id和duplicate。 + 如果duplicate为true,则返回提供的knowledge_id对应的知识对象;否则返回深拷贝的对象。 +* `KnowledgeBank.equip`: 它接受三个参数,`agent`,`knowledge_id_list` 和`duplicate`。 +该函数会根据`knowledge_id_list`为`agent`提供相应的知识(放入`agent.knowledge_list`)。`duplicate` 同样决定是否是深拷贝。 + + + +### RAG 智能体 +RAG 智能体是可以基于检索到的知识生成答案的智能体。 + * 让智能体使用RAG: RAG agent配有一个`knowledge_list`的列表 + * 可以在初始化时就给RAG agent传入`knowledge_list` + ```python + knowledge = knowledge_bank.get_knowledge(knowledge_id) + agent = LlamaIndexAgent( + name="rag_worker", + sys_prompt="{your_prompt}", + model_config_name="{your_model}", + knowledge_list=[knowledge], # provide knowledge object directly + similarity_top_k=3, + log_retrieval=False, + recent_n_mem_for_retrieve=1, + ) + ``` + * 如果通过配置文件来批量启动agent,也可以给agent提供`knowledge_id_list`。这样也可以通过将agent和它的`knowledge_id_list`一起传入`KnowledgeBank.equip`来为agent赋予`knowledge_list`。 + ```python + # >>> agent.knowledge_list + # >>> [] + knowledge_bank.equip(agent, agent.knowledge_id_list) + # >>> agent.knowledge_list + # [] + ``` + * Agent 智能体可以在`reply`函数中使用从`Knowledge`中检索到的信息,将其提示组合到LLM的提示词中。 + +**自己搭建 RAG 智能体.** 只要您的智能体配置具有`knowledge_id_list`,您就可以将一个agent和这个列表传递给`KnowledgeBank.equip`;这样该agent就是被装配`knowledge_id`。 +您可以在`reply`函数中自己决定如何从`Knowledge`对象中提取和使用信息,甚至通过`Knowledge`修改知识库。 + + +## (拓展) 架设自己的embedding model服务 + +我们在此也对架设本地embedding model感兴趣的用户提供以下的样例。 +以下样例基于在embedding model范围中很受欢迎的`sentence_transformers` 包(基于`transformer` 而且兼容HuggingFace和ModelScope的模型)。 +这个样例中,我们会使用当下最好的文本向量模型之一`gte-Qwen2-7B-instruct`。 + + +* 第一步: 遵循在 [HuggingFace](https://huggingface.co/Alibaba-NLP/gte-Qwen2-7B-instruct) 或者 [ModelScope](https://www.modelscope.cn/models/iic/gte_Qwen2-7B-instruct )的指示下载模型。 + (如果无法直接从HuggingFace下载模型,也可以考虑使用HuggingFace镜像:bash命令行`export HF_ENDPOINT=https://hf-mirror.com`,或者在Python代码中加入`os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"`) +* 第二步: 设置服务器。以下是一段参考代码。 + +```python +import datetime +import argparse + +from flask import Flask +from flask import request +from sentence_transformers import SentenceTransformer + +def create_timestamp(format_: str = "%Y-%m-%d %H:%M:%S") -> str: + """Get current timestamp.""" + return datetime.datetime.now().strftime(format_) + +app = Flask(__name__) + +@app.route("/embedding/", methods=["POST"]) +def get_embedding() -> dict: + """Receive post request and return response""" + json = request.get_json() + + inputs = json.pop("inputs") + + global model + + if isinstance(inputs, str): + inputs = [inputs] + + embeddings = model.encode(inputs) + + return { + "data": { + "completion_tokens": 0, + "messages": {}, + "prompt_tokens": 0, + "response": { + "data": [ + { + "embedding": emb.astype(float).tolist(), + } + for emb in embeddings + ], + "created": "", + "id": create_timestamp(), + "model": "flask_model", + "object": "text_completion", + "usage": { + "completion_tokens": 0, + "prompt_tokens": 0, + "total_tokens": 0, + }, + }, + "total_tokens": 0, + "username": "", + }, + } + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model_name_or_path", type=str, required=True) + parser.add_argument("--device", type=str, default="auto") + parser.add_argument("--port", type=int, default=8000) + args = parser.parse_args() + + global model + + print("setting up for embedding model....") + model = SentenceTransformer( + args.model_name_or_path + ) + + app.run(port=args.port) +``` + +* 第三部:启动服务器。 +```bash +python setup_ms_service.py --model_name_or_path {$PATH_TO_gte_Qwen2_7B_instruct} +``` + + +测试服务是否成功启动。 +```python +from agentscope.models.post_model import PostAPIEmbeddingWrapper + + +model = PostAPIEmbeddingWrapper( + config_name="test_config", + api_url="http://127.0.0.1:8000/embedding/", + json_args={ + "max_length": 4096, + "temperature": 0.5 + } +) + +print(model("testing")) +``` \ No newline at end of file diff --git a/docs/tutorial/zh/source/tutorial/streaming.py b/docs/tutorial/zh/source/tutorial/streaming.py new file mode 100644 index 000000000..97890ac00 --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/streaming.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- +""" +.. _streaming: + +流式输出 +========================= + +AgentScope 支持在终端和 AgentScope Studio 中以打字机效果显示流式输出。 + +.. list-table:: + :header-rows: 1 + + * - API + - 类 + - 是否支持流式输出 + * - OpenAI Chat API + - `OpenAIChatWrapper` + - ✓ + * - DashScope Chat API + - `DashScopeChatWrapper` + - ✓ + * - Gemini Chat API + - `GeminiChatWrapper` + - ✓ + * - ZhipuAI Chat API + - `ZhipuAIChatWrapper` + - ✓ + * - Ollama Chat API + - `OllamaChatWrapper` + - ✓ + * - LiteLLM Chat API + - `LiteLLMChatWrapper` + - ✓ + * - Anthropic Chat API + - `AnthropicChatWrapper` + - ✓ + +本节将展示如何在 AgentScope 中启用流式输出,以及如何在智能体中处理流式返回。 +""" + +# %% +# 启用流式输出 +# ---------------------------- +# +# 通过设置模型类的 `stream` 参数,启用流式输出。 +# 你可以在初始化或配置中直接指定`stream`参数。 +# +# - 在初始化中指定 +# + +from agentscope.models import DashScopeChatWrapper +import os + +model = DashScopeChatWrapper( + config_name="_", + model_name="qwen-max", + api_key=os.environ["DASHSCOPE_API_KEY"], + stream=True, # 启用流式输出 +) + +# %% +# - 在模型配置中指定 + +model_config = { + "model_type": "dashscope_chat", + "config_name": "qwen_config", + "model_name": "qwen-max", + "stream": True, +} + +# %% +# 使用上述模型配置,我们可以在 AgentScope 中使用内置智能体获取流式输出。 +# +# 接下来,我们展示如何在智能体中处理流式输出。 + +# %% +# 处理流式响应 +# ------------------------------------------- +# +# 一旦我们启用了流式输出,模型返回对象中的 `stream` 字段将包含一个生成器。 +# + +prompt = [{"role": "user", "content": "Hi!"}] + +response = model(prompt) +print("response.stream的类型:", type(response.stream)) + +# %% +# 我们可以遍历生成器以获取流式文本。 +# 该生成器同时也会生成一个布尔值,标识当前是否为最后一个文本块。 + +for index, chunk in enumerate(response.stream): + print(f"{index}.", chunk) + print(f"当前text字段:", response.text, "\n") + +# %% +# .. note:: 注意 `response.stream` 挂载的生成器是增量的,并且只能使用一次。 +# 在遍历过程中,`response` 的 `text` 字段会自动拼接字符串。 +# 为了与非流式模式兼容,你也可以直接使用`response.text`一次获取所有文本。 + +prompt = [{"role": "user", "content": "Hi!"}] +response = model(prompt) +# 一次性获取所有文本 +print(response.text) + +# %% +# 打字机效果 +# ------------------------------------------- +# 为了实现打字机的显示效果,AgentScope 在 `AgentBase` 类中提供了一个 `speak` 函数。 +# 如果给定了一个生成器,`speak` 函数会遍历生成器并在终端或 AgentScope Studio 中以打字机效果打印文本。 +# +# .. code-block:: python +# +# def reply(*args, **kwargs): +# # ... +# self.speak(response.stream) +# # ... +# +# 为了使一套代码同时兼容流式和非流式模式,AgentScope 的所有内置智能体中使用以下代码片段。 +# +# .. code-block:: python +# +# def reply(*args, **kwargs): +# # ... +# self.speak(response.stream or response.text) +# # ... +# diff --git a/docs/tutorial/zh/source/tutorial/structured_output.py b/docs/tutorial/zh/source/tutorial/structured_output.py new file mode 100644 index 000000000..5cadd106a --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/structured_output.py @@ -0,0 +1,235 @@ +# -*- coding: utf-8 -*- +""" +.. _structured-output: + +结构化输出 +========================== + +在本教程中,我们将构建一个简单的智能体,使用 `agentscope.parsers` 模块以 JSON 字典格式输出结构化数据。 +""" +from agentscope.models import ModelResponse + +# %% +# 定义解析器 +# ------------------- + +from agentscope.parsers import MarkdownJsonDictParser + + +parser = MarkdownJsonDictParser( + content_hint='{"thought": "你的想法", "speak": "你对用户说的话"}', + required_keys=["thought", "speak"], +) + + +# %% +# 解析器将根据你的输入生成一个格式说明。你可以在提示中使用 `format_instruction` 属性来指导 LLM 生成所需的输出。 + +print(parser.format_instruction) + +# %% +# 解析输出 +# ------------------- +# 当从 LLM 接收到输出时,使用 `parse` 方法来提取结构化数据。 +# 它接受一个 `agentscope.models.ModelResponse` 对象作为输入,解析 `text` 字段的值,并在 `parsed` 字段中返回解析后的字典。 + +dummy_response = ModelResponse( + text="""```json +{ + "thought": "我应该向用户打招呼", + "speak": "嗨!我能为您做些什么?" +} +```""", +) + +print(f"解析前parsed字段: {dummy_response.parsed}") + +parsed_response = parser.parse(dummy_response) + +print(f"解析后parsed字段: {parsed_response.parsed}") +print(type(parsed_response.parsed)) + +# %% +# 错误处理 +# ------------------- +# 如果LLM的输出与预期格式不匹配,解析器将抛出一个包含详细信息的错误。 +# 因此开发人员可以将错误消息呈现给 LLM,以指导它纠正输出。 +# + +error_response = ModelResponse( + text="""```json +{ + "thought": "我应该向用户打招呼" +} +```""", +) + +try: + parsed_response = parser.parse(error_response) +except Exception as e: + print(e) + +# %% +# 进阶用法 +# ------------------- +# 复杂结构化输出 +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# 要求 LLM 直接生成 JSON 字典可能具有挑战性,特别是当 JSON 内容很复杂时(例如代码片段、嵌套结构)。 +# 在这种情况下,你可以使用更高级的解析器来指导 LLM 生成所需的输出。 +# 这里是一个更复杂的解析器示例,可以处理代码片段。 +# + +from agentscope.parsers import RegexTaggedContentParser + +parser = RegexTaggedContentParser( + format_instruction="""按以下格式作答: +你的想法 +这里放一个随机数字 +你的python代码 +""", + try_parse_json=True, # 会尝试将每个键值解析为JSON对象,如果失败则保留为字符串 + required_keys=[ # 解析字典中的必需键 + "thought", + "number", + "code", + ], +) + +print(parser.format_instruction) + +# %% +# `RegexTaggedContentParser` 支持使用正则表达式匹配文本中的标记内容并返回解析后的字典。 +# +# .. note:: `RegexTaggedContentParser`的解析输出是一个字典,这意味着必需键应该是唯一的。 +# 你也可以在初始化解析器时通过设置 `tagged_content_pattern` 参数来更改正则表达式模式。 + +import json + +dummy_response = ModelResponse( + text="""打印当前日期 +42 +import datetime +print(datetime.datetime.now()) + +""", +) + +parsed_response = parser.parse(dummy_response) + +print("解析响应的类型: ", type(parsed_response.parsed)) +print("number的类型: ", type(parsed_response.parsed["number"])) +print(json.dumps(parsed_response.parsed, indent=4, ensure_ascii=False)) + +# %% +# 自动后处理 +# ^^^^^^^^^^^^^^^^^^^^ +# +# 在解析后的字典中,不同的键可能需要不同的后处理步骤。 +# 例如,在狼人杀游戏中,LLM 扮演预言家的角色,输出应该包含以下键值: +# +# - `thought`: 预言家的想法 +# - `speak`: 预言家的发言 +# - `use_ability`: 一个布尔值,表示预言家是否应该使用其能力 +# +# 在这种情况下,`thought` 和 `speak` 内容应该存储在智能体的记忆中,以确保智能体行为/策略的一致性。 +# `speak` 内容应该暴露给其它智能体或玩家。 +# `use_ability` 键应该能在主流程中访问到,从而确定游戏下一步的操作(例如是否使用能力)。 +# +# AgentScope 通过以下参数来自动对解析后的字典进行后处理。 +# +# - `keys_to_memory`: 应存储在智能体记忆中的键 +# - `keys_to_content`: 应存储在返回消息的 content 字段中的键,会暴露给其它智能体 +# - `keys_to_metadata`: 应存储在返回消息的元数据(metadata)字段中的键 +# +# .. note:: 如果提供了一个字符串,解析器将从解析后的字典中提取给定键的值。如果提供了一个字符串列表,将创建一个包含给定键的子字典。 +# +# 下面是使用 `MarkdownJsonDictParser` 自动后处理解析后字典的示例。 +# + +parser = MarkdownJsonDictParser( + content_hint='{"thought": "你的想法", "speak": "你对用户说的话", "use_ability": "是否使用能力"}', + keys_to_memory=["thought", "speak"], + keys_to_content="speak", + keys_to_metadata="use_ability", +) + +dummy_response = ModelResponse( + text="""```json +{ + "thought": "我应该...", + "speak": "我不会使用我的能力", + "use_ability": false +}``` +""", +) + +parsed_response = parser.parse(dummy_response) + +print("解析后的响应: ", parsed_response.parsed) +print("存储到记忆", parser.to_memory(parsed_response.parsed)) +print("存储到消息 content 字段: ", parser.to_content(parsed_response.parsed)) +print("存储到消息 metadata 字段: ", parser.to_metadata(parsed_response.parsed)) + +# %% +# 这里我们展示如何创建一个智能体,它在 `reply` 方法中通过以下步骤实现自动化的后处理。 +# +# 1. 在提示中放入格式说明,以指导 LLM 生成所需的输出 +# 2. 解析 LLM 的返回值 +# 3. 使用 `to_memory`、`to_content` 和 `to_metadata` 方法后处理解析后的字典 +# +# .. tip:: 通过更改不同的解析器,智能体可以适应不同的场景,并以各种格式生成结构化输出。 +# + +from agentscope.models import DashScopeChatWrapper +from agentscope.agents import AgentBase +from agentscope.message import Msg + + +class Agent(AgentBase): + def __init__(self): + self.name = "Alice" + super().__init__(name=self.name) + + self.sys_prompt = f"你是一个名为{self.name}的有用助手。" + + self.model = DashScopeChatWrapper( + config_name="_", + model_name="qwen-max", + ) + + self.parser = MarkdownJsonDictParser( + content_hint='{"thought": "你的想法", "speak": "你对用户说的话", "use_ability": "是否使用能力"}', + keys_to_memory=["thought", "speak"], + keys_to_content="speak", + keys_to_metadata="use_ability", + ) + + self.memory.add(Msg("system", self.sys_prompt, "system")) + + def reply(self, msg): + self.memory.add(msg) + + prompt = self.model.format( + self.memory.get_memory(), + # 指示模型按要求的格式作答 + Msg("system", self.parser.format_instruction, "system"), + ) + + response = self.model(prompt) + + parsed_response = self.parser.parse(response) + + self.memory.add( + Msg( + name=self.name, + content=self.parser.to_memory(parsed_response.parsed), + role="assistant", + ), + ) + + return Msg( + name=self.name, + content=self.parser.to_content(parsed_response.parsed), + role="assistant", + metadata=self.parser.to_metadata(parsed_response.parsed), + ) diff --git a/docs/tutorial/zh/source/tutorial/tool.py b/docs/tutorial/zh/source/tutorial/tool.py new file mode 100644 index 000000000..50bfb1e0c --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/tool.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +""" +.. _tools: + +工具 +==================== + +在本教程中,我们将展示如何使用 AgentScope 中内置的工具函数,以及如何创建自定义工具函数。 +""" +import json + +import agentscope +from agentscope.message import Msg + +# %% +# 内置工具函数 +# -------------------------- +# AgentScope 提供了一个 `ServiceToolkit` 模块,支持以下功能: +# +# - 工具介绍生成, +# - 提供一套默认的调用格式, +# - 模型返回值解析、工具调用和面向智能体的错误处理。 +# +# 在使用 `ServiceToolkit` 之前,我们可以先看一下 `agentscope.service` 模块中可用的工具。 +# + +from agentscope.service import get_help, ServiceResponse, ServiceExecStatus + +get_help() + +# %% +# 以上所有函数都是用 Python 函数实现的。 +# 可以通过调用 `add` 方法注册到 `ServiceToolkit` 中。 +# + +from agentscope.service import ServiceToolkit +from agentscope.service import bing_search, execute_shell_command + +toolkit = ServiceToolkit() +toolkit.add(execute_shell_command) + +# 注意,一些工具函数的参数(例如 api_key)应该由开发人员处理。 +# 你可以直接在 add 方法中以关键字参数的形式传递这些参数,保留其他参数留给智能体填写。 + +toolkit.add(bing_search, api_key="xxx") + +print("工具说明:") +print(toolkit.tools_instruction) + +# %% +# 内置的默认调用格式: +# + +print(toolkit.tools_calling_format) + +# %% +# 自动生成的工具函数 JSON Schema 格式说明: +# +print(json.dumps(toolkit.json_schemas, indent=2)) + + +# %% +# AgentScope 提供了 `ReActAgent` 智能体类来使用工具,只需要将 `ServiceToolkit` 对象传递给这个智能体。 +# 有关该智能体的实现细节,请参阅 :ref:`builtin_agent`。 +# + +from agentscope.agents import ReActAgent + +agentscope.init( + model_configs={ + "config_name": "my-qwen-max", + "model_type": "dashscope_chat", + "model_name": "qwen-max", + }, +) + +agent = ReActAgent( + name="Friday", + model_config_name="my-qwen-max", + service_toolkit=toolkit, + sys_prompt="你是一个名为 Friday 的助手。", +) + +msg_task = Msg("user", "帮我计算一下 1615114134*4343434343", "user") + +res = agent(msg_task) + + +# %% +# 创建工具函数 +# -------------------------- +# 自定义工具函数必须遵循以下规则: +# +# - 参数使用 typing 指定类型 +# - 使用 Google 风格书写完整的 docstring +# - 函数返回值必须用 `ServiceResponse` 包装 +# + + +def new_function(arg1: str, arg2: int) -> ServiceResponse: + """简单介绍该函数。 + + Args: + arg1 (`str`): + 对 arg1 的简单描述 + arg2 (`int`): + 对 arg2 的简单描述 + """ + return ServiceResponse( + status=ServiceExecStatus.SUCCESS, + content="完成!", + ) diff --git a/docs/tutorial/zh/source/tutorial/visual.py b/docs/tutorial/zh/source/tutorial/visual.py new file mode 100644 index 000000000..e24332b04 --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/visual.py @@ -0,0 +1,202 @@ +# -*- coding: utf-8 -*- +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: sphinx +# format_version: '1.1' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 +# language: python +# name: python3 +# --- + +""" +.. _visual-interface: + +可视化 +========================= + +AgentScope 支持包括 Gradio 和 AgentScope Studio 在内的可视化,以提高用户体验。 + +Gradio +~~~~~~~~~~~~~~~~~~~~~~ + +首先,请确保已安装完整版本的 AgentScope, 其中包含 Gradio 包。 + +.. code-block:: bash + + # From pypi + pip install agentscope[full] + + # From source code + cd agentscope + pip install .[full] + + +之后,请确保您的应用程序被封装在一个 `main` 函数中。 + +.. code-block:: python + + from agentscope.agents import DialogAgent, UserAgent + import agentscope + + + def main(): + # Your code here + agentscope.init(model_configs={ + "config_name": "my-qwen-max", + "model_type": "dashscope_chat", + "model_name": "qwen-max" + }) + + agent = DialogAgent( + name="Alice, + model_config_name="my-qwen-max", + sys_prompt="You're a helpful assistant named Alice." + ) + user = UserAgent(agent) + + msg = None + while True: + msg = agent(msg) + msg = user(msg) + if msg.content == "exit": + break + + +然后在终端执行以下命令启动 Gradio UI: + +.. code-block :: bash + + as_gradio {path_to_your_python_code} + +最后,您可以访问 Gradio UI,如下所示: + +.. image:: https://img.alicdn.com/imgextra/i1/O1CN0181KSfH1oNbfzjUAVT_!!6000000005213-0-tps-3022-1530.jpg + :align: center + :class: bordered-image + +------------------------------ + +AgentScope Studio +~~~~~~~~~~~~~~~~~~ + +AgentScope Studio 是一个开源的 Web UI 工具包,用于构建和监控多智能体应用程序。它提供以下功能: + +* **仪表板**: 一个用于监控正在运行的应用程序,查看、管理运行历史的界面。 + +* **工作站**: 一个拖拽式构建应用的低代码开发界面。 + +* **服务器管理器**: 一个用于管理大规模分布式应用的界面。 + +* **画廊**: 工作站中应用程序示例。(即将推出!) + +.. _studio: + +启动 AgentScope Studio +---------------------------- + +要启动 Studio,首先确保您已安装最新版本的 AgentScope。然后运行以下 Python 代码: + +.. code-block:: python + + import agentscope + agentscope.studio.init() + +或者可以在终端中运行以下命令: + +.. code-block :: python + + as_studio + +之后,可以访问 http://127.0.0.1:5000 上的 AgentScope Studio,将显示以下页面: + +.. image:: https://img.alicdn.com/imgextra/i3/O1CN01Xic0GQ1ZkJ4M0iD8F_!!6000000003232-0-tps-3452-1610.jpg + :align: center + :class: bordered-image + +当然,也可以更改主机和端口,并通过提供以下参数链接到你的应用程序运行历史记录: + +.. code-block:: python + + import agentscope + + agentscope.studio.init( + host="127.0.0.1", # AgentScope Studio的IP地址 + port=5000, # AgentScope Studio的端口号 + run_dirs = [ # 应用运行历史的文件目录 + "xxx/xxx/runs", + "xxx/xxx/runs" + ] + ) + + +仪表板 +----------------- + +仪表板是一个 Web 界面,用于监控您正在运行的应用程序并查看运行历史记录。 + + +注意 +^^^^^^^^^^^^^^^^^^^^^ + +目前,仪表板存在以下限制,我们正在努力改进。欢迎任何反馈、贡献或建议! + +* 运行的应用程序和 AgentScope Studio 必须运行在同一台机器上,以保证"URL/路径一致性"。如果您想在其他机器上访问 AgentScope,您可以尝试通过在远程机器上运行以下命令来转发端口: + +.. code-block :: bash + + # 假设 AgentScope 运行在{as_host}:{as_port},远程机器的端口是{remote_machine_port} + ssh -L {remote_machine_port}:{as_host}:{as_port} [{user_name}@]{as_host} + +* 对于分布式应用程序,支持单机多进程模式,但尚不支持多机多进程模式。 + +注册应用程序 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +在启动 AgentScope Studio 后,可以通过在 `agentscope.init()` 中指定 `studio_url` 来注册正在运行的应用程序: + +.. code-block:: python + + import agentscope + + agentscope.init( + # ... + project="xxx", + name="xxx", + studio_url="http://127.0.0.1:5000" # AgentScope Studio的URL + ) + +注册后,可以在仪表板中查看正在运行的应用程序。为了区分不同的应用程序,可以指定应用程序的项目和名称。 + +.. image:: https://img.alicdn.com/imgextra/i2/O1CN01zcUmuJ1I3OUXy1Q35_!!6000000000837-0-tps-3426-1718.jpg + :align: center + :class: bordered-image + +单击状态为 `waiting` 的程序,即可进入执行界面。例如,下图显示了一个对话界面。 + +.. image:: https://img.alicdn.com/imgextra/i3/O1CN01sA3VUc1h7OLKVLfr3_!!6000000004230-0-tps-3448-1736.jpg + :align: center + :class: bordered-image + + +.. note:: 一旦注册了正在运行的应用程序,`agentscope.agents.UserAgent` 类中的输入操作将转移到 AgentScope Studio 的仪表板。 + +导入运行历史记录 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +在 AgentScope 中,运行历史记录默认保存在 `./runs` 目录中。如果您想在仪表板中查看这些运行历史记录,可以在 `agentscope.studio.init()` 中指定 `run_dirs` : + + +.. code-block:: python + + import agentscope + + agentscope.studio.init( + run_dirs = ["xxx/runs"] + ) + +""" diff --git a/docs/tutorial/zh/source/tutorial/web_browser.py b/docs/tutorial/zh/source/tutorial/web_browser.py new file mode 100644 index 000000000..572798940 --- /dev/null +++ b/docs/tutorial/zh/source/tutorial/web_browser.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +""" +.. _web-browser-control: + +浏览器控制 +==================== + +本节重定向到 +`conversation_with_web_browser_agent/README.md +`_。 +"""