Add files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .ipynb_checkpoints/app-checkpoint.py +30 -0
- .ipynb_checkpoints/requirements-checkpoint.txt +40 -0
- .pre-commit-config.yaml +46 -0
- .pylintrc +428 -0
- .readthedocs.yaml +15 -0
- LICENSE +201 -0
- MANIFEST.in +1 -0
- app.py +30 -0
- docs/en/Makefile +20 -0
- docs/en/_static/css/readthedocs.css +6 -0
- docs/en/_static/images/lagent_icon.png +0 -0
- docs/en/_static/images/robot.png +0 -0
- docs/en/_static/js/collapsed.js +1 -0
- docs/en/_static/js/table.js +31 -0
- docs/en/_templates/autoapi/index.rst +14 -0
- docs/en/_templates/autoapi/python/module.rst +112 -0
- docs/en/_templates/classtemplate.rst +14 -0
- docs/en/changelog.md +16 -0
- docs/en/conf.py +108 -0
- docs/en/docutils.conf +2 -0
- docs/en/get_started/install.md +19 -0
- docs/en/get_started/quickstart.md +485 -0
- docs/en/index.rst +40 -0
- docs/en/make.bat +36 -0
- docs/en/requirements.txt +4 -0
- docs/en/switch_language.md +3 -0
- docs/en/tutorials/action.md +400 -0
- docs/imgs/lagent_icon.png +0 -0
- docs/imgs/lagent_logo.png +0 -0
- docs/zh_cn/.readthedocs.yaml +15 -0
- docs/zh_cn/Makefile +20 -0
- docs/zh_cn/_static/css/readthedocs.css +6 -0
- docs/zh_cn/_static/images/lagent_icon.png +0 -0
- docs/zh_cn/_static/images/robot.png +0 -0
- docs/zh_cn/_static/js/collapsed.js +1 -0
- docs/zh_cn/_static/js/table.js +31 -0
- docs/zh_cn/_templates/autoapi/index.rst +14 -0
- docs/zh_cn/_templates/autoapi/python/module.rst +112 -0
- docs/zh_cn/_templates/classtemplate.rst +14 -0
- docs/zh_cn/conf.py +108 -0
- docs/zh_cn/cp_origin_docs.sh +9 -0
- docs/zh_cn/docutils.conf +2 -0
- docs/zh_cn/get_started/install.md +19 -0
- docs/zh_cn/index.rst +39 -0
- docs/zh_cn/make.bat +36 -0
- docs/zh_cn/switch_language.md +3 -0
- docs/zh_cn/tutorials/action.md +398 -0
- examples/.ipynb_checkpoints/agent_api_web_demo-checkpoint.py +196 -0
- examples/.ipynb_checkpoints/multi_agents_api_web_demo-checkpoint.py +198 -0
- examples/agent_api_web_demo.py +196 -0
.ipynb_checkpoints/app-checkpoint.py
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import os
|
3 |
+
import runpy
|
4 |
+
st.set_page_config(layout="wide", page_title="My Multi-Page App")
|
5 |
+
def set_env_variable(key, value):
|
6 |
+
os.environ[key] = value
|
7 |
+
def home_page():
|
8 |
+
st.header("欢迎来到首页")
|
9 |
+
# 设置输入框为隐私状态
|
10 |
+
token = st.text_input("请输入浦语token:", type="password", key="token")
|
11 |
+
weather_token = st.text_input("请输入和风天气token:", type="password", key="weather_token")
|
12 |
+
if st.button("保存并体验agent"):
|
13 |
+
if token and weather_token:
|
14 |
+
set_env_variable("token", token) # 设置环境变量为 'token'
|
15 |
+
set_env_variable("weather_token", weather_token) # 设置环境变量为 'weather_token'
|
16 |
+
st.session_state.token_entered = True
|
17 |
+
st.rerun()
|
18 |
+
else:
|
19 |
+
st.error("请输入所有token")
|
20 |
+
if 'token_entered' not in st.session_state:
|
21 |
+
st.session_state.token_entered = False
|
22 |
+
if not st.session_state.token_entered:
|
23 |
+
home_page()
|
24 |
+
else:
|
25 |
+
# 动态加载子页面
|
26 |
+
page = st.sidebar.radio("选择页面", ["天气查询助手", "博客写作助手"])
|
27 |
+
if page == "天气查询助手":
|
28 |
+
runpy.run_path("examples/agent_api_web_demo.py", run_name="__main__")
|
29 |
+
elif page == "博客写作助手":
|
30 |
+
runpy.run_path("examples/multi_agents_api_web_demo.py", run_name="__main__")
|
.ipynb_checkpoints/requirements-checkpoint.txt
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# -r requirements/optional.txt
|
2 |
+
# -r requirements/runtime.txt
|
3 |
+
torch==2.1.2
|
4 |
+
torchvision==0.16.2
|
5 |
+
torchaudio==2.1.2
|
6 |
+
termcolor==2.4.0
|
7 |
+
streamlit==1.39.0
|
8 |
+
class_registry==2.1.2
|
9 |
+
datasets==3.1.0
|
10 |
+
# -r requirements/optional.txt
|
11 |
+
google-search-results
|
12 |
+
lmdeploy>=0.2.5
|
13 |
+
pillow
|
14 |
+
python-pptx
|
15 |
+
timeout_decorator
|
16 |
+
torch
|
17 |
+
transformers>=4.34,<=4.40
|
18 |
+
vllm>=0.3.3
|
19 |
+
# -r requirements/runtime.txt
|
20 |
+
aiohttp
|
21 |
+
arxiv
|
22 |
+
asyncache
|
23 |
+
asyncer
|
24 |
+
distro
|
25 |
+
duckduckgo_search==5.3.1b1
|
26 |
+
filelock
|
27 |
+
func_timeout
|
28 |
+
griffe<1.0
|
29 |
+
json5
|
30 |
+
jsonschema
|
31 |
+
jupyter==1.0.0
|
32 |
+
jupyter_client==8.6.2
|
33 |
+
jupyter_core==5.7.2
|
34 |
+
pydantic==2.6.4
|
35 |
+
requests
|
36 |
+
termcolor
|
37 |
+
tiktoken
|
38 |
+
timeout-decorator
|
39 |
+
typing-extensions
|
40 |
+
griffe==0.48.0
|
.pre-commit-config.yaml
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
exclude: ^(tests/data|scripts|ftdp/protocols|ftdp/template_configs|ftdp/tool_dicts)/
|
2 |
+
repos:
|
3 |
+
- repo: https://github.com/PyCQA/flake8
|
4 |
+
rev: 7.0.0
|
5 |
+
hooks:
|
6 |
+
- id: flake8
|
7 |
+
- repo: https://github.com/PyCQA/isort
|
8 |
+
rev: 5.13.2
|
9 |
+
hooks:
|
10 |
+
- id: isort
|
11 |
+
- repo: https://github.com/psf/black
|
12 |
+
rev: 22.8.0
|
13 |
+
hooks:
|
14 |
+
- id: black
|
15 |
+
args: ["--line-length", "119", "--skip-string-normalization"]
|
16 |
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
17 |
+
rev: v4.5.0
|
18 |
+
hooks:
|
19 |
+
- id: trailing-whitespace
|
20 |
+
- id: check-yaml
|
21 |
+
- id: end-of-file-fixer
|
22 |
+
- id: requirements-txt-fixer
|
23 |
+
- id: double-quote-string-fixer
|
24 |
+
- id: check-merge-conflict
|
25 |
+
- id: fix-encoding-pragma
|
26 |
+
args: ["--remove"]
|
27 |
+
- id: mixed-line-ending
|
28 |
+
args: ["--fix=lf"]
|
29 |
+
- repo: https://github.com/executablebooks/mdformat
|
30 |
+
rev: 0.7.17
|
31 |
+
hooks:
|
32 |
+
- id: mdformat
|
33 |
+
args: ["--number"]
|
34 |
+
additional_dependencies:
|
35 |
+
- mdformat-openmmlab
|
36 |
+
- mdformat_frontmatter
|
37 |
+
- linkify-it-py
|
38 |
+
- repo: https://github.com/codespell-project/codespell
|
39 |
+
rev: v2.2.6
|
40 |
+
hooks:
|
41 |
+
- id: codespell
|
42 |
+
- repo: https://github.com/asottile/pyupgrade
|
43 |
+
rev: v3.15.0
|
44 |
+
hooks:
|
45 |
+
- id: pyupgrade
|
46 |
+
args: ["--py36-plus"]
|
.pylintrc
ADDED
@@ -0,0 +1,428 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# This Pylint rcfile contains a best-effort configuration to uphold the
|
2 |
+
# best-practices and style described in the Google Python style guide:
|
3 |
+
# https://google.github.io/styleguide/pyguide.html
|
4 |
+
#
|
5 |
+
# Its canonical open-source location is:
|
6 |
+
# https://google.github.io/styleguide/pylintrc
|
7 |
+
|
8 |
+
[MASTER]
|
9 |
+
|
10 |
+
# Files or directories to be skipped. They should be base names, not paths.
|
11 |
+
ignore=third_party,storage
|
12 |
+
|
13 |
+
# Files or directories matching the regex patterns are skipped. The regex
|
14 |
+
# matches against base names, not paths.
|
15 |
+
ignore-patterns=
|
16 |
+
|
17 |
+
# Pickle collected data for later comparisons.
|
18 |
+
persistent=no
|
19 |
+
|
20 |
+
# List of plugins (as comma separated values of python modules names) to load,
|
21 |
+
# usually to register additional checkers.
|
22 |
+
load-plugins=
|
23 |
+
|
24 |
+
# Use multiple processes to speed up Pylint.
|
25 |
+
jobs=4
|
26 |
+
|
27 |
+
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
28 |
+
# active Python interpreter and may run arbitrary code.
|
29 |
+
unsafe-load-any-extension=no
|
30 |
+
|
31 |
+
|
32 |
+
[MESSAGES CONTROL]
|
33 |
+
|
34 |
+
# Only show warnings with the listed confidence levels. Leave empty to show
|
35 |
+
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
|
36 |
+
confidence=
|
37 |
+
|
38 |
+
# Enable the message, report, category or checker with the given id(s). You can
|
39 |
+
# either give multiple identifier separated by comma (,) or put this option
|
40 |
+
# multiple time (only on the command line, not in the configuration file where
|
41 |
+
# it should appear only once). See also the "--disable" option for examples.
|
42 |
+
#enable=
|
43 |
+
|
44 |
+
# Disable the message, report, category or checker with the given id(s). You
|
45 |
+
# can either give multiple identifiers separated by comma (,) or put this
|
46 |
+
# option multiple times (only on the command line, not in the configuration
|
47 |
+
# file where it should appear only once).You can also use "--disable=all" to
|
48 |
+
# disable everything first and then reenable specific checks. For example, if
|
49 |
+
# you want to run only the similarities checker, you can use "--disable=all
|
50 |
+
# --enable=similarities". If you want to run only the classes checker, but have
|
51 |
+
# no Warning level messages displayed, use"--disable=all --enable=classes
|
52 |
+
# --disable=W"
|
53 |
+
disable=abstract-method,
|
54 |
+
apply-builtin,
|
55 |
+
arguments-differ,
|
56 |
+
attribute-defined-outside-init,
|
57 |
+
backtick,
|
58 |
+
bad-option-value,
|
59 |
+
basestring-builtin,
|
60 |
+
buffer-builtin,
|
61 |
+
c-extension-no-member,
|
62 |
+
consider-using-enumerate,
|
63 |
+
cmp-builtin,
|
64 |
+
cmp-method,
|
65 |
+
coerce-builtin,
|
66 |
+
coerce-method,
|
67 |
+
delslice-method,
|
68 |
+
div-method,
|
69 |
+
duplicate-code,
|
70 |
+
eq-without-hash,
|
71 |
+
execfile-builtin,
|
72 |
+
file-builtin,
|
73 |
+
filter-builtin-not-iterating,
|
74 |
+
fixme,
|
75 |
+
getslice-method,
|
76 |
+
global-statement,
|
77 |
+
hex-method,
|
78 |
+
idiv-method,
|
79 |
+
implicit-str-concat,
|
80 |
+
import-error,
|
81 |
+
import-self,
|
82 |
+
import-star-module-level,
|
83 |
+
inconsistent-return-statements,
|
84 |
+
input-builtin,
|
85 |
+
intern-builtin,
|
86 |
+
invalid-str-codec,
|
87 |
+
locally-disabled,
|
88 |
+
long-builtin,
|
89 |
+
long-suffix,
|
90 |
+
map-builtin-not-iterating,
|
91 |
+
misplaced-comparison-constant,
|
92 |
+
missing-function-docstring,
|
93 |
+
metaclass-assignment,
|
94 |
+
next-method-called,
|
95 |
+
next-method-defined,
|
96 |
+
no-absolute-import,
|
97 |
+
no-else-break,
|
98 |
+
no-else-continue,
|
99 |
+
no-else-raise,
|
100 |
+
no-else-return,
|
101 |
+
no-init, # added
|
102 |
+
no-member,
|
103 |
+
no-name-in-module,
|
104 |
+
no-self-use,
|
105 |
+
nonzero-method,
|
106 |
+
oct-method,
|
107 |
+
old-division,
|
108 |
+
old-ne-operator,
|
109 |
+
old-octal-literal,
|
110 |
+
old-raise-syntax,
|
111 |
+
parameter-unpacking,
|
112 |
+
print-statement,
|
113 |
+
raising-string,
|
114 |
+
range-builtin-not-iterating,
|
115 |
+
raw_input-builtin,
|
116 |
+
rdiv-method,
|
117 |
+
reduce-builtin,
|
118 |
+
relative-import,
|
119 |
+
reload-builtin,
|
120 |
+
round-builtin,
|
121 |
+
setslice-method,
|
122 |
+
signature-differs,
|
123 |
+
standarderror-builtin,
|
124 |
+
suppressed-message,
|
125 |
+
sys-max-int,
|
126 |
+
too-few-public-methods,
|
127 |
+
too-many-ancestors,
|
128 |
+
too-many-arguments,
|
129 |
+
too-many-boolean-expressions,
|
130 |
+
too-many-branches,
|
131 |
+
too-many-instance-attributes,
|
132 |
+
too-many-locals,
|
133 |
+
too-many-nested-blocks,
|
134 |
+
too-many-public-methods,
|
135 |
+
too-many-return-statements,
|
136 |
+
too-many-statements,
|
137 |
+
trailing-newlines,
|
138 |
+
unichr-builtin,
|
139 |
+
unicode-builtin,
|
140 |
+
unnecessary-pass,
|
141 |
+
unpacking-in-except,
|
142 |
+
useless-else-on-loop,
|
143 |
+
useless-object-inheritance,
|
144 |
+
useless-suppression,
|
145 |
+
using-cmp-argument,
|
146 |
+
wrong-import-order,
|
147 |
+
xrange-builtin,
|
148 |
+
zip-builtin-not-iterating,
|
149 |
+
|
150 |
+
|
151 |
+
[REPORTS]
|
152 |
+
|
153 |
+
# Set the output format. Available formats are text, parseable, colorized, msvs
|
154 |
+
# (visual studio) and html. You can also give a reporter class, eg
|
155 |
+
# mypackage.mymodule.MyReporterClass.
|
156 |
+
output-format=colorized
|
157 |
+
|
158 |
+
# Tells whether to display a full report or only the messages
|
159 |
+
reports=no
|
160 |
+
|
161 |
+
# Python expression which should return a note less than 10 (10 is the highest
|
162 |
+
# note). You have access to the variables errors warning, statement which
|
163 |
+
# respectively contain the number of errors / warnings messages and the total
|
164 |
+
# number of statements analyzed. This is used by the global evaluation report
|
165 |
+
# (RP0004).
|
166 |
+
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
167 |
+
|
168 |
+
# Template used to display messages. This is a python new-style format string
|
169 |
+
# used to format the message information. See doc for all details
|
170 |
+
#msg-template=
|
171 |
+
|
172 |
+
|
173 |
+
[BASIC]
|
174 |
+
|
175 |
+
# Good variable names which should always be accepted, separated by a comma
|
176 |
+
good-names=main,_
|
177 |
+
|
178 |
+
# Bad variable names which should always be refused, separated by a comma
|
179 |
+
bad-names=
|
180 |
+
|
181 |
+
# Colon-delimited sets of names that determine each other's naming style when
|
182 |
+
# the name regexes allow several styles.
|
183 |
+
name-group=
|
184 |
+
|
185 |
+
# Include a hint for the correct naming format with invalid-name
|
186 |
+
include-naming-hint=no
|
187 |
+
|
188 |
+
# List of decorators that produce properties, such as abc.abstractproperty. Add
|
189 |
+
# to this list to register other decorators that produce valid properties.
|
190 |
+
property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl
|
191 |
+
|
192 |
+
# Regular expression matching correct function names
|
193 |
+
function-rgx=^(?:(?P<exempt>setUp|tearDown|setUpModule|tearDownModule)|(?P<camel_case>_?[A-Z][a-zA-Z0-9]*)|(?P<snake_case>_?[a-z][a-z0-9_]*))$
|
194 |
+
|
195 |
+
# Regular expression matching correct variable names
|
196 |
+
variable-rgx=^[a-z][a-z0-9_]*$
|
197 |
+
|
198 |
+
# Regular expression matching correct constant names
|
199 |
+
const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
|
200 |
+
|
201 |
+
# Regular expression matching correct attribute names
|
202 |
+
attr-rgx=^_{0,2}[a-z][a-z0-9_]*$
|
203 |
+
|
204 |
+
# Regular expression matching correct argument names
|
205 |
+
argument-rgx=^[a-z][a-z0-9_]*$
|
206 |
+
|
207 |
+
# Regular expression matching correct class attribute names
|
208 |
+
class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
|
209 |
+
|
210 |
+
# Regular expression matching correct inline iteration names
|
211 |
+
inlinevar-rgx=^[a-z][a-z0-9_]*$
|
212 |
+
|
213 |
+
# Regular expression matching correct class names
|
214 |
+
class-rgx=^_?[A-Z][a-zA-Z0-9]*$
|
215 |
+
|
216 |
+
# Regular expression matching correct module names
|
217 |
+
module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$
|
218 |
+
|
219 |
+
# Regular expression matching correct method names
|
220 |
+
method-rgx=(?x)^(?:(?P<exempt>_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P<camel_case>_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P<snake_case>_{0,2}[a-z][a-z0-9_]*))$
|
221 |
+
|
222 |
+
# Regular expression which should only match function or class names that do
|
223 |
+
# not require a docstring.
|
224 |
+
no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$
|
225 |
+
|
226 |
+
# Minimum line length for functions/classes that require docstrings, shorter
|
227 |
+
# ones are exempt.
|
228 |
+
docstring-min-length=10
|
229 |
+
|
230 |
+
|
231 |
+
[TYPECHECK]
|
232 |
+
|
233 |
+
# List of decorators that produce context managers, such as
|
234 |
+
# contextlib.contextmanager. Add to this list to register other decorators that
|
235 |
+
# produce valid context managers.
|
236 |
+
contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager
|
237 |
+
|
238 |
+
# Tells whether missing members accessed in mixin class should be ignored. A
|
239 |
+
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
240 |
+
ignore-mixin-members=yes
|
241 |
+
|
242 |
+
# List of module names for which member attributes should not be checked
|
243 |
+
# (useful for modules/projects where namespaces are manipulated during runtime
|
244 |
+
# and thus existing member attributes cannot be deduced by static analysis. It
|
245 |
+
# supports qualified module names, as well as Unix pattern matching.
|
246 |
+
ignored-modules=
|
247 |
+
|
248 |
+
# List of class names for which member attributes should not be checked (useful
|
249 |
+
# for classes with dynamically set attributes). This supports the use of
|
250 |
+
# qualified names.
|
251 |
+
ignored-classes=optparse.Values,thread._local,_thread._local
|
252 |
+
|
253 |
+
# List of members which are set dynamically and missed by pylint inference
|
254 |
+
# system, and so shouldn't trigger E1101 when accessed. Python regular
|
255 |
+
# expressions are accepted.
|
256 |
+
generated-members=
|
257 |
+
|
258 |
+
|
259 |
+
[FORMAT]
|
260 |
+
|
261 |
+
# Maximum number of characters on a single line.
|
262 |
+
max-line-length=120
|
263 |
+
|
264 |
+
# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt
|
265 |
+
# lines made too long by directives to pytype.
|
266 |
+
|
267 |
+
# Regexp for a line that is allowed to be longer than the limit.
|
268 |
+
ignore-long-lines=(?x)(
|
269 |
+
^\s*(\#\ )?<?https?://\S+>?$|
|
270 |
+
^\s*(from\s+\S+\s+)?import\s+.+$)
|
271 |
+
|
272 |
+
# Allow the body of an if to be on the same line as the test if there is no
|
273 |
+
# else.
|
274 |
+
single-line-if-stmt=yes
|
275 |
+
|
276 |
+
# Maximum number of lines in a module
|
277 |
+
max-module-lines=99999
|
278 |
+
|
279 |
+
# String used as indentation unit. The internal Google style guide mandates 2
|
280 |
+
# spaces. Google's externaly-published style guide says 4, consistent with
|
281 |
+
# PEP 8. Here, we use 2 spaces, for conformity with many open-sourced Google
|
282 |
+
# projects (like TensorFlow).
|
283 |
+
indent-string=' '
|
284 |
+
|
285 |
+
# Number of spaces of indent required inside a hanging or continued line.
|
286 |
+
indent-after-paren=4
|
287 |
+
|
288 |
+
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
289 |
+
expected-line-ending-format=
|
290 |
+
|
291 |
+
|
292 |
+
[MISCELLANEOUS]
|
293 |
+
|
294 |
+
# List of note tags to take in consideration, separated by a comma.
|
295 |
+
notes=TODO
|
296 |
+
|
297 |
+
|
298 |
+
[STRING]
|
299 |
+
|
300 |
+
# This flag controls whether inconsistent-quotes generates a warning when the
|
301 |
+
# character used as a quote delimiter is used inconsistently within a module.
|
302 |
+
check-quote-consistency=yes
|
303 |
+
|
304 |
+
|
305 |
+
[VARIABLES]
|
306 |
+
|
307 |
+
# Tells whether we should check for unused import in __init__ files.
|
308 |
+
init-import=no
|
309 |
+
|
310 |
+
# A regular expression matching the name of dummy variables (i.e. expectedly
|
311 |
+
# not used).
|
312 |
+
dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_)
|
313 |
+
|
314 |
+
# List of additional names supposed to be defined in builtins. Remember that
|
315 |
+
# you should avoid to define new builtins when possible.
|
316 |
+
additional-builtins=
|
317 |
+
|
318 |
+
# List of strings which can identify a callback function by name. A callback
|
319 |
+
# name must start or end with one of those strings.
|
320 |
+
callbacks=cb_,_cb
|
321 |
+
|
322 |
+
# List of qualified module names which can have objects that can redefine
|
323 |
+
# builtins.
|
324 |
+
redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools
|
325 |
+
|
326 |
+
|
327 |
+
[LOGGING]
|
328 |
+
|
329 |
+
# Logging modules to check that the string format arguments are in logging
|
330 |
+
# function parameter format
|
331 |
+
logging-modules=logging,absl.logging,tensorflow.io.logging
|
332 |
+
|
333 |
+
|
334 |
+
[SIMILARITIES]
|
335 |
+
|
336 |
+
# Minimum lines number of a similarity.
|
337 |
+
min-similarity-lines=4
|
338 |
+
|
339 |
+
# Ignore comments when computing similarities.
|
340 |
+
ignore-comments=yes
|
341 |
+
|
342 |
+
# Ignore docstrings when computing similarities.
|
343 |
+
ignore-docstrings=yes
|
344 |
+
|
345 |
+
# Ignore imports when computing similarities.
|
346 |
+
ignore-imports=no
|
347 |
+
|
348 |
+
|
349 |
+
[SPELLING]
|
350 |
+
|
351 |
+
# Spelling dictionary name. Available dictionaries: none. To make it working
|
352 |
+
# install python-enchant package.
|
353 |
+
spelling-dict=
|
354 |
+
|
355 |
+
# List of comma separated words that should not be checked.
|
356 |
+
spelling-ignore-words=
|
357 |
+
|
358 |
+
# A path to a file that contains private dictionary; one word per line.
|
359 |
+
spelling-private-dict-file=
|
360 |
+
|
361 |
+
# Tells whether to store unknown words to indicated private dictionary in
|
362 |
+
# --spelling-private-dict-file option instead of raising a message.
|
363 |
+
spelling-store-unknown-words=no
|
364 |
+
|
365 |
+
|
366 |
+
[IMPORTS]
|
367 |
+
|
368 |
+
# Deprecated modules which should not be used, separated by a comma
|
369 |
+
deprecated-modules=regsub,
|
370 |
+
TERMIOS,
|
371 |
+
Bastion,
|
372 |
+
rexec,
|
373 |
+
sets
|
374 |
+
|
375 |
+
# Create a graph of every (i.e. internal and external) dependencies in the
|
376 |
+
# given file (report RP0402 must not be disabled)
|
377 |
+
import-graph=
|
378 |
+
|
379 |
+
# Create a graph of external dependencies in the given file (report RP0402 must
|
380 |
+
# not be disabled)
|
381 |
+
ext-import-graph=
|
382 |
+
|
383 |
+
# Create a graph of internal dependencies in the given file (report RP0402 must
|
384 |
+
# not be disabled)
|
385 |
+
int-import-graph=
|
386 |
+
|
387 |
+
# Force import order to recognize a module as part of the standard
|
388 |
+
# compatibility libraries.
|
389 |
+
known-standard-library=
|
390 |
+
|
391 |
+
# Force import order to recognize a module as part of a third party library.
|
392 |
+
known-third-party=enchant, absl
|
393 |
+
|
394 |
+
# Analyse import fallback blocks. This can be used to support both Python 2 and
|
395 |
+
# 3 compatible code, which means that the block might have code that exists
|
396 |
+
# only in one or another interpreter, leading to false positives when analysed.
|
397 |
+
analyse-fallback-blocks=no
|
398 |
+
|
399 |
+
|
400 |
+
[CLASSES]
|
401 |
+
|
402 |
+
# List of method names used to declare (i.e. assign) instance attributes.
|
403 |
+
defining-attr-methods=__init__,
|
404 |
+
__new__,
|
405 |
+
setUp
|
406 |
+
|
407 |
+
# List of member names, which should be excluded from the protected access
|
408 |
+
# warning.
|
409 |
+
exclude-protected=_asdict,
|
410 |
+
_fields,
|
411 |
+
_replace,
|
412 |
+
_source,
|
413 |
+
_make
|
414 |
+
|
415 |
+
# List of valid names for the first argument in a class method.
|
416 |
+
valid-classmethod-first-arg=cls,
|
417 |
+
class_
|
418 |
+
|
419 |
+
# List of valid names for the first argument in a metaclass class method.
|
420 |
+
valid-metaclass-classmethod-first-arg=mcs
|
421 |
+
|
422 |
+
|
423 |
+
[EXCEPTIONS]
|
424 |
+
|
425 |
+
# Exceptions that will emit a warning when being caught. Defaults to
|
426 |
+
# "Exception"
|
427 |
+
overgeneral-exceptions=builtins.BaseException,
|
428 |
+
builtins.Exception
|
.readthedocs.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
version: 2
|
2 |
+
|
3 |
+
formats: all
|
4 |
+
|
5 |
+
build:
|
6 |
+
os: ubuntu-22.04
|
7 |
+
tools:
|
8 |
+
python: "3.10"
|
9 |
+
|
10 |
+
python:
|
11 |
+
install:
|
12 |
+
- requirements: requirements/docs.txt
|
13 |
+
|
14 |
+
sphinx:
|
15 |
+
configuration: docs/en/conf.py
|
LICENSE
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Apache License
|
2 |
+
Version 2.0, January 2004
|
3 |
+
http://www.apache.org/licenses/
|
4 |
+
|
5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
6 |
+
|
7 |
+
1. Definitions.
|
8 |
+
|
9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
11 |
+
|
12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
13 |
+
the copyright owner that is granting the License.
|
14 |
+
|
15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
16 |
+
other entities that control, are controlled by, or are under common
|
17 |
+
control with that entity. For the purposes of this definition,
|
18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
19 |
+
direction or management of such entity, whether by contract or
|
20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
22 |
+
|
23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
24 |
+
exercising permissions granted by this License.
|
25 |
+
|
26 |
+
"Source" form shall mean the preferred form for making modifications,
|
27 |
+
including but not limited to software source code, documentation
|
28 |
+
source, and configuration files.
|
29 |
+
|
30 |
+
"Object" form shall mean any form resulting from mechanical
|
31 |
+
transformation or translation of a Source form, including but
|
32 |
+
not limited to compiled object code, generated documentation,
|
33 |
+
and conversions to other media types.
|
34 |
+
|
35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
36 |
+
Object form, made available under the License, as indicated by a
|
37 |
+
copyright notice that is included in or attached to the work
|
38 |
+
(an example is provided in the Appendix below).
|
39 |
+
|
40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
41 |
+
form, that is based on (or derived from) the Work and for which the
|
42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
44 |
+
of this License, Derivative Works shall not include works that remain
|
45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
46 |
+
the Work and Derivative Works thereof.
|
47 |
+
|
48 |
+
"Contribution" shall mean any work of authorship, including
|
49 |
+
the original version of the Work and any modifications or additions
|
50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
54 |
+
means any form of electronic, verbal, or written communication sent
|
55 |
+
to the Licensor or its representatives, including but not limited to
|
56 |
+
communication on electronic mailing lists, source code control systems,
|
57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
59 |
+
excluding communication that is conspicuously marked or otherwise
|
60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
61 |
+
|
62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
64 |
+
subsequently incorporated within the Work.
|
65 |
+
|
66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
71 |
+
Work and such Derivative Works in Source or Object form.
|
72 |
+
|
73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
76 |
+
(except as stated in this section) patent license to make, have made,
|
77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
78 |
+
where such license applies only to those patent claims licensable
|
79 |
+
by such Contributor that are necessarily infringed by their
|
80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
82 |
+
institute patent litigation against any entity (including a
|
83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
84 |
+
or a Contribution incorporated within the Work constitutes direct
|
85 |
+
or contributory patent infringement, then any patent licenses
|
86 |
+
granted to You under this License for that Work shall terminate
|
87 |
+
as of the date such litigation is filed.
|
88 |
+
|
89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
90 |
+
Work or Derivative Works thereof in any medium, with or without
|
91 |
+
modifications, and in Source or Object form, provided that You
|
92 |
+
meet the following conditions:
|
93 |
+
|
94 |
+
(a) You must give any other recipients of the Work or
|
95 |
+
Derivative Works a copy of this License; and
|
96 |
+
|
97 |
+
(b) You must cause any modified files to carry prominent notices
|
98 |
+
stating that You changed the files; and
|
99 |
+
|
100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
101 |
+
that You distribute, all copyright, patent, trademark, and
|
102 |
+
attribution notices from the Source form of the Work,
|
103 |
+
excluding those notices that do not pertain to any part of
|
104 |
+
the Derivative Works; and
|
105 |
+
|
106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
107 |
+
distribution, then any Derivative Works that You distribute must
|
108 |
+
include a readable copy of the attribution notices contained
|
109 |
+
within such NOTICE file, excluding those notices that do not
|
110 |
+
pertain to any part of the Derivative Works, in at least one
|
111 |
+
of the following places: within a NOTICE text file distributed
|
112 |
+
as part of the Derivative Works; within the Source form or
|
113 |
+
documentation, if provided along with the Derivative Works; or,
|
114 |
+
within a display generated by the Derivative Works, if and
|
115 |
+
wherever such third-party notices normally appear. The contents
|
116 |
+
of the NOTICE file are for informational purposes only and
|
117 |
+
do not modify the License. You may add Your own attribution
|
118 |
+
notices within Derivative Works that You distribute, alongside
|
119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
120 |
+
that such additional attribution notices cannot be construed
|
121 |
+
as modifying the License.
|
122 |
+
|
123 |
+
You may add Your own copyright statement to Your modifications and
|
124 |
+
may provide additional or different license terms and conditions
|
125 |
+
for use, reproduction, or distribution of Your modifications, or
|
126 |
+
for any such Derivative Works as a whole, provided Your use,
|
127 |
+
reproduction, and distribution of the Work otherwise complies with
|
128 |
+
the conditions stated in this License.
|
129 |
+
|
130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
132 |
+
by You to the Licensor shall be under the terms and conditions of
|
133 |
+
this License, without any additional terms or conditions.
|
134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
135 |
+
the terms of any separate license agreement you may have executed
|
136 |
+
with Licensor regarding such Contributions.
|
137 |
+
|
138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
140 |
+
except as required for reasonable and customary use in describing the
|
141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
142 |
+
|
143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
144 |
+
agreed to in writing, Licensor provides the Work (and each
|
145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
147 |
+
implied, including, without limitation, any warranties or conditions
|
148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
150 |
+
appropriateness of using or redistributing the Work and assume any
|
151 |
+
risks associated with Your exercise of permissions under this License.
|
152 |
+
|
153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
154 |
+
whether in tort (including negligence), contract, or otherwise,
|
155 |
+
unless required by applicable law (such as deliberate and grossly
|
156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
157 |
+
liable to You for damages, including any direct, indirect, special,
|
158 |
+
incidental, or consequential damages of any character arising as a
|
159 |
+
result of this License or out of the use or inability to use the
|
160 |
+
Work (including but not limited to damages for loss of goodwill,
|
161 |
+
work stoppage, computer failure or malfunction, or any and all
|
162 |
+
other commercial damages or losses), even if such Contributor
|
163 |
+
has been advised of the possibility of such damages.
|
164 |
+
|
165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
168 |
+
or other liability obligations and/or rights consistent with this
|
169 |
+
License. However, in accepting such obligations, You may act only
|
170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
171 |
+
of any other Contributor, and only if You agree to indemnify,
|
172 |
+
defend, and hold each Contributor harmless for any liability
|
173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
174 |
+
of your accepting any such warranty or additional liability.
|
175 |
+
|
176 |
+
END OF TERMS AND CONDITIONS
|
177 |
+
|
178 |
+
APPENDIX: How to apply the Apache License to your work.
|
179 |
+
|
180 |
+
To apply the Apache License to your work, attach the following
|
181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
182 |
+
replaced with your own identifying information. (Don't include
|
183 |
+
the brackets!) The text should be enclosed in the appropriate
|
184 |
+
comment syntax for the file format. We also recommend that a
|
185 |
+
file or class name and description of purpose be included on the
|
186 |
+
same "printed page" as the copyright notice for easier
|
187 |
+
identification within third-party archives.
|
188 |
+
|
189 |
+
Copyright [yyyy] [name of copyright owner]
|
190 |
+
|
191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
192 |
+
you may not use this file except in compliance with the License.
|
193 |
+
You may obtain a copy of the License at
|
194 |
+
|
195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
196 |
+
|
197 |
+
Unless required by applicable law or agreed to in writing, software
|
198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
200 |
+
See the License for the specific language governing permissions and
|
201 |
+
limitations under the License.
|
MANIFEST.in
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
include requirements/*.txt
|
app.py
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import os
|
3 |
+
import runpy
|
4 |
+
st.set_page_config(layout="wide", page_title="My Multi-Page App")
|
5 |
+
def set_env_variable(key, value):
|
6 |
+
os.environ[key] = value
|
7 |
+
def home_page():
|
8 |
+
st.header("欢迎来到首页")
|
9 |
+
# 设置输入框为隐私状态
|
10 |
+
token = st.text_input("请输入浦语token:", type="password", key="token")
|
11 |
+
weather_token = st.text_input("请输入和风天气token:", type="password", key="weather_token")
|
12 |
+
if st.button("保存并体验agent"):
|
13 |
+
if token and weather_token:
|
14 |
+
set_env_variable("token", token) # 设置环境变量为 'token'
|
15 |
+
set_env_variable("weather_token", weather_token) # 设置环境变量为 'weather_token'
|
16 |
+
st.session_state.token_entered = True
|
17 |
+
st.rerun()
|
18 |
+
else:
|
19 |
+
st.error("请输入所有token")
|
20 |
+
if 'token_entered' not in st.session_state:
|
21 |
+
st.session_state.token_entered = False
|
22 |
+
if not st.session_state.token_entered:
|
23 |
+
home_page()
|
24 |
+
else:
|
25 |
+
# 动态加载子页面
|
26 |
+
page = st.sidebar.radio("选择页面", ["天气查询助手", "博客写作助手"])
|
27 |
+
if page == "天气查询助手":
|
28 |
+
runpy.run_path("examples/agent_api_web_demo.py", run_name="__main__")
|
29 |
+
elif page == "博客写作助手":
|
30 |
+
runpy.run_path("examples/multi_agents_api_web_demo.py", run_name="__main__")
|
docs/en/Makefile
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Minimal makefile for Sphinx documentation
|
2 |
+
#
|
3 |
+
|
4 |
+
# You can set these variables from the command line, and also
|
5 |
+
# from the environment for the first two.
|
6 |
+
SPHINXOPTS ?=
|
7 |
+
SPHINXBUILD ?= sphinx-build
|
8 |
+
SOURCEDIR = .
|
9 |
+
BUILDDIR = _build
|
10 |
+
|
11 |
+
# Put it first so that "make" without argument is like "make help".
|
12 |
+
help:
|
13 |
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
14 |
+
|
15 |
+
.PHONY: help Makefile
|
16 |
+
|
17 |
+
# Catch-all target: route all unknown targets to Sphinx using the new
|
18 |
+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
19 |
+
%: Makefile
|
20 |
+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
docs/en/_static/css/readthedocs.css
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.header-logo {
|
2 |
+
background-image: url("../images/lagent_icon.png");
|
3 |
+
background-size: 40px 40px;
|
4 |
+
height: 40px;
|
5 |
+
width: 40px;
|
6 |
+
}
|
docs/en/_static/images/lagent_icon.png
ADDED
docs/en/_static/images/robot.png
ADDED
docs/en/_static/js/collapsed.js
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
var collapsedSections = ['API Reference']
|
docs/en/_static/js/table.js
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$(document).ready(function () {
|
2 |
+
table = $('.model-summary').DataTable({
|
3 |
+
"stateSave": false,
|
4 |
+
"lengthChange": false,
|
5 |
+
"pageLength": 10,
|
6 |
+
"order": [],
|
7 |
+
"scrollX": true,
|
8 |
+
"columnDefs": [
|
9 |
+
{ "type": "summary", targets: '_all' },
|
10 |
+
]
|
11 |
+
});
|
12 |
+
// Override the default sorting for the summary columns, which
|
13 |
+
// never takes the "-" character into account.
|
14 |
+
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
15 |
+
"summary-asc": function (str1, str2) {
|
16 |
+
if (str1 == "<p>-</p>")
|
17 |
+
return 1;
|
18 |
+
if (str2 == "<p>-</p>")
|
19 |
+
return -1;
|
20 |
+
return ((str1 < str2) ? -1 : ((str1 > str2) ? 1 : 0));
|
21 |
+
},
|
22 |
+
|
23 |
+
"summary-desc": function (str1, str2) {
|
24 |
+
if (str1 == "<p>-</p>")
|
25 |
+
return 1;
|
26 |
+
if (str2 == "<p>-</p>")
|
27 |
+
return -1;
|
28 |
+
return ((str1 < str2) ? 1 : ((str1 > str2) ? -1 : 0));
|
29 |
+
}
|
30 |
+
});
|
31 |
+
})
|
docs/en/_templates/autoapi/index.rst
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
API Reference
|
2 |
+
=============
|
3 |
+
|
4 |
+
This page contains auto-generated API reference documentation.
|
5 |
+
|
6 |
+
.. toctree::
|
7 |
+
:titlesonly:
|
8 |
+
:maxdepth: 3
|
9 |
+
|
10 |
+
{% for page in pages %}
|
11 |
+
{% if page.top_level_object and page.display %}
|
12 |
+
{{ page.include_path }}
|
13 |
+
{% endif %}
|
14 |
+
{% endfor %}
|
docs/en/_templates/autoapi/python/module.rst
ADDED
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{% if not obj.display %}
|
2 |
+
:orphan:
|
3 |
+
|
4 |
+
{% endif %}
|
5 |
+
:py:mod:`{{ obj.name if obj.name.count(".") <= 1 else obj.short_name }}`
|
6 |
+
=========={{ "=" * (obj.name|length if obj.name.count(".") <= 1 else obj.short_name|length) }}
|
7 |
+
|
8 |
+
.. py:module:: {{ obj.name }}
|
9 |
+
|
10 |
+
{% if obj.docstring %}
|
11 |
+
.. autoapi-nested-parse::
|
12 |
+
|
13 |
+
{{ obj.docstring|indent(3) }}
|
14 |
+
|
15 |
+
{% endif %}
|
16 |
+
|
17 |
+
{% block subpackages %}
|
18 |
+
{% set visible_subpackages = obj.subpackages|selectattr("display")|list %}
|
19 |
+
{% if visible_subpackages %}
|
20 |
+
Subpackages
|
21 |
+
-----------
|
22 |
+
.. toctree::
|
23 |
+
:titlesonly:
|
24 |
+
:maxdepth: 3
|
25 |
+
|
26 |
+
{% for subpackage in visible_subpackages %}
|
27 |
+
{{ subpackage.short_name }}/index.rst
|
28 |
+
{% endfor %}
|
29 |
+
|
30 |
+
|
31 |
+
{% endif %}
|
32 |
+
{% endblock %}
|
33 |
+
{% block submodules %}
|
34 |
+
{% set visible_submodules = obj.submodules|selectattr("display")|list %}
|
35 |
+
{% if visible_submodules %}
|
36 |
+
Submodules
|
37 |
+
----------
|
38 |
+
.. toctree::
|
39 |
+
:titlesonly:
|
40 |
+
:maxdepth: 1
|
41 |
+
|
42 |
+
{% for submodule in visible_submodules %}
|
43 |
+
{{ submodule.short_name }}/index.rst
|
44 |
+
{% endfor %}
|
45 |
+
|
46 |
+
|
47 |
+
{% endif %}
|
48 |
+
{% endblock %}
|
49 |
+
{% block content %}
|
50 |
+
{% if obj.type is equalto("package") %}
|
51 |
+
{% set visible_children = obj.children|selectattr("display")|list %}
|
52 |
+
{% else %}
|
53 |
+
{% set visible_children = obj.children|selectattr("display")|rejectattr("imported")|list %}
|
54 |
+
{% endif %}
|
55 |
+
{% if visible_children %}
|
56 |
+
{{ obj.type|title }} Contents
|
57 |
+
{{ "-" * obj.type|length }}---------
|
58 |
+
|
59 |
+
{% set visible_classes = visible_children|selectattr("type", "equalto", "class")|list %}
|
60 |
+
{% set visible_functions = visible_children|selectattr("type", "equalto", "function")|list %}
|
61 |
+
{% set visible_attributes = visible_children|selectattr("type", "equalto", "data")|list %}
|
62 |
+
{% if "show-module-summary" in autoapi_options and (visible_classes or visible_functions) %}
|
63 |
+
{% block classes scoped %}
|
64 |
+
{% if visible_classes %}
|
65 |
+
Classes
|
66 |
+
~~~~~~~
|
67 |
+
|
68 |
+
.. autoapisummary::
|
69 |
+
|
70 |
+
{% for klass in visible_classes %}
|
71 |
+
{{ klass.id }}
|
72 |
+
{% endfor %}
|
73 |
+
|
74 |
+
|
75 |
+
{% endif %}
|
76 |
+
{% endblock %}
|
77 |
+
|
78 |
+
{% block functions scoped %}
|
79 |
+
{% if visible_functions %}
|
80 |
+
Functions
|
81 |
+
~~~~~~~~~
|
82 |
+
|
83 |
+
.. autoapisummary::
|
84 |
+
|
85 |
+
{% for function in visible_functions %}
|
86 |
+
{{ function.id }}
|
87 |
+
{% endfor %}
|
88 |
+
|
89 |
+
|
90 |
+
{% endif %}
|
91 |
+
{% endblock %}
|
92 |
+
|
93 |
+
{% block attributes scoped %}
|
94 |
+
{% if visible_attributes %}
|
95 |
+
Attributes
|
96 |
+
~~~~~~~~~~
|
97 |
+
|
98 |
+
.. autoapisummary::
|
99 |
+
|
100 |
+
{% for attribute in visible_attributes %}
|
101 |
+
{{ attribute.id }}
|
102 |
+
{% endfor %}
|
103 |
+
|
104 |
+
|
105 |
+
{% endif %}
|
106 |
+
{% endblock %}
|
107 |
+
{% endif %}
|
108 |
+
{% for obj_item in visible_children %}
|
109 |
+
{{ obj_item.render()|indent(0) }}
|
110 |
+
{% endfor %}
|
111 |
+
{% endif %}
|
112 |
+
{% endblock %}
|
docs/en/_templates/classtemplate.rst
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.. role:: hidden
|
2 |
+
:class: hidden-section
|
3 |
+
.. currentmodule:: {{ module }}
|
4 |
+
|
5 |
+
|
6 |
+
{{ name | underline}}
|
7 |
+
|
8 |
+
.. autoclass:: {{ name }}
|
9 |
+
:members:
|
10 |
+
|
11 |
+
|
12 |
+
..
|
13 |
+
autogenerated from source/_templates/classtemplate.rst
|
14 |
+
note it does not have :inherited-members:
|
docs/en/changelog.md
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## Changelog
|
2 |
+
|
3 |
+
### v0.1.2 (24/10/2023)
|
4 |
+
|
5 |
+
#### Highlights
|
6 |
+
|
7 |
+
- Support Efficient Inference Engine [lmdeploy turbomind](https://github.com/InternLM/lmdeploy/tree/main)
|
8 |
+
|
9 |
+
#### New Features
|
10 |
+
|
11 |
+
- Support Efficient Inference Engine [TurboMind](https://github.com/InternLM/lmdeploy/tree/main): Based on lmdeploy turbomind, Lagent supports the inference of LLaMA and its variant models on NVIDIA GPUs. (#47)
|
12 |
+
|
13 |
+
#### Contributors
|
14 |
+
|
15 |
+
A total of 2 developers contributed to this release.
|
16 |
+
Thanks @Harold-lkk @jiangningliu30
|
docs/en/conf.py
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Configuration file for the Sphinx documentation builder.
|
2 |
+
#
|
3 |
+
# This file only contains a selection of the most common options. For a full
|
4 |
+
# list see the documentation:
|
5 |
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
6 |
+
|
7 |
+
# -- Path setup --------------------------------------------------------------
|
8 |
+
|
9 |
+
# If extensions (or modules to document with autodoc) are in another directory,
|
10 |
+
# add these directories to sys.path here. If the directory is relative to the
|
11 |
+
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
12 |
+
|
13 |
+
import os
|
14 |
+
import re
|
15 |
+
import sys
|
16 |
+
|
17 |
+
sys.path.insert(0, os.path.abspath('../..'))
|
18 |
+
|
19 |
+
# -- Project information -----------------------------------------------------
|
20 |
+
project = 'Lagent'
|
21 |
+
copyright = '2020-2030, InternLM'
|
22 |
+
author = 'InternLM'
|
23 |
+
language = 'en'
|
24 |
+
|
25 |
+
# The full version, including alpha/beta/rc tags
|
26 |
+
version_file = '../../lagent/version.py'
|
27 |
+
with open(version_file) as f:
|
28 |
+
exec(compile(f.read(), version_file, 'exec'))
|
29 |
+
__version__ = locals()['__version__']
|
30 |
+
release = __version__
|
31 |
+
|
32 |
+
# -- General configuration ---------------------------------------------------
|
33 |
+
|
34 |
+
# Add any Sphinx extension module names here, as strings. They can be
|
35 |
+
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
36 |
+
# ones.
|
37 |
+
extensions = [
|
38 |
+
'sphinx_rtd_theme',
|
39 |
+
'myst_nb',
|
40 |
+
'autoapi.extension',
|
41 |
+
'sphinx_markdown_tables',
|
42 |
+
'sphinx.ext.autodoc',
|
43 |
+
'sphinx.ext.napoleon',
|
44 |
+
'sphinx.ext.viewcode',
|
45 |
+
]
|
46 |
+
|
47 |
+
nb_output_stderr = 'remove-warn'
|
48 |
+
autodoc_typehints = 'description'
|
49 |
+
|
50 |
+
# sphinx-autoapi configuration
|
51 |
+
autoapi_dirs = ['../../lagent']
|
52 |
+
autoapi_options = [
|
53 |
+
'members',
|
54 |
+
'undoc-members',
|
55 |
+
'show-inheritance',
|
56 |
+
'show-module-summary',
|
57 |
+
]
|
58 |
+
autoapi_ignore = ['*migrations*', '*command.py', '*cli.py']
|
59 |
+
autoapi_template_dir = '_templates/autoapi'
|
60 |
+
autoapi_add_toctree_entry = False
|
61 |
+
|
62 |
+
# Add any paths that contain templates here, relative to this directory.
|
63 |
+
templates_path = ['_templates']
|
64 |
+
|
65 |
+
# List of patterns, relative to source directory, that match files and
|
66 |
+
# directories to ignore when looking for source files.
|
67 |
+
# This pattern also affects html_static_path and html_extra_path.
|
68 |
+
exclude_patterns = []
|
69 |
+
|
70 |
+
# -- Options for HTML output -------------------------------------------------
|
71 |
+
|
72 |
+
# The theme to use for HTML and HTML Help pages. See the documentation for
|
73 |
+
# a list of builtin themes.
|
74 |
+
#
|
75 |
+
html_theme = 'sphinx_rtd_theme'
|
76 |
+
html_theme_options = {
|
77 |
+
'navigation_depth': 3,
|
78 |
+
'titles_only': False,
|
79 |
+
'style_nav_header_background': '#4fabab',
|
80 |
+
}
|
81 |
+
html_context = {
|
82 |
+
'display_github': True,
|
83 |
+
'github_host': 'github.com',
|
84 |
+
'github_user': 'InternLM',
|
85 |
+
'github_repo': 'lagent',
|
86 |
+
'github_version': 'main',
|
87 |
+
'conf_py_path': '/docs/en/',
|
88 |
+
}
|
89 |
+
html_title = 'Lagent'
|
90 |
+
html_logo = '../imgs/lagent_logo.png'
|
91 |
+
html_favicon = '../imgs/lagent_icon.png'
|
92 |
+
|
93 |
+
master_doc = 'index'
|
94 |
+
|
95 |
+
# Add any paths that contain custom static files (such as style sheets) here,
|
96 |
+
# relative to this directory. They are copied after the builtin static files,
|
97 |
+
# so a file named 'default.css' will overwrite the builtin 'default.css'.
|
98 |
+
html_static_path = ['_static']
|
99 |
+
|
100 |
+
|
101 |
+
def custom_skip(app, what, name, obj, skip, options):
|
102 |
+
if what in ['data', 'function', 'class'] and re.search('logger', name):
|
103 |
+
skip = True
|
104 |
+
return skip
|
105 |
+
|
106 |
+
|
107 |
+
def setup(sphinx):
|
108 |
+
sphinx.connect('autoapi-skip-member', custom_skip)
|
docs/en/docutils.conf
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
[html writers]
|
2 |
+
table_style: colwidths-auto
|
docs/en/get_started/install.md
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Installation
|
2 |
+
|
3 |
+
## With pip
|
4 |
+
|
5 |
+
Install with pip (Recommended).
|
6 |
+
|
7 |
+
```bash
|
8 |
+
pip install lagent
|
9 |
+
```
|
10 |
+
|
11 |
+
## From source
|
12 |
+
|
13 |
+
Optionally, you could also build Lagent from source in case you want to modify the code:
|
14 |
+
|
15 |
+
```bash
|
16 |
+
git clone https://github.com/InternLM/lagent.git
|
17 |
+
cd lagent
|
18 |
+
pip install -e .
|
19 |
+
```
|
docs/en/get_started/quickstart.md
ADDED
@@ -0,0 +1,485 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# How to Use Lagent
|
2 |
+
|
3 |
+
Lagent v1.0 is inspired by the design philosophy of PyTorch. We expect that the analogy of neural network layers will make the workflow clearer and more intuitive, so users only need to focus on creating layers and defining message passing between them in a Pythonic way. This is a simple tutorial to get you quickly started with building multi-agent applications.
|
4 |
+
|
5 |
+
## Core Ideas
|
6 |
+
|
7 |
+
### Models as Agents
|
8 |
+
|
9 |
+
Agents use `AgentMessage` for communication.
|
10 |
+
|
11 |
+
```python
|
12 |
+
from typing import Dict, List
|
13 |
+
from lagent.agents import Agent
|
14 |
+
from lagent.schema import AgentMessage
|
15 |
+
from lagent.llms import VllmModel, INTERNLM2_META
|
16 |
+
|
17 |
+
llm = VllmModel(
|
18 |
+
path='Qwen/Qwen2-7B-Instruct',
|
19 |
+
meta_template=INTERNLM2_META,
|
20 |
+
tp=1,
|
21 |
+
top_k=1,
|
22 |
+
temperature=1.0,
|
23 |
+
stop_words=['<|im_end|>'],
|
24 |
+
max_new_tokens=1024,
|
25 |
+
)
|
26 |
+
system_prompt = '你的回答只能从“典”、“孝”、“急”三个字中选一个。'
|
27 |
+
agent = Agent(llm, system_prompt)
|
28 |
+
|
29 |
+
user_msg = AgentMessage(sender='user', content='今天天气情况')
|
30 |
+
bot_msg = agent(user_msg)
|
31 |
+
print(bot_msg)
|
32 |
+
```
|
33 |
+
|
34 |
+
```
|
35 |
+
content='急' sender='Agent' formatted=None extra_info=None type=None receiver=None stream_state=<AgentStatusCode.END: 0>
|
36 |
+
```
|
37 |
+
|
38 |
+
### Memory as State
|
39 |
+
|
40 |
+
Both input and output messages will be added to the memory of `Agent` in each forward pass. This is performed in `__call__` rather than `forward`. See the following pseudo code
|
41 |
+
|
42 |
+
```python
|
43 |
+
def __call__(self, *message):
|
44 |
+
message = pre_hooks(message)
|
45 |
+
add_memory(message)
|
46 |
+
message = self.forward(*message)
|
47 |
+
add_memory(message)
|
48 |
+
message = post_hooks(message)
|
49 |
+
return message
|
50 |
+
```
|
51 |
+
|
52 |
+
Inspect the memory in two ways
|
53 |
+
|
54 |
+
```python
|
55 |
+
memory: List[AgentMessage] = agent.memory.get_memory()
|
56 |
+
print(memory)
|
57 |
+
print('-' * 120)
|
58 |
+
dumped_memory: Dict[str, List[dict]] = agent.state_dict()
|
59 |
+
print(dumped_memory['memory'])
|
60 |
+
```
|
61 |
+
|
62 |
+
```
|
63 |
+
[AgentMessage(content='今天天气情况', sender='user', formatted=None, extra_info=None, type=None, receiver=None, stream_state=<AgentStatusCode.END: 0>), AgentMessage(content='急', sender='Agent', formatted=None, extra_info=None, type=None, receiver=None, stream_state=<AgentStatusCode.END: 0>)]
|
64 |
+
------------------------------------------------------------------------------------------------------------------------
|
65 |
+
[{'content': '今天天气情况', 'sender': 'user', 'formatted': None, 'extra_info': None, 'type': None, 'receiver': None, 'stream_state': <AgentStatusCode.END: 0>}, {'content': '急', 'sender': 'Agent', 'formatted': None, 'extra_info': None, 'type': None, 'receiver': None, 'stream_state': <AgentStatusCode.END: 0>}]
|
66 |
+
```
|
67 |
+
|
68 |
+
Clear the memory of this session(`session_id=0` by default):
|
69 |
+
|
70 |
+
```python
|
71 |
+
agent.memory.reset()
|
72 |
+
```
|
73 |
+
|
74 |
+
### Custom Message Aggregation
|
75 |
+
|
76 |
+
`DefaultAggregator` is called under the hood to assemble and convert `AgentMessage` to OpenAI message format.
|
77 |
+
|
78 |
+
```python
|
79 |
+
def forward(self, *message: AgentMessage, session_id=0, **kwargs) -> Union[AgentMessage, str]:
|
80 |
+
formatted_messages = self.aggregator.aggregate(
|
81 |
+
self.memory.get(session_id),
|
82 |
+
self.name,
|
83 |
+
self.output_format,
|
84 |
+
self.template,
|
85 |
+
)
|
86 |
+
llm_response = self.llm.chat(formatted_messages, **kwargs)
|
87 |
+
...
|
88 |
+
```
|
89 |
+
|
90 |
+
Implement a simple aggregator that can receive few-shots
|
91 |
+
|
92 |
+
```python
|
93 |
+
from typing import List, Union
|
94 |
+
from lagent.memory import Memory
|
95 |
+
from lagent.prompts import StrParser
|
96 |
+
from lagent.agents.aggregator import DefaultAggregator
|
97 |
+
|
98 |
+
class FewshotAggregator(DefaultAggregator):
|
99 |
+
def __init__(self, few_shot: List[dict] = None):
|
100 |
+
self.few_shot = few_shot or []
|
101 |
+
|
102 |
+
def aggregate(self,
|
103 |
+
messages: Memory,
|
104 |
+
name: str,
|
105 |
+
parser: StrParser = None,
|
106 |
+
system_instruction: Union[str, dict, List[dict]] = None) -> List[dict]:
|
107 |
+
_message = []
|
108 |
+
if system_instruction:
|
109 |
+
_message.extend(
|
110 |
+
self.aggregate_system_intruction(system_instruction))
|
111 |
+
_message.extend(self.few_shot)
|
112 |
+
messages = messages.get_memory()
|
113 |
+
for message in messages:
|
114 |
+
if message.sender == name:
|
115 |
+
_message.append(
|
116 |
+
dict(role='assistant', content=str(message.content)))
|
117 |
+
else:
|
118 |
+
user_message = message.content
|
119 |
+
if len(_message) > 0 and _message[-1]['role'] == 'user':
|
120 |
+
_message[-1]['content'] += user_message
|
121 |
+
else:
|
122 |
+
_message.append(dict(role='user', content=user_message))
|
123 |
+
return _message
|
124 |
+
|
125 |
+
agent = Agent(
|
126 |
+
llm,
|
127 |
+
aggregator=FewshotAggregator(
|
128 |
+
[
|
129 |
+
{"role": "user", "content": "今天天气"},
|
130 |
+
{"role": "assistant", "content": "【晴】"},
|
131 |
+
]
|
132 |
+
)
|
133 |
+
)
|
134 |
+
user_msg = AgentMessage(sender='user', content='昨天天气')
|
135 |
+
bot_msg = agent(user_msg)
|
136 |
+
print(bot_msg)
|
137 |
+
```
|
138 |
+
|
139 |
+
```
|
140 |
+
content='【多云转晴,夜间有轻微降温】' sender='Agent' formatted=None extra_info=None type=None receiver=None stream_state=<AgentStatusCode.END: 0>
|
141 |
+
```
|
142 |
+
|
143 |
+
### Flexible Response Formatting
|
144 |
+
|
145 |
+
In `AgentMessage`, `formatted` is reserved to store information parsed by `output_format` from the model output.
|
146 |
+
|
147 |
+
```python
|
148 |
+
def forward(self, *message: AgentMessage, session_id=0, **kwargs) -> Union[AgentMessage, str]:
|
149 |
+
...
|
150 |
+
llm_response = self.llm.chat(formatted_messages, **kwargs)
|
151 |
+
if self.output_format:
|
152 |
+
formatted_messages = self.output_format.parse_response(llm_response)
|
153 |
+
return AgentMessage(
|
154 |
+
sender=self.name,
|
155 |
+
content=llm_response,
|
156 |
+
formatted=formatted_messages,
|
157 |
+
)
|
158 |
+
...
|
159 |
+
```
|
160 |
+
|
161 |
+
Use a tool parser as follows
|
162 |
+
|
163 |
+
````python
|
164 |
+
from lagent.prompts.parsers import ToolParser
|
165 |
+
|
166 |
+
system_prompt = "逐步分析并编写Python代码解决以下问题。"
|
167 |
+
parser = ToolParser(tool_type='code interpreter', begin='```python\n', end='\n```\n')
|
168 |
+
llm.gen_params['stop_words'].append('\n```\n')
|
169 |
+
agent = Agent(llm, system_prompt, output_format=parser)
|
170 |
+
|
171 |
+
user_msg = AgentMessage(
|
172 |
+
sender='user',
|
173 |
+
content='Marie is thinking of a multiple of 63, while Jay is thinking of a '
|
174 |
+
'factor of 63. They happen to be thinking of the same number. There are '
|
175 |
+
'two possibilities for the number that each of them is thinking of, one '
|
176 |
+
'positive and one negative. Find the product of these two numbers.')
|
177 |
+
bot_msg = agent(user_msg)
|
178 |
+
print(bot_msg.model_dump_json(indent=4))
|
179 |
+
````
|
180 |
+
|
181 |
+
````
|
182 |
+
{
|
183 |
+
"content": "首先,我们需要找出63的所有正因数和负因数。63的正因数可以通过分解63的质因数来找出,即\\(63 = 3^2 \\times 7\\)。因此,63的正因数包括1, 3, 7, 9, 21, 和 63。对于负因数,我们只需将上述正因数乘以-1。\n\n接下来,我们需要找出与63的正因数相乘的结果为63的数,以及与63的负因数相乘的结果为63的数。这可以通过将63除以每个正因数和负因数来实现。\n\n最后,我们将找到的两个数相乘得到最终答案。\n\n下面是Python代码实现:\n\n```python\ndef find_numbers():\n # 正因数\n positive_factors = [1, 3, 7, 9, 21, 63]\n # 负因数\n negative_factors = [-1, -3, -7, -9, -21, -63]\n \n # 找到与正因数相乘的结果为63的数\n positive_numbers = [63 / factor for factor in positive_factors]\n # 找到与负因数相乘的结果为63的数\n negative_numbers = [-63 / factor for factor in negative_factors]\n \n # 计算两个数的乘积\n product = positive_numbers[0] * negative_numbers[0]\n \n return product\n\nresult = find_numbers()\nprint(result)",
|
184 |
+
"sender": "Agent",
|
185 |
+
"formatted": {
|
186 |
+
"tool_type": "code interpreter",
|
187 |
+
"thought": "首先,我们需要找出63的所有正因数和负因数。63的正因数可以通过分解63的质因数来找出,即\\(63 = 3^2 \\times 7\\)。因此,63的正因数包括1, 3, 7, 9, 21, 和 63。对于负因数,我们只需将上述正因数乘以-1。\n\n接下来,我们需要找出与63的正因数相乘的结果为63的数,以及与63的负因数相乘的结果为63的数。这可以通过将63除以每个正因数和负因数来实现。\n\n最后,我们将找到的两个数相乘得到最终答案。\n\n下面是Python代码实现:\n\n",
|
188 |
+
"action": "def find_numbers():\n # 正因数\n positive_factors = [1, 3, 7, 9, 21, 63]\n # 负因数\n negative_factors = [-1, -3, -7, -9, -21, -63]\n \n # 找到与正因数相乘的结果为63的数\n positive_numbers = [63 / factor for factor in positive_factors]\n # 找到与负因数相乘的结果为63的数\n negative_numbers = [-63 / factor for factor in negative_factors]\n \n # 计算两个数的乘积\n product = positive_numbers[0] * negative_numbers[0]\n \n return product\n\nresult = find_numbers()\nprint(result)",
|
189 |
+
"status": 1
|
190 |
+
},
|
191 |
+
"extra_info": null,
|
192 |
+
"type": null,
|
193 |
+
"receiver": null,
|
194 |
+
"stream_state": 0
|
195 |
+
}
|
196 |
+
````
|
197 |
+
|
198 |
+
### Consistency of Tool Calling
|
199 |
+
|
200 |
+
`ActionExecutor` uses the same communication data structure as `Agent`, but requires the content of input `AgentMessage` to be a dict containing:
|
201 |
+
|
202 |
+
- `name`: tool name, e.g. `'IPythonInterpreter'`, `'WebBrowser.search'`.
|
203 |
+
- `parameters`: keyword arguments of the tool API, e.g. `{'command': 'import math;math.sqrt(2)'}`, `{'query': ['recent progress in AI']}`.
|
204 |
+
|
205 |
+
You can register custom hooks for message conversion.
|
206 |
+
|
207 |
+
```python
|
208 |
+
from lagent.hooks import Hook
|
209 |
+
from lagent.schema import ActionReturn, ActionStatusCode, AgentMessage
|
210 |
+
from lagent.actions import ActionExecutor, IPythonInteractive
|
211 |
+
|
212 |
+
class CodeProcessor(Hook):
|
213 |
+
def before_action(self, executor, message, session_id):
|
214 |
+
message = message.copy(deep=True)
|
215 |
+
message.content = dict(
|
216 |
+
name='IPythonInteractive', parameters={'command': message.formatted['action']}
|
217 |
+
)
|
218 |
+
return message
|
219 |
+
|
220 |
+
def after_action(self, executor, message, session_id):
|
221 |
+
action_return = message.content
|
222 |
+
if isinstance(action_return, ActionReturn):
|
223 |
+
if action_return.state == ActionStatusCode.SUCCESS:
|
224 |
+
response = action_return.format_result()
|
225 |
+
else:
|
226 |
+
response = action_return.errmsg
|
227 |
+
else:
|
228 |
+
response = action_return
|
229 |
+
message.content = response
|
230 |
+
return message
|
231 |
+
|
232 |
+
executor = ActionExecutor(actions=[IPythonInteractive()], hooks=[CodeProcessor()])
|
233 |
+
bot_msg = AgentMessage(
|
234 |
+
sender='Agent',
|
235 |
+
content='首先,我们需要...',
|
236 |
+
formatted={
|
237 |
+
'tool_type': 'code interpreter',
|
238 |
+
'thought': '首先,我们需要...',
|
239 |
+
'action': 'def find_numbers():\n # 正因数\n positive_factors = [1, 3, 7, 9, 21, 63]\n # 负因数\n negative_factors = [-1, -3, -7, -9, -21, -63]\n \n # 找到与正因数相乘的结果为63的数\n positive_numbers = [63 / factor for factor in positive_factors]\n # 找到与负因数相乘的结果为63的数\n negative_numbers = [-63 / factor for factor in negative_factors]\n \n # 计算两个数的乘积\n product = positive_numbers[0] * negative_numbers[0]\n \n return product\n\nresult = find_numbers()\nprint(result)',
|
240 |
+
'status': 1
|
241 |
+
})
|
242 |
+
executor_msg = executor(bot_msg)
|
243 |
+
print(executor_msg)
|
244 |
+
```
|
245 |
+
|
246 |
+
```
|
247 |
+
content='3969.0' sender='ActionExecutor' formatted=None extra_info=None type=None receiver=None stream_state=<AgentStatusCode.END: 0>
|
248 |
+
```
|
249 |
+
|
250 |
+
**For convenience, Lagent provides `InternLMActionProcessor` which is adapted to messages formatted by `ToolParser` as mentioned above.**
|
251 |
+
|
252 |
+
### Dual Interfaces
|
253 |
+
|
254 |
+
Lagent adopts dual interface design, where almost every component(LLMs, actions, action executors...) has the corresponding asynchronous variant by prefixing its identifier with 'Async'. It is recommended to use synchronous agents for debugging and asynchronous ones for large-scale inference to make the most of idle CPU and GPU resources.
|
255 |
+
|
256 |
+
However, make sure the internal consistency of agents, i.e. asynchronous agents should be equipped with asynchronous LLMs and asynchronous action executors that drive asynchronous tools.
|
257 |
+
|
258 |
+
```python
|
259 |
+
from lagent.llms import VllmModel, AsyncVllmModel, LMDeployPipeline, AsyncLMDeployPipeline
|
260 |
+
from lagent.actions import ActionExecutor, AsyncActionExecutor, WebBrowser, AsyncWebBrowser
|
261 |
+
from lagent.agents import Agent, AsyncAgent, AgentForInternLM, AsyncAgentForInternLM
|
262 |
+
```
|
263 |
+
|
264 |
+
______________________________________________________________________
|
265 |
+
|
266 |
+
## Practice
|
267 |
+
|
268 |
+
- **Try to implement `forward` instead of `__call__` of subclasses unless necessary.**
|
269 |
+
- **Always include the `session_id` argument explicitly, which is designed for isolation of memory, LLM requests and tool invocation(e.g. maintain multiple independent IPython environments) in concurrency.**
|
270 |
+
|
271 |
+
### Single Agent
|
272 |
+
|
273 |
+
Math agents that solve problems by programming
|
274 |
+
|
275 |
+
````python
|
276 |
+
from lagent.agents.aggregator import InternLMToolAggregator
|
277 |
+
|
278 |
+
class Coder(Agent):
|
279 |
+
def __init__(self, model_path, system_prompt, max_turn=3):
|
280 |
+
super().__init__()
|
281 |
+
llm = VllmModel(
|
282 |
+
path=model_path,
|
283 |
+
meta_template=INTERNLM2_META,
|
284 |
+
tp=1,
|
285 |
+
top_k=1,
|
286 |
+
temperature=1.0,
|
287 |
+
stop_words=['\n```\n', '<|im_end|>'],
|
288 |
+
max_new_tokens=1024,
|
289 |
+
)
|
290 |
+
self.agent = Agent(
|
291 |
+
llm,
|
292 |
+
system_prompt,
|
293 |
+
output_format=ToolParser(
|
294 |
+
tool_type='code interpreter', begin='```python\n', end='\n```\n'
|
295 |
+
),
|
296 |
+
# `InternLMToolAggregator` is adapted to `ToolParser` for aggregating
|
297 |
+
# messages with tool invocations and execution results
|
298 |
+
aggregator=InternLMToolAggregator(),
|
299 |
+
)
|
300 |
+
self.executor = ActionExecutor([IPythonInteractive()], hooks=[CodeProcessor()])
|
301 |
+
self.max_turn = max_turn
|
302 |
+
|
303 |
+
def forward(self, message: AgentMessage, session_id=0) -> AgentMessage:
|
304 |
+
for _ in range(self.max_turn):
|
305 |
+
message = self.agent(message, session_id=session_id)
|
306 |
+
if message.formatted['tool_type'] is None:
|
307 |
+
return message
|
308 |
+
message = self.executor(message, session_id=session_id)
|
309 |
+
return message
|
310 |
+
|
311 |
+
coder = Coder('Qwen/Qwen2-7B-Instruct', 'Solve the problem step by step with assistance of Python code')
|
312 |
+
query = AgentMessage(
|
313 |
+
sender='user',
|
314 |
+
content='Find the projection of $\\mathbf{a}$ onto $\\mathbf{b} = '
|
315 |
+
'\\begin{pmatrix} 1 \\\\ -3 \\end{pmatrix}$ if $\\mathbf{a} \\cdot \\mathbf{b} = 2.$'
|
316 |
+
)
|
317 |
+
answer = coder(query)
|
318 |
+
print(answer.content)
|
319 |
+
print('-' * 120)
|
320 |
+
for msg in coder.state_dict()['agent.memory']:
|
321 |
+
print('*' * 80)
|
322 |
+
print(f'{msg["sender"]}:\n\n{msg["content"]}')
|
323 |
+
````
|
324 |
+
|
325 |
+
### Multiple Agents
|
326 |
+
|
327 |
+
Asynchronous blogging agents that improve writing quality by self-refinement ([original AutoGen example](https://microsoft.github.io/autogen/0.2/docs/topics/prompting-and-reasoning/reflection/))
|
328 |
+
|
329 |
+
```python
|
330 |
+
import asyncio
|
331 |
+
import os
|
332 |
+
from lagent.llms import AsyncGPTAPI
|
333 |
+
from lagent.agents import AsyncAgent
|
334 |
+
os.environ['OPENAI_API_KEY'] = 'YOUR_API_KEY'
|
335 |
+
|
336 |
+
class PrefixedMessageHook(Hook):
|
337 |
+
def __init__(self, prefix: str, senders: list = None):
|
338 |
+
self.prefix = prefix
|
339 |
+
self.senders = senders or []
|
340 |
+
|
341 |
+
def before_agent(self, agent, messages, session_id):
|
342 |
+
for message in messages:
|
343 |
+
if message.sender in self.senders:
|
344 |
+
message.content = self.prefix + message.content
|
345 |
+
|
346 |
+
class AsyncBlogger(AsyncAgent):
|
347 |
+
def __init__(self, model_path, writer_prompt, critic_prompt, critic_prefix='', max_turn=3):
|
348 |
+
super().__init__()
|
349 |
+
llm = AsyncGPTAPI(model_type=model_path, retry=5, max_new_tokens=2048)
|
350 |
+
self.writer = AsyncAgent(llm, writer_prompt, name='writer')
|
351 |
+
self.critic = AsyncAgent(
|
352 |
+
llm, critic_prompt, name='critic', hooks=[PrefixedMessageHook(critic_prefix, ['writer'])]
|
353 |
+
)
|
354 |
+
self.max_turn = max_turn
|
355 |
+
|
356 |
+
async def forward(self, message: AgentMessage, session_id=0) -> AgentMessage:
|
357 |
+
for _ in range(self.max_turn):
|
358 |
+
message = await self.writer(message, session_id=session_id)
|
359 |
+
message = await self.critic(message, session_id=session_id)
|
360 |
+
return await self.writer(message, session_id=session_id)
|
361 |
+
|
362 |
+
blogger = AsyncBlogger(
|
363 |
+
'gpt-4o-2024-05-13',
|
364 |
+
writer_prompt="You are an writing assistant tasked to write engaging blogpost. You try to generate the best blogpost possible for the user's request. "
|
365 |
+
"If the user provides critique, then respond with a revised version of your previous attempts",
|
366 |
+
critic_prompt="Generate critique and recommendations on the writing. Provide detailed recommendations, including requests for length, depth, style, etc..",
|
367 |
+
critic_prefix='Reflect and provide critique on the following writing. \n\n',
|
368 |
+
)
|
369 |
+
user_prompt = (
|
370 |
+
"Write an engaging blogpost on the recent updates in {topic}. "
|
371 |
+
"The blogpost should be engaging and understandable for general audience. "
|
372 |
+
"Should have more than 3 paragraphes but no longer than 1000 words.")
|
373 |
+
bot_msgs = asyncio.get_event_loop().run_until_complete(
|
374 |
+
asyncio.gather(
|
375 |
+
*[
|
376 |
+
blogger(AgentMessage(sender='user', content=user_prompt.format(topic=topic)), session_id=i)
|
377 |
+
for i, topic in enumerate(['AI', 'Biotechnology', 'New Energy', 'Video Games', 'Pop Music'])
|
378 |
+
]
|
379 |
+
)
|
380 |
+
)
|
381 |
+
print(bot_msgs[0].content)
|
382 |
+
print('-' * 120)
|
383 |
+
for msg in blogger.state_dict(session_id=0)['writer.memory']:
|
384 |
+
print('*' * 80)
|
385 |
+
print(f'{msg["sender"]}:\n\n{msg["content"]}')
|
386 |
+
print('-' * 120)
|
387 |
+
for msg in blogger.state_dict(session_id=0)['critic.memory']:
|
388 |
+
print('*' * 80)
|
389 |
+
print(f'{msg["sender"]}:\n\n{msg["content"]}')
|
390 |
+
```
|
391 |
+
|
392 |
+
A multi-agent workflow that performs information retrieval, data collection and chart plotting ([original LangGraph example](https://vijaykumarkartha.medium.com/multiple-ai-agents-creating-multi-agent-workflows-using-langgraph-and-langchain-0587406ec4e6))
|
393 |
+
|
394 |
+
<div align="center">
|
395 |
+
<img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*ffzadZCKXJT7n4JaRVFvcQ.jpeg" width="850" />
|
396 |
+
</div>
|
397 |
+
|
398 |
+
````python
|
399 |
+
import json
|
400 |
+
from lagent.actions import IPythonInterpreter, WebBrowser, ActionExecutor
|
401 |
+
from lagent.agents.stream import get_plugin_prompt
|
402 |
+
from lagent.llms import GPTAPI
|
403 |
+
from lagent.hooks import InternLMActionProcessor
|
404 |
+
|
405 |
+
TOOL_TEMPLATE = (
|
406 |
+
"You are a helpful AI assistant, collaborating with other assistants. Use the provided tools to progress"
|
407 |
+
" towards answering the question. If you are unable to fully answer, that's OK, another assistant with"
|
408 |
+
" different tools will help where you left off. Execute what you can to make progress. If you or any of"
|
409 |
+
" the other assistants have the final answer or deliverable, prefix your response with {finish_pattern}"
|
410 |
+
" so the team knows to stop. You have access to the following tools:\n{tool_description}\nPlease provide"
|
411 |
+
" your thought process when you need to use a tool, followed by the call statement in this format:"
|
412 |
+
"\n{invocation_format}\\\\n**{system_prompt}**"
|
413 |
+
)
|
414 |
+
|
415 |
+
class DataVisualizer(Agent):
|
416 |
+
def __init__(self, model_path, research_prompt, chart_prompt, finish_pattern="Final Answer", max_turn=10):
|
417 |
+
super().__init__()
|
418 |
+
llm = GPTAPI(model_path, key='YOUR_OPENAI_API_KEY', retry=5, max_new_tokens=1024, stop_words=["```\n"])
|
419 |
+
interpreter, browser = IPythonInterpreter(), WebBrowser("BingSearch", api_key="YOUR_BING_API_KEY")
|
420 |
+
self.researcher = Agent(
|
421 |
+
llm,
|
422 |
+
TOOL_TEMPLATE.format(
|
423 |
+
finish_pattern=finish_pattern,
|
424 |
+
tool_description=get_plugin_prompt(browser),
|
425 |
+
invocation_format='```json\n{"name": {{tool name}}, "parameters": {{keyword arguments}}}\n```\n',
|
426 |
+
system_prompt=research_prompt,
|
427 |
+
),
|
428 |
+
output_format=ToolParser(
|
429 |
+
"browser",
|
430 |
+
begin="```json\n",
|
431 |
+
end="\n```\n",
|
432 |
+
validate=lambda x: json.loads(x.rstrip('`')),
|
433 |
+
),
|
434 |
+
aggregator=InternLMToolAggregator(),
|
435 |
+
name="researcher",
|
436 |
+
)
|
437 |
+
self.charter = Agent(
|
438 |
+
llm,
|
439 |
+
TOOL_TEMPLATE.format(
|
440 |
+
finish_pattern=finish_pattern,
|
441 |
+
tool_description=interpreter.name,
|
442 |
+
invocation_format='```python\n{{code}}\n```\n',
|
443 |
+
system_prompt=chart_prompt,
|
444 |
+
),
|
445 |
+
output_format=ToolParser(
|
446 |
+
"interpreter",
|
447 |
+
begin="```python\n",
|
448 |
+
end="\n```\n",
|
449 |
+
validate=lambda x: x.rstrip('`'),
|
450 |
+
),
|
451 |
+
aggregator=InternLMToolAggregator(),
|
452 |
+
name="charter",
|
453 |
+
)
|
454 |
+
self.executor = ActionExecutor([interpreter, browser], hooks=[InternLMActionProcessor()])
|
455 |
+
self.finish_pattern = finish_pattern
|
456 |
+
self.max_turn = max_turn
|
457 |
+
|
458 |
+
def forward(self, message, session_id=0):
|
459 |
+
for _ in range(self.max_turn):
|
460 |
+
message = self.researcher(message, session_id=session_id, stop_words=["```\n", "```python"]) # override llm stop words
|
461 |
+
while message.formatted["tool_type"]:
|
462 |
+
message = self.executor(message, session_id=session_id)
|
463 |
+
message = self.researcher(message, session_id=session_id, stop_words=["```\n", "```python"])
|
464 |
+
if self.finish_pattern in message.content:
|
465 |
+
return message
|
466 |
+
message = self.charter(message)
|
467 |
+
while message.formatted["tool_type"]:
|
468 |
+
message = self.executor(message, session_id=session_id)
|
469 |
+
message = self.charter(message, session_id=session_id)
|
470 |
+
if self.finish_pattern in message.content:
|
471 |
+
return message
|
472 |
+
return message
|
473 |
+
|
474 |
+
visualizer = DataVisualizer(
|
475 |
+
"gpt-4o-2024-05-13",
|
476 |
+
research_prompt="You should provide accurate data for the chart generator to use.",
|
477 |
+
chart_prompt="Any charts you display will be visible by the user.",
|
478 |
+
)
|
479 |
+
user_msg = AgentMessage(
|
480 |
+
sender='user',
|
481 |
+
content="Fetch the China's GDP over the past 5 years, then draw a line graph of it. Once you code it up, finish.")
|
482 |
+
bot_msg = visualizer(user_msg)
|
483 |
+
print(bot_msg.content)
|
484 |
+
json.dump(visualizer.state_dict(), open('visualizer.json', 'w'), ensure_ascii=False, indent=4)
|
485 |
+
````
|
docs/en/index.rst
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Welcome to Lagent's documentation!
|
2 |
+
=======================================
|
3 |
+
|
4 |
+
You can switch between English and Chinese in the lower-left corner of the layout.
|
5 |
+
|
6 |
+
.. toctree::
|
7 |
+
:maxdepth: 2
|
8 |
+
:caption: Get Started
|
9 |
+
|
10 |
+
get_started/install.md
|
11 |
+
get_started/quickstart.md
|
12 |
+
|
13 |
+
.. toctree::
|
14 |
+
:maxdepth: 2
|
15 |
+
:caption: Tutorials
|
16 |
+
|
17 |
+
tutorials/action.md
|
18 |
+
|
19 |
+
.. toctree::
|
20 |
+
:caption: Switch Language
|
21 |
+
|
22 |
+
switch_language.md
|
23 |
+
|
24 |
+
.. toctree::
|
25 |
+
:maxdepth: 1
|
26 |
+
:caption: API Reference
|
27 |
+
|
28 |
+
autoapi/lagent/actions/index
|
29 |
+
autoapi/lagent/agents/index
|
30 |
+
autoapi/lagent/llms/index
|
31 |
+
autoapi/lagent/utils/index
|
32 |
+
autoapi/lagent/schema/index
|
33 |
+
autoapi/lagent/version/index
|
34 |
+
|
35 |
+
|
36 |
+
Indices and tables
|
37 |
+
==================
|
38 |
+
|
39 |
+
* :ref:`genindex`
|
40 |
+
* :ref:`search`
|
docs/en/make.bat
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@ECHO OFF
|
2 |
+
|
3 |
+
pushd %~dp0
|
4 |
+
|
5 |
+
REM Command file for Sphinx documentation
|
6 |
+
|
7 |
+
if "%SPHINXBUILD%" == "" (
|
8 |
+
set SPHINXBUILD=sphinx-build
|
9 |
+
)
|
10 |
+
set SOURCEDIR=.
|
11 |
+
set BUILDDIR=_build
|
12 |
+
|
13 |
+
if "%1" == "" goto help
|
14 |
+
|
15 |
+
%SPHINXBUILD% >NUL 2>NUL
|
16 |
+
if errorlevel 9009 (
|
17 |
+
echo.
|
18 |
+
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
19 |
+
echo.installed, then set the SPHINXBUILD environment variable to point
|
20 |
+
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
21 |
+
echo.may add the Sphinx directory to PATH.
|
22 |
+
echo.
|
23 |
+
echo.If you don't have Sphinx installed, grab it from
|
24 |
+
echo.http://sphinx-doc.org/
|
25 |
+
exit /b 1
|
26 |
+
)
|
27 |
+
|
28 |
+
|
29 |
+
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
30 |
+
goto end
|
31 |
+
|
32 |
+
:help
|
33 |
+
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
34 |
+
|
35 |
+
:end
|
36 |
+
popd
|
docs/en/requirements.txt
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
recommonmark
|
2 |
+
sphinx
|
3 |
+
sphinx_markdown_tables
|
4 |
+
sphinx_rtd_theme
|
docs/en/switch_language.md
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
## <a href='https://lagent.readthedocs.io/en/latest/'>English</a>
|
2 |
+
|
3 |
+
## <a href='https://lagent.readthedocs.io/zh-cn/latest/'>简体中文</a>
|
docs/en/tutorials/action.md
ADDED
@@ -0,0 +1,400 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Action
|
2 |
+
|
3 |
+
Actions, also called **tools**, provide a suite of functions LLM-driven agents can use to interact with the real world and perform complex tasks.
|
4 |
+
|
5 |
+
## Basic Concepts
|
6 |
+
|
7 |
+
### Tool & Toolkit
|
8 |
+
|
9 |
+
There are two categories of tools:
|
10 |
+
|
11 |
+
- tool: provide only one API to call.
|
12 |
+
- toolkit: implement multiple APIs that undertake different sub-tasks.
|
13 |
+
|
14 |
+
### Tool Description
|
15 |
+
|
16 |
+
In Lagent, the tool description is a dictionary containing the action's core information of usage, observed by LLMs for decision-making.
|
17 |
+
|
18 |
+
For simple tools, the description can be created as follows
|
19 |
+
|
20 |
+
```python
|
21 |
+
TOOL_DESCRIPTION = {
|
22 |
+
'name': 'bold', # name of the tool
|
23 |
+
'description': 'a function used to make text bold', # introduce the tool's function
|
24 |
+
'parameters': [ # a list of parameters the tool take.
|
25 |
+
{
|
26 |
+
'name': 'text', 'type': 'STRING', 'description': 'input content'
|
27 |
+
}
|
28 |
+
],
|
29 |
+
'required': ['text'], # specify names of parameters required
|
30 |
+
}
|
31 |
+
```
|
32 |
+
|
33 |
+
In some situations there may be optional `return_data`, `parameter_description` keys describing the returns and argument passing format respectively.
|
34 |
+
|
35 |
+
```{attention}
|
36 |
+
`parameter_description` is usually inserted into the tool description automatically by the action's parser. It will be introduced in [Interface Design](#interface-design) .
|
37 |
+
```
|
38 |
+
|
39 |
+
For toolkits, the description is very similar but nest submethods
|
40 |
+
|
41 |
+
```python
|
42 |
+
TOOL_DESCRIPTION = {
|
43 |
+
'name': 'PhraseEmphasis', # name of the toolkit
|
44 |
+
'description': 'a toolkit which provides different styles of text emphasis', # introduce the tool's function
|
45 |
+
'api_list': [
|
46 |
+
{
|
47 |
+
'name': 'bold',
|
48 |
+
'description': 'make text bold',
|
49 |
+
'parameters': [
|
50 |
+
{
|
51 |
+
'name': 'text', 'type': 'STRING', 'description': 'input content'
|
52 |
+
}
|
53 |
+
],
|
54 |
+
'required': ['text']
|
55 |
+
},
|
56 |
+
{
|
57 |
+
'name': 'italic',
|
58 |
+
'description': 'make text italic',
|
59 |
+
'parameters': [
|
60 |
+
{
|
61 |
+
'name': 'text', 'type': 'STRING', 'description': 'input content'
|
62 |
+
}
|
63 |
+
],
|
64 |
+
'required': ['text']
|
65 |
+
}
|
66 |
+
]
|
67 |
+
}
|
68 |
+
```
|
69 |
+
|
70 |
+
## Make Functions Tools
|
71 |
+
|
72 |
+
It's not necessary to prepare an extra description for a defined function. In Lagent we provide a decorator `tool_api` which can conveniently turn a function into a tool by automatically parsing the function's typehints and dosctrings to generate the description dictionary and binding it to an attribute `api_description`.
|
73 |
+
|
74 |
+
```python
|
75 |
+
from lagent import tool_api
|
76 |
+
|
77 |
+
@tool_api
|
78 |
+
def bold(text: str) -> str:
|
79 |
+
"""make text bold
|
80 |
+
|
81 |
+
Args:
|
82 |
+
text (str): input text
|
83 |
+
|
84 |
+
Returns:
|
85 |
+
str: bold text
|
86 |
+
"""
|
87 |
+
return '**' + text + '**'
|
88 |
+
|
89 |
+
|
90 |
+
bold.api_description
|
91 |
+
```
|
92 |
+
|
93 |
+
```python
|
94 |
+
{'name': 'bold',
|
95 |
+
'description': 'make text bold',
|
96 |
+
'parameters': [{'name': 'text',
|
97 |
+
'type': 'STRING',
|
98 |
+
'description': 'input text'}],
|
99 |
+
'required': ['text']}
|
100 |
+
```
|
101 |
+
|
102 |
+
Once `returns_named_value` is enabled you should declare the name of the return data, which will be processed to form a new field `return_data`:
|
103 |
+
|
104 |
+
```python
|
105 |
+
@tool_api(returns_named_value=True)
|
106 |
+
def bold(text: str) -> str:
|
107 |
+
"""make text bold
|
108 |
+
|
109 |
+
Args:
|
110 |
+
text (str): input text
|
111 |
+
|
112 |
+
Returns:
|
113 |
+
bold_text (str): bold text
|
114 |
+
"""
|
115 |
+
return '**' + text + '**'
|
116 |
+
|
117 |
+
bold.api_description
|
118 |
+
```
|
119 |
+
|
120 |
+
```python
|
121 |
+
{'name': 'bold',
|
122 |
+
'description': 'make text bold',
|
123 |
+
'parameters': [{'name': 'text',
|
124 |
+
'type': 'STRING',
|
125 |
+
'description': 'input text'}],
|
126 |
+
'required': ['text'],
|
127 |
+
'return_data': [{'name': 'bold_text',
|
128 |
+
'description': 'bold text',
|
129 |
+
'type': 'STRING'}]}
|
130 |
+
```
|
131 |
+
|
132 |
+
Sometimes the tool may return a `dict` or `tuple`, and you want to elaborate each member in `return_data` rather than take them as a whole. Set `explode_return=True` and list them in the return part of docstrings.
|
133 |
+
|
134 |
+
```python
|
135 |
+
@tool_api(explode_return=True)
|
136 |
+
def list_args(a: str, b: int, c: float = 0.0) -> dict:
|
137 |
+
"""Return arguments in dict format
|
138 |
+
|
139 |
+
Args:
|
140 |
+
a (str): a
|
141 |
+
b (int): b
|
142 |
+
c (float): c
|
143 |
+
|
144 |
+
Returns:
|
145 |
+
dict: input arguments
|
146 |
+
- a (str): a
|
147 |
+
- b (int): b
|
148 |
+
- c: c
|
149 |
+
"""
|
150 |
+
return {'a': a, 'b': b, 'c': c}
|
151 |
+
```
|
152 |
+
|
153 |
+
```python
|
154 |
+
{'name': 'list_args',
|
155 |
+
'description': 'Return arguments in dict format',
|
156 |
+
'parameters': [{'name': 'a', 'type': 'STRING', 'description': 'a'},
|
157 |
+
{'name': 'b', 'type': 'NUMBER', 'description': 'b'},
|
158 |
+
{'name': 'c', 'type': 'FLOAT', 'description': 'c'}],
|
159 |
+
'required': ['a', 'b'],
|
160 |
+
'return_data': [{'name': 'a', 'description': 'a', 'type': 'STRING'},
|
161 |
+
{'name': 'b', 'description': 'b', 'type': 'NUMBER'},
|
162 |
+
{'name': 'c', 'description': 'c'}]}
|
163 |
+
```
|
164 |
+
|
165 |
+
```{warning}
|
166 |
+
Only Google style Python docstrings is currently supported.
|
167 |
+
```
|
168 |
+
|
169 |
+
## Interface Design
|
170 |
+
|
171 |
+
`BaseAction(description=None, parser=JsonParser, enable=True)` is the base class all actions should inherit from. It takes three initialization arguments
|
172 |
+
|
173 |
+
- **description**: a tool description dictionary, used set instance attribute `description`. Mostly you don't need explicitly pass this argument since the meta class of `BaseAction` will search methods decorated by `tool_api` and assemble their `api_description` as a class attribute `__tool_description__`, and if the initial `description` is left null, then `__tool_description__` will be copied as `description`.
|
174 |
+
|
175 |
+
- **parser**: `BaseParser` class. It will instantialize a parser used to validate the arguments of APIs in `description`.
|
176 |
+
|
177 |
+
For example, `JsonParser` requires arguments passed in the format of JSON or `dict`. To make LLMs aware of this, It inserts a field `parameter_description` into the `description`.
|
178 |
+
|
179 |
+
```python
|
180 |
+
from lagent import BaseAction
|
181 |
+
|
182 |
+
action = BaseAction(
|
183 |
+
{
|
184 |
+
'name': 'bold',
|
185 |
+
'description': 'a function used to make text bold',
|
186 |
+
'parameters': [
|
187 |
+
{
|
188 |
+
'name': 'text', 'type': 'STRING', 'description': 'input content'
|
189 |
+
}
|
190 |
+
],
|
191 |
+
'required': ['text']
|
192 |
+
}
|
193 |
+
)
|
194 |
+
action.description
|
195 |
+
```
|
196 |
+
|
197 |
+
```python
|
198 |
+
{'name': 'bold',
|
199 |
+
'description': 'a function used to make text bold',
|
200 |
+
'parameters': [{'name': 'text',
|
201 |
+
'type': 'STRING',
|
202 |
+
'description': 'input content'}],
|
203 |
+
'required': ['text'],
|
204 |
+
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'}
|
205 |
+
```
|
206 |
+
|
207 |
+
- **enable**: specify whether the tool is available.
|
208 |
+
|
209 |
+
### Custom Action
|
210 |
+
|
211 |
+
A simple tool must have its `run` method implemented, while APIs of toolkits should avoid naming conflicts with this reserved word.
|
212 |
+
|
213 |
+
```{tip}
|
214 |
+
`run` is allowed not to be decorated by `tool_api` for simple tools unless you want to hint the return data.
|
215 |
+
```
|
216 |
+
|
217 |
+
```python
|
218 |
+
class Bold(BaseAction):
|
219 |
+
|
220 |
+
def run(self, text: str):
|
221 |
+
"""make text bold
|
222 |
+
|
223 |
+
Args:
|
224 |
+
text (str): input text
|
225 |
+
|
226 |
+
Returns:
|
227 |
+
str: bold text
|
228 |
+
"""
|
229 |
+
return '**' + text + '**'
|
230 |
+
|
231 |
+
class PhraseEmphasis(BaseAction):
|
232 |
+
"""a toolkit which provides different styles of text emphasis"""
|
233 |
+
|
234 |
+
@tool_api
|
235 |
+
def bold(self, text):
|
236 |
+
"""make text bold
|
237 |
+
|
238 |
+
Args:
|
239 |
+
text (str): input text
|
240 |
+
|
241 |
+
Returns:
|
242 |
+
str: bold text
|
243 |
+
"""
|
244 |
+
return '**' + text + '**'
|
245 |
+
|
246 |
+
@tool_api
|
247 |
+
def italic(self, text):
|
248 |
+
"""make text italic
|
249 |
+
|
250 |
+
Args:
|
251 |
+
text (str): input text
|
252 |
+
|
253 |
+
Returns:
|
254 |
+
str: italic text
|
255 |
+
"""
|
256 |
+
return '*' + text + '*'
|
257 |
+
|
258 |
+
# Inspect the default description
|
259 |
+
# Bold.__tool_description__, PhraseEmphasis.__tool_description__
|
260 |
+
```
|
261 |
+
|
262 |
+
### Auto-registration
|
263 |
+
|
264 |
+
Any subclass of `BaseAction` will be registered automatically. You can use `list_tools()` and `get_tool()` to view all tools and initialize by name.
|
265 |
+
|
266 |
+
```python
|
267 |
+
from lagent import list_tools, get_tool
|
268 |
+
|
269 |
+
list_tools()
|
270 |
+
```
|
271 |
+
|
272 |
+
```python
|
273 |
+
['BaseAction',
|
274 |
+
'InvalidAction',
|
275 |
+
'NoAction',
|
276 |
+
'FinishAction',
|
277 |
+
'ArxivSearch',
|
278 |
+
'BINGMap',
|
279 |
+
'GoogleScholar',
|
280 |
+
'GoogleSearch',
|
281 |
+
'IPythonInterpreter',
|
282 |
+
'PPT',
|
283 |
+
'PythonInterpreter',
|
284 |
+
'Bold',
|
285 |
+
'PhraseEmphasis']
|
286 |
+
```
|
287 |
+
|
288 |
+
Create a `PhraseEmphasis` object
|
289 |
+
|
290 |
+
```python
|
291 |
+
action = get_tool('PhraseEmphasis')
|
292 |
+
action.description
|
293 |
+
```
|
294 |
+
|
295 |
+
```python
|
296 |
+
{'name': 'PhraseEmphasis',
|
297 |
+
'description': 'a toolkit which provides different styles of text emphasis',
|
298 |
+
'api_list': [{'name': 'bold',
|
299 |
+
'description': 'make text bold',
|
300 |
+
'parameters': [{'name': 'text',
|
301 |
+
'type': 'STRING',
|
302 |
+
'description': 'input text'}],
|
303 |
+
'required': ['text'],
|
304 |
+
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'},
|
305 |
+
{'name': 'italic',
|
306 |
+
'description': 'make text italic',
|
307 |
+
'parameters': [{'name': 'text',
|
308 |
+
'type': 'STRING',
|
309 |
+
'description': 'input text'}],
|
310 |
+
'required': ['text'],
|
311 |
+
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'}]}
|
312 |
+
```
|
313 |
+
|
314 |
+
## Tool Calling
|
315 |
+
|
316 |
+
### Run a Tool
|
317 |
+
|
318 |
+
`__call__` method of `Action` takes two arguments
|
319 |
+
|
320 |
+
- `inputs`: It depends on the action's parser. Often a string in specific formats generated by LLMs.
|
321 |
+
- `JsonParser`: Allow passing arguments in the format of JSON string or Python `dict`.
|
322 |
+
- `TupleParser`: Allow passing arguments in the format of tuple string format or Python `tuple`.
|
323 |
+
- `name`: Which API to call. Default is `run`.
|
324 |
+
|
325 |
+
It returns an `ActionReturn` object which encapsulates calling details
|
326 |
+
|
327 |
+
- `args`: Dictionary of action inputs.
|
328 |
+
- `type`: Action name.
|
329 |
+
- `result`: List of dicts. Each contains two keys: 'type' and 'content'. when errors occur, it is `None`.
|
330 |
+
- `errmsg`: Error message. Default is `None`.
|
331 |
+
|
332 |
+
Below is an example
|
333 |
+
|
334 |
+
```python
|
335 |
+
from lagent import IPythonInterpreter, TupleParser
|
336 |
+
|
337 |
+
action1 = IPythonInterpreter()
|
338 |
+
ret = action1('{"command": "import math;math.sqrt(100)"}')
|
339 |
+
print(ret.result)
|
340 |
+
ret = action1({'command': 'import math;math.sqrt(100)'})
|
341 |
+
print(ret.result)
|
342 |
+
|
343 |
+
action2 = IPythonInterpreter(parser=TupleParser)
|
344 |
+
ret = action2('("import math;math.sqrt(100)", )')
|
345 |
+
print(ret.result)
|
346 |
+
ret = action2(('import math;math.sqrt(100)',))
|
347 |
+
print(ret.result)
|
348 |
+
```
|
349 |
+
|
350 |
+
```python
|
351 |
+
[{'type': 'text', 'content': '10.0'}]
|
352 |
+
[{'type': 'text', 'content': '10.0'}]
|
353 |
+
[{'type': 'text', 'content': '10.0'}]
|
354 |
+
[{'type': 'text', 'content': '10.0'}]
|
355 |
+
```
|
356 |
+
|
357 |
+
### Dynamic Invocation
|
358 |
+
|
359 |
+
Lagent provides an `ActionExecutor` to manage multiple tools. It will flatten `api_list` of toolkits and rename each `{tool_name}.{api_name}`.
|
360 |
+
|
361 |
+
```python
|
362 |
+
from lagent import ActionExecutor, ArxivSearch, IPythonInterpreter
|
363 |
+
|
364 |
+
executor = ActionExecutor(actions=[ArxivSearch(), IPythonInterpreter()])
|
365 |
+
executor.get_actions_info() # This information is fed to LLMs as the tool meta prompt
|
366 |
+
```
|
367 |
+
|
368 |
+
```python
|
369 |
+
[{'name': 'ArxivSearch.get_arxiv_article_information',
|
370 |
+
'description': 'Run Arxiv search and get the article meta information.',
|
371 |
+
'parameters': [{'name': 'query',
|
372 |
+
'type': 'STRING',
|
373 |
+
'description': 'the content of search query'}],
|
374 |
+
'required': ['query'],
|
375 |
+
'return_data': [{'name': 'content',
|
376 |
+
'description': 'a list of 3 arxiv search papers',
|
377 |
+
'type': 'STRING'}],
|
378 |
+
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'},
|
379 |
+
{'name': 'IPythonInterpreter',
|
380 |
+
'description': "When you send a message containing Python code to python, it will be executed in a stateful Jupyter notebook environment. python will respond with the output of the execution or time out after 60.0 seconds. The drive at '/mnt/data' can be used to save and persist user files. Internet access for this session is disabled. Do not make external web requests or API calls as they will fail.",
|
381 |
+
'parameters': [{'name': 'command',
|
382 |
+
'type': 'STRING',
|
383 |
+
'description': 'Python code'},
|
384 |
+
{'name': 'timeout',
|
385 |
+
'type': 'NUMBER',
|
386 |
+
'description': 'Upper bound of waiting time for Python script execution.'}],
|
387 |
+
'required': ['command'],
|
388 |
+
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'}]
|
389 |
+
```
|
390 |
+
|
391 |
+
Trigger an action through the executor
|
392 |
+
|
393 |
+
```python
|
394 |
+
ret = executor('IPythonInterpreter', '{"command": "import math;math.sqrt(100)"}')
|
395 |
+
ret.result
|
396 |
+
```
|
397 |
+
|
398 |
+
```python
|
399 |
+
[{'type': 'text', 'content': '10.0'}]
|
400 |
+
```
|
docs/imgs/lagent_icon.png
ADDED
docs/imgs/lagent_logo.png
ADDED
docs/zh_cn/.readthedocs.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
version: 2
|
2 |
+
|
3 |
+
formats: all
|
4 |
+
|
5 |
+
build:
|
6 |
+
os: ubuntu-22.04
|
7 |
+
tools:
|
8 |
+
python: "3.10"
|
9 |
+
|
10 |
+
python:
|
11 |
+
install:
|
12 |
+
- requirements: requirements/docs.txt
|
13 |
+
|
14 |
+
sphinx:
|
15 |
+
configuration: docs/zh_cn/conf.py
|
docs/zh_cn/Makefile
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Minimal makefile for Sphinx documentation
|
2 |
+
#
|
3 |
+
|
4 |
+
# You can set these variables from the command line, and also
|
5 |
+
# from the environment for the first two.
|
6 |
+
SPHINXOPTS ?=
|
7 |
+
SPHINXBUILD ?= sphinx-build
|
8 |
+
SOURCEDIR = .
|
9 |
+
BUILDDIR = _build
|
10 |
+
|
11 |
+
# Put it first so that "make" without argument is like "make help".
|
12 |
+
help:
|
13 |
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
14 |
+
|
15 |
+
.PHONY: help Makefile
|
16 |
+
|
17 |
+
# Catch-all target: route all unknown targets to Sphinx using the new
|
18 |
+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
19 |
+
%: Makefile
|
20 |
+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
docs/zh_cn/_static/css/readthedocs.css
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.header-logo {
|
2 |
+
background-image: url("../images/lagent_icon.png");
|
3 |
+
background-size: 40px 40px;
|
4 |
+
height: 40px;
|
5 |
+
width: 40px;
|
6 |
+
}
|
docs/zh_cn/_static/images/lagent_icon.png
ADDED
docs/zh_cn/_static/images/robot.png
ADDED
docs/zh_cn/_static/js/collapsed.js
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
var collapsedSections = ['API 文档']
|
docs/zh_cn/_static/js/table.js
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$(document).ready(function () {
|
2 |
+
table = $('.model-summary').DataTable({
|
3 |
+
"stateSave": false,
|
4 |
+
"lengthChange": false,
|
5 |
+
"pageLength": 10,
|
6 |
+
"order": [],
|
7 |
+
"scrollX": true,
|
8 |
+
"columnDefs": [
|
9 |
+
{ "type": "summary", targets: '_all' },
|
10 |
+
]
|
11 |
+
});
|
12 |
+
// Override the default sorting for the summary columns, which
|
13 |
+
// never takes the "-" character into account.
|
14 |
+
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
15 |
+
"summary-asc": function (str1, str2) {
|
16 |
+
if (str1 == "<p>-</p>")
|
17 |
+
return 1;
|
18 |
+
if (str2 == "<p>-</p>")
|
19 |
+
return -1;
|
20 |
+
return ((str1 < str2) ? -1 : ((str1 > str2) ? 1 : 0));
|
21 |
+
},
|
22 |
+
|
23 |
+
"summary-desc": function (str1, str2) {
|
24 |
+
if (str1 == "<p>-</p>")
|
25 |
+
return 1;
|
26 |
+
if (str2 == "<p>-</p>")
|
27 |
+
return -1;
|
28 |
+
return ((str1 < str2) ? 1 : ((str1 > str2) ? -1 : 0));
|
29 |
+
}
|
30 |
+
});
|
31 |
+
})
|
docs/zh_cn/_templates/autoapi/index.rst
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
API Reference
|
2 |
+
=============
|
3 |
+
|
4 |
+
This page contains auto-generated API reference documentation.
|
5 |
+
|
6 |
+
.. toctree::
|
7 |
+
:titlesonly:
|
8 |
+
:maxdepth: 3
|
9 |
+
|
10 |
+
{% for page in pages %}
|
11 |
+
{% if page.top_level_object and page.display %}
|
12 |
+
{{ page.include_path }}
|
13 |
+
{% endif %}
|
14 |
+
{% endfor %}
|
docs/zh_cn/_templates/autoapi/python/module.rst
ADDED
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{% if not obj.display %}
|
2 |
+
:orphan:
|
3 |
+
|
4 |
+
{% endif %}
|
5 |
+
:py:mod:`{{ obj.name if obj.name.count(".") <= 1 else obj.short_name }}`
|
6 |
+
=========={{ "=" * (obj.name|length if obj.name.count(".") <= 1 else obj.short_name|length) }}
|
7 |
+
|
8 |
+
.. py:module:: {{ obj.name }}
|
9 |
+
|
10 |
+
{% if obj.docstring %}
|
11 |
+
.. autoapi-nested-parse::
|
12 |
+
|
13 |
+
{{ obj.docstring|indent(3) }}
|
14 |
+
|
15 |
+
{% endif %}
|
16 |
+
|
17 |
+
{% block subpackages %}
|
18 |
+
{% set visible_subpackages = obj.subpackages|selectattr("display")|list %}
|
19 |
+
{% if visible_subpackages %}
|
20 |
+
Subpackages
|
21 |
+
-----------
|
22 |
+
.. toctree::
|
23 |
+
:titlesonly:
|
24 |
+
:maxdepth: 3
|
25 |
+
|
26 |
+
{% for subpackage in visible_subpackages %}
|
27 |
+
{{ subpackage.short_name }}/index.rst
|
28 |
+
{% endfor %}
|
29 |
+
|
30 |
+
|
31 |
+
{% endif %}
|
32 |
+
{% endblock %}
|
33 |
+
{% block submodules %}
|
34 |
+
{% set visible_submodules = obj.submodules|selectattr("display")|list %}
|
35 |
+
{% if visible_submodules %}
|
36 |
+
Submodules
|
37 |
+
----------
|
38 |
+
.. toctree::
|
39 |
+
:titlesonly:
|
40 |
+
:maxdepth: 1
|
41 |
+
|
42 |
+
{% for submodule in visible_submodules %}
|
43 |
+
{{ submodule.short_name }}/index.rst
|
44 |
+
{% endfor %}
|
45 |
+
|
46 |
+
|
47 |
+
{% endif %}
|
48 |
+
{% endblock %}
|
49 |
+
{% block content %}
|
50 |
+
{% if obj.type is equalto("package") %}
|
51 |
+
{% set visible_children = obj.children|selectattr("display")|list %}
|
52 |
+
{% else %}
|
53 |
+
{% set visible_children = obj.children|selectattr("display")|rejectattr("imported")|list %}
|
54 |
+
{% endif %}
|
55 |
+
{% if visible_children %}
|
56 |
+
{{ obj.type|title }} Contents
|
57 |
+
{{ "-" * obj.type|length }}---------
|
58 |
+
|
59 |
+
{% set visible_classes = visible_children|selectattr("type", "equalto", "class")|list %}
|
60 |
+
{% set visible_functions = visible_children|selectattr("type", "equalto", "function")|list %}
|
61 |
+
{% set visible_attributes = visible_children|selectattr("type", "equalto", "data")|list %}
|
62 |
+
{% if "show-module-summary" in autoapi_options and (visible_classes or visible_functions) %}
|
63 |
+
{% block classes scoped %}
|
64 |
+
{% if visible_classes %}
|
65 |
+
Classes
|
66 |
+
~~~~~~~
|
67 |
+
|
68 |
+
.. autoapisummary::
|
69 |
+
|
70 |
+
{% for klass in visible_classes %}
|
71 |
+
{{ klass.id }}
|
72 |
+
{% endfor %}
|
73 |
+
|
74 |
+
|
75 |
+
{% endif %}
|
76 |
+
{% endblock %}
|
77 |
+
|
78 |
+
{% block functions scoped %}
|
79 |
+
{% if visible_functions %}
|
80 |
+
Functions
|
81 |
+
~~~~~~~~~
|
82 |
+
|
83 |
+
.. autoapisummary::
|
84 |
+
|
85 |
+
{% for function in visible_functions %}
|
86 |
+
{{ function.id }}
|
87 |
+
{% endfor %}
|
88 |
+
|
89 |
+
|
90 |
+
{% endif %}
|
91 |
+
{% endblock %}
|
92 |
+
|
93 |
+
{% block attributes scoped %}
|
94 |
+
{% if visible_attributes %}
|
95 |
+
Attributes
|
96 |
+
~~~~~~~~~~
|
97 |
+
|
98 |
+
.. autoapisummary::
|
99 |
+
|
100 |
+
{% for attribute in visible_attributes %}
|
101 |
+
{{ attribute.id }}
|
102 |
+
{% endfor %}
|
103 |
+
|
104 |
+
|
105 |
+
{% endif %}
|
106 |
+
{% endblock %}
|
107 |
+
{% endif %}
|
108 |
+
{% for obj_item in visible_children %}
|
109 |
+
{{ obj_item.render()|indent(0) }}
|
110 |
+
{% endfor %}
|
111 |
+
{% endif %}
|
112 |
+
{% endblock %}
|
docs/zh_cn/_templates/classtemplate.rst
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.. role:: hidden
|
2 |
+
:class: hidden-section
|
3 |
+
.. currentmodule:: {{ module }}
|
4 |
+
|
5 |
+
|
6 |
+
{{ name | underline}}
|
7 |
+
|
8 |
+
.. autoclass:: {{ name }}
|
9 |
+
:members:
|
10 |
+
|
11 |
+
|
12 |
+
..
|
13 |
+
autogenerated from source/_templates/classtemplate.rst
|
14 |
+
note it does not have :inherited-members:
|
docs/zh_cn/conf.py
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Configuration file for the Sphinx documentation builder.
|
2 |
+
#
|
3 |
+
# This file only contains a selection of the most common options. For a full
|
4 |
+
# list see the documentation:
|
5 |
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
6 |
+
|
7 |
+
# -- Path setup --------------------------------------------------------------
|
8 |
+
|
9 |
+
# If extensions (or modules to document with autodoc) are in another directory,
|
10 |
+
# add these directories to sys.path here. If the directory is relative to the
|
11 |
+
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
12 |
+
|
13 |
+
import os
|
14 |
+
import re
|
15 |
+
import sys
|
16 |
+
|
17 |
+
sys.path.insert(0, os.path.abspath('../..'))
|
18 |
+
|
19 |
+
# -- Project information -----------------------------------------------------
|
20 |
+
project = 'Lagent'
|
21 |
+
copyright = '2020-2030, InternLM'
|
22 |
+
author = 'InternLM'
|
23 |
+
language = 'zh_CN'
|
24 |
+
|
25 |
+
# The full version, including alpha/beta/rc tags
|
26 |
+
version_file = '../../lagent/version.py'
|
27 |
+
with open(version_file) as f:
|
28 |
+
exec(compile(f.read(), version_file, 'exec'))
|
29 |
+
__version__ = locals()['__version__']
|
30 |
+
release = __version__
|
31 |
+
|
32 |
+
# -- General configuration ---------------------------------------------------
|
33 |
+
|
34 |
+
# Add any Sphinx extension module names here, as strings. They can be
|
35 |
+
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
36 |
+
# ones.
|
37 |
+
extensions = [
|
38 |
+
'sphinx_rtd_theme',
|
39 |
+
'myst_nb',
|
40 |
+
'autoapi.extension',
|
41 |
+
'sphinx_markdown_tables',
|
42 |
+
'sphinx.ext.autodoc',
|
43 |
+
'sphinx.ext.napoleon',
|
44 |
+
'sphinx.ext.viewcode',
|
45 |
+
]
|
46 |
+
|
47 |
+
nb_output_stderr = 'remove-warn'
|
48 |
+
autodoc_typehints = 'description'
|
49 |
+
|
50 |
+
# sphinx-autoapi configuration
|
51 |
+
autoapi_dirs = ['../../lagent']
|
52 |
+
autoapi_options = [
|
53 |
+
'members',
|
54 |
+
'undoc-members',
|
55 |
+
'show-inheritance',
|
56 |
+
'show-module-summary',
|
57 |
+
]
|
58 |
+
autoapi_ignore = ['*migrations*', '*command.py', '*cli.py']
|
59 |
+
autoapi_template_dir = '_templates/autoapi'
|
60 |
+
autoapi_add_toctree_entry = False
|
61 |
+
|
62 |
+
# Add any paths that contain templates here, relative to this directory.
|
63 |
+
templates_path = ['_templates']
|
64 |
+
|
65 |
+
# List of patterns, relative to source directory, that match files and
|
66 |
+
# directories to ignore when looking for source files.
|
67 |
+
# This pattern also affects html_static_path and html_extra_path.
|
68 |
+
exclude_patterns = []
|
69 |
+
|
70 |
+
# -- Options for HTML output -------------------------------------------------
|
71 |
+
|
72 |
+
# The theme to use for HTML and HTML Help pages. See the documentation for
|
73 |
+
# a list of builtin themes.
|
74 |
+
#
|
75 |
+
html_theme = 'sphinx_rtd_theme'
|
76 |
+
html_theme_options = {
|
77 |
+
'navigation_depth': 3,
|
78 |
+
'titles_only': False,
|
79 |
+
'style_nav_header_background': '#4fabab',
|
80 |
+
}
|
81 |
+
html_context = {
|
82 |
+
'display_github': True,
|
83 |
+
'github_host': 'github.com',
|
84 |
+
'github_user': 'InternLM',
|
85 |
+
'github_repo': 'lagent',
|
86 |
+
'github_version': 'main',
|
87 |
+
'conf_py_path': '/docs/zh_cn/',
|
88 |
+
}
|
89 |
+
html_title = 'Lagent'
|
90 |
+
html_logo = '../imgs/lagent_logo.png'
|
91 |
+
html_favicon = '../imgs/lagent_icon.png'
|
92 |
+
|
93 |
+
master_doc = 'index'
|
94 |
+
|
95 |
+
# Add any paths that contain custom static files (such as style sheets) here,
|
96 |
+
# relative to this directory. They are copied after the builtin static files,
|
97 |
+
# so a file named 'default.css' will overwrite the builtin 'default.css'.
|
98 |
+
html_static_path = ['_static']
|
99 |
+
|
100 |
+
|
101 |
+
def custom_skip(app, what, name, obj, skip, options):
|
102 |
+
if what in ['data', 'function', 'class'] and re.search('logger', name):
|
103 |
+
skip = True
|
104 |
+
return skip
|
105 |
+
|
106 |
+
|
107 |
+
def setup(sphinx):
|
108 |
+
sphinx.connect('autoapi-skip-member', custom_skip)
|
docs/zh_cn/cp_origin_docs.sh
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env bash
|
2 |
+
|
3 |
+
# Copy *.md files from docs/ if it doesn't have a Chinese translation
|
4 |
+
|
5 |
+
for filename in $(find ../en/ -name '*.md' -printf "%P\n");
|
6 |
+
do
|
7 |
+
mkdir -p $(dirname $filename)
|
8 |
+
cp -n ../en/$filename ./$filename
|
9 |
+
done
|
docs/zh_cn/docutils.conf
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
[html writers]
|
2 |
+
table_style: colwidths-auto
|
docs/zh_cn/get_started/install.md
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 安装方式
|
2 |
+
|
3 |
+
## pip安装
|
4 |
+
|
5 |
+
推荐使用 pip 安装
|
6 |
+
|
7 |
+
```bash
|
8 |
+
pip install lagent
|
9 |
+
```
|
10 |
+
|
11 |
+
## 源码安装
|
12 |
+
|
13 |
+
如需修改部分功能,可以从源码构建 Lagent
|
14 |
+
|
15 |
+
```bash
|
16 |
+
git clone https://github.com/InternLM/lagent.git
|
17 |
+
cd lagent
|
18 |
+
pip install -e .
|
19 |
+
```
|
docs/zh_cn/index.rst
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
欢迎来到 Lagent 的中文文档!
|
2 |
+
=======================================
|
3 |
+
|
4 |
+
您可以在页面左下角切换中英文文档。
|
5 |
+
|
6 |
+
.. toctree::
|
7 |
+
:maxdepth: 2
|
8 |
+
:caption: 新手入门
|
9 |
+
|
10 |
+
get_started/install.md
|
11 |
+
|
12 |
+
.. toctree::
|
13 |
+
:maxdepth: 2
|
14 |
+
:caption: 教程
|
15 |
+
|
16 |
+
tutorials/action.md
|
17 |
+
|
18 |
+
.. toctree::
|
19 |
+
:caption: 切换语言
|
20 |
+
|
21 |
+
switch_language.md
|
22 |
+
|
23 |
+
.. toctree::
|
24 |
+
:maxdepth: 1
|
25 |
+
:caption: API 参考
|
26 |
+
|
27 |
+
autoapi/lagent/actions/index
|
28 |
+
autoapi/lagent/agents/index
|
29 |
+
autoapi/lagent/llms/index
|
30 |
+
autoapi/lagent/utils/index
|
31 |
+
autoapi/lagent/schema/index
|
32 |
+
autoapi/lagent/version/index
|
33 |
+
|
34 |
+
|
35 |
+
导引
|
36 |
+
==================
|
37 |
+
|
38 |
+
* :ref:`genindex`
|
39 |
+
* :ref:`search`
|
docs/zh_cn/make.bat
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@ECHO OFF
|
2 |
+
|
3 |
+
pushd %~dp0
|
4 |
+
|
5 |
+
REM Command file for Sphinx documentation
|
6 |
+
|
7 |
+
if "%SPHINXBUILD%" == "" (
|
8 |
+
set SPHINXBUILD=sphinx-build
|
9 |
+
)
|
10 |
+
set SOURCEDIR=.
|
11 |
+
set BUILDDIR=_build
|
12 |
+
|
13 |
+
if "%1" == "" goto help
|
14 |
+
|
15 |
+
%SPHINXBUILD% >NUL 2>NUL
|
16 |
+
if errorlevel 9009 (
|
17 |
+
echo.
|
18 |
+
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
19 |
+
echo.installed, then set the SPHINXBUILD environment variable to point
|
20 |
+
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
21 |
+
echo.may add the Sphinx directory to PATH.
|
22 |
+
echo.
|
23 |
+
echo.If you don't have Sphinx installed, grab it from
|
24 |
+
echo.http://sphinx-doc.org/
|
25 |
+
exit /b 1
|
26 |
+
)
|
27 |
+
|
28 |
+
|
29 |
+
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
30 |
+
goto end
|
31 |
+
|
32 |
+
:help
|
33 |
+
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
34 |
+
|
35 |
+
:end
|
36 |
+
popd
|
docs/zh_cn/switch_language.md
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
## <a href='https://lagent.readthedocs.io/en/latest/'>English</a>
|
2 |
+
|
3 |
+
## <a href='https://lagent.readthedocs.io/zh-cn/latest/'>简体中文</a>
|
docs/zh_cn/tutorials/action.md
ADDED
@@ -0,0 +1,398 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 动作
|
2 |
+
|
3 |
+
动作,也被称为工具,提供了一套LLM驱动的智能体用来与真实世界交互并执行复杂任务的函数。
|
4 |
+
|
5 |
+
## 基本概念
|
6 |
+
|
7 |
+
### 工具 & 工具包
|
8 |
+
|
9 |
+
有两种类型的工具:
|
10 |
+
|
11 |
+
- 简单工具: 只提供一个API接口供调用。
|
12 |
+
- 工具包: 实现多个API接口,承担不同的子任务。
|
13 |
+
|
14 |
+
### 工具描述
|
15 |
+
|
16 |
+
在Lagent中,工具描述是一个刻画工具调用方式的字典,能够被LLM观察并用于决策。
|
17 |
+
|
18 |
+
对于简单工具,描述可按如下格式声明:
|
19 |
+
|
20 |
+
```python
|
21 |
+
TOOL_DESCRIPTION = {
|
22 |
+
'name': 'bold', # 工具名称
|
23 |
+
'description': 'a function used to make text bold', # 介绍工具的功能
|
24 |
+
'parameters': [ # 这个工具所需要的参数列表
|
25 |
+
{
|
26 |
+
'name': 'text', 'type': 'STRING', 'description': 'input content'
|
27 |
+
}
|
28 |
+
],
|
29 |
+
'required': ['text'], # 指定必需的参数名
|
30 |
+
}
|
31 |
+
```
|
32 |
+
|
33 |
+
在某些情况下,可能还包含 `return_data`,`parameter_description` 字段,分别描述返回内容及参数传递格式。
|
34 |
+
|
35 |
+
```{attention}
|
36 |
+
`parameter_description` 通常被动作的解析器自动插入到工具描述中,这部分将在[接口设计](#id6)中进行介绍。
|
37 |
+
```
|
38 |
+
|
39 |
+
对于工具包,描述非常相似,但嵌套了子方法
|
40 |
+
|
41 |
+
```python
|
42 |
+
TOOL_DESCRIPTION = {
|
43 |
+
'name': 'PhraseEmphasis', # 工具包的名字
|
44 |
+
'description': 'a toolkit which provides different styles of text emphasis', # 介绍工具包的功能
|
45 |
+
'api_list': [
|
46 |
+
{
|
47 |
+
'name': 'bold',
|
48 |
+
'description': 'make text bold',
|
49 |
+
'parameters': [
|
50 |
+
{
|
51 |
+
'name': 'text', 'type': 'STRING', 'description': 'input content'
|
52 |
+
}
|
53 |
+
],
|
54 |
+
'required': ['text']
|
55 |
+
},
|
56 |
+
{
|
57 |
+
'name': 'italic',
|
58 |
+
'description': 'make text italic',
|
59 |
+
'parameters': [
|
60 |
+
{
|
61 |
+
'name': 'text', 'type': 'STRING', 'description': 'input content'
|
62 |
+
}
|
63 |
+
],
|
64 |
+
'required': ['text']
|
65 |
+
}
|
66 |
+
]
|
67 |
+
}
|
68 |
+
```
|
69 |
+
|
70 |
+
## 将函数转换为工具
|
71 |
+
|
72 |
+
对于已定义好的函数,无需人工添加额外的描述。在 Lagent 中,我们提供了一个修饰器 `tool_api`,它可以通过自动解析函数的类型提示和文档字符串来生成描述字典,并将其绑定到属性 `api_description`。
|
73 |
+
|
74 |
+
```python
|
75 |
+
from lagent import tool_api
|
76 |
+
|
77 |
+
@tool_api
|
78 |
+
def bold(text: str) -> str:
|
79 |
+
"""make text bold
|
80 |
+
|
81 |
+
Args:
|
82 |
+
text (str): input text
|
83 |
+
|
84 |
+
Returns:
|
85 |
+
str: bold text
|
86 |
+
"""
|
87 |
+
return '**' + text + '**'
|
88 |
+
|
89 |
+
|
90 |
+
bold.api_description
|
91 |
+
```
|
92 |
+
|
93 |
+
```python
|
94 |
+
{'name': 'bold',
|
95 |
+
'description': 'make text bold',
|
96 |
+
'parameters': [{'name': 'text',
|
97 |
+
'type': 'STRING',
|
98 |
+
'description': 'input text'}],
|
99 |
+
'required': ['text']}
|
100 |
+
```
|
101 |
+
|
102 |
+
一旦启用 `returns_named_value`,您应当声明返回值的名称,这将被处理成一个新的字段 `return_data`:
|
103 |
+
|
104 |
+
```python
|
105 |
+
@tool_api(returns_named_value=True)
|
106 |
+
def bold(text: str) -> str:
|
107 |
+
"""make text bold
|
108 |
+
|
109 |
+
Args:
|
110 |
+
text (str): input text
|
111 |
+
|
112 |
+
Returns:
|
113 |
+
bold_text (str): bold text
|
114 |
+
"""
|
115 |
+
return '**' + text + '**'
|
116 |
+
|
117 |
+
bold.api_description
|
118 |
+
```
|
119 |
+
|
120 |
+
```python
|
121 |
+
{'name': 'bold',
|
122 |
+
'description': 'make text bold',
|
123 |
+
'parameters': [{'name': 'text',
|
124 |
+
'type': 'STRING',
|
125 |
+
'description': 'input text'}],
|
126 |
+
'required': ['text'],
|
127 |
+
'return_data': [{'name': 'bold_text',
|
128 |
+
'description': 'bold text',
|
129 |
+
'type': 'STRING'}]}
|
130 |
+
```
|
131 |
+
|
132 |
+
有时工具可能返回一个 `dict` 或 `tuple`,如果你想在 `return_data` 中详细说明每个成员的含义而不是把它们当作一个整体,设置 `explode_return=True` 并在文档字符串的 Returns 部分中罗列它们。
|
133 |
+
|
134 |
+
```python
|
135 |
+
@tool_api(explode_return=True)
|
136 |
+
def list_args(a: str, b: int, c: float = 0.0) -> dict:
|
137 |
+
"""Return arguments in dict format
|
138 |
+
|
139 |
+
Args:
|
140 |
+
a (str): a
|
141 |
+
b (int): b
|
142 |
+
c (float): c
|
143 |
+
|
144 |
+
Returns:
|
145 |
+
dict: input arguments
|
146 |
+
- a (str): a
|
147 |
+
- b (int): b
|
148 |
+
- c: c
|
149 |
+
"""
|
150 |
+
return {'a': a, 'b': b, 'c': c}
|
151 |
+
```
|
152 |
+
|
153 |
+
```python
|
154 |
+
{'name': 'list_args',
|
155 |
+
'description': 'Return arguments in dict format',
|
156 |
+
'parameters': [{'name': 'a', 'type': 'STRING', 'description': 'a'},
|
157 |
+
{'name': 'b', 'type': 'NUMBER', 'description': 'b'},
|
158 |
+
{'name': 'c', 'type': 'FLOAT', 'description': 'c'}],
|
159 |
+
'required': ['a', 'b'],
|
160 |
+
'return_data': [{'name': 'a', 'description': 'a', 'type': 'STRING'},
|
161 |
+
{'name': 'b', 'description': 'b', 'type': 'NUMBER'},
|
162 |
+
{'name': 'c', 'description': 'c'}]}
|
163 |
+
```
|
164 |
+
|
165 |
+
```{warning}
|
166 |
+
目前仅支持 Google 格式的 Python 文档字符串。
|
167 |
+
```
|
168 |
+
|
169 |
+
## 接口设计
|
170 |
+
|
171 |
+
`BaseAction(description=None, parser=JsonParser, enable=True)` 是所有动作应该继承的基类,它接收三个初始化参数:
|
172 |
+
|
173 |
+
- **description**:一个工具描述的字典,用于设置实例属性 `description`。通常不需要显式地传递这个参数,因为 `BaseAction` 的元类将查找被 `tool_api` 装饰的方法,并组装它��的 `api_description` 构造一个类属性 `__tool_description__`,如果实例化时 `description` 为空,那么该实例属性将置为 `__tool_description__`。
|
174 |
+
|
175 |
+
- **parser**:`BaseParser` 类,用于实例化一个动作解析器校验 `description` 所描述的工具的参数。例如,`JsonParser` 会要求模型在调用工具时传入一个 JSON 格式字符串或者 Python 字典,为了让 LLM 感知到该指令,它会在 `description` 中插入一个 `parameter_description` 字段。
|
176 |
+
|
177 |
+
```python
|
178 |
+
from lagent import BaseAction
|
179 |
+
|
180 |
+
action = BaseAction(
|
181 |
+
{
|
182 |
+
'name': 'bold',
|
183 |
+
'description': 'a function used to make text bold',
|
184 |
+
'parameters': [
|
185 |
+
{
|
186 |
+
'name': 'text', 'type': 'STRING', 'description': 'input content'
|
187 |
+
}
|
188 |
+
],
|
189 |
+
'required': ['text']
|
190 |
+
}
|
191 |
+
)
|
192 |
+
action.description
|
193 |
+
```
|
194 |
+
|
195 |
+
```python
|
196 |
+
{'name': 'bold',
|
197 |
+
'description': 'a function used to make text bold',
|
198 |
+
'parameters': [{'name': 'text',
|
199 |
+
'type': 'STRING',
|
200 |
+
'description': 'input content'}],
|
201 |
+
'required': ['text'],
|
202 |
+
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'}
|
203 |
+
```
|
204 |
+
|
205 |
+
- **enable**: 指明该动作是否生效。
|
206 |
+
|
207 |
+
### 自定义动作
|
208 |
+
|
209 |
+
一个简单工具必须实现 `run` 方法,而工具包则应当避免将各子API名称定义为该保留字段。
|
210 |
+
|
211 |
+
```{tip}
|
212 |
+
对于非工具包的 Action,`run` 允许不被 `tool_api` 装饰,除非你想提示返回信息。
|
213 |
+
```
|
214 |
+
|
215 |
+
```python
|
216 |
+
class Bold(BaseAction):
|
217 |
+
|
218 |
+
def run(self, text: str):
|
219 |
+
"""make text bold
|
220 |
+
|
221 |
+
Args:
|
222 |
+
text (str): input text
|
223 |
+
|
224 |
+
Returns:
|
225 |
+
str: bold text
|
226 |
+
"""
|
227 |
+
return '**' + text + '**'
|
228 |
+
|
229 |
+
class PhraseEmphasis(BaseAction):
|
230 |
+
"""a toolkit which provides different styles of text emphasis"""
|
231 |
+
|
232 |
+
@tool_api
|
233 |
+
def bold(self, text):
|
234 |
+
"""make text bold
|
235 |
+
|
236 |
+
Args:
|
237 |
+
text (str): input text
|
238 |
+
|
239 |
+
Returns:
|
240 |
+
str: bold text
|
241 |
+
"""
|
242 |
+
return '**' + text + '**'
|
243 |
+
|
244 |
+
@tool_api
|
245 |
+
def italic(self, text):
|
246 |
+
"""make text italic
|
247 |
+
|
248 |
+
Args:
|
249 |
+
text (str): input text
|
250 |
+
|
251 |
+
Returns:
|
252 |
+
str: italic text
|
253 |
+
"""
|
254 |
+
return '*' + text + '*'
|
255 |
+
|
256 |
+
# 查看默认工具描述
|
257 |
+
# Bold.__tool_description__, PhraseEmphasis.__tool_description__
|
258 |
+
```
|
259 |
+
|
260 |
+
### 自动注册
|
261 |
+
|
262 |
+
任何 `BaseAction` 的子类都会自动被注册。你可以使用 `list_tools()` 和 `get_tool()` 来查看所有工具类并通过工具名进行初始化。
|
263 |
+
|
264 |
+
```python
|
265 |
+
from lagent import list_tools, get_tool
|
266 |
+
|
267 |
+
list_tools()
|
268 |
+
```
|
269 |
+
|
270 |
+
```python
|
271 |
+
['BaseAction',
|
272 |
+
'InvalidAction',
|
273 |
+
'NoAction',
|
274 |
+
'FinishAction',
|
275 |
+
'ArxivSearch',
|
276 |
+
'BINGMap',
|
277 |
+
'GoogleScholar',
|
278 |
+
'GoogleSearch',
|
279 |
+
'IPythonInterpreter',
|
280 |
+
'PPT',
|
281 |
+
'PythonInterpreter',
|
282 |
+
'Bold',
|
283 |
+
'PhraseEmphasis']
|
284 |
+
```
|
285 |
+
|
286 |
+
创建一个 `PhraseEmphasis` 对象。
|
287 |
+
|
288 |
+
```python
|
289 |
+
action = get_tool('PhraseEmphasis')
|
290 |
+
action.description
|
291 |
+
```
|
292 |
+
|
293 |
+
```python
|
294 |
+
{'name': 'PhraseEmphasis',
|
295 |
+
'description': 'a toolkit which provides different styles of text emphasis',
|
296 |
+
'api_list': [{'name': 'bold',
|
297 |
+
'description': 'make text bold',
|
298 |
+
'parameters': [{'name': 'text',
|
299 |
+
'type': 'STRING',
|
300 |
+
'description': 'input text'}],
|
301 |
+
'required': ['text'],
|
302 |
+
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'},
|
303 |
+
{'name': 'italic',
|
304 |
+
'description': 'make text italic',
|
305 |
+
'parameters': [{'name': 'text',
|
306 |
+
'type': 'STRING',
|
307 |
+
'description': 'input text'}],
|
308 |
+
'required': ['text'],
|
309 |
+
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'}]}
|
310 |
+
```
|
311 |
+
|
312 |
+
## 工具调用
|
313 |
+
|
314 |
+
### 执行工具
|
315 |
+
|
316 |
+
`Action` 的 `__call__` 方法需要传入两个参数
|
317 |
+
|
318 |
+
- `inputs`: 其类型与动作绑定的 `BaseParser` 相关,通常是由大语言模型生成的字符串。
|
319 |
+
- `JsonParser`: 允许传入 JSON 格式字符串或 Python 字典。
|
320 |
+
- `TupleParser`: 允许传入字面量为元组的字符串或 Python 元组。
|
321 |
+
- `name`: 调用哪个 API,默认为 `run`。
|
322 |
+
|
323 |
+
工具会返回一个封装了调用细节的 `ActionReturn` 对象。
|
324 |
+
|
325 |
+
- `args`: 一个字典,表示该动作的入参。
|
326 |
+
- `type`: 动作名称。
|
327 |
+
- `result`: 以字典为成员的列表,每个字典包含两个键——'type' 和 'content',发生异常时该字段为 `None`。
|
328 |
+
- `errmsg`: 错误信息,默认为 `None`。
|
329 |
+
|
330 |
+
以下是一个例子:
|
331 |
+
|
332 |
+
```python
|
333 |
+
from lagent import IPythonInterpreter, TupleParser
|
334 |
+
|
335 |
+
action1 = IPythonInterpreter()
|
336 |
+
ret = action1('{"command": "import math;math.sqrt(100)"}')
|
337 |
+
print(ret.result)
|
338 |
+
ret = action1({'command': 'import math;math.sqrt(100)'})
|
339 |
+
print(ret.result)
|
340 |
+
|
341 |
+
action2 = IPythonInterpreter(parser=TupleParser)
|
342 |
+
ret = action2('("import math;math.sqrt(100)", )')
|
343 |
+
print(ret.result)
|
344 |
+
ret = action2(('import math;math.sqrt(100)',))
|
345 |
+
print(ret.result)
|
346 |
+
```
|
347 |
+
|
348 |
+
```python
|
349 |
+
[{'type': 'text', 'content': '10.0'}]
|
350 |
+
[{'type': 'text', 'content': '10.0'}]
|
351 |
+
[{'type': 'text', 'content': '10.0'}]
|
352 |
+
[{'type': 'text', 'content': '10.0'}]
|
353 |
+
```
|
354 |
+
|
355 |
+
### 动态触发
|
356 |
+
|
357 |
+
Lagent 提供 `ActionExecutor` 接口管理多个工具,它会将工具包的 `api_list` 平展并将各 API 更名为 `{tool_name}.{api_name}`。
|
358 |
+
|
359 |
+
```python
|
360 |
+
from lagent import ActionExecutor, ArxivSearch, IPythonInterpreter
|
361 |
+
|
362 |
+
executor = ActionExecutor(actions=[ArxivSearch(), IPythonInterpreter()])
|
363 |
+
executor.get_actions_info() # 该结果会作为LLM系统提示词的一部分
|
364 |
+
```
|
365 |
+
|
366 |
+
```python
|
367 |
+
[{'name': 'ArxivSearch.get_arxiv_article_information',
|
368 |
+
'description': 'Run Arxiv search and get the article meta information.',
|
369 |
+
'parameters': [{'name': 'query',
|
370 |
+
'type': 'STRING',
|
371 |
+
'description': 'the content of search query'}],
|
372 |
+
'required': ['query'],
|
373 |
+
'return_data': [{'name': 'content',
|
374 |
+
'description': 'a list of 3 arxiv search papers',
|
375 |
+
'type': 'STRING'}],
|
376 |
+
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'},
|
377 |
+
{'name': 'IPythonInterpreter',
|
378 |
+
'description': "When you send a message containing Python code to python, it will be executed in a stateful Jupyter notebook environment. python will respond with the output of the execution or time out after 60.0 seconds. The drive at '/mnt/data' can be used to save and persist user files. Internet access for this session is disabled. Do not make external web requests or API calls as they will fail.",
|
379 |
+
'parameters': [{'name': 'command',
|
380 |
+
'type': 'STRING',
|
381 |
+
'description': 'Python code'},
|
382 |
+
{'name': 'timeout',
|
383 |
+
'type': 'NUMBER',
|
384 |
+
'description': 'Upper bound of waiting time for Python script execution.'}],
|
385 |
+
'required': ['command'],
|
386 |
+
'parameter_description': '如果调用该工具,你必须使用Json格式 {key: value} 传参,其中key为参数名称'}]
|
387 |
+
```
|
388 |
+
|
389 |
+
通过动作执行器来触发一个工具
|
390 |
+
|
391 |
+
```python
|
392 |
+
ret = executor('IPythonInterpreter', '{"command": "import math;math.sqrt(100)"}')
|
393 |
+
ret.result
|
394 |
+
```
|
395 |
+
|
396 |
+
```python
|
397 |
+
[{'type': 'text', 'content': '10.0'}]
|
398 |
+
```
|
examples/.ipynb_checkpoints/agent_api_web_demo-checkpoint.py
ADDED
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import copy
|
2 |
+
import os
|
3 |
+
from typing import List
|
4 |
+
import streamlit as st
|
5 |
+
from lagent.actions import ArxivSearch, WeatherQuery
|
6 |
+
from lagent.prompts.parsers import PluginParser
|
7 |
+
from lagent.agents.stream import INTERPRETER_CN, META_CN, PLUGIN_CN, AgentForInternLM, get_plugin_prompt
|
8 |
+
from lagent.llms import GPTAPI
|
9 |
+
|
10 |
+
class SessionState:
|
11 |
+
"""管理会话状态的类。"""
|
12 |
+
|
13 |
+
def init_state(self):
|
14 |
+
"""初始化会话状态变量。"""
|
15 |
+
st.session_state['assistant'] = [] # 助手消息历史
|
16 |
+
st.session_state['user'] = [] # 用户消息历史
|
17 |
+
# 初始化插件列表
|
18 |
+
action_list = [
|
19 |
+
ArxivSearch(),
|
20 |
+
WeatherQuery(),
|
21 |
+
]
|
22 |
+
st.session_state['plugin_map'] = {action.name: action for action in action_list}
|
23 |
+
st.session_state['model_map'] = {} # 存储模型实例
|
24 |
+
st.session_state['model_selected'] = None # 当前选定模型
|
25 |
+
st.session_state['plugin_actions'] = set() # 当前激活插件
|
26 |
+
st.session_state['history'] = [] # 聊天历史
|
27 |
+
st.session_state['api_base'] = None # 初始化API base地址
|
28 |
+
|
29 |
+
def clear_state(self):
|
30 |
+
"""清除当前会话状态。"""
|
31 |
+
st.session_state['assistant'] = []
|
32 |
+
st.session_state['user'] = []
|
33 |
+
st.session_state['model_selected'] = None
|
34 |
+
|
35 |
+
|
36 |
+
class StreamlitUI:
|
37 |
+
"""管理 Streamlit 界面的类。"""
|
38 |
+
|
39 |
+
def __init__(self, session_state: SessionState):
|
40 |
+
self.session_state = session_state
|
41 |
+
self.plugin_action = [] # 当前选定的插件
|
42 |
+
# 初始化提示词
|
43 |
+
self.meta_prompt = META_CN
|
44 |
+
self.plugin_prompt = PLUGIN_CN
|
45 |
+
self.init_streamlit()
|
46 |
+
|
47 |
+
def init_streamlit(self):
|
48 |
+
"""初始化 Streamlit 的 UI 设置。"""
|
49 |
+
#st.set_page_config(
|
50 |
+
# layout='wide',
|
51 |
+
# page_title='lagent-web',
|
52 |
+
# page_icon='./docs/imgs/lagent_icon.png'
|
53 |
+
#)
|
54 |
+
st.header(':robot_face: :blue[Lagent] Web Demo ', divider='rainbow')
|
55 |
+
|
56 |
+
def setup_sidebar(self):
|
57 |
+
"""设置侧边栏,选择模型和插件。"""
|
58 |
+
# 模型名称和 API Base 输入框
|
59 |
+
model_name = st.sidebar.text_input('模型名称:', value='internlm2.5-latest')
|
60 |
+
|
61 |
+
# ================================== 硅基流动的API ==================================
|
62 |
+
# 注意,如果采用硅基流动API,模型名称需要更改为:internlm/internlm2_5-7b-chat 或者 internlm/internlm2_5-20b-chat
|
63 |
+
# api_base = st.sidebar.text_input(
|
64 |
+
# 'API Base 地址:', value='https://api.siliconflow.cn/v1/chat/completions'
|
65 |
+
# )
|
66 |
+
# ================================== 浦语官方的API ==================================
|
67 |
+
api_base = st.sidebar.text_input(
|
68 |
+
'API Base 地址:', value='https://internlm-chat.intern-ai.org.cn/puyu/api/v1/chat/completions'
|
69 |
+
)
|
70 |
+
# ==================================================================================
|
71 |
+
# 插件选择
|
72 |
+
plugin_name = st.sidebar.multiselect(
|
73 |
+
'插件选择',
|
74 |
+
options=list(st.session_state['plugin_map'].keys()),
|
75 |
+
default=[],
|
76 |
+
)
|
77 |
+
|
78 |
+
# 根据选择的插件生成插件操作列表
|
79 |
+
self.plugin_action = [st.session_state['plugin_map'][name] for name in plugin_name]
|
80 |
+
|
81 |
+
# 动态生成插件提示
|
82 |
+
if self.plugin_action:
|
83 |
+
self.plugin_prompt = get_plugin_prompt(self.plugin_action)
|
84 |
+
|
85 |
+
# 清空对话按钮
|
86 |
+
if st.sidebar.button('清空对话', key='clear'):
|
87 |
+
self.session_state.clear_state()
|
88 |
+
|
89 |
+
return model_name, api_base, self.plugin_action
|
90 |
+
|
91 |
+
def initialize_chatbot(self, model_name, api_base, plugin_action):
|
92 |
+
"""初始化 GPTAPI 实例作为 chatbot。"""
|
93 |
+
token = os.getenv("token")
|
94 |
+
if not token:
|
95 |
+
st.error("未检测到环境变量 `token`,请设置环境变量,例如 `export token='your_token_here'` 后重新运行 X﹏X")
|
96 |
+
st.stop() # 停止运行应用
|
97 |
+
|
98 |
+
# 创建完整的 meta_prompt,保留原始结构并动态插入侧边栏配置
|
99 |
+
meta_prompt = [
|
100 |
+
{"role": "system", "content": self.meta_prompt, "api_role": "system"},
|
101 |
+
{"role": "user", "content": "", "api_role": "user"},
|
102 |
+
{"role": "assistant", "content": self.plugin_prompt, "api_role": "assistant"},
|
103 |
+
{"role": "environment", "content": "", "api_role": "environment"}
|
104 |
+
]
|
105 |
+
|
106 |
+
api_model = GPTAPI(
|
107 |
+
model_type=model_name,
|
108 |
+
api_base=api_base,
|
109 |
+
key=token, # 从环境变量中获取授权令牌
|
110 |
+
meta_template=meta_prompt,
|
111 |
+
max_new_tokens=512,
|
112 |
+
temperature=0.8,
|
113 |
+
top_p=0.9
|
114 |
+
)
|
115 |
+
return api_model
|
116 |
+
|
117 |
+
def render_user(self, prompt: str):
|
118 |
+
"""渲染用户输入内容。"""
|
119 |
+
with st.chat_message('user'):
|
120 |
+
st.markdown(prompt)
|
121 |
+
|
122 |
+
def render_assistant(self, agent_return):
|
123 |
+
"""渲染助手响应内容。"""
|
124 |
+
with st.chat_message('assistant'):
|
125 |
+
content = getattr(agent_return, "content", str(agent_return))
|
126 |
+
st.markdown(content if isinstance(content, str) else str(content))
|
127 |
+
|
128 |
+
|
129 |
+
def main():
|
130 |
+
"""主函数,运行 Streamlit 应用。"""
|
131 |
+
if 'ui' not in st.session_state:
|
132 |
+
session_state = SessionState()
|
133 |
+
session_state.init_state()
|
134 |
+
st.session_state['ui'] = StreamlitUI(session_state)
|
135 |
+
else:
|
136 |
+
#st.set_page_config(
|
137 |
+
# layout='wide',
|
138 |
+
# page_title='lagent-web',
|
139 |
+
# page_icon='./docs/imgs/lagent_icon.png'
|
140 |
+
#)
|
141 |
+
st.header(':robot_face: :blue[Lagent] Web Demo ', divider='rainbow')
|
142 |
+
|
143 |
+
# 设置侧边栏并获取模型和插件信息
|
144 |
+
model_name, api_base, plugin_action = st.session_state['ui'].setup_sidebar()
|
145 |
+
plugins = [dict(type=f"lagent.actions.{plugin.__class__.__name__}") for plugin in plugin_action]
|
146 |
+
|
147 |
+
if (
|
148 |
+
'chatbot' not in st.session_state or
|
149 |
+
model_name != st.session_state['chatbot'].model_type or
|
150 |
+
'last_plugin_action' not in st.session_state or
|
151 |
+
plugin_action != st.session_state['last_plugin_action'] or
|
152 |
+
api_base != st.session_state['api_base']
|
153 |
+
):
|
154 |
+
# 更新 Chatbot
|
155 |
+
st.session_state['chatbot'] = st.session_state['ui'].initialize_chatbot(model_name, api_base, plugin_action)
|
156 |
+
st.session_state['last_plugin_action'] = plugin_action # 更新插件状态
|
157 |
+
st.session_state['api_base'] = api_base # 更新 API Base 地址
|
158 |
+
|
159 |
+
# 初始化 AgentForInternLM
|
160 |
+
st.session_state['agent'] = AgentForInternLM(
|
161 |
+
llm=st.session_state['chatbot'],
|
162 |
+
plugins=plugins,
|
163 |
+
output_format=dict(
|
164 |
+
type=PluginParser,
|
165 |
+
template=PLUGIN_CN,
|
166 |
+
prompt=get_plugin_prompt(plugin_action)
|
167 |
+
)
|
168 |
+
)
|
169 |
+
# 清空对话历史
|
170 |
+
st.session_state['session_history'] = []
|
171 |
+
|
172 |
+
if 'agent' not in st.session_state:
|
173 |
+
st.session_state['agent'] = None
|
174 |
+
|
175 |
+
agent = st.session_state['agent']
|
176 |
+
for prompt, agent_return in zip(st.session_state['user'], st.session_state['assistant']):
|
177 |
+
st.session_state['ui'].render_user(prompt)
|
178 |
+
st.session_state['ui'].render_assistant(agent_return)
|
179 |
+
|
180 |
+
# 处理用户输入
|
181 |
+
if user_input := st.chat_input(''):
|
182 |
+
st.session_state['ui'].render_user(user_input)
|
183 |
+
|
184 |
+
# 调用模型时确保侧边栏的系统提示词和插件提示词生效
|
185 |
+
res = agent(user_input, session_id=0)
|
186 |
+
st.session_state['ui'].render_assistant(res)
|
187 |
+
|
188 |
+
# 更新会话状态
|
189 |
+
st.session_state['user'].append(user_input)
|
190 |
+
st.session_state['assistant'].append(copy.deepcopy(res))
|
191 |
+
|
192 |
+
st.session_state['last_status'] = None
|
193 |
+
|
194 |
+
|
195 |
+
if __name__ == '__main__':
|
196 |
+
main()
|
examples/.ipynb_checkpoints/multi_agents_api_web_demo-checkpoint.py
ADDED
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import asyncio
|
3 |
+
import json
|
4 |
+
import re
|
5 |
+
import requests
|
6 |
+
import streamlit as st
|
7 |
+
|
8 |
+
from lagent.agents import Agent
|
9 |
+
from lagent.prompts.parsers import PluginParser
|
10 |
+
from lagent.agents.stream import PLUGIN_CN, get_plugin_prompt
|
11 |
+
from lagent.schema import AgentMessage
|
12 |
+
from lagent.actions import ArxivSearch
|
13 |
+
from lagent.hooks import Hook
|
14 |
+
from lagent.llms import GPTAPI
|
15 |
+
|
16 |
+
YOUR_TOKEN_HERE = os.getenv("token")
|
17 |
+
if not YOUR_TOKEN_HERE:
|
18 |
+
raise EnvironmentError("未找到环境变量 'token',请设置后再运行程序。")
|
19 |
+
|
20 |
+
# Hook类,用于对消息添加前缀
|
21 |
+
class PrefixedMessageHook(Hook):
|
22 |
+
def __init__(self, prefix, senders=None):
|
23 |
+
"""
|
24 |
+
初始化Hook
|
25 |
+
:param prefix: 消息前缀
|
26 |
+
:param senders: 指定发送者列表
|
27 |
+
"""
|
28 |
+
self.prefix = prefix
|
29 |
+
self.senders = senders or []
|
30 |
+
|
31 |
+
def before_agent(self, agent, messages, session_id):
|
32 |
+
"""
|
33 |
+
在代理处理消息前修改消息内容
|
34 |
+
:param agent: 当前代理
|
35 |
+
:param messages: 消息列表
|
36 |
+
:param session_id: 会话ID
|
37 |
+
"""
|
38 |
+
for message in messages:
|
39 |
+
if message.sender in self.senders:
|
40 |
+
message.content = self.prefix + message.content
|
41 |
+
|
42 |
+
class AsyncBlogger:
|
43 |
+
"""博客生成类,整合写作者和批评者。"""
|
44 |
+
|
45 |
+
def __init__(self, model_type, api_base, writer_prompt, critic_prompt, critic_prefix='', max_turn=2):
|
46 |
+
"""
|
47 |
+
初始化博客生成器
|
48 |
+
:param model_type: 模型类型
|
49 |
+
:param api_base: API 基地址
|
50 |
+
:param writer_prompt: 写作者提示词
|
51 |
+
:param critic_prompt: 批评者提示词
|
52 |
+
:param critic_prefix: 批评消息前缀
|
53 |
+
:param max_turn: 最大轮次
|
54 |
+
"""
|
55 |
+
self.model_type = model_type
|
56 |
+
self.api_base = api_base
|
57 |
+
self.llm = GPTAPI(
|
58 |
+
model_type=model_type,
|
59 |
+
api_base=api_base,
|
60 |
+
key=YOUR_TOKEN_HERE,
|
61 |
+
max_new_tokens=4096,
|
62 |
+
)
|
63 |
+
self.plugins = [dict(type='lagent.actions.ArxivSearch')]
|
64 |
+
self.writer = Agent(
|
65 |
+
self.llm,
|
66 |
+
writer_prompt,
|
67 |
+
name='写作者',
|
68 |
+
output_format=dict(
|
69 |
+
type=PluginParser,
|
70 |
+
template=PLUGIN_CN,
|
71 |
+
prompt=get_plugin_prompt(self.plugins)
|
72 |
+
)
|
73 |
+
)
|
74 |
+
self.critic = Agent(
|
75 |
+
self.llm,
|
76 |
+
critic_prompt,
|
77 |
+
name='批评者',
|
78 |
+
hooks=[PrefixedMessageHook(critic_prefix, ['写作者'])]
|
79 |
+
)
|
80 |
+
self.max_turn = max_turn
|
81 |
+
|
82 |
+
async def forward(self, message: AgentMessage, update_placeholder):
|
83 |
+
"""
|
84 |
+
执行多阶段博客生成流程
|
85 |
+
:param message: 初始消息
|
86 |
+
:param update_placeholder: Streamlit占位符
|
87 |
+
:return: 最终优化的博客内容
|
88 |
+
"""
|
89 |
+
step1_placeholder = update_placeholder.container()
|
90 |
+
step2_placeholder = update_placeholder.container()
|
91 |
+
step3_placeholder = update_placeholder.container()
|
92 |
+
|
93 |
+
# 第一步:生成初始内容
|
94 |
+
step1_placeholder.markdown("**Step 1: 生成初始内容...**")
|
95 |
+
message = self.writer(message)
|
96 |
+
if message.content:
|
97 |
+
step1_placeholder.markdown(f"**生成的初始内容**:\n\n{message.content}")
|
98 |
+
else:
|
99 |
+
step1_placeholder.markdown("**生成的初始内容为空,请检查生成逻辑。**")
|
100 |
+
|
101 |
+
# 第二步:批评者提供反馈
|
102 |
+
step2_placeholder.markdown("**Step 2: 批评者正在提供反馈和文献推荐...**")
|
103 |
+
message = self.critic(message)
|
104 |
+
if message.content:
|
105 |
+
# 解析批评者反馈
|
106 |
+
suggestions = re.search(r"1\. 批评建议:\n(.*?)2\. 推荐的关键词:", message.content, re.S)
|
107 |
+
keywords = re.search(r"2\. 推荐的关键词:\n- (.*)", message.content)
|
108 |
+
feedback = suggestions.group(1).strip() if suggestions else "未提供批评建议"
|
109 |
+
keywords = keywords.group(1).strip() if keywords else "未提供关键词"
|
110 |
+
|
111 |
+
# Arxiv 文献查询
|
112 |
+
arxiv_search = ArxivSearch()
|
113 |
+
arxiv_results = arxiv_search.get_arxiv_article_information(keywords)
|
114 |
+
|
115 |
+
# 显示批评内容和文献推荐
|
116 |
+
message.content = f"**批评建议**:\n{feedback}\n\n**推荐的文献**:\n{arxiv_results}"
|
117 |
+
step2_placeholder.markdown(f"**批评和文献推荐**:\n\n{message.content}")
|
118 |
+
else:
|
119 |
+
step2_placeholder.markdown("**批评内容为空,请检查批评逻辑。**")
|
120 |
+
|
121 |
+
# 第三步:写作者根据反馈优化内容
|
122 |
+
step3_placeholder.markdown("**Step 3: 根据反馈改进内容...**")
|
123 |
+
improvement_prompt = AgentMessage(
|
124 |
+
sender="critic",
|
125 |
+
content=(
|
126 |
+
f"根据以下批评建议和推荐文献对内容进行改进:\n\n"
|
127 |
+
f"批评建议:\n{feedback}\n\n"
|
128 |
+
f"推荐文献:\n{arxiv_results}\n\n"
|
129 |
+
f"请优化��始内容,使其更加清晰、丰富,并符合专业水准。"
|
130 |
+
),
|
131 |
+
)
|
132 |
+
message = self.writer(improvement_prompt)
|
133 |
+
if message.content:
|
134 |
+
step3_placeholder.markdown(f"**最终优化的博客内容**:\n\n{message.content}")
|
135 |
+
else:
|
136 |
+
step3_placeholder.markdown("**最终优化的博客内容为空,请检查生成逻辑。**")
|
137 |
+
|
138 |
+
return message
|
139 |
+
|
140 |
+
def setup_sidebar():
|
141 |
+
"""设置侧边栏,选择模型。"""
|
142 |
+
model_name = st.sidebar.text_input('模型名称:', value='internlm2.5-latest')
|
143 |
+
api_base = st.sidebar.text_input(
|
144 |
+
'API Base 地址:', value='https://internlm-chat.intern-ai.org.cn/puyu/api/v1/chat/completions'
|
145 |
+
)
|
146 |
+
|
147 |
+
return model_name, api_base
|
148 |
+
|
149 |
+
def main():
|
150 |
+
"""
|
151 |
+
主函数:构建Streamlit界面并处理用户交互
|
152 |
+
"""
|
153 |
+
st.set_page_config(layout='wide', page_title='Lagent Web Demo', page_icon='🤖')
|
154 |
+
st.title("多代理博客优化助手")
|
155 |
+
|
156 |
+
model_type, api_base = setup_sidebar()
|
157 |
+
topic = st.text_input('输入一个话题:', 'Self-Supervised Learning')
|
158 |
+
generate_button = st.button('生成博客内容')
|
159 |
+
|
160 |
+
if (
|
161 |
+
'blogger' not in st.session_state or
|
162 |
+
st.session_state['model_type'] != model_type or
|
163 |
+
st.session_state['api_base'] != api_base
|
164 |
+
):
|
165 |
+
st.session_state['blogger'] = AsyncBlogger(
|
166 |
+
model_type=model_type,
|
167 |
+
api_base=api_base,
|
168 |
+
writer_prompt="你是一位优秀的AI内容写作者,请撰写一篇有吸引力且信息丰富的博客内容。",
|
169 |
+
critic_prompt="""
|
170 |
+
作为一位严谨的批评者,请给出建设性的批评和改进建议,并基于相关主题使用已有的工具推荐一些参考文献,推荐的关键词应该是英语形式,简洁且切题。
|
171 |
+
请按照以下格式提供反馈:
|
172 |
+
1. 批评建议:
|
173 |
+
- (具体建议)
|
174 |
+
2. 推荐的关键词:
|
175 |
+
- (关键词1, 关键词2, ...)
|
176 |
+
""",
|
177 |
+
critic_prefix="请批评以下内容,并提供改进建议:\n\n"
|
178 |
+
)
|
179 |
+
st.session_state['model_type'] = model_type
|
180 |
+
st.session_state['api_base'] = api_base
|
181 |
+
|
182 |
+
if generate_button:
|
183 |
+
update_placeholder = st.empty()
|
184 |
+
|
185 |
+
async def run_async_blogger():
|
186 |
+
message = AgentMessage(
|
187 |
+
sender='user',
|
188 |
+
content=f"请撰写一篇关于{topic}的博客文章,要求表达专业,生动有趣,并且易于理解。"
|
189 |
+
)
|
190 |
+
result = await st.session_state['blogger'].forward(message, update_placeholder)
|
191 |
+
return result
|
192 |
+
|
193 |
+
loop = asyncio.new_event_loop()
|
194 |
+
asyncio.set_event_loop(loop)
|
195 |
+
loop.run_until_complete(run_async_blogger())
|
196 |
+
|
197 |
+
if __name__ == '__main__':
|
198 |
+
main()
|
examples/agent_api_web_demo.py
ADDED
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import copy
|
2 |
+
import os
|
3 |
+
from typing import List
|
4 |
+
import streamlit as st
|
5 |
+
from lagent.actions import ArxivSearch, WeatherQuery
|
6 |
+
from lagent.prompts.parsers import PluginParser
|
7 |
+
from lagent.agents.stream import INTERPRETER_CN, META_CN, PLUGIN_CN, AgentForInternLM, get_plugin_prompt
|
8 |
+
from lagent.llms import GPTAPI
|
9 |
+
|
10 |
+
class SessionState:
|
11 |
+
"""管理会话状态的类。"""
|
12 |
+
|
13 |
+
def init_state(self):
|
14 |
+
"""初始化会话状态变量。"""
|
15 |
+
st.session_state['assistant'] = [] # 助手消息历史
|
16 |
+
st.session_state['user'] = [] # 用户消息历史
|
17 |
+
# 初始化插件列表
|
18 |
+
action_list = [
|
19 |
+
ArxivSearch(),
|
20 |
+
WeatherQuery(),
|
21 |
+
]
|
22 |
+
st.session_state['plugin_map'] = {action.name: action for action in action_list}
|
23 |
+
st.session_state['model_map'] = {} # 存储模型实例
|
24 |
+
st.session_state['model_selected'] = None # 当前选定模型
|
25 |
+
st.session_state['plugin_actions'] = set() # 当前激活插件
|
26 |
+
st.session_state['history'] = [] # 聊天历史
|
27 |
+
st.session_state['api_base'] = None # 初始化API base地址
|
28 |
+
|
29 |
+
def clear_state(self):
|
30 |
+
"""清除当前会话状态。"""
|
31 |
+
st.session_state['assistant'] = []
|
32 |
+
st.session_state['user'] = []
|
33 |
+
st.session_state['model_selected'] = None
|
34 |
+
|
35 |
+
|
36 |
+
class StreamlitUI:
|
37 |
+
"""管理 Streamlit 界面的类。"""
|
38 |
+
|
39 |
+
def __init__(self, session_state: SessionState):
|
40 |
+
self.session_state = session_state
|
41 |
+
self.plugin_action = [] # 当前选定的插件
|
42 |
+
# 初始化提示词
|
43 |
+
self.meta_prompt = META_CN
|
44 |
+
self.plugin_prompt = PLUGIN_CN
|
45 |
+
self.init_streamlit()
|
46 |
+
|
47 |
+
def init_streamlit(self):
|
48 |
+
"""初始化 Streamlit 的 UI 设置。"""
|
49 |
+
#st.set_page_config(
|
50 |
+
# layout='wide',
|
51 |
+
# page_title='lagent-web',
|
52 |
+
# page_icon='./docs/imgs/lagent_icon.png'
|
53 |
+
#)
|
54 |
+
st.header(':robot_face: :blue[Lagent] Web Demo ', divider='rainbow')
|
55 |
+
|
56 |
+
def setup_sidebar(self):
|
57 |
+
"""设置侧边栏,选择模型和插件。"""
|
58 |
+
# 模型名称和 API Base 输入框
|
59 |
+
model_name = st.sidebar.text_input('模型名称:', value='internlm2.5-latest')
|
60 |
+
|
61 |
+
# ================================== 硅基流动的API ==================================
|
62 |
+
# 注意,如果采用硅基流动API,模型名称需要更改为:internlm/internlm2_5-7b-chat 或者 internlm/internlm2_5-20b-chat
|
63 |
+
# api_base = st.sidebar.text_input(
|
64 |
+
# 'API Base 地址:', value='https://api.siliconflow.cn/v1/chat/completions'
|
65 |
+
# )
|
66 |
+
# ================================== 浦语官方的API ==================================
|
67 |
+
api_base = st.sidebar.text_input(
|
68 |
+
'API Base 地址:', value='https://internlm-chat.intern-ai.org.cn/puyu/api/v1/chat/completions'
|
69 |
+
)
|
70 |
+
# ==================================================================================
|
71 |
+
# 插件选择
|
72 |
+
plugin_name = st.sidebar.multiselect(
|
73 |
+
'插件选择',
|
74 |
+
options=list(st.session_state['plugin_map'].keys()),
|
75 |
+
default=[],
|
76 |
+
)
|
77 |
+
|
78 |
+
# 根据选择的插件生成插件操作列表
|
79 |
+
self.plugin_action = [st.session_state['plugin_map'][name] for name in plugin_name]
|
80 |
+
|
81 |
+
# 动态生成插件提示
|
82 |
+
if self.plugin_action:
|
83 |
+
self.plugin_prompt = get_plugin_prompt(self.plugin_action)
|
84 |
+
|
85 |
+
# 清空对话按钮
|
86 |
+
if st.sidebar.button('清空对话', key='clear'):
|
87 |
+
self.session_state.clear_state()
|
88 |
+
|
89 |
+
return model_name, api_base, self.plugin_action
|
90 |
+
|
91 |
+
def initialize_chatbot(self, model_name, api_base, plugin_action):
|
92 |
+
"""初始化 GPTAPI 实例作为 chatbot。"""
|
93 |
+
token = os.getenv("token")
|
94 |
+
if not token:
|
95 |
+
st.error("未检测到环境变量 `token`,请设置环境变量,例如 `export token='your_token_here'` 后重新运行 X﹏X")
|
96 |
+
st.stop() # 停止运行应用
|
97 |
+
|
98 |
+
# 创建完整的 meta_prompt,保留原始结构并动态插入侧边栏配置
|
99 |
+
meta_prompt = [
|
100 |
+
{"role": "system", "content": self.meta_prompt, "api_role": "system"},
|
101 |
+
{"role": "user", "content": "", "api_role": "user"},
|
102 |
+
{"role": "assistant", "content": self.plugin_prompt, "api_role": "assistant"},
|
103 |
+
{"role": "environment", "content": "", "api_role": "environment"}
|
104 |
+
]
|
105 |
+
|
106 |
+
api_model = GPTAPI(
|
107 |
+
model_type=model_name,
|
108 |
+
api_base=api_base,
|
109 |
+
key=token, # 从环境变量中获取授权令牌
|
110 |
+
meta_template=meta_prompt,
|
111 |
+
max_new_tokens=512,
|
112 |
+
temperature=0.8,
|
113 |
+
top_p=0.9
|
114 |
+
)
|
115 |
+
return api_model
|
116 |
+
|
117 |
+
def render_user(self, prompt: str):
|
118 |
+
"""渲染用户输入内容。"""
|
119 |
+
with st.chat_message('user'):
|
120 |
+
st.markdown(prompt)
|
121 |
+
|
122 |
+
def render_assistant(self, agent_return):
|
123 |
+
"""渲染助手响应内容。"""
|
124 |
+
with st.chat_message('assistant'):
|
125 |
+
content = getattr(agent_return, "content", str(agent_return))
|
126 |
+
st.markdown(content if isinstance(content, str) else str(content))
|
127 |
+
|
128 |
+
|
129 |
+
def main():
|
130 |
+
"""主函数,运行 Streamlit 应用。"""
|
131 |
+
if 'ui' not in st.session_state:
|
132 |
+
session_state = SessionState()
|
133 |
+
session_state.init_state()
|
134 |
+
st.session_state['ui'] = StreamlitUI(session_state)
|
135 |
+
else:
|
136 |
+
#st.set_page_config(
|
137 |
+
# layout='wide',
|
138 |
+
# page_title='lagent-web',
|
139 |
+
# page_icon='./docs/imgs/lagent_icon.png'
|
140 |
+
#)
|
141 |
+
st.header(':robot_face: :blue[Lagent] Web Demo ', divider='rainbow')
|
142 |
+
|
143 |
+
# 设置侧边栏并获取模型和插件信息
|
144 |
+
model_name, api_base, plugin_action = st.session_state['ui'].setup_sidebar()
|
145 |
+
plugins = [dict(type=f"lagent.actions.{plugin.__class__.__name__}") for plugin in plugin_action]
|
146 |
+
|
147 |
+
if (
|
148 |
+
'chatbot' not in st.session_state or
|
149 |
+
model_name != st.session_state['chatbot'].model_type or
|
150 |
+
'last_plugin_action' not in st.session_state or
|
151 |
+
plugin_action != st.session_state['last_plugin_action'] or
|
152 |
+
api_base != st.session_state['api_base']
|
153 |
+
):
|
154 |
+
# 更新 Chatbot
|
155 |
+
st.session_state['chatbot'] = st.session_state['ui'].initialize_chatbot(model_name, api_base, plugin_action)
|
156 |
+
st.session_state['last_plugin_action'] = plugin_action # 更新插件状态
|
157 |
+
st.session_state['api_base'] = api_base # 更新 API Base 地址
|
158 |
+
|
159 |
+
# 初始化 AgentForInternLM
|
160 |
+
st.session_state['agent'] = AgentForInternLM(
|
161 |
+
llm=st.session_state['chatbot'],
|
162 |
+
plugins=plugins,
|
163 |
+
output_format=dict(
|
164 |
+
type=PluginParser,
|
165 |
+
template=PLUGIN_CN,
|
166 |
+
prompt=get_plugin_prompt(plugin_action)
|
167 |
+
)
|
168 |
+
)
|
169 |
+
# 清空对话历史
|
170 |
+
st.session_state['session_history'] = []
|
171 |
+
|
172 |
+
if 'agent' not in st.session_state:
|
173 |
+
st.session_state['agent'] = None
|
174 |
+
|
175 |
+
agent = st.session_state['agent']
|
176 |
+
for prompt, agent_return in zip(st.session_state['user'], st.session_state['assistant']):
|
177 |
+
st.session_state['ui'].render_user(prompt)
|
178 |
+
st.session_state['ui'].render_assistant(agent_return)
|
179 |
+
|
180 |
+
# 处理用户输入
|
181 |
+
if user_input := st.chat_input(''):
|
182 |
+
st.session_state['ui'].render_user(user_input)
|
183 |
+
|
184 |
+
# 调用模型时确保侧边栏的系统提示词和插件提示词生效
|
185 |
+
res = agent(user_input, session_id=0)
|
186 |
+
st.session_state['ui'].render_assistant(res)
|
187 |
+
|
188 |
+
# 更新会话状态
|
189 |
+
st.session_state['user'].append(user_input)
|
190 |
+
st.session_state['assistant'].append(copy.deepcopy(res))
|
191 |
+
|
192 |
+
st.session_state['last_status'] = None
|
193 |
+
|
194 |
+
|
195 |
+
if __name__ == '__main__':
|
196 |
+
main()
|