SutaLXY commited on
Commit
861dfcc
·
verified ·
1 Parent(s): 03ca818

Upload 188 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +47 -35
  2. .ipynb_checkpoints/app-checkpoint.py +30 -0
  3. .ipynb_checkpoints/requirements-checkpoint.txt +50 -0
  4. .pre-commit-config.yaml +46 -0
  5. .pylintrc +428 -0
  6. .readthedocs.yaml +15 -0
  7. MANIFEST.in +1 -0
  8. app.py +30 -1
  9. docs/en/Makefile +20 -0
  10. docs/en/_static/css/readthedocs.css +6 -0
  11. docs/en/_static/images/lagent_icon.png +0 -0
  12. docs/en/_static/images/robot.png +0 -0
  13. docs/en/_static/js/collapsed.js +1 -0
  14. docs/en/_static/js/table.js +31 -0
  15. docs/en/_templates/autoapi/index.rst +14 -0
  16. docs/en/_templates/autoapi/python/module.rst +112 -0
  17. docs/en/_templates/classtemplate.rst +14 -0
  18. docs/en/changelog.md +16 -0
  19. docs/en/conf.py +108 -0
  20. docs/en/docutils.conf +2 -0
  21. docs/en/get_started/install.md +19 -0
  22. docs/en/get_started/quickstart.md +485 -0
  23. docs/en/index.rst +40 -0
  24. docs/en/make.bat +36 -0
  25. docs/en/requirements.txt +4 -0
  26. docs/en/switch_language.md +3 -0
  27. docs/en/tutorials/action.md +400 -0
  28. docs/imgs/lagent_icon.png +0 -0
  29. docs/imgs/lagent_logo.png +0 -0
  30. docs/zh_cn/.readthedocs.yaml +15 -0
  31. docs/zh_cn/Makefile +20 -0
  32. docs/zh_cn/_static/css/readthedocs.css +6 -0
  33. docs/zh_cn/_static/images/lagent_icon.png +0 -0
  34. docs/zh_cn/_static/images/robot.png +0 -0
  35. docs/zh_cn/_static/js/collapsed.js +1 -0
  36. docs/zh_cn/_static/js/table.js +31 -0
  37. docs/zh_cn/_templates/autoapi/index.rst +14 -0
  38. docs/zh_cn/_templates/autoapi/python/module.rst +112 -0
  39. docs/zh_cn/_templates/classtemplate.rst +14 -0
  40. docs/zh_cn/conf.py +108 -0
  41. docs/zh_cn/cp_origin_docs.sh +9 -0
  42. docs/zh_cn/docutils.conf +2 -0
  43. docs/zh_cn/get_started/install.md +19 -0
  44. docs/zh_cn/index.rst +39 -0
  45. docs/zh_cn/make.bat +36 -0
  46. docs/zh_cn/switch_language.md +3 -0
  47. docs/zh_cn/tutorials/action.md +398 -0
  48. examples/.ipynb_checkpoints/agent_api_web_demo-checkpoint.py +196 -0
  49. examples/.ipynb_checkpoints/multi_agents_api_web_demo-checkpoint.py +198 -0
  50. examples/agent_api_web_demo.py +196 -0
.gitattributes CHANGED
@@ -1,35 +1,47 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bin.* filter=lfs diff=lfs merge=lfs -text
5
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.model filter=lfs diff=lfs merge=lfs -text
12
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
13
+ *.onnx filter=lfs diff=lfs merge=lfs -text
14
+ *.ot filter=lfs diff=lfs merge=lfs -text
15
+ *.parquet filter=lfs diff=lfs merge=lfs -text
16
+ *.pb filter=lfs diff=lfs merge=lfs -text
17
+ *.pt filter=lfs diff=lfs merge=lfs -text
18
+ *.pth filter=lfs diff=lfs merge=lfs -text
19
+ *.rar filter=lfs diff=lfs merge=lfs -text
20
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
21
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
22
+ *.tflite filter=lfs diff=lfs merge=lfs -text
23
+ *.tgz filter=lfs diff=lfs merge=lfs -text
24
+ *.xz filter=lfs diff=lfs merge=lfs -text
25
+ *.zip filter=lfs diff=lfs merge=lfs -text
26
+ *.zstandard filter=lfs diff=lfs merge=lfs -text
27
+ *.tfevents* filter=lfs diff=lfs merge=lfs -text
28
+ *.db* filter=lfs diff=lfs merge=lfs -text
29
+ *.ark* filter=lfs diff=lfs merge=lfs -text
30
+ **/*ckpt*data* filter=lfs diff=lfs merge=lfs -text
31
+ **/*ckpt*.meta filter=lfs diff=lfs merge=lfs -text
32
+ **/*ckpt*.index filter=lfs diff=lfs merge=lfs -text
33
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
34
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
35
+ *.gguf* filter=lfs diff=lfs merge=lfs -text
36
+ *.ggml filter=lfs diff=lfs merge=lfs -text
37
+ *.llamafile* filter=lfs diff=lfs merge=lfs -text
38
+ *.pt2 filter=lfs diff=lfs merge=lfs -text
39
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
40
+ *.npy filter=lfs diff=lfs merge=lfs -text
41
+ *.npz filter=lfs diff=lfs merge=lfs -text
42
+ *.pickle filter=lfs diff=lfs merge=lfs -text
43
+ *.pkl filter=lfs diff=lfs merge=lfs -text
44
+ *.tar filter=lfs diff=lfs merge=lfs -text
45
+ *.wasm filter=lfs diff=lfs merge=lfs -text
46
+ *.zst filter=lfs diff=lfs merge=lfs -text
47
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.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,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -r requirements/optional.txt
2
+ -r requirements/runtime.txt
3
+
4
+ torch==2.1.2
5
+ torchvision==0.16.2
6
+ torchaudio==2.1.2
7
+ termcolor==2.4.0
8
+ streamlit==1.39.0
9
+ class_registry==2.1.2
10
+ datasets==3.1.0
11
+ griffe==0.48.0
12
+
13
+ torch==2.1.2
14
+ torchvision==0.16.2
15
+ torchaudio==2.1.2
16
+ termcolor==2.4.0
17
+ streamlit==1.39.0
18
+ class_registry==2.1.2
19
+ datasets==3.1.0
20
+ # -r requirements/optional.txt
21
+ google-search-results
22
+ lmdeploy>=0.2.5
23
+ pillow
24
+ python-pptx
25
+ timeout_decorator
26
+ torch
27
+ transformers>=4.34,<=4.40
28
+ vllm>=0.3.3
29
+ # -r requirements/runtime.txt
30
+ aiohttp
31
+ arxiv
32
+ asyncache
33
+ asyncer
34
+ distro
35
+ duckduckgo_search==5.3.1b1
36
+ filelock
37
+ func_timeout
38
+ griffe<1.0
39
+ json5
40
+ jsonschema
41
+ jupyter==1.0.0
42
+ jupyter_client==8.6.2
43
+ jupyter_core==5.7.2
44
+ pydantic==2.6.4
45
+ requests
46
+ termcolor
47
+ tiktoken
48
+ timeout-decorator
49
+ typing-extensions
50
+ 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
MANIFEST.in ADDED
@@ -0,0 +1 @@
 
 
1
+ include requirements/*.txt
app.py CHANGED
@@ -1 +1,30 @@
1
- 111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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()