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