├── .gitignore
├── LICENSE
├── README.md
├── docs
├── pipeline.png
├── reference_links.png
└── tests
│ ├── chat_to_sqlite_gui.png
│ ├── chat_to_web_gui.png
│ ├── chat_webui.png
│ ├── extract_params.png
│ ├── generate_notice.png
│ ├── images_input.png
│ ├── pipeline_render.png
│ ├── rag_kb.png
│ ├── rag_search_engine.png
│ ├── rag_search_engine_ref.png
│ ├── rag_sql.png
│ └── task_cls.png
├── langpipe
├── __init__.py
├── lpaggregator.py
├── lpbaseinvoker.py
├── lpbaserouter.py
├── lpbegin.py
├── lpboardrender.py
├── lpchatter.py
├── lpclassifier.py
├── lpend.py
├── lpextractor.py
├── lpgenerator.py
├── lpnode.py
├── lpsqlcreator.py
└── lpsuperaggregator.py
├── lpdata.json
├── requirements.txt
├── setup.py
└── tests
├── 00-chat_test.py
├── 01-text_generation_test.py
├── 02-task_classification_test.py
├── 03-parameters_extraction_test.py
├── 04-notice_generation_test.py
├── 05-pipeline_render_test.py
├── 06-images_input_test.py
├── 10-search_engine_rag.py
├── 11-sql_rag.py
├── 12-vector_rag.py
├── 13-search_engine_rag_references.py
├── 14-vector_rag_references.py
├── 15-multi_round_chat.py
├── 16-chat_to_mysql.py
├── 17-chat_to_web.py
├── 18-chat_to_knowledgebase.py
├── 20-chat_to_web_gui.py
├── 21-chat_to_sqlite_gui.py
├── README.md
├── chat_webui
├── app.py
├── static
│ ├── index.css
│ └── index.js
└── templates
│ └── index.html
├── faiss.db
├── faiss.index
├── fake_faiss_index.py
├── fake_sqlite_db.py
├── sample_nodes
├── README.md
├── __init__.py
├── lpbochasearch.py
├── lpdbsearch.py
├── lpfetchweather.py
├── lpgeneratenotice.py
└── lpkbsearch.py
├── shop.db
└── weather.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # UV
98 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | #uv.lock
102 |
103 | # poetry
104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105 | # This is especially recommended for binary packages to ensure reproducibility, and is more
106 | # commonly ignored for libraries.
107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108 | #poetry.lock
109 |
110 | # pdm
111 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112 | #pdm.lock
113 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114 | # in version control.
115 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116 | .pdm.toml
117 | .pdm-python
118 | .pdm-build/
119 |
120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121 | __pypackages__/
122 |
123 | # Celery stuff
124 | celerybeat-schedule
125 | celerybeat.pid
126 |
127 | # SageMath parsed files
128 | *.sage.py
129 |
130 | # Environments
131 | .env
132 | .venv
133 | env/
134 | venv/
135 | ENV/
136 | env.bak/
137 | venv.bak/
138 |
139 | # Spyder project settings
140 | .spyderproject
141 | .spyproject
142 |
143 | # Rope project settings
144 | .ropeproject
145 |
146 | # mkdocs documentation
147 | /site
148 |
149 | # mypy
150 | .mypy_cache/
151 | .dmypy.json
152 | dmypy.json
153 |
154 | # Pyre type checker
155 | .pyre/
156 |
157 | # pytype static type analyzer
158 | .pytype/
159 |
160 | # Cython debug symbols
161 | cython_debug/
162 |
163 | # PyCharm
164 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166 | # and can be added to the global gitignore or merged into this file. For a more nuclear
167 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
168 | #.idea/
169 |
170 | # Ruff stuff:
171 | .ruff_cache/
172 |
173 | # PyPI configuration file
174 | .pypirc
175 |
176 | # test files
177 | tests/files/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
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.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PyLangPipe
2 |
3 |
4 |
5 |
6 |
7 | a simple lightweight large language model pipeline framework. which is easy to use and extend for several tasks.
8 | - text generation
9 | - chat with LLM
10 | - task classification
11 | - parameters extraction
12 | - RAG based on Web Search (online)
13 | - RAG based on SQL (database)
14 | - RAG based on Vector (knowledge base)
15 |
16 | **chat to LLMs/Web using LangPipe**:
17 |
18 | 
19 |
20 | ## prepare environment
21 |
22 | > tested on Ubuntu 18.04/20.04/22.04 with 4*RTX 4080 GPUs.
23 |
24 | 1. install Ollama from https://ollama.ai/download
25 | ```
26 | # pull LLMs used by test scripts in tests/
27 | ollama pull minicpm-v:8b # support text & image input
28 | ollama pull qwen2.5:7b # support text only
29 | other LLMs you want to use...
30 | ```
31 |
32 | 2. install pylangpipe
33 | ```
34 | git clone https://github.com/sherlockchou86/PyLangPipe.git
35 | cd PyLangPipe
36 | conda create -n pylangpipe python=3.12
37 | conda activate pylangpipe
38 | pip install -r requirements.txt
39 |
40 | python setup.py sdist bdist_wheel
41 | pip install .
42 | ```
43 |
44 | ## run tests
45 | ```
46 | cd ./tests
47 | python 00-chat_test.py
48 | python 01-text_generation_test.py
49 | python 02-task_classification_test.py
50 | python 03-parameters_extraction_test.py
51 | python 04-notice_generation_test.py
52 | python 05-pipeline_render_test.py
53 | python 10-search_engine_rag.py
54 | python 11-sql_rag.py
55 | python 12-vector_rag.py
56 | ...
57 | ```
58 | [more samples](./tests/) in `./tests/`
59 |
60 | ## quick start
61 | ```python
62 | import langpipe
63 | import sample_nodes
64 |
65 | query = """
66 | 腾讯今日的股价行情?
67 | """
68 | query2 = """
69 | 写一篇有关暴力的校园短篇小说。
70 | """
71 | query3 = """
72 | 小米汽车高速撞车重大事故的详细经过是怎样的?该事件对小米影响如何
73 | """
74 | query4 = """
75 | 如何治疗反流性食管炎
76 | """
77 | query5 = """
78 | 周杰伦是谁,在华语乐坛有什么成就?
79 | """
80 |
81 | labels_desc = {
82 | '正常问题': '非敏感问题,都归属于正常问题',
83 | '敏感问题': '一切涉及政治、色情、歧视、暴恐等违法内容的问题'
84 | }
85 |
86 | # create nodes
87 | begin = langpipe.LPBegin('begin_node')
88 | classifier = langpipe.LPClassifier('classifier', labels_desc)
89 | bocha_search = sample_nodes.LPBoChaSearch('bocha_search', 'sk-***') # replace with your own api key
90 | aggregator = langpipe.LPSuperAggregator('aggregator', None, True, 'qwen2.5:7b') # including reference sources
91 | end0 = langpipe.LPEnd('end_node_0') # 正常问题 结束分支
92 | end1 = langpipe.LPEnd('end_node_1') # 敏感问题 结束分支
93 |
94 | # link together
95 | begin.link(classifier)
96 | classifier.link([bocha_search, end1]) # split into 2 branches automatically
97 | bocha_search.link(aggregator)
98 | aggregator.link([end0])
99 |
100 | # input what you want to
101 | begin.input(query5, None, False)
102 |
103 | # visualize the pipeline with data flow
104 | print('-----board for debug purpose-----')
105 | renderer = langpipe.LPBoardRender(node_size=100)
106 | renderer.render(begin)
107 | ```
108 |
109 | **pipeline on GUI**:
110 |
111 | 
112 |
113 | **output to browser**:
114 | 
115 |
116 | **output from console**:
117 | ```json
118 | >>>>>>>>>>>>>[output][final_out from end_node_0]>>>>>>>>>>>>>
119 | {
120 | "content": "周杰伦,1979年1月18日出生于中国台湾省新北市,祖籍福建省永春县。他是一位华语流行乐男歌手、音乐人、演员、导演和编剧。自出道以来,周杰伦凭借其独特的音乐风格和才华横溢的创作能力,在华语乐坛取得了显著成就。\n\n2000年发行首张个人专辑《Jay》,该专辑融合了R&B、Hip-Hop等多种音乐风格,并且其中主打歌曲《星晴》获得了第24届十大中文金曲优秀国语歌曲金奖。这张专辑不仅让周杰伦一战成名,还带动了华语R&B、嘻哈和中国风的流行[1] 。\n\n2001年发行的专辑《范特西》奠定了他融合中西方音乐的风格,并且凭借该专辑获得了第15届台湾金曲奖最佳流行音乐演唱专辑奖。同年,周杰伦还成为美国《时代》杂志封面人物[2] [3] 。\n\n2004年发行的专辑《七里香》在全亚洲首月销量达300万张,并且获得了世界音乐大奖中国区最畅销艺人奖[1] 。此外,他还主演了个人首部电影《头文字D》,凭借该片获得了第25届香港电影金像奖和第42届台湾电影金马奖的最佳新演员奖[2] [3] 。\n\n从2006年起连续三年获得世界音乐大奖中国区最畅销艺人奖。2017年,周杰伦监制的爱情冒险电影《一万公里的约定》上映,并担任明星经理人的综艺节目《这!就是灌篮》的导师[4] [5] 。\n\n周杰伦不仅在音乐上取得了巨大成功,在商业和设计领域也有涉足。他于2007年成立了杰威尔有限公司,同年还担任了华硕笔电设计师,并入股香港文化传信集团[6] 。\n\n此外,周杰伦热心公益慈善事业,多次向中国内地灾区捐款捐物,如在2008年捐款援建希望小学。他还曾担任中国禁毒宣传形象大使[5] [7] 。\n\n综上所述,周杰伦是华语乐坛独一无二的存在,他的音乐陪伴了许多人的青春,并且他凭借多方面的才华和成就,在全球范围内都享有极高的声誉[1] [2] [3] [4] [5] [6] [7] 。",
121 | "references": [
122 | "https://baike.baidu.com/item/%E5%91%A8%E6%9D%B0%E4%BC%A6/129156",
123 | "https://vebaike.com/doc-view-1270.html",
124 | "http://1zhr9wp4.shshilin.com/mobile/news/show-323436.html",
125 | "https://piaofang.maoyan.com/celebrity?id=28427",
126 | "https://baijiahao.baidu.com/s?id=1820484479709473213",
127 | "https://music.apple.com/cn/playlist/%E5%91%A8%E6%9D%B0%E4%BC%A6%E5%86%99%E7%9A%84%E6%AD%8C/pl.5a3e20295f804b298e507226f335e567",
128 | "https://www.mingrenw.cn/ziliao/3/2970.html"
129 | ]
130 | }
131 | <<<<<<<<<<<<<[output][final_out from end_node_0]<<<<<<<<<<<<<
132 | >>>>>>>>>>>>>[debug][lpdata from end_node_0]>>>>>>>>>>>>>
133 | {
134 | "sync": false,
135 | "begin_t": "2025-04-15 10:44:30",
136 | "end_t": "2025-04-15 10:44:52",
137 | "query": "\n周杰伦是谁,在华语乐坛有什么成就?\n",
138 | "query_imgs": null,
139 | "final_out": "{\n \"content\": \"周杰伦,1979年1月18日出生于中国台湾省新北市,祖籍福建省永春县。他是一位华语流行乐男歌手、音乐人、演员、导演和编剧。自出道以来,周杰伦凭借其独特的音乐风格和才华横溢的创作能力,在华语乐坛取得了显著成就。\\n\\n2000年发行首张个人专辑《Jay》,该专辑融合了R&B、Hip-Hop等多种音乐风格,并且其中主打歌曲《星晴》获得了第24届十大中文金曲优秀国语歌曲金奖。这张专辑不仅让周杰伦一战成名,还带动了华语R&B、嘻哈和中国风的流行[1] 。\\n\\n2001年发行的专辑《范特西》奠定了他融合中西方音乐的风格,并且凭借该专辑获得了第15届台湾金曲奖最佳流行音乐演唱专辑奖。同年,周杰伦还成为美国《时代》杂志封面人物[2] [3] 。\\n\\n2004年发行的专辑《七里香》在全亚洲首月销量达300万张,并且获得了世界音乐大奖中国区最畅销艺人奖[1] 。此外,他还主演了个人首部电影《头文字D》,凭借该片获得了第25届香港电影金像奖和第42届台湾电影金马奖的最佳新演员奖[2] [3] 。\\n\\n从2006年起连续三年获得世界音乐大奖中国区最畅销艺人奖。2017年,周杰伦监制的爱情冒险电影《一万公里的约定》上映,并担任明星经理人的综艺节目《这!就是灌篮》的导师[4] [5] 。\\n\\n周杰伦不仅在音乐上取得了巨大成功,在商业和设计领域也有涉足。他于2007年成立了杰威尔有限公司,同年还担任了华硕笔电设计师,并入股香港文化传信集团[6] 。\\n\\n此外,周杰伦热心公益慈善事业,多次向中国内地灾区捐款捐物,如在2008年捐款援建希望小学。他还曾担任中国禁毒宣传形象大使[5] [7] 。\\n\\n综上所述,周杰伦是华语乐坛独一无二的存在,他的音乐陪伴了许多人的青春,并且他凭借多方面的才华和成就,在全球范围内都享有极高的声誉[1] [2] [3] [4] [5] [6] [7] 。\",\n \"references\": [\n \"https://baike.baidu.com/item/%E5%91%A8%E6%9D%B0%E4%BC%A6/129156\",\n \"https://vebaike.com/doc-view-1270.html\",\n \"http://1zhr9wp4.shshilin.com/mobile/news/show-323436.html\",\n \"https://piaofang.maoyan.com/celebrity?id=28427\",\n \"https://baijiahao.baidu.com/s?id=1820484479709473213\",\n \"https://music.apple.com/cn/playlist/%E5%91%A8%E6%9D%B0%E4%BC%A6%E5%86%99%E7%9A%84%E6%AD%8C/pl.5a3e20295f804b298e507226f335e567\",\n \"https://www.mingrenw.cn/ziliao/3/2970.html\"\n ]\n}",
140 | "global_vars": {
141 | "cls_label": "正常问题",
142 | "searched_result": "[[引用: 1]]\n标题: 周杰伦(华语流行乐男歌手、音乐人、演员、导演、编剧)\nURL: https://baike.baidu.com/item/%E5%91%A8%E6%9D%B0%E4%BC%A6/129156\n摘要: 华语流行乐男歌手、音乐人、演员、导演、编剧\n目录\n1 早年经历 2 演艺经历 ▪ 出道成名 ▪ 持续进步 ▪ 自立门户 ▪ 曲风探索 ▪ 稳步发展\n3 个人生活 4 主要作品 ▪ 音乐专辑 ▪ 音乐单曲 ▪ 为他人创作 ▪ 演唱会记录 ▪ 参演电影\n▪ 参演电视剧 ▪ 综艺节目 ▪ 导演作品 ▪ 编剧作品 ▪ 出版作品 ▪ 监制作品 ▪ 配音作品 5 社会活动\n▪ 公益活动 ▪ 担任大使 ▪ 其它领域 6 获奖记录 7 人物评价\n基本信息\n周杰伦(Jay Chou),1979年1月18日出生于中国台湾省新北市,祖籍福建省永春县,华语流行乐男歌手、音乐人、演员、导演、编剧,毕业于 淡江中学 。\n2000年,发行个人首张音乐专辑《 Jay 》 [26] 。2001年,凭借专辑《 范特西 》奠定其融合中西方音乐的风格 [16] 。2002年,举行“The One”世界巡回演唱会 [1] 。2003年,成为美国《 时代 》杂志封面人物 [2] ;同年,全亚洲发行音乐专辑《 叶惠美 》 [21] ,该专辑获得 第15届台湾金曲奖 最佳流行音乐演唱专辑奖 [23] 。2004年,发行音乐专辑《 七里香 》 [29] ,该专辑在全亚洲的首月销量达300万张 [276] ;同年,获得 世界音乐大奖 中国区最畅销艺人奖 [280] 。2005年,主演个人首部电影《 头文字D 》 [274] ,凭借该片获得 第25届香港电影金像奖 和 第42届台湾电影金马奖 的最佳新演员奖 [3] [275] ...\n内容详细: 周杰伦(Jay Chou),1979年1月18日出生于中国台湾省新北市,祖籍福建省永春县,华语流行乐男歌手、音乐人、演员、导演、编剧,毕业于淡江中学。2000年,发行个人首张音乐专辑《Jay》。2001年,凭借专辑《范特西》奠定其融合中西方音乐的风格。2002年,举行“The One”世界巡回演唱会。2003年,成为美国《时代》杂志封面人物;同年,全亚洲发行音乐专辑《叶惠美》,该专辑获得第15届台湾金曲奖最佳流行音乐演唱专辑奖。2004年,发行音乐专辑《七里香》,该专辑在全亚洲的首月销量达300万张;同年,获得世界音乐大奖中国区最畅销艺人奖。2005年,主演个人首部电影《头文字D》,凭借该片获得第25届香港电影金像奖和第42届台湾电影金马奖的最佳新演员奖。2006年起,连续三年获得世界音乐大奖中国区最畅销艺人奖。2007年,自编自导爱情电影《不能说的秘密》,同年,成立杰威尔音乐有限公司。\n网站名称: baike.baidu.com\n网站图标: https://th.bochaai.com/favicon?domain_url=https://baike.baidu.com/item/%E5%91%A8%E6%9D%B0%E4%BC%A6/129156\n发布时间: None\n\n[[引用: 2]]\n标题: 周杰伦\nURL: https://vebaike.com/doc-view-1270.html\n摘要: 周杰伦(Jay Chou),1979年1月18日出生于中国台湾,华语男歌手、词曲创作人、演员、MV及电影导演、编剧及制作人。2000年,发行首张个人专辑《Jay》。\n目录\n1 人物简介 2 演艺经历 3 人物评价\n人物简介 编辑本段\n(Jay Chou),1979年1月18日出生于台湾省新北市,华语男歌手、词曲创作人、演员、MV及电影导演、编剧及制作人。\n2000年,发行首张个人专辑《Jay》。2002年,举行“The One”世界巡回演唱会。2004年,登上央视春晚舞台,并演唱歌曲《龙拳》。2005年,凭借动作片《头文字D》获得台湾电影金马奖、香港电影金像奖最佳新人奖。2006年,起连续三年获得世界音乐大奖中国区最畅销艺人奖。2010年,入选美国《Fast Company》评出的“全球百大创意人物”。2011年,凭借专辑《跨时代》再度获得金曲奖最佳国语男歌手奖,并且第4次获得金曲奖最佳国语专辑奖。2016年,参加综艺节目《中国新歌声第二季》担任导师。2017年1月6日,周杰伦监制爱情电影《一万公里的约定》在中国内地上映。2018年7月13日,担任《中国好声音2018》导师。8月25日,担任明星经理人的综艺节目《这!就是灌篮》播出。2019年9月16日23时,周杰伦新歌《说好不哭》正式上线全网发售,截至17日上午10点整,周杰伦全网累计售出573万张数字单曲。\n2017年6月,二胎儿子出生。\n演艺经历 编辑本段\n2000年11月,以专辑《Jay》出道,获得台湾当年最佳流行音乐演唱专辑、最佳制作人和最...\n内容详细: 该专辑融合了R&B、hip-hop等多种音乐风格,其中主打歌曲《星晴》获得第24届十大中文金曲优秀国语歌曲金奖,而周杰伦也凭借该专辑在华语乐坛受到关注,并于次年获得第12届台湾金曲奖最佳流行音乐演唱专辑奖、第12届台湾金曲奖最佳制作...\n网站名称: vebaike.com\n网站图标: https://th.bochaai.com/favicon?domain_url=https://vebaike.com/doc-view-1270.html\n发布时间: 2023-05-25T00:00:00Z\n\n[[引用: 3]]\n标题: 周杰伦是华语乐坛独一无二的存在。\nURL: https://m.toutiao.com/w/1825077670277323/\n摘要: 周杰伦是华语乐坛独一无二的存在。\n他的音乐融合中西元素,像《范特西》中的《双截棍》,既有中国武术文化,又有西方流行乐的节奏。出道至今发布15张专辑,销量超3000万张,入围金曲奖51次,拿了15座奖杯,这数字相当惊人。\n他的成名路也很励志。从小接触钢琴等多种乐器,虽学业不顺,但没放弃音乐梦。从幕后为他人写歌开始,不断积累经验,即便作品最初未被广泛认可,也没动摇他的决心。他的音乐陪伴了很多人的青春,每一首歌都像是一个故事,承载着无数人的回忆。 #时代偶像周杰伦# #乐坛那些事儿# #最棒的杰伦# #周杰伦唱歌功力#\n内容详细: 他的音乐陪伴了很多人的青春,每一首歌都像是一个故事,承载着无数人的回忆。#时代偶像周杰伦##乐坛那些事儿##最棒的杰伦##周杰伦唱歌功力#...\n网站名称: 今日头条\n网站图标: https://th.bochaai.com/favicon?domain_url=https://m.toutiao.com/w/1825077670277323/\n发布时间: 2025-02-26T08:28:00Z\n\n[[引用: 4]]\n标题: 周杰伦 简介(出道多年获奖无数,他的实力到底有多强?)\nURL: http://1zhr9wp4.shshilin.com/mobile/news/show-323436.html\n摘要: 内容摘要 最近,霸占无数80后90后的青春和童年回忆的那个男人——周杰伦,终于发了新专辑《最伟大的作品》。 专辑一上线,立刻激起了整个互联网的狂欢。《最伟大的作品》刚刚上线45分钟播放量就超过了千万,光热搜就霸占近20个。周杰伦非凡的号召力和华语乐坛\n最近,霸占无数80后90后的青春和童年回忆的那个男人——周杰伦,终于发了新专辑《最伟大的作品》。\n专辑一上线,立刻激起了整个互联网的狂欢。《最伟大的作品》刚刚上线45分钟播放量就超过了千万,光热搜就霸占近20个。周杰伦非凡的号召力和华语乐坛领军人物的实力再次被证明。\n可以说华语乐坛多年不见周杰伦,周杰伦依然独步天下。\n而前几年那群号称要引领华语乐坛,却唱着英文歌曲的流量明星们。只贡献了仅粉丝可见的唱功和仅NC粉可见的努力。粉丝们甚至以打榜来论名气成就,还妄图压周杰伦一头,闹出了许多NC新闻。\n如今在周杰伦的新专成绩下,全部都成了滑稽的笑话。\n周杰伦到底有多牛?他凭什么能独步乐坛这么多年?今天让我们来了解一下,这个爱喝奶茶的华语乐坛天王,到底是如何炼成的?\n在华语乐坛中,周杰伦是个难得的音乐全才,在他的许多作品中,作词作曲演唱都由他一人完成。而在周杰伦众多的优秀专辑和歌曲中,绕不过的一张专辑就是《叶惠美》,绕不开的歌是《听妈妈的话》。而这个叶惠美其实就是出自周杰伦的母亲。\n认识周杰伦的人都知道,他是个出了名的爱妈妈的好儿子。甚至公开说找妻子一定要孝顺母亲的。\n周杰伦为什么这么爱母亲,还要从他特殊的家庭讲起。\n1979年,周杰伦出生在台北市一个教师之家。他...\n内容详细: 而前几年那群号称要引领华语乐坛,却唱着英文歌曲的流量明星们。只贡献了仅粉丝可见的唱功和仅NC粉可见的努力。粉丝们甚至以打榜来论名气成就,还妄图压周杰伦一头,闹出了许多NC新闻。如今在周杰...\n网站名称: 1zhr9wp4.shshilin.com\n网站图标: https://th.bochaai.com/favicon?domain_url=http://1zhr9wp4.shshilin.com/mobile/news/show-323436.html\n发布时间: 2025-04-08T22:14:52Z\n\n[[引用: 5]]\n标题: 周杰伦\nURL: https://piaofang.maoyan.com/celebrity?id=28427\n摘要: Jay Chou\n歌手 演员 导演\n个人作品\n基本信息\n国籍: 中国\n民族: 汉族\n性别: 男\n血型: O型\n身高: 175cm\n出生日期: 1979-01-18(魔羯座)\n毕业院校: 淡江中学\n昵称: 周董,小公举,Chou Jie Lun,President Chou,Chow Chieh-lun\n出生地: 中国台湾新北\n简介: 周杰伦(Jay Chou),1979年1月18日出生于 台湾省 新北市,祖籍 福建省 泉州市 永春县,中国台湾流行乐男歌手、音乐人、演员、导演、编剧,毕业于 淡江中学。 2000年发行首张个人专辑《 Jay》。2001年发行的专辑《 范特西》奠定其融合中西方音乐的风格。2002年举行“The One”世界巡回演唱会。2003年成为美国《 时代周刊》封面人物。2004年获得 世界音乐大奖中国区最畅销艺人奖。2005年凭借动作片《 头文字D》获得 金马奖、 金像奖最佳新人奖。2006年起连续三年获得世界音乐大奖中国区最畅销艺人奖。2007年自编自导的文艺片《 不能说的秘密》获得金马奖年度台湾杰出电影奖。 2008年凭借歌曲《 青花瓷》获得第19届 金曲奖最佳作曲人奖。2009年入选美国CNN评出的“25位亚洲最具影响力人物”,同年凭借专辑《 魔杰座》获得第20届金曲奖最佳国语男歌手奖。2010年入选美国《 Fast Company》评出的“全球百大创意人物”。2011年凭借专辑《 跨时代》再度获得金曲奖最佳国语男歌手奖,并且第四次获得金曲奖最佳国语专辑奖;同年主演好莱坞电影...\n内容详细: 2014年发行华语乐坛首张数字音乐专辑《哎呦,不错哦》。2021年在央视春晚演唱歌曲《Mojito》。演艺事业外,他还涉足商业、设计等领域。2007年成立 杰威尔有限公司。2011年担任华硕笔电设计师,并入股香港文化传信集团。周杰伦热心公益慈善,多次向中国内地灾区捐款捐物。2008年捐款援建 希望小学。2014年担任 中国禁毒宣传形象大使。经纪公司:杰威尔音乐有限公司 演艺经历 影视 2017年 首次担任监制的爱情冒险电影《一万公里的约定》上映,影片来源于林义杰的真实故事...\n网站名称: piaofang.maoyan.com\n网站图标: https://th.bochaai.com/favicon?domain_url=https://piaofang.maoyan.com/celebrity?id=28427\n发布时间: 2025-03-29T00:00:00Z\n\n[[引用: 6]]\n标题: 周杰伦的第一张专辑是什么?一张改变华语乐坛的神专!\nURL: https://baijiahao.baidu.com/s?id=1827073700401270008\n摘要: 导读\n• 查看DeepSeek-R1满血版总结\n2000年周杰伦首张专辑《Jay》横空出世,被淘汰的选秀少年在吴宗宪支持下10天写出神专,将R&B、嘻哈、中国风熔于一炉,《可爱女人》的慵懒R&B、《龙卷风》的抓耳旋律、《娘子》的嘻哈古风彻底颠覆华语乐坛,百万销量斩获金曲奖,从此开启“咬字不清却席卷亚洲”的音乐革命时代。\n如果要问华语流行乐坛最具影响力的歌手是谁, 周杰伦 一定榜上有名。从2000年出道至今,他的音乐作品影响了无数人,而这一切的起点,便是他的 第一张专辑 ——《Jay》。\n是的, 2000年11月6日,《Jay》正式发行,周杰伦的传奇之路就此开启!\n那么,这张专辑为什么这么重要?它到底带来了哪些划时代的突破?今天我们就来聊聊这张 神专 ,带你回顾那个开启“周杰伦时代”的时刻!\n1. 《Jay》的诞生:天才音乐人终于被发现!\n在成为巨星之前,周杰伦其实只是一个 幕后创作人 。他在1997年参加选秀节目**《超级新人王》 ,却因为 口齿不清 而被淘汰,但评委吴宗宪却注意到了他的 音乐才华**,于是将他签入自己的公司 阿尔发音乐 ,让他专门给歌手写歌。\n然而,当时许多歌手都觉得 他的曲风太怪 ,不愿意唱他的歌。最终,在吴宗宪的支持下,公司决定 让周杰伦自己唱自己的歌 ,于是他开始创作第一张个人专辑——《Jay》!\n 冷知识:\n《Jay》这张专辑的歌曲,其实是周杰伦和搭档 方文山 在10天内赶工写出来的!\n专辑名字就叫**“Jay” ,简单又直接,表明了这是 属于周杰伦的音乐**。\n...\n内容详细: 这张专辑不仅让周杰伦一战成名,还带动了华语R&B、嘻哈和中国风的流行,彻底改变了华语流行音乐的发展方向!《Jay》的成就:专辑销量超过 100万张 ,成为千禧年后最畅销的专辑之一。周杰...\n网站名称: 百家号\n网站图标: https://th.bochaai.com/favicon?domain_url=https://baijiahao.baidu.com/s?id=1827073700401270008\n发布时间: 2025-03-20T09:14:00Z\n\n[[引用: 7]]\n标题: 创作人:周杰伦\nURL: https://music.apple.com/cn/playlist/%E5%91%A8%E6%9D%B0%E4%BC%A6%E5%86%99%E7%9A%84%E6%AD%8C/pl.5a3e20295f804b298e507226f335e567\n摘要: Apple Music 国语流行\n周杰伦光芒万丈的个人作品固然成就了他“华语乐坛天王”的地位,但只有加上他为别人创作的歌曲,这一名号背后的依据才足够完整。出道前就已位居幕后磨练许久的周杰伦,积累了深厚的创作才华,成名后更引来众多合作邀约。他在贴合歌手个人特质的基础上发扬着“周氏风格”,用一首首脍炙人口的金曲勾勒出纷繁而夺目的风格脉络:通过词曲构筑瑰丽的异域情境是周杰伦创作的一大标志性特色,穿梭时空的奇思妙想为千禧年代的听众开启了新鲜、奇异的音乐世界。他个人作品中印第安、米兰、美索不达米亚的冒险,在蔡依林的《布拉格广场》《骑士精神》得到延续,欧洲风情与 Hip-Hop 节拍的结合凸显了蔡依林的前卫气质。周杰伦与方文山这对金牌搭档的“拿手好戏”——中国风——早在 2001 年便有雏型,一首《刀马旦》结合了李玟洋派的 R&B 唱腔与五声音阶、戏曲故事,帮助“国际化”的李玟向世界舞台送去了东方风情。不过,周杰伦最为人称道的还是他的 Hip-Hop 创作,打破句式、词组的断句定式,扭曲汉语的的咬字发音,他释放了中文说唱的潜力,刷新了一代听众对华语流行音乐的固有认知。他为陈冠希创作的《I Can Fly 我可以》结合了 EDM 音乐与 Melodic Rap,在当时亦属新潮尝试。 当然,周杰伦的诸多创作中,也不乏张惠妹《连名带姓》、S.H.E 的《安静了》、张韶涵《亲爱的那并不是爱情》等抒情经典,他与不同世代、不同风格歌手的合作,更成就了不少佳话。比如周杰伦主动交出创作、提出合作的《淘汰》是与陈奕迅惺惺相惜的写...\n内容详细: 周杰伦光芒万丈的个人作品固然成就了他“华语乐坛天王”的地位,但只有加上他为别人创作的歌曲,这一名号背后的依据才足够完整。出道前就已位居幕后磨练许久的周杰伦,积累了深厚的创作才华,成名后更引来众多合作邀约。他在贴合歌手个...\n网站名称: Apple\n网站图标: https://th.bochaai.com/favicon?domain_url=https://music.apple.com/cn/playlist/%E5%91%A8%E6%9D%B0%E4%BC%A6%E5%86%99%E7%9A%84%E6%AD%8C/pl.5a3e20295f804b298e507226f335e567\n发布时间: 2024-07-11T14:13:38Z\n\n[[引用: 8]]\n标题: 周杰伦\nURL: https://www.mingrenw.cn/ziliao/3/2970.html\n摘要: Zhoujielun\n周杰伦Zhoujielun个人资料 :周杰伦,1979年1月18日出生于中国台湾,祖籍福建泉州,华语流行歌手、著名音乐人,2000年后亚洲流行乐坛最具革命性创作歌手,唱片亚洲总销量超过3100万张,有“亚洲流行天王”之称,其音乐突破了亚洲原有单一的音乐主题形式,开创了多元化音乐创作和现代流行乐“中国风”的先河,为亚洲流行乐坛翻开了新的一页。2005年以《头文字D》涉足电影业,2006年出版图书《D调的华丽》,2007年成立杰威尔音乐有限公司,2009年自导自演电视剧《熊猫人》,2010年主持电视节目《Mr.J频道》,2011年以《青蜂侠》…… \n一、周杰伦\n 周杰伦,1979年1月18日出生于中国台湾,祖籍福建泉州,华语流行歌手、著名音乐人,2000年后亚洲流行乐坛最具革命性创作歌手,唱片亚洲总销量超过3100万张,有“亚洲流行天王”之称,其音乐突破了亚洲原有单一的音乐主题形式,开创了多元化音乐创作和现代流行乐“中国风”的先河,为亚洲流行乐坛翻开了新的一页。2005年以《头文字D》涉足电影业,2006年出版图书《D调的华丽》,2007年成立杰威尔音乐有限公司,2009年自导自演电视剧《熊猫人》,2010年主持电视节目《Mr.J频道》,2011年以《青蜂侠》进军好莱坞。 \n二、基本资料\n 出生:1979年1月18日\n 血型:O型\n 体重:60kg\n 身高:174cm\n星座:魔羯座\n口头禅:哎哟,不错哦职业:音乐人(作曲人、歌手)\n副业:主持人(Mr.J频道)、老板、导演、演员、...\n内容详细: 周杰伦Zhoujielun个人资料:周杰伦,1979年1月18日出生于中国台湾,祖籍福建泉州,华语流行歌手、著名音乐人,2000年后亚洲流行乐坛最具革命性创作歌手,唱片亚洲总销量超过3100万张,有“亚洲...\n网站名称: www.mingrenw.cn\n网站图标: https://th.bochaai.com/favicon?domain_url=https://www.mingrenw.cn/ziliao/3/2970.html\n发布时间: 2025-02-09T22:17:40Z\n\n[[引用: 9]]\n标题: 周杰伦:华语乐坛的传奇之光\nURL: https://baijiahao.baidu.com/s?id=1820484479709473213\n摘要: 一、初露锋芒:音乐天才的崛起之路\n周杰伦,这位华语乐坛的传奇巨星,1979 年 1 月 18 日出生于中国台湾新北市,祖籍在福建省泉州市永春县。自小,他便展现出了超凡的音乐天赋,在会走路之时,母亲叶惠美就察觉到他对音乐的敏锐感知,于是在他四岁那年,便送他去学习钢琴。童年的周杰伦,在钢琴旁度过了无数个日夜,母亲的严格督促,让他练就了扎实的基本功,即便窗外同伴的嬉闹声充满诱惑,他也在母亲的 “棍棒教育” 下,坚持练琴,这为他日后的音乐创作之路奠定了深厚基础。\n高中时期,周杰伦就读于淡江中学音乐班,每天花费两个多小时从台北往返淡江。那时的他,已然是学校里的风云人物,弹得一手好琴,又擅长打篮球,成为许多女同学心中的白马王子。也是在这个时期,他开始尝试自己创作歌曲,同学们对他的作品赞赏有加,纷纷抢着收藏,这无疑给了他莫大的鼓励,让他在音乐创作的道路上愈发坚定。\n然而,高中毕业后的周杰伦,却陷入了一段迷茫期。他一时未能找到理想的工作,只好应聘成为一家餐馆的服务生。这份工作并不轻松,忙碌且容易出错,一旦犯错,不仅要承受顾客的怒火,还会面临老板的扣薪惩罚。但即便身处困境,周杰伦对音乐的热爱从未有丝毫减退,他省吃俭用,用微薄的薪水购买音乐资料,利用业余时间不断学习、创作。\n命运的转折悄然降临。1997 年,在家人的鼓励下,周杰伦参加了台湾的 “超级新人王” 真人秀节目。尽管演出过程略带青涩,表现并未达到预期的成功,但他独特的音乐才华却如同一颗璀璨的星星,被节目主持人兼阿尔法公司老板吴宗宪敏锐地捕捉到。节目结束后,周杰...\n内容详细: 说唱,是周杰伦音乐中极具标志性的元素,他所开创的“周氏 Rap”更是在华语乐坛留下了浓墨重彩的一笔。与传统说唱强调节奏的规整性不同,“周氏 Rap”节奏自由化的特点十分突出。他大胆突破传统节奏的强弱概念,不强调节奏重音,甚至...\n网站名称: 百家号\n网站图标: https://th.bochaai.com/favicon?domain_url=https://baijiahao.baidu.com/s?id=1820484479709473213\n发布时间: 2025-01-06T15:41:00Z\n\n[[引用: 10]]\n标题: 华语歌坛唯一真神,周杰伦5件封神之事!\nURL: https://m.163.com/news/article/JAOV78MH0552R0ST.html\n摘要: 说起「周董」,大家或会想起他的名曲《搁浅》、《龙卷风》、《星晴》,当然还有更多。\n出道超二十载,周杰伦从一名歌手发展成为创作人、音乐人,更是一位艺术收藏家。\n随着岁月洗礼,他一路走来有哪些时刻让人印象深刻?一起来回顾关于他的大小事。\n被称为「最具影响力的华语流行歌手」, 周杰伦 从2000年开始就以自己的风格震撼整个乐坛。\n各种风格他都试过,抒情、浪漫、节拍强劲的歌曲,每个风格都有代表作,实力毋庸置疑。\n18岁参加《超级新人王》被发掘\n出生书香世家的周杰伦,父母都是老师,小时候妈妈发掘了他的音乐天赋,安排他学习钢琴。\n后来他开始写歌,题材都很天马行空,顺着自己的兴趣和灵感而写作。\n高中毕业后,周杰伦陪朋友参加吴宗宪主持的新秀节目《超级新人王》并担任钢琴伴奏。\n虽然 歌曲 未获奖,但作为伴奏的周杰伦却因而被吴宗宪赏识,认为他的音乐天赋值得更好发挥。\n于是让周杰伦为自己的唱片公司阿尔发音乐旗下多位歌手创作,为周杰伦的音乐之路奠下基础。\n2000年推出专辑《Jay》震撼 乐坛\n当时已经对歌曲创作有足够经验的周杰伦,终于发行自己的第一张专辑《Jay》。\n当时十首歌曲由吴宗宪挑选,融合R&B、Hip-Hop、古典,以及标志性的中国风,让大家看见周杰伦独树一帜的音乐风格。\n《Jay》打响头炮,后一年推出的《范特西》同样收获好成绩,更让大众记住了这位才华洋溢的乐坛新人。\n也让周杰伦夺得第13届金曲奖最佳 作曲人 奖、最佳专辑制作人奖、最佳流行音乐演唱专辑奖等五项大奖,奠定他在华语乐坛的重要地位。\n2003年登《时...\n内容详细: 奠定他在华语乐坛的重要地位。2003年登《时代》封面,成亚洲第三人 当时只有王菲、张...\n网站名称: 网易\n网站图标: https://th.bochaai.com/favicon?domain_url=https://m.163.com/news/article/JAOV78MH0552R0ST.html\n发布时间: 2024-08-29T13:48:00Z",
143 | "aggregated_data": {
144 | "content": "周杰伦,1979年1月18日出生于中国台湾省新北市,祖籍福建省永春县。他是一位华语流行乐男歌手、音乐人、演员、导演和编剧。自出道以来,周杰伦凭借其独特的音乐风格和才华横溢的创作能力,在华语乐坛取得了显著成就。\n\n2000年发行首张个人专辑《Jay》,该专辑融合了R&B、Hip-Hop等多种音乐风格,并且其中主打歌曲《星晴》获得了第24届十大中文金曲优秀国语歌曲金奖。这张专辑不仅让周杰伦一战成名,还带动了华语R&B、嘻哈和中国风的流行[1] 。\n\n2001年发行的专辑《范特西》奠定了他融合中西方音乐的风格,并且凭借该专辑获得了第15届台湾金曲奖最佳流行音乐演唱专辑奖。同年,周杰伦还成为美国《时代》杂志封面人物[2] [3] 。\n\n2004年发行的专辑《七里香》在全亚洲首月销量达300万张,并且获得了世界音乐大奖中国区最畅销艺人奖[1] 。此外,他还主演了个人首部电影《头文字D》,凭借该片获得了第25届香港电影金像奖和第42届台湾电影金马奖的最佳新演员奖[2] [3] 。\n\n从2006年起连续三年获得世界音乐大奖中国区最畅销艺人奖。2017年,周杰伦监制的爱情冒险电影《一万公里的约定》上映,并担任明星经理人的综艺节目《这!就是灌篮》的导师[4] [5] 。\n\n周杰伦不仅在音乐上取得了巨大成功,在商业和设计领域也有涉足。他于2007年成立了杰威尔有限公司,同年还担任了华硕笔电设计师,并入股香港文化传信集团[6] 。\n\n此外,周杰伦热心公益慈善事业,多次向中国内地灾区捐款捐物,如在2008年捐款援建希望小学。他还曾担任中国禁毒宣传形象大使[5] [7] 。\n\n综上所述,周杰伦是华语乐坛独一无二的存在,他的音乐陪伴了许多人的青春,并且他凭借多方面的才华和成就,在全球范围内都享有极高的声誉[1] [2] [3] [4] [5] [6] [7] 。",
145 | "references": [
146 | "https://baike.baidu.com/item/%E5%91%A8%E6%9D%B0%E4%BC%A6/129156",
147 | "https://vebaike.com/doc-view-1270.html",
148 | "http://1zhr9wp4.shshilin.com/mobile/news/show-323436.html",
149 | "https://piaofang.maoyan.com/celebrity?id=28427",
150 | "https://baijiahao.baidu.com/s?id=1820484479709473213",
151 | "https://music.apple.com/cn/playlist/%E5%91%A8%E6%9D%B0%E4%BC%A6%E5%86%99%E7%9A%84%E6%AD%8C/pl.5a3e20295f804b298e507226f335e567",
152 | "https://www.mingrenw.cn/ziliao/3/2970.html"
153 | ]
154 | }
155 | },
156 | "records": [
157 | {
158 | "begin_t": "2025-04-15 10:44:30",
159 | "end_t": "2025-04-15 10:44:30",
160 | "node_name": "begin_node",
161 | "node_type": "LPNodeType.Begin",
162 | "model": null,
163 | "local_vars": {},
164 | "messages": []
165 | },
166 | {
167 | "begin_t": "2025-04-15 10:44:30",
168 | "end_t": "2025-04-15 10:44:30",
169 | "node_name": "classifier",
170 | "node_type": "LPNodeType.LLM",
171 | "model": "minicpm-v:8b",
172 | "local_vars": {
173 | "__labels_desc": "{\n \"正常问题\": \"非敏感问题,都归属于正常问题\",\n \"敏感问题\": \"一切涉及政治、色情、歧视、暴恐等违法内容的问题\"\n}",
174 | "cls_label_id": 0,
175 | "cls_label": "正常问题"
176 | },
177 | "messages": [
178 | {
179 | "role": "user",
180 | "content": "\n 你是一个强大的文本分类助手,能够根据用户定义的分类标签对任何文本进行精准分类。\n 用户提供的分类标签及其描述如下(JSON 格式):\n {\n \"正常问题\": \"非敏感问题,都归属于正常问题\",\n \"敏感问题\": \"一切涉及政治、色情、歧视、暴恐等违法内容的问题\"\n}\n\n 请按照以下要求进行文本分类:\n 1. 认真阅读待分类的文本,并理解其主要内容。\n 2. 根据用户提供的标签及描述,选出最符合该文本内容的分类标签。\n 3. 仅返回一个最合适的分类标签,不要输出多余内容。 \n 4. 如果文本内容不属于任何分类,直接返回None。\n\n 待分类文本:\n \"\n周杰伦是谁,在华语乐坛有什么成就?\n\"\n\n 请直接返回最符合该文本的分类标签(不要用引号包裹标签):\n "
181 | },
182 | {
183 | "role": "assistant",
184 | "content": "正常问题"
185 | }
186 | ]
187 | },
188 | {
189 | "begin_t": "2025-04-15 10:44:30",
190 | "end_t": "2025-04-15 10:44:31",
191 | "node_name": "bocha_search",
192 | "node_type": "LPNodeType.Invoke",
193 | "model": null,
194 | "local_vars": {
195 | "__api_key": "sk-***",
196 | "__api_url": "https://api.bochaai.com/v1/web-search",
197 | "__search_keywords": "\n周杰伦是谁,在华语乐坛有什么成就?\n",
198 | "__search_count": 10,
199 | "__ai_search": false
200 | },
201 | "messages": []
202 | },
203 | {
204 | "begin_t": "2025-04-15 10:44:31",
205 | "end_t": "2025-04-15 10:44:52",
206 | "node_name": "aggregator",
207 | "node_type": "LPNodeType.LLM",
208 | "model": "qwen2.5:7b",
209 | "local_vars": {
210 | "__aggregate_desc": null
211 | },
212 | "messages": [
213 | {
214 | "role": "user",
215 | "content": "\n 你是一个强大的智能信息聚合助手(Aggregator),擅长根据上下文信息,结合自己的理解、生成有引用标注的回答。\n 以下是可供参考的上下文:\n ---\n {\n \"cls_label\": \"正常问题\",\n \"searched_result\": \"[[引用: 1]]\\n标题: 周杰伦(华语流行乐男歌手、音乐人、演员、导演、编剧)\\nURL: https://baike.baidu.com/item/%E5%91%A8%E6%9D%B0%E4%BC%A6/129156\\n摘要: 华语流行乐男歌手、音乐人、演员、导演、编剧\\n目录\\n1 早年经历 2 演艺经历 ▪ 出道成名 ▪ 持续进步 ▪ 自立门户 ▪ 曲风探索 ▪ 稳步发展\\n3 个人生活 4 主要作品 ▪ 音乐专辑 ▪ 音乐单曲 ▪ 为他人创作 ▪ 演唱会记录 ▪ 参演电影\\n▪ 参演电视剧 ▪ 综艺节目 ▪ 导演作品 ▪ 编剧作品 ▪ 出版作品 ▪ 监制作品 ▪ 配音作品 5 社会活动\\n▪ 公益活动 ▪ 担任大使 ▪ 其它领域 6 获奖记录 7 人物评价\\n基本信息\\n周杰伦(Jay Chou),1979年1月18日出生于中国台湾省新北市,祖籍福建省永春县,华语流行乐男歌手、音乐人、演员、导演、编剧,毕业于 淡江中学 。\\n2000年,发行个人首张音乐专辑《 Jay 》 [26] 。2001年,凭借专辑《 范特西 》奠定其融合中西方音乐的风格 [16] 。2002年,举行“The One”世界巡回演唱会 [1] 。2003年,成为美国《 时代 》杂志封面人物 [2] ;同年,全亚洲发行音乐专辑《 叶惠美 》 [21] ,该专辑获得 第15届台湾金曲奖 最佳流行音乐演唱专辑奖 [23] 。2004年,发行音乐专辑《 七里香 》 [29] ,该专辑在全亚洲的首月销量达300万张 [276] ;同年,获得 世界音乐大奖 中国区最畅销艺人奖 [280] 。2005年,主演个人首部电影《 头文字D 》 [274] ,凭借该片获得 第25届香港电影金像奖 和 第42届台湾电影金马奖 的最佳新演员奖 [3] [275] ...\\n内容详细: 周杰伦(Jay Chou),1979年1月18日出生于中国台湾省新北市,祖籍福建省永春县,华语流行乐男歌手、音乐人、演员、导演、编剧,毕业于淡江中学。2000年,发行个人首张音乐专辑《Jay》。2001年,凭借专辑《范特西》奠定其融合中西方音乐的风格。2002年,举行“The One”世界巡回演唱会。2003年,成为美国《时代》杂志封面人物;同年,全亚洲发行音乐专辑《叶惠美》,该专辑获得第15届台湾金曲奖最佳流行音乐演唱专辑奖。2004年,发行音乐专辑《七里香》,该专辑在全亚洲的首月销量达300万张;同年,获得世界音乐大奖中国区最畅销艺人奖。2005年,主演个人首部电影《头文字D》,凭借该片获得第25届香港电影金像奖和第42届台湾电影金马奖的最佳新演员奖。2006年起,连续三年获得世界音乐大奖中国区最畅销艺人奖。2007年,自编自导爱情电影《不能说的秘密》,同年,成立杰威尔音乐有限公司。\\n网站名称: baike.baidu.com\\n网站图标: https://th.bochaai.com/favicon?domain_url=https://baike.baidu.com/item/%E5%91%A8%E6%9D%B0%E4%BC%A6/129156\\n发布时间: None\\n\\n[[引用: 2]]\\n标题: 周杰伦\\nURL: https://vebaike.com/doc-view-1270.html\\n摘要: 周杰伦(Jay Chou),1979年1月18日出生于中国台湾,华语男歌手、词曲创作人、演员、MV及电影导演、编剧及制作人。2000年,发行首张个人专辑《Jay》。\\n目录\\n1 人物简介 2 演艺经历 3 人物评价\\n人物简介 编辑本段\\n(Jay Chou),1979年1月18日出生于台湾省新北市,华语男歌手、词曲创作人、演员、MV及电影导演、编剧及制作人。\\n2000年,发行首张个人专辑《Jay》。2002年,举行“The One”世界巡回演唱会。2004年,登上央视春晚舞台,并演唱歌曲《龙拳》。2005年,凭借动作片《头文字D》获得台湾电影金马奖、香港电影金像奖最佳新人奖。2006年,起连续三年获得世界音乐大奖中国区最畅销艺人奖。2010年,入选美国《Fast Company》评出的“全球百大创意人物”。2011年,凭借专辑《跨时代》再度获得金曲奖最佳国语男歌手奖,并且第4次获得金曲奖最佳国语专辑奖。2016年,参加综艺节目《中国新歌声第二季》担任导师。2017年1月6日,周杰伦监制爱情电影《一万公里的约定》在中国内地上映。2018年7月13日,担任《中国好声音2018》导师。8月25日,担任明星经理人的综艺节目《这!就是灌篮》播出。2019年9月16日23时,周杰伦新歌《说好不哭》正式上线全网发售,截至17日上午10点整,周杰伦全网累计售出573万张数字单曲。\\n2017年6月,二胎儿子出生。\\n演艺经历 编辑本段\\n2000年11月,以专辑《Jay》出道,获得台湾当年最佳流行音乐演唱专辑、最佳制作人和最...\\n内容详细: 该专辑融合了R&B、hip-hop等多种音乐风格,其中主打歌曲《星晴》获得第24届十大中文金曲优秀国语歌曲金奖,而周杰伦也凭借该专辑在华语乐坛受到关注,并于次年获得第12届台湾金曲奖最佳流行音乐演唱专辑奖、第12届台湾金曲奖最佳制作...\\n网站名称: vebaike.com\\n网站图标: https://th.bochaai.com/favicon?domain_url=https://vebaike.com/doc-view-1270.html\\n发布时间: 2023-05-25T00:00:00Z\\n\\n[[引用: 3]]\\n标题: 周杰伦是华语乐坛独一无二的存在。\\nURL: https://m.toutiao.com/w/1825077670277323/\\n摘要: 周杰伦是华语乐坛独一无二的存在。\\n他的音乐融合中西元素,像《范特西》中的《双截棍》,既有中国武术文化,又有西方流行乐的节奏。出道至今发布15张专辑,销量超3000万张,入围金曲奖51次,拿了15座奖杯,这数字相当惊人。\\n他的成名路也很励志。从小接触钢琴等多种乐器,虽学业不顺,但没放弃音乐梦。从幕后为他人写歌开始,不断积累经验,即便作品最初未被广泛认可,也没动摇他的决心。他的音乐陪伴了很多人的青春,每一首歌都像是一个故事,承载着无数人的回忆。 #时代偶像周杰伦# #乐坛那些事儿# #最棒的杰伦# #周杰伦唱歌功力#\\n内容详细: 他的音乐陪伴了很多人的青春,每一首歌都像是一个故事,承载着无数人的回忆。#时代偶像周杰伦##乐坛那些事儿##最棒的杰伦##周杰伦唱歌功力#...\\n网站名称: 今日头条\\n网站图标: https://th.bochaai.com/favicon?domain_url=https://m.toutiao.com/w/1825077670277323/\\n发布时间: 2025-02-26T08:28:00Z\\n\\n[[引用: 4]]\\n标题: 周杰伦 简介(出道多年获奖无数,他的实力到底有多强?)\\nURL: http://1zhr9wp4.shshilin.com/mobile/news/show-323436.html\\n摘要: 内容摘要 最近,霸占无数80后90后的青春和童年回忆的那个男人——周杰伦,终于发了新专辑《最伟大的作品》。 专辑一上线,立刻激起了整个互联网的狂欢。《最伟大的作品》刚刚上线45分钟播放量就超过了千万,光热搜就霸占近20个。周杰伦非凡的号召力和华语乐坛\\n最近,霸占无数80后90后的青春和童年回忆的那个男人——周杰伦,终于发了新专辑《最伟大的作品》。\\n专辑一上线,立刻激起了整个互联网的狂欢。《最伟大的作品》刚刚上线45分钟播放量就超过了千万,光热搜就霸占近20个。周杰伦非凡的号召力和华语乐坛领军人物的实力再次被证明。\\n可以说华语乐坛多年不见周杰伦,周杰伦依然独步天下。\\n而前几年那群号称要引领华语乐坛,却唱着英文歌曲的流量明星们。只贡献了仅粉丝可见的唱功和仅NC粉可见的努力。粉丝们甚至以打榜来论名气成就,还妄图压周杰伦一头,闹出了许多NC新闻。\\n如今在周杰伦的新专成绩下,全部都成了滑稽的笑话。\\n周杰伦到底有多牛?他凭什么能独步乐坛这么多年?今天让我们来了解一下,这个爱喝奶茶的华语乐坛天王,到底是如何炼成的?\\n在华语乐坛中,周杰伦是个难得的音乐全才,在他的许多作品中,作词作曲演唱都由他一人完成。而在周杰伦众多的优秀专辑和歌曲中,绕不过的一张专辑就是《叶惠美》,绕不开的歌是《听妈妈的话》。而这个叶惠美其实就是出自周杰伦的母亲。\\n认识周杰伦的人都知道,他是个出了名的爱妈妈的好儿子。甚至公开说找妻子一定要孝顺母亲的。\\n周杰伦为什么这么爱母亲,还要从他特殊的家庭讲起。\\n1979年,周杰伦出生在台北市一个教师之家。他...\\n内容详细: 而前几年那群号称要引领华语乐坛,却唱着英文歌曲的流量明星们。只贡献了仅粉丝可见的唱功和仅NC粉可见的努力。粉丝们甚至以打榜来论名气成就,还妄图压周杰伦一头,闹出了许多NC新闻。如今在周杰...\\n网站名称: 1zhr9wp4.shshilin.com\\n网站图标: https://th.bochaai.com/favicon?domain_url=http://1zhr9wp4.shshilin.com/mobile/news/show-323436.html\\n发布时间: 2025-04-08T22:14:52Z\\n\\n[[引用: 5]]\\n标题: 周杰伦\\nURL: https://piaofang.maoyan.com/celebrity?id=28427\\n摘要: Jay Chou\\n歌手 演员 导演\\n个人作品\\n基本信息\\n国籍: 中国\\n民族: 汉族\\n性别: 男\\n血型: O型\\n身高: 175cm\\n出生日期: 1979-01-18(魔羯座)\\n毕业院校: 淡江中学\\n昵称: 周董,小公举,Chou Jie Lun,President Chou,Chow Chieh-lun\\n出生地: 中国台湾新北\\n简介: 周杰伦(Jay Chou),1979年1月18日出生于 台湾省 新北市,祖籍 福建省 泉州市 永春县,中国台湾流行乐男歌手、音乐人、演员、导演、编剧,毕业于 淡江中学。 2000年发行首张个人专辑《 Jay》。2001年发行的专辑《 范特西》奠定其融合中西方音乐的风格。2002年举行“The One”世界巡回演唱会。2003年成为美国《 时代周刊》封面人物。2004年获得 世界音乐大奖中国区最畅销艺人奖。2005年凭借动作片《 头文字D》获得 金马奖、 金像奖最佳新人奖。2006年起连续三年获得世界音乐大奖中国区最畅销艺人奖。2007年自编自导的文艺片《 不能说的秘密》获得金马奖年度台湾杰出电影奖。 2008年凭借歌曲《 青花瓷》获得第19届 金曲奖最佳作曲人奖。2009年入选美国CNN评出的“25位亚洲最具影响力人物”,同年凭借专辑《 魔杰座》获得第20届金曲奖最佳国语男歌手奖。2010年入选美国《 Fast Company》评出的“全球百大创意人物”。2011年凭借专辑《 跨时代》再度获得金曲奖最佳国语男歌手奖,并且第四次获得金曲奖最佳国语专辑奖;同年主演好莱坞电影...\\n内容详细: 2014年发行华语乐坛首张数字音乐专辑《哎呦,不错哦》。2021年在央视春晚演唱歌曲《Mojito》。演艺事业外,他还涉足商业、设计等领域。2007年成立 杰威尔有限公司。2011年担任华硕笔电设计师,并入股香港文化传信集团。周杰伦热心公益慈善,多次向中国内地灾区捐款捐物。2008年捐款援建 希望小学。2014年担任 中国禁毒宣传形象大使。经纪公司:杰威尔音乐有限公司 演艺经历 影视 2017年 首次担任监制的爱情冒险电影《一万公里的约定》上映,影片来源于林义杰的真实故事...\\n网站名称: piaofang.maoyan.com\\n网站图标: https://th.bochaai.com/favicon?domain_url=https://piaofang.maoyan.com/celebrity?id=28427\\n发布时间: 2025-03-29T00:00:00Z\\n\\n[[引用: 6]]\\n标题: 周杰伦的第一张专辑是什么?一张改变华语乐坛的神专!\\nURL: https://baijiahao.baidu.com/s?id=1827073700401270008\\n摘要: 导读\\n• 查看DeepSeek-R1满血版总结\\n2000年周杰伦首张专辑《Jay》横空出世,被淘汰的选秀少年在吴宗宪支持下10天写出神专,将R&B、嘻哈、中国风熔于一炉,《可爱女人》的慵懒R&B、《龙卷风》的抓耳旋律、《娘子》的嘻哈古风彻底颠覆华语乐坛,百万销量斩获金曲奖,从此开启“咬字不清却席卷亚洲”的音乐革命时代。\\n如果要问华语流行乐坛最具影响力的歌手是谁, 周杰伦 一定榜上有名。从2000年出道至今,他的音乐作品影响了无数人,而这一切的起点,便是他的 第一张专辑 ——《Jay》。\\n是的, 2000年11月6日,《Jay》正式发行,周杰伦的传奇之路就此开启!\\n那么,这张专辑为什么这么重要?它到底带来了哪些划时代的突破?今天我们就来聊聊这张 神专 ,带你回顾那个开启“周杰伦时代”的时刻!\\n1. 《Jay》的诞生:天才音乐人终于被发现!\\n在成为巨星之前,周杰伦其实只是一个 幕后创作人 。他在1997年参加选秀节目**《超级新人王》 ,却因为 口齿不清 而被淘汰,但评委吴宗宪却注意到了他的 音乐才华**,于是将他签入自己的公司 阿尔发音乐 ,让他专门给歌手写歌。\\n然而,当时许多歌手都觉得 他的曲风太怪 ,不愿意唱他的歌。最终,在吴宗宪的支持下,公司决定 让周杰伦自己唱自己的歌 ,于是他开始创作第一张个人专辑——《Jay》!\\n 冷知识:\\n《Jay》这张专辑的歌曲,其实是周杰伦和搭档 方文山 在10天内赶工写出来的!\\n专辑名字就叫**“Jay” ,简单又直接,表明了这是 属于周杰伦的音乐**。\\n...\\n内容详细: 这张专辑不仅让周杰伦一战成名,还带动了华语R&B、嘻哈和中国风的流行,彻底改变了华语流行音乐的发展方向!《Jay》的成就:专辑销量超过 100万张 ,成为千禧年后最畅销的专辑之一。周杰...\\n网站名称: 百家号\\n网站图标: https://th.bochaai.com/favicon?domain_url=https://baijiahao.baidu.com/s?id=1827073700401270008\\n发布时间: 2025-03-20T09:14:00Z\\n\\n[[引用: 7]]\\n标题: 创作人:周杰伦\\nURL: https://music.apple.com/cn/playlist/%E5%91%A8%E6%9D%B0%E4%BC%A6%E5%86%99%E7%9A%84%E6%AD%8C/pl.5a3e20295f804b298e507226f335e567\\n摘要: Apple Music 国语流行\\n周杰伦光芒万丈的个人作品固然成就了他“华语乐坛天王”的地位,但只有加上他为别人创作的歌曲,这一名号背后的依据才足够完整。出道前就已位居幕后磨练许久的周杰伦,积累了深厚的创作才华,成名后更引来众多合作邀约。他在贴合歌手个人特质的基础上发扬着“周氏风格”,用一首首脍炙人口的金曲勾勒出纷繁而夺目的风格脉络:通过词曲构筑瑰丽的异域情境是周杰伦创作的一大标志性特色,穿梭时空的奇思妙想为千禧年代的听众开启了新鲜、奇异的音乐世界。他个人作品中印第安、米兰、美索不达米亚的冒险,在蔡依林的《布拉格广场》《骑士精神》得到延续,欧洲风情与 Hip-Hop 节拍的结合凸显了蔡依林的前卫气质。周杰伦与方文山这对金牌搭档的“拿手好戏”——中国风——早在 2001 年便有雏型,一首《刀马旦》结合了李玟洋派的 R&B 唱腔与五声音阶、戏曲故事,帮助“国际化”的李玟向世界舞台送去了东方风情。不过,周杰伦最为人称道的还是他的 Hip-Hop 创作,打破句式、词组的断句定式,扭曲汉语的的咬字发音,他释放了中文说唱的潜力,刷新了一代听众对华语流行音乐的固有认知。他为陈冠希创作的《I Can Fly 我可以》结合了 EDM 音乐与 Melodic Rap,在当时亦属新潮尝试。 当然,周杰伦的诸多创作中,也不乏张惠妹《连名带姓》、S.H.E 的《安静了》、张韶涵《亲爱的那并不是爱情》等抒情经典,他与不同世代、不同风格歌手的合作,更成就了不少佳话。比如周杰伦主动交出创作、提出合作的《淘汰》是与陈奕迅惺惺相惜的写...\\n内容详细: 周杰伦光芒万丈的个人作品固然成就了他“华语乐坛天王”的地位,但只有加上他为别人创作的歌曲,这一名号背后的依据才足够完整。出道前就已位居幕后磨练许久的周杰伦,积累了深厚的创作才华,成名后更引来众多合作邀约。他在贴合歌手个...\\n网站名称: Apple\\n网站图标: https://th.bochaai.com/favicon?domain_url=https://music.apple.com/cn/playlist/%E5%91%A8%E6%9D%B0%E4%BC%A6%E5%86%99%E7%9A%84%E6%AD%8C/pl.5a3e20295f804b298e507226f335e567\\n发布时间: 2024-07-11T14:13:38Z\\n\\n[[引用: 8]]\\n标题: 周杰伦\\nURL: https://www.mingrenw.cn/ziliao/3/2970.html\\n摘要: Zhoujielun\\n周杰伦Zhoujielun个人资料 :周杰伦,1979年1月18日出生于中国台湾,祖籍福建泉州,华语流行歌手、著名音乐人,2000年后亚洲流行乐坛最具革命性创作歌手,唱片亚洲总销量超过3100万张,有“亚洲流行天王”之称,其音乐突破了亚洲原有单一的音乐主题形式,开创了多元化音乐创作和现代流行乐“中国风”的先河,为亚洲流行乐坛翻开了新的一页。2005年以《头文字D》涉足电影业,2006年出版图书《D调的华丽》,2007年成立杰威尔音乐有限公司,2009年自导自演电视剧《熊猫人》,2010年主持电视节目《Mr.J频道》,2011年以《青蜂侠》…… \\n一、周杰伦\\n 周杰伦,1979年1月18日出生于中国台湾,祖籍福建泉州,华语流行歌手、著名音乐人,2000年后亚洲流行乐坛最具革命性创作歌手,唱片亚洲总销量超过3100万张,有“亚洲流行天王”之称,其音乐突破了亚洲原有单一的音乐主题形式,开创了多元化音乐创作和现代流行乐“中国风”的先河,为亚洲流行乐坛翻开了新的一页。2005年以《头文字D》涉足电影业,2006年出版图书《D调的华丽》,2007年成立杰威尔音乐有限公司,2009年自导自演电视剧《熊猫人》,2010年主持电视节目《Mr.J频道》,2011年以《青蜂侠》进军好莱坞。 \\n二、基本资料\\n 出生:1979年1月18日\\n 血型:O型\\n 体重:60kg\\n 身高:174cm\\n星座:魔羯座\\n口头禅:哎哟,不错哦职业:音乐人(作曲人、歌手)\\n副业:主持人(Mr.J频道)、老板、导演、演员、...\\n内容详细: 周杰伦Zhoujielun个人资料:周杰伦,1979年1月18日出生于中国台湾,祖籍福建泉州,华语流行歌手、著名音乐人,2000年后亚洲流行乐坛最具革命性创作歌手,唱片亚洲总销量超过3100万张,有“亚洲...\\n网站名称: www.mingrenw.cn\\n网站图标: https://th.bochaai.com/favicon?domain_url=https://www.mingrenw.cn/ziliao/3/2970.html\\n发布时间: 2025-02-09T22:17:40Z\\n\\n[[引用: 9]]\\n标题: 周杰伦:华语乐坛的传奇之光\\nURL: https://baijiahao.baidu.com/s?id=1820484479709473213\\n摘要: 一、初露锋芒:音乐天才的崛起之路\\n周杰伦,这位华语乐坛的传奇巨星,1979 年 1 月 18 日出生于中国台湾新北市,祖籍在福建省泉州市永春县。自小,他便展现出了超凡的音乐天赋,在会走路之时,母亲叶惠美就察觉到他对音乐的敏锐感知,于是在他四岁那年,便送他去学习钢琴。童年的周杰伦,在钢琴旁度过了无数个日夜,母亲的严格督促,让他练就了扎实的基本功,即便窗外同伴的嬉闹声充满诱惑,他也在母亲的 “棍棒教育” 下,坚持练琴,这为他日后的音乐创作之路奠定了深厚基础。\\n高中时期,周杰伦就读于淡江中学音乐班,每天花费两个多小时从台北往返淡江。那时的他,已然是学校里的风云人物,弹得一手好琴,又擅长打篮球,成为许多女同学心中的白马王子。也是在这个时期,他开始尝试自己创作歌曲,同学们对他的作品赞赏有加,纷纷抢着收藏,这无疑给了他莫大的鼓励,让他在音乐创作的道路上愈发坚定。\\n然而,高中毕业后的周杰伦,却陷入了一段迷茫期。他一时未能找到理想的工作,只好应聘成为一家餐馆的服务生。这份工作并不轻松,忙碌且容易出错,一旦犯错,不仅要承受顾客的怒火,还会面临老板的扣薪惩罚。但即便身处困境,周杰伦对音乐的热爱从未有丝毫减退,他省吃俭用,用微薄的薪水购买音乐资料,利用业余时间不断学习、创作。\\n命运的转折悄然降临。1997 年,在家人的鼓励下,周杰伦参加了台湾的 “超级新人王” 真人秀节目。尽管演出过程略带青涩,表现并未达到预期的成功,但他独特的音乐才华却如同一颗璀璨的星星,被节目主持人兼阿尔法公司老板吴宗宪敏锐地捕捉到。节目结束后,周杰...\\n内容详细: 说唱,是周杰伦音乐中极具标志性的元素,他所开创的“周氏 Rap”更是在华语乐坛留下了浓墨重彩的一笔。与传统说唱强调节奏的规整性不同,“周氏 Rap”节奏自由化的特点十分突出。他大胆突破传统节奏的强弱概念,不强调节奏重音,甚至...\\n网站名称: 百家号\\n网站图标: https://th.bochaai.com/favicon?domain_url=https://baijiahao.baidu.com/s?id=1820484479709473213\\n发布时间: 2025-01-06T15:41:00Z\\n\\n[[引用: 10]]\\n标题: 华语歌坛唯一真神,周杰伦5件封神之事!\\nURL: https://m.163.com/news/article/JAOV78MH0552R0ST.html\\n摘要: 说起「周董」,大家或会想起他的名曲《搁浅》、《龙卷风》、《星晴》,当然还有更多。\\n出道超二十载,周杰伦从一名歌手发展成为创作人、音乐人,更是一位艺术收藏家。\\n随着岁月洗礼,他一路走来有哪些时刻让人印象深刻?一起来回顾关于他的大小事。\\n被称为「最具影响力的华语流行歌手」, 周杰伦 从2000年开始就以自己的风格震撼整个乐坛。\\n各种风格他都试过,抒情、浪漫、节拍强劲的歌曲,每个风格都有代表作,实力毋庸置疑。\\n18岁参加《超级新人王》被发掘\\n出生书香世家的周杰伦,父母都是老师,小时候妈妈发掘了他的音乐天赋,安排他学习钢琴。\\n后来他开始写歌,题材都很天马行空,顺着自己的兴趣和灵感而写作。\\n高中毕业后,周杰伦陪朋友参加吴宗宪主持的新秀节目《超级新人王》并担任钢琴伴奏。\\n虽然 歌曲 未获奖,但作为伴奏的周杰伦却因而被吴宗宪赏识,认为他的音乐天赋值得更好发挥。\\n于是让周杰伦为自己的唱片公司阿尔发音乐旗下多位歌手创作,为周杰伦的音乐之路奠下基础。\\n2000年推出专辑《Jay》震撼 乐坛\\n当时已经对歌曲创作有足够经验的周杰伦,终于发行自己的第一张专辑《Jay》。\\n当时十首歌曲由吴宗宪挑选,融合R&B、Hip-Hop、古典,以及标志性的中国风,让大家看见周杰伦独树一帜的音乐风格。\\n《Jay》打响头炮,后一年推出的《范特西》同样收获好成绩,更让大众记住了这位才华洋溢的乐坛新人。\\n也让周杰伦夺得第13届金曲奖最佳 作曲人 奖、最佳专辑制作人奖、最佳流行音乐演唱专辑奖等五项大奖,奠定他在华语乐坛的重要地位。\\n2003年登《时...\\n内容详细: 奠定他在华语乐坛的重要地位。2003年登《时代》封面,成亚洲第三人 当时只有王菲、张...\\n网站名称: 网易\\n网站图标: https://th.bochaai.com/favicon?domain_url=https://m.163.com/news/article/JAOV78MH0552R0ST.html\\n发布时间: 2024-08-29T13:48:00Z\"\n}\n ---\n\n 以下是待回答的问题:\n ---\n \n周杰伦是谁,在华语乐坛有什么成就?\n\n ---\n\n 请注意以下任务要求:\n 1. 回答正文中如有引用某些具体数据、事实、日期、标准、文档结论等内容,请在该信息后添加引用占位符(如 `[1]`、`[2]` 等)。\n 2. 按顺序列出这些引用编号所对应的来源(可以是网址、文档路径、书名等),来源不可重复。引用编号顺序必须与正文中使用的一致。\n\n 重点注意:\n 1. **信息整合**:充分利用所有来源的数据,确保信息完整,不遗漏任何重要内容。\n 2. **语义流畅**:避免直接罗列数据,而是用自然语言组织,使回答清晰易懂。\n \n 注意最终要以JSON格式输出:\n {\n \"content\": \"回答正文,包含引用占位符[1][2][N]……\",\n \"references\": [\n \"对应的引用来源1\",\n \"对应的引用来源2\",\n \"对应的引用来源N\"\n ]\n }\n\n 现在给出你的回答:\n "
216 | },
217 | {
218 | "role": "assistant",
219 | "content": "{\n \"content\": \"周杰伦,1979年1月18日出生于中国台湾省新北市,祖籍福建省永春县。他是一位华语流行乐男歌手、音乐人、演员、导演和编剧。自出道以来,周杰伦凭借其独特的音乐风格和才华横溢的创作能力,在华语乐坛取得了显著成就。\\n\\n2000年发行首张个人专辑《Jay》,该专辑融合了R&B、Hip-Hop等多种音乐风格,并且其中主打歌曲《星晴》获得了第24届十大中文金曲优秀国语歌曲金奖。这张专辑不仅让周杰伦一战成名,还带动了华语R&B、嘻哈和中国风的流行[1]。\\n\\n2001年发行的专辑《范特西》奠定了他融合中西方音乐的风格,并且凭借该专辑获得了第15届台湾金曲奖最佳流行音乐演唱专辑奖。同年,周杰伦还成为美国《时代》杂志封面人物[2][3]。\\n\\n2004年发行的专辑《七里香》在全亚洲首月销量达300万张,并且获得了世界音乐大奖中国区最畅销艺人奖[1]。此外,他还主演了个人首部电影《头文字D》,凭借该片获得了第25届香港电影金像奖和第42届台湾电影金马奖的最佳新演员奖[2][3]。\\n\\n从2006年起连续三年获得世界音乐大奖中国区最畅销艺人奖。2017年,周杰伦监制的爱情冒险电影《一万公里的约定》上映,并担任明星经理人的综艺节目《这!就是灌篮》的导师[4][5]。\\n\\n周杰伦不仅在音乐上取得了巨大成功,在商业和设计领域也有涉足。他于2007年成立了杰威尔有限公司,同年还担任了华硕笔电设计师,并入股香港文化传信集团[6]。\\n\\n此外,周杰伦热心公益慈善事业,多次向中国内地灾区捐款捐物,如在2008年捐款援建希望小学。他还曾担任中国禁毒宣传形象大使[5][7]。\\n\\n综上所述,周杰伦是华语乐坛独一无二的存在,他的音乐陪伴了许多人的青春,并且他凭借多方面的才华和成就,在全球范围内都享有极高的声誉[1][2][3][4][5][6][7]。\",\n \"references\": [\n \"https://baike.baidu.com/item/%E5%91%A8%E6%9D%B0%E4%BC%A6/129156\",\n \"https://vebaike.com/doc-view-1270.html\",\n \"http://1zhr9wp4.shshilin.com/mobile/news/show-323436.html\",\n \"https://piaofang.maoyan.com/celebrity?id=28427\",\n \"https://baijiahao.baidu.com/s?id=1820484479709473213\",\n \"https://music.apple.com/cn/playlist/%E5%91%A8%E6%9D%B0%E4%BC%A6%E5%86%99%E7%9A%84%E6%AD%8C/pl.5a3e20295f804b298e507226f335e567\",\n \"https://www.mingrenw.cn/ziliao/3/2970.html\"\n ]\n}"
220 | }
221 | ]
222 | },
223 | {
224 | "begin_t": "2025-04-15 10:44:52",
225 | "end_t": "2025-04-15 10:44:52",
226 | "node_name": "end_node_0",
227 | "node_type": "LPNodeType.End",
228 | "model": null,
229 | "local_vars": {
230 | "__callback": null,
231 | "__debug": true,
232 | "__print_final_out": true,
233 | "__remove_thinking_txt": true
234 | },
235 | "messages": []
236 | }
237 | ]
238 | }
239 | <<<<<<<<<<<<<[debug][lpdata from end_node_0]<<<<<<<<<<<<<
240 | ```
241 |
--------------------------------------------------------------------------------
/docs/pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/docs/pipeline.png
--------------------------------------------------------------------------------
/docs/reference_links.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/docs/reference_links.png
--------------------------------------------------------------------------------
/docs/tests/chat_to_sqlite_gui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/docs/tests/chat_to_sqlite_gui.png
--------------------------------------------------------------------------------
/docs/tests/chat_to_web_gui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/docs/tests/chat_to_web_gui.png
--------------------------------------------------------------------------------
/docs/tests/chat_webui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/docs/tests/chat_webui.png
--------------------------------------------------------------------------------
/docs/tests/extract_params.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/docs/tests/extract_params.png
--------------------------------------------------------------------------------
/docs/tests/generate_notice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/docs/tests/generate_notice.png
--------------------------------------------------------------------------------
/docs/tests/images_input.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/docs/tests/images_input.png
--------------------------------------------------------------------------------
/docs/tests/pipeline_render.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/docs/tests/pipeline_render.png
--------------------------------------------------------------------------------
/docs/tests/rag_kb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/docs/tests/rag_kb.png
--------------------------------------------------------------------------------
/docs/tests/rag_search_engine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/docs/tests/rag_search_engine.png
--------------------------------------------------------------------------------
/docs/tests/rag_search_engine_ref.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/docs/tests/rag_search_engine_ref.png
--------------------------------------------------------------------------------
/docs/tests/rag_sql.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/docs/tests/rag_sql.png
--------------------------------------------------------------------------------
/docs/tests/task_cls.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/docs/tests/task_cls.png
--------------------------------------------------------------------------------
/langpipe/__init__.py:
--------------------------------------------------------------------------------
1 | from .lpnode import LPNode, LPNodeType, LPNodeState
2 | from .lpbegin import LPBegin
3 | from .lpend import LPEnd
4 | from .lpclassifier import LPClassifier
5 | from .lpextractor import LPExtractor
6 | from .lpaggregator import LPAggregator
7 | from .lpsuperaggregator import LPSuperAggregator
8 | from .lpgenerator import LPGenerator
9 | from .lpchatter import LPChatter
10 | from .lpbaseinvoker import LPBaseInvoker
11 | from .lpbaserouter import LPBaseRouter
12 | from .lpsqlcreator import LPSQLCreator
13 | from .lpboardrender import LPBoardRender
--------------------------------------------------------------------------------
/langpipe/lpaggregator.py:
--------------------------------------------------------------------------------
1 | import json
2 | from ollama import generate
3 | from .lpnode import LPNode, LPNodeType
4 |
5 | class LPAggregator(LPNode):
6 | """
7 | data aggregation using LLM based on Ollama.
8 | access `lpdata['global_vars']['aggregated_data']` for the aggregated data.
9 | """
10 | def __init__(self, name, aggregate_desc=None, model='minicpm-v:8b') -> None:
11 | super().__init__(name, LPNodeType.LLM, model)
12 | self.__aggregate_desc = aggregate_desc
13 | self.__aggregated_data = None
14 | self.__aggregate_prompt_template = """
15 | 你是一个智能信息聚合器(Aggregator),负责整合多个来源的信息,并基于所有可用数据生成高质量、清晰且有逻辑性的最终回答。
16 | 以下是所有可用的信息:
17 | ---
18 | {0}
19 | ---
20 |
21 | 以下是待回答的问题:
22 | ---
23 | {1}
24 | ---
25 |
26 | 任务要求:
27 | 1. **信息整合**:充分利用所有来源的数据,确保信息完整,不遗漏任何重要内容。
28 | 2. **语义流畅**:避免直接罗列数据,而是用自然语言组织,使回答清晰易懂。
29 |
30 | 请给出最终整合后的完整回答:
31 | """
32 |
33 | def _handle(self, lpdata) -> None:
34 | query = lpdata['query'] if self.__aggregate_desc is None else self.__aggregate_desc
35 | prompt = self.__aggregate_prompt_template.format(json.dumps(lpdata['global_vars'], indent=4, ensure_ascii=False), query)
36 |
37 | response = generate(model=self.model,
38 | prompt=prompt,
39 | options={
40 | 'top_k': 1,
41 | 'temperature': 0.5,
42 | 'num_ctx': 32768 # 32K
43 | })
44 | self.__aggregated_data = response['response']
45 |
46 | # update records
47 | messages = lpdata['records'][-1]['messages']
48 | message = {}
49 | message['role'] = 'user'
50 | message['content'] = prompt
51 | messages.append(message)
52 |
53 | message = {}
54 | message['role'] = 'assistant'
55 | message['content'] = response['response']
56 | messages.append(message)
57 |
58 | def _after_handle(self, lpdata) -> None:
59 | super()._after_handle(lpdata)
60 |
61 | # update local vars
62 | record = lpdata['records'][-1]
63 | record['local_vars']['__aggregate_desc'] = self.__aggregate_desc
64 |
65 | # update global variables
66 | lpdata['final_out'] = self.__aggregated_data
67 | lpdata['global_vars']['aggregated_data'] = self.__aggregated_data
68 |
--------------------------------------------------------------------------------
/langpipe/lpbaseinvoker.py:
--------------------------------------------------------------------------------
1 |
2 | from abc import ABC, abstractmethod
3 | from typing import final
4 | from .lpnode import LPNode, LPNodeType
5 |
6 | class LPBaseInvoker(ABC, LPNode):
7 | """
8 | base class for all invoker nodes, which invokes external services, access database, or call 3rd tools.
9 | """
10 | def __init__(self, name) -> None:
11 | super().__init__(name, LPNodeType.Invoke, None)
12 |
13 | @final
14 | def _handle(self, lpdata) -> None:
15 | # hidden _handle(...) in derived classes, using _invoke(...) instead
16 | self._invoke(lpdata)
17 |
18 | @abstractmethod
19 | def _invoke(self, lpdata) -> None:
20 | pass
--------------------------------------------------------------------------------
/langpipe/lpbaserouter.py:
--------------------------------------------------------------------------------
1 | import threading
2 | from abc import ABC, abstractmethod
3 | from typing import final
4 | from .lpnode import LPNode, LPNodeType
5 |
6 | class LPBaseRouter(ABC, LPNode):
7 | """
8 | base class for router node, which is used to route the data to different branches in pipeline.
9 | """
10 | def __init__(self, name) -> None:
11 | super().__init__(name, LPNodeType.Invoke, None)
12 |
13 | @abstractmethod
14 | def _condition_check(self, lpdata) -> int:
15 | """
16 | if/elif/.../else condition check, return int value to identity which branch to run.
17 | - 0 means first branch to run
18 | - 1 means second branch to run
19 | - ...
20 | """
21 | pass
22 |
23 | def _dispatch(self, lpdata) -> None:
24 | route_id = self._condition_check(lpdata)
25 |
26 | if route_id >= 0 and len(self.next_nodes) > route_id:
27 | node = self.next_nodes[route_id]
28 | if lpdata['sync']:
29 | node.run(lpdata)
30 | else:
31 | threading.Thread(target=lambda d: node.run(d), args=(lpdata,)).start()
--------------------------------------------------------------------------------
/langpipe/lpbegin.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from .lpnode import LPNode, LPNodeType
3 |
4 |
5 | class LPBegin(LPNode):
6 | """
7 | begin node in pipeline.
8 | """
9 |
10 | def __init__(self, name) -> None:
11 | super().__init__(name, LPNodeType.Begin, None)
12 |
13 | def input(self, query, query_images=None, sync=True):
14 | """
15 | construct lpdata and call super().run(), start to run the pipeline.
16 |
17 | **parameters**
18 | - query(str): input text or prompt.
19 | - query_images([]): input images with cv2.mat format, None by default.
20 | - sync(bool): start pipeline with sync or aysnc mode.
21 | """
22 |
23 | # construct lpdata which will flowing the piepline
24 | lpdata = {}
25 | # running mode
26 | lpdata['sync'] = sync
27 | # begin time
28 | lpdata['begin_t'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
29 | # end time
30 | lpdata['end_t'] = None
31 | # input text or prompt
32 | lpdata['query'] = query
33 | # input images
34 | lpdata['query_imgs'] = query_images
35 | # final output generated by pipeline
36 | lpdata['final_out'] = None
37 | # global variables when lpdata flowing the pipeline
38 | lpdata['global_vars'] = {}
39 | # handle records when lpdata flowing the pipeline
40 | lpdata['records'] = []
41 |
42 | super().run(lpdata)
43 |
44 |
45 |
--------------------------------------------------------------------------------
/langpipe/lpboardrender.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import threading
3 | import numpy as np
4 | from datetime import datetime
5 | from .lpnode import LPNodeState
6 |
7 | class LPBoardRender:
8 | """
9 | pipeline visualization tools based on OpenCV library.
10 | """
11 | def __init__(self, node_size=50, h_spacing=100, v_spacing=80):
12 | self.__node_size = node_size
13 | self.__h_spacing = h_spacing
14 | self.__v_spacing = v_spacing
15 | self.__y_positions = {}
16 | self.__img = None
17 | self.__colors = {
18 | LPNodeState.Pending: (0, 0, 0), # bgr is black
19 | LPNodeState.Runing: (0, 0, 255), # bgr is red
20 | LPNodeState.Completed: (255, 0, 0) # bgr is blue
21 | }
22 | self.__runing = False
23 |
24 | def __del__(self):
25 | self.__runing = False
26 |
27 | def __get_tree_depth(self, root):
28 | if not root.next_nodes:
29 | return 1
30 | return 1 + max(self.__get_tree_depth(child) for child in root.next_nodes)
31 |
32 | def __get_layer_nodes(self, root, depth=0, layers=None):
33 | if layers is None:
34 | layers = {}
35 | if depth not in layers:
36 | layers[depth] = []
37 | layers[depth].append(root)
38 | for child in root.next_nodes:
39 | self.__get_layer_nodes(child, depth + 1, layers)
40 | return layers
41 |
42 | def __draw_node(self, node, x, y):
43 | cv2.rectangle(self.__img, (x, y),
44 | (x + self.__node_size, y + self.__node_size),
45 | self.__colors[node.state], 2)
46 |
47 | # name
48 | text_size = cv2.getTextSize(node.name, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)[0]
49 | text_x = x + (self.__node_size - text_size[0]) // 2
50 | text_y = y + (self.__node_size + text_size[1]) // 2
51 | cv2.putText(self.__img, node.name, (text_x, text_y),
52 | cv2.FONT_HERSHEY_SIMPLEX, 0.5, self.__colors[node.state], 1)
53 |
54 | # type
55 | text_size = cv2.getTextSize('[' + node.type.name + ']', cv2.FONT_HERSHEY_SIMPLEX, 0.4, 1)[0]
56 | text_x = x + (self.__node_size - text_size[0]) // 2
57 | text_y = y + text_size[1] + 5
58 | cv2.putText(self.__img, '[' + node.type.name + ']', (text_x, text_y),
59 | cv2.FONT_HERSHEY_SIMPLEX, 0.4, self.__colors[node.state], 1)
60 |
61 | # cost time
62 | if node.state == LPNodeState.Completed:
63 | text_size = cv2.getTextSize(str(node.cost_time) + 'sec', cv2.FONT_HERSHEY_SIMPLEX, 0.4, 1)[0]
64 | text_x = x + (self.__node_size - text_size[0]) // 2
65 | text_y = y + self.__node_size - 5
66 | cv2.putText(self.__img, str(node.cost_time) + 'sec', (text_x, text_y),
67 | cv2.FONT_HERSHEY_SIMPLEX, 0.4, self.__colors[node.state], 1)
68 |
69 | # child nodes
70 | child_x = x + self.__node_size + self.__h_spacing
71 | for child in node.next_nodes:
72 | child_y = self.__y_positions[child]
73 | cv2.line(self.__img, (x + self.__node_size, y + self.__node_size // 2),
74 | (child_x, child_y + self.__node_size // 2), self.__colors[child.state], 1, cv2.LINE_AA)
75 | cv2.circle(self.__img, (child_x, child_y + self.__node_size // 2),
76 | 5, self.__colors[child.state], -1)
77 | self.__draw_node(child, child_x, child_y)
78 |
79 | def __draw_info(self):
80 | cv2.putText(self.__img, datetime.now().strftime("%Y-%m-%d %H:%M:%S"), (20, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
81 |
82 | def __draw_board(self, root):
83 | layers = self.__get_layer_nodes(root)
84 | max_depth = max(layers.keys())
85 | max_width = max(len(layers[d]) for d in layers)
86 |
87 | img_height = max(400, max_width * (self.__node_size + self.__v_spacing))
88 | img_width = (max_depth + 1) * (self.__node_size + self.__h_spacing)
89 |
90 | for depth, nodes in layers.items():
91 | layer_height = len(nodes) * (self.__node_size + self.__v_spacing)
92 | start_y = (img_height - layer_height + self.__v_spacing) // 2
93 | self.__y_positions.update({node: start_y + i * (self.__node_size + self.__v_spacing) for i, node in enumerate(nodes)})
94 |
95 | root_x = self.__h_spacing // 2
96 | root_y = self.__y_positions[root]
97 |
98 | while self.__runing:
99 | self.__img = np.ones((img_height, img_width, 3), dtype=np.uint8) * 255
100 | self.__draw_node(root, root_x, root_y)
101 | self.__draw_info()
102 | cv2.imshow("LangPipe", self.__img)
103 | if cv2.waitKey(100) & 0xFF == 27:
104 | break
105 |
106 | def render(self, root, block=True):
107 | self.__runing = True
108 | render_th = threading.Thread(target=self.__draw_board, args=(root,), daemon=True)
109 | render_th.start()
110 |
111 | if block:
112 | render_th.join()
113 |
--------------------------------------------------------------------------------
/langpipe/lpchatter.py:
--------------------------------------------------------------------------------
1 | import json
2 | from ollama import chat
3 | from .lpnode import LPNode, LPNodeType
4 |
5 | class LPChatter(LPNode):
6 | """
7 | chat with LLM based on Ollama, supporting chat histories which is different from text generation.
8 | access `lpdata['global_vars']['chatted_text']` to get the chat text.
9 | """
10 | def __init__(self, name, model='minicpm-v:8b') -> None:
11 | super().__init__(name, LPNodeType.LLM, model)
12 | self.__chatted_text = None
13 |
14 | def _handle(self, lpdata) -> None:
15 | # convert query to json object something like: [{'role': 'user', 'content': 'Hello, how are you?'}, {...}, {...}]
16 | messages_from_query = json.loads(lpdata['query'])
17 | response = chat(model=self.model,
18 | messages=messages_from_query,
19 | options={
20 | 'top_k': 100,
21 | 'temperature': 0.8
22 | })
23 | # take care ... in some reason models
24 | self.__chatted_text = response['message']['content']
25 |
26 | # update records
27 | messages = lpdata['records'][-1]['messages']
28 | messages.extend(messages_from_query)
29 |
30 | message = {}
31 | message['role'] = 'assistant'
32 | message['content'] = response['message']['content']
33 | messages.append(message)
34 |
35 | def _after_handle(self, lpdata) -> None:
36 | super()._after_handle(lpdata)
37 |
38 | # update global variables
39 | lpdata['final_out'] = self.__chatted_text
40 | lpdata['global_vars']['chatted_text'] = self.__chatted_text
--------------------------------------------------------------------------------
/langpipe/lpclassifier.py:
--------------------------------------------------------------------------------
1 |
2 | import threading
3 | import json
4 | from ollama import generate
5 | from .lpnode import LPNode, LPNodeType
6 |
7 |
8 | class LPClassifier(LPNode):
9 | """
10 | task classification using LLM based on Ollama.
11 | access `lpdata['global_vars']['cls_label']` to get the classification label.
12 | """
13 |
14 | def __init__(self, name, labels_desc:dict[str,str], model='minicpm-v:8b') -> None:
15 | super().__init__(name, LPNodeType.LLM, model)
16 | self.__labels_desc = labels_desc
17 | self.__cls_label_id = -1
18 | self.__cls_label = None
19 | self.__cls_prompt_template = """
20 | 你是一个强大的文本分类助手,能够根据用户定义的分类标签对任何文本进行精准分类。
21 | 用户提供的分类标签及其描述如下(JSON 格式):
22 | {0}
23 |
24 | 请按照以下要求进行文本分类:
25 | 1. 认真阅读待分类的文本,并理解其主要内容。
26 | 2. 根据用户提供的标签及描述,选出最符合该文本内容的分类标签。
27 | 3. 仅返回一个最合适的分类标签,不要输出多余内容。
28 | 4. 如果文本内容不属于任何分类,直接返回None。
29 |
30 | 待分类文本:
31 | "{1}"
32 |
33 | 请直接返回最符合该文本的分类标签(不要用引号包裹标签):
34 | """
35 |
36 | def _handle(self, lpdata) -> None:
37 | query = lpdata['query']
38 | prompt = self.__cls_prompt_template.format(json.dumps(self.__labels_desc, indent=4, ensure_ascii=False), query)
39 |
40 | response = generate(model=self.model,
41 | prompt=prompt,
42 | options={
43 | 'top_k': 1,
44 | 'temperature': 0.5
45 | })
46 | self.__cls_label = response['response']
47 | if self.__cls_label in self.__labels_desc:
48 | self.__cls_label_id = list(self.__labels_desc.keys()).index(self.__cls_label)
49 |
50 | # update record
51 | messages = lpdata['records'][-1]['messages']
52 | message = {}
53 | message['role'] = 'user'
54 | message['content'] = prompt
55 | messages.append(message)
56 |
57 | message = {}
58 | message['role'] = 'assistant'
59 | message['content'] = response['response']
60 | messages.append(message)
61 |
62 | def _after_handle(self, lpdata) -> None:
63 | super()._after_handle(lpdata)
64 |
65 | # insert local variables
66 | record = lpdata['records'][-1]
67 | record['local_vars']['__labels_desc'] = json.dumps(self.__labels_desc, indent=4, ensure_ascii=False)
68 | record['local_vars']['cls_label_id'] = self.__cls_label_id
69 | record['local_vars']['cls_label'] = self.__cls_label
70 |
71 | # update global variables
72 | lpdata['final_out'] = self.__cls_label
73 | lpdata['global_vars']['cls_label'] = self.__cls_label
74 |
75 | def _dispatch(self, lpdata) -> None:
76 | """
77 | dispatch lpdata according to the label id.
78 | """
79 |
80 | # classification fails, disptach lpdata to the last one only if it is end node
81 | if self.__cls_label_id == -1 and len(self.next_nodes) > 0:
82 | node = self.next_nodes[-1]
83 | if node.type == LPNodeType.End:
84 | if lpdata['sync']:
85 | node.run(lpdata)
86 | else:
87 | threading.Thread(target=lambda d: node.run(d), args=(lpdata,)).start()
88 |
89 | for i, node in enumerate(self.next_nodes):
90 | if i == self.__cls_label_id:
91 | if lpdata['sync']:
92 | node.run(lpdata)
93 | else:
94 | threading.Thread(target=lambda d: node.run(d), args=(lpdata,)).start()
95 | break
--------------------------------------------------------------------------------
/langpipe/lpend.py:
--------------------------------------------------------------------------------
1 | import json
2 | import re
3 | from datetime import datetime
4 | from .lpnode import LPNode, LPNodeType
5 |
6 | class LPEnd(LPNode):
7 | """
8 | end node in pipeline.
9 | """
10 |
11 | def __init__(self,
12 | name,
13 | callback=None,
14 | debug=True,
15 | print_final_out=True,
16 | remove_thinking_txt=True) -> None:
17 | super().__init__(name, LPNodeType.End, None)
18 | self.__callback = callback
19 | self.__debug = debug
20 | self.__print_final_out = print_final_out
21 | self.__remove_thinking_txt = remove_thinking_txt
22 | self.final_out = None
23 |
24 | def _dispatch(self, lpdata) -> None:
25 | """
26 | override dispatch() since no lpdata flowing downstream.
27 | """
28 | pass
29 |
30 | def _handle(self, lpdata) -> None:
31 | """
32 | postprocess for final_out if nessessary
33 | """
34 | self.final_out = lpdata['final_out']
35 |
36 | # remove ...
37 | if self.__remove_thinking_txt:
38 | self.final_out = re.sub(r".*? ", "", self.final_out, flags=re.DOTALL)
39 |
40 | if self.__print_final_out:
41 | print(f'>>>>>>>>>>>>>[output][final_out from {self.name}]>>>>>>>>>>>>>')
42 | print(self.final_out)
43 | print(f'<<<<<<<<<<<<<[output][final_out from {self.name}]<<<<<<<<<<<<<')
44 |
45 | def _after_handle(self, lpdata) -> None:
46 | """
47 | override _after_handle() since we need callback/print/update when all works done.
48 | """
49 | super()._after_handle(lpdata)
50 |
51 | # update local vars
52 | record = lpdata['records'][-1]
53 | record['local_vars']['__callback'] = self.__callback if self.__callback is None else self.__callback.__name__
54 | record['local_vars']['__debug'] = self.__debug
55 | record['local_vars']['__print_final_out'] = self.__print_final_out
56 | record['local_vars']['__remove_thinking_txt'] = self.__remove_thinking_txt
57 |
58 | # update global vars
59 | lpdata['end_t'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
60 |
61 | # print for debug purpose
62 | if self.__debug:
63 | print(f'>>>>>>>>>>>>>[debug][lpdata from {self.name}]>>>>>>>>>>>>>')
64 | print(json.dumps(lpdata, indent=4, ensure_ascii=False))
65 | print(f'<<<<<<<<<<<<<[debug][lpdata from {self.name}]<<<<<<<<<<<<<')
66 |
67 | # callback to notify external codes
68 | if self.__callback is not None:
69 | self.__callback(lpdata)
70 |
71 | def link(self, next_nodes) -> int:
72 | """
73 | override link() since linking not approved for end node.
74 | """
75 | return 0
--------------------------------------------------------------------------------
/langpipe/lpextractor.py:
--------------------------------------------------------------------------------
1 | import json
2 | import re
3 | from ollama import generate
4 | from .lpnode import LPNode, LPNodeType
5 |
6 | class LPExtractor(LPNode):
7 | """
8 | parameters extraction using LLM based on Ollama.
9 | access `lpdata['global_vars']['extracted_params']` to get the values of extracted parameters.
10 | """
11 |
12 | def __init__(self, name, params_desc:dict[str,str], model='minicpm-v:8b') -> None:
13 | super().__init__(name, LPNodeType.LLM, model)
14 | self.__params_desc = params_desc
15 | self.__extracted_params = None
16 | self.__extract_prompt_template = """
17 | 你是一个强大的参数提取助手,能够根据用户定义的参数名称和描述,从给定文本中提取相关参数值。
18 | 以下是用户定义的参数名称和描述:
19 | {0}
20 |
21 | 任务要求:
22 | 1. 请从用户提供的文本中,依据定义的参数名称和描述,提取出相应的参数值。
23 | 2. 有些参数值可能无法直接获取,需要你从上下文中推断参数值。
24 | 3. 若提取成功,请输出以JSON格式呈现,key为参数名,value为提取的参数值。
25 | 4. 如果某个参数无法从文本中提取到,请将其值设为null。
26 | 5. 输出JSON中的参数名称、顺序以及数量必须与用户给定的参数定义完全一致。
27 | 6. 输出仅包含纯粹的JSON数据,不要包含任何额外字符、标点或说明文字。
28 |
29 | 待提取参数值的文本为:
30 | "{1}"
31 |
32 | 请输出提取到的结果(JSON格式):
33 | """
34 |
35 | def _handle(self, lpdata) -> None:
36 | query = lpdata['query']
37 | prompt = self.__extract_prompt_template.format(json.dumps(self.__params_desc, indent=4, ensure_ascii=False), query)
38 |
39 | response = generate(model=self.model,
40 | prompt=prompt,
41 | options={
42 | 'top_k': 1,
43 | 'temperature': 0.5
44 | })
45 | # in case ... in some reason models
46 | self.__extracted_params = re.sub(r".*? ", "", response['response'], flags=re.DOTALL)
47 |
48 | # update records
49 | messages = lpdata['records'][-1]['messages']
50 | message = {}
51 | message['role'] = 'user'
52 | message['content'] = prompt
53 | messages.append(message)
54 |
55 | message = {}
56 | message['role'] = 'assistant'
57 | message['content'] = response['response']
58 | messages.append(message)
59 |
60 | def _after_handle(self, lpdata) -> None:
61 | super()._after_handle(lpdata)
62 |
63 | # update local variables
64 | record = lpdata['records'][-1]
65 | record['local_vars']['__params_desc'] = json.dumps(self.__params_desc, indent=4, ensure_ascii=False)
66 |
67 | # update global variables
68 | lpdata['final_out'] = self.__extracted_params
69 | lpdata['global_vars']['extracted_params'] = json.loads(self.__extracted_params)
70 |
--------------------------------------------------------------------------------
/langpipe/lpgenerator.py:
--------------------------------------------------------------------------------
1 |
2 | from ollama import generate
3 | from .lpnode import LPNode, LPNodeType
4 |
5 |
6 | class LPGenerator(LPNode):
7 | """
8 | text generation using LLM based on Ollama.
9 | access `lpdata['global_vars']['generated_txt']` to get generated text.
10 | """
11 |
12 | def __init__(self, name, model='minicpm-v:8b') -> None:
13 | super().__init__(name, LPNodeType.LLM, model)
14 | self.__generated_txt = None
15 |
16 | def _handle(self, lpdata) -> None:
17 | query = lpdata['query']
18 | query_imgs = lpdata['query_imgs']
19 | response = generate(model=self.model,
20 | prompt=query,
21 | images=query_imgs,
22 | options={
23 | 'top_k': 100,
24 | 'temperature': 0.8
25 | })
26 | # take care ... in some reason models
27 | self.__generated_txt = response['response']
28 |
29 | # update records
30 | messages = lpdata['records'][-1]['messages']
31 | message = {}
32 | message['role'] = 'user'
33 | message['content'] = query
34 | messages.append(message)
35 |
36 | message = {}
37 | message['role'] = 'assistant'
38 | message['content'] = response['response']
39 | messages.append(message)
40 |
41 | def _after_handle(self, lpdata) -> None:
42 | super()._after_handle(lpdata)
43 |
44 | # update global variables
45 | lpdata['final_out'] = self.__generated_txt
46 | lpdata['global_vars']['generated_txt'] = self.__generated_txt
--------------------------------------------------------------------------------
/langpipe/lpnode.py:
--------------------------------------------------------------------------------
1 |
2 | import threading
3 | from enum import Enum
4 | from datetime import datetime
5 |
6 | class LPNodeState(Enum):
7 | """
8 | node state in LangPipe.
9 | """
10 | Pending = -1
11 | Runing = 0
12 | Completed = 1
13 |
14 |
15 | class LPNodeType(Enum):
16 | """
17 | node type in LangPipe.
18 |
19 | - Begin: begin point in pipeline.
20 | - LLM: node which works based on LLM.
21 | - Invoke: node which need call external tools or 3rd service to get more data.
22 | - End: end point in pipeline.
23 | """
24 | Begin = 1
25 | LLM = 2
26 | Invoke = 3
27 | End = 4
28 |
29 | class LPNode:
30 | """
31 | base class for all nodes in LangPipe.
32 | """
33 |
34 | def __init__(self, name, type, model) -> None:
35 | self.name = name
36 | self.type = type
37 | self.model = model
38 | self.state = LPNodeState.Pending
39 | self.cost_time = 0
40 | self.next_nodes = []
41 |
42 | def __str__(self):
43 | return self._str_helper(is_root=True)
44 |
45 | def _str_helper(self, prefix='', is_last=True, is_root=False):
46 | title = f"{self.name}[type:{str(self.type.name)}]"
47 | lines = []
48 | if is_root:
49 | lines.append(title)
50 | else:
51 | connector = '└── ' if is_last else '├── '
52 | lines.append(prefix + connector + title)
53 |
54 | new_prefix = prefix + (' ' if is_last else '│ ')
55 | child_count = len(self.next_nodes)
56 | for i, child in enumerate(self.next_nodes):
57 | is_last_child = (i == child_count - 1)
58 | lines.append(child._str_helper(new_prefix, is_last_child))
59 | return '\n'.join(lines)
60 |
61 | def run(self, lpdata) -> None:
62 | """
63 | execute main logic in node.
64 | """
65 | self._before_handle(lpdata)
66 | self._handle(lpdata)
67 | self._after_handle(lpdata)
68 | self._dispatch(lpdata)
69 |
70 | def link(self, next_nodes) -> int:
71 | """
72 | link node to next nodes and return total size of next nodes.
73 | """
74 | if isinstance(next_nodes, list):
75 | self.next_nodes.extend(next_nodes)
76 | else:
77 | self.next_nodes.append(next_nodes)
78 | return len(self.next_nodes)
79 |
80 | def _before_handle(self, lpdata) -> None:
81 | """
82 | add a new record in lpdata.
83 | """
84 | record = {}
85 | record['begin_t'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
86 | record['end_t'] = None
87 | record['node_name'] = self.name
88 | record['node_type'] = str(self.type)
89 | record['model'] = self.model
90 | record['local_vars'] = {}
91 | record['messages'] = []
92 |
93 | lpdata['records'].append(record)
94 |
95 | # enter running state
96 | self.state = LPNodeState.Runing
97 |
98 | def _handle(self, lpdata) -> None:
99 | """
100 | main logic for handling lpdata and update key/values in lpdata if nessessary.
101 | """
102 | pass
103 |
104 | def _after_handle(self, lpdata) -> None:
105 | """
106 | update record in lpdata.
107 | """
108 | record = lpdata['records'][-1]
109 | record['end_t'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
110 |
111 | self.cost_time = int((datetime.strptime(record['end_t'], "%Y-%m-%d %H:%M:%S")
112 | - datetime.strptime(record['begin_t'], "%Y-%m-%d %H:%M:%S")).total_seconds())
113 | # enter completed state
114 | self.state = LPNodeState.Completed
115 |
116 | def _dispatch(self, lpdata) -> None:
117 | """
118 | dispatch data to next nodes in sync or async mode.
119 |
120 | Depth-First Search(DFS) for the pipeline in sync mode.
121 | """
122 | for node in self.next_nodes:
123 | if lpdata['sync']:
124 | node.run(lpdata)
125 | else:
126 | threading.Thread(target=lambda d: node.run(d), args=(lpdata,)).start()
--------------------------------------------------------------------------------
/langpipe/lpsqlcreator.py:
--------------------------------------------------------------------------------
1 |
2 | import re
3 | from ollama import generate
4 | from .lpnode import LPNode, LPNodeType
5 |
6 |
7 | class LPSQLCreator(LPNode):
8 | """
9 | SQL creator using LLM based on Ollama.
10 | access `lpdata['global_vars']['created_sql']` to get created SQL.
11 | """
12 | def __init__(self, name, ddl_desc:str=None, model='minicpm-v:8b') -> None:
13 | super().__init__(name, LPNodeType.LLM, model)
14 | self.__ddl_desc = ddl_desc
15 | self.__created_sql = None
16 | self.__sql_create_template_prompt = """
17 | 你是一个强大的SQL语句生成助手,专门用于基于数据库的结构来生成SQL查询。
18 | 以下是数据库结构(DDL语句):
19 | ---
20 | {0}
21 | ---
22 |
23 | 任务描述:
24 | 1. 请根据用户输入的问题,生成正确的SQL查询语句。
25 | 2. 你需要确保查询符合数据库的表结构。
26 | 3. 使用SQL语法规范,避免SQL语法错误。
27 | 4. 优先生成可读性高的SQL语句,并适当使用`JOIN`、`WHERE`、`ORDER BY`等。
28 | 5. 如果查询需要聚合数据,请使用`GROUP BY`和适当的聚合函数(如 COUNT、SUM、AVG等)。
29 | 6. 如果问题不明确,你可以假设合理的查询条件。
30 | 7. 输出只包含SQL语句,SQL语句以分号结束,不要包含其他任何解释或注释。
31 |
32 | 参考示例1:
33 | 用户问题:“查询所有用户的姓名和电子邮件。”
34 | 示例输出:SELECT name, email FROM users;
35 |
36 | 参考示例2:
37 | 用户问题:“获取 2024 年 3 月订单总数。”
38 | 示例输出:SELECT COUNT(*) FROM orders WHERE order_date BETWEEN '2024-03-01' AND '2024-03-31';
39 |
40 | 请根据以下用户输入,返回对应的SQL查询:
41 | ---
42 | {1}
43 | ---
44 | """
45 |
46 | def _handle(self, lpdata) -> None:
47 | query = lpdata['query']
48 | prompt = self.__sql_create_template_prompt.format(self.__ddl_desc, query)
49 |
50 | response = generate(model=self.model,
51 | prompt=prompt,
52 | options={
53 | 'top_k': 1,
54 | 'temperature': 0.5
55 | })
56 | # in case ... in some reason models
57 | self.__created_sql = re.sub(r".*? ", "", response['response'], flags=re.DOTALL)
58 | # no `\n` in sql
59 | self.__created_sql = self.__created_sql.replace('\n', ' ')
60 |
61 | # update records
62 | messages = lpdata['records'][-1]['messages']
63 | message = {}
64 | message['role'] = 'user'
65 | message['content'] = prompt
66 | messages.append(message)
67 |
68 | message = {}
69 | message['role'] = 'assistant'
70 | message['content'] = response['response']
71 | messages.append(message)
72 |
73 | def _after_handle(self, lpdata) -> None:
74 | super()._after_handle(lpdata)
75 |
76 | # update local variables
77 | record = lpdata['records'][-1]
78 | record['local_vars']['__ddl_desc'] = self.__ddl_desc
79 |
80 | # update global variables
81 | lpdata['final_out'] = self.__created_sql
82 | lpdata['global_vars']['created_sql'] = self.__created_sql
83 |
84 |
--------------------------------------------------------------------------------
/langpipe/lpsuperaggregator.py:
--------------------------------------------------------------------------------
1 | import json
2 | import re
3 | from ollama import generate
4 | from .lpnode import LPNode, LPNodeType
5 |
6 | class LPSuperAggregator(LPNode):
7 | """
8 | data aggregation using LLM based on Ollama, which able to list reference sources from context.
9 | access `lpdata['global_vars']['aggregated_data']` for the aggregated data with json format.
10 |
11 | NOTE: this node is also able to convert aggregated data into html format with reference links clickable, which can be used to render the data into a web page directly.
12 | """
13 | def __init__(self, name, aggregate_desc=None, to_html=False, model='minicpm-v:8b') -> None:
14 | super().__init__(name, LPNodeType.LLM, model)
15 | self.__aggregate_desc = aggregate_desc
16 | self.__to_html = to_html
17 | self.__aggregated_data = None
18 | self.__aggregate_prompt_template = """
19 | 你是一个强大的智能信息聚合助手(Aggregator),擅长根据上下文信息,结合自己的理解、生成有引用标注的回答。
20 | 以下是可供参考的上下文:
21 | ---
22 | {0}
23 | ---
24 |
25 | 以下是待回答的问题:
26 | ---
27 | {1}
28 | ---
29 |
30 | 请注意以下任务要求:
31 | 1. 回答正文中如有引用某些具体数据、事实、日期、标准、文档结论等内容,请在该信息后添加引用占位符(如 `[1]`、`[2]` 等)。
32 | 2. 按顺序列出这些引用编号所对应的来源(可以是网址、文档路径、书名等),来源不可重复。引用编号顺序必须与正文中使用的一致。
33 |
34 | 重点注意:
35 | 1. **信息整合**:充分利用所有来源的数据,确保信息完整,不遗漏任何重要内容。
36 | 2. **语义流畅**:避免直接罗列数据,而是用自然语言组织,使回答清晰易懂。
37 |
38 | 注意最终要以JSON格式输出:
39 | {{
40 | "content": "回答正文,包含引用占位符[1][2][N]……",
41 | "references": [
42 | "对应的引用来源1",
43 | "对应的引用来源2",
44 | "对应的引用来源N"
45 | ]
46 | }}
47 |
48 | 现在给出你的回答:
49 | """
50 |
51 | def _handle(self, lpdata) -> None:
52 | query = lpdata['query'] if self.__aggregate_desc is None else self.__aggregate_desc
53 | prompt = self.__aggregate_prompt_template.format(json.dumps(lpdata['global_vars'], indent=4, ensure_ascii=False), query)
54 | response = generate(model=self.model,
55 | prompt=prompt,
56 | format='json',
57 | options={
58 | 'top_k': 1,
59 | 'temperature': 0.5,
60 | 'num_ctx': 32768 # 32K
61 | })
62 | self.__aggregated_data = response['response']
63 |
64 | # update records
65 | messages = lpdata['records'][-1]['messages']
66 | message = {}
67 | message['role'] = 'user'
68 | message['content'] = prompt
69 | messages.append(message)
70 |
71 | message = {}
72 | message['role'] = 'assistant'
73 | message['content'] = response['response']
74 | messages.append(message)
75 |
76 | def _after_handle(self, lpdata) -> None:
77 | super()._after_handle(lpdata)
78 |
79 | # update local vars
80 | record = lpdata['records'][-1]
81 | record['local_vars']['__aggregate_desc'] = self.__aggregate_desc
82 | record['local_vars']['__to_html'] = self.__to_html
83 |
84 | # update global variables
85 | if self.__to_html:
86 | self.__aggregated_data = self.__convert_html() # convert to html with reference links clickable
87 | lpdata['final_out'] = self.__aggregated_data
88 | lpdata['global_vars']['aggregated_data'] = json.loads(self.__aggregated_data)
89 |
90 | def __convert_html(self) -> str:
91 | data = json.loads(self.__aggregated_data)
92 | content = data["content"]
93 | links = data["references"]
94 |
95 | def replace(match):
96 | num = int(match.group(1))
97 | index = num - 1
98 | if 0 <= index < len(links):
99 | url = links[index]
100 | return f'[{num}] '
101 | else:
102 | return match.group(0)
103 |
104 | html_content = re.sub(r'\[(\d+)\]', replace, content)
105 | data['content'] = html_content
106 | return json.dumps(data, indent=4, ensure_ascii=False)
107 |
--------------------------------------------------------------------------------
/lpdata.json:
--------------------------------------------------------------------------------
1 | {
2 | "sync": false,
3 | "begin_t": "2025-04-11 15:37:24",
4 | "end_t": "2025-04-11 15:38:09",
5 | "query": "\n购买力前5的用户有哪些,各消费了多少?我需要他们详细资料\n",
6 | "query_imgs": null,
7 | "final_out": "根据数据库查询结果,购买力排名前五的用户及其详细资料如下:\n\n1. 用户名:钱七(ID:5),邮箱地址为qianqi@163.com,总消费金额为101,102.82元。\n2. 用户名:孙八(ID:6),邮箱地址为sunba_001@163.com,总消费金额为58,619.30元。\n3. 用户名:王五(ID:3),邮箱地址为wangwu2023@163.com,总消费金额为33,469.71元。\n4. 用户名:吴十(ID:8),邮箱地址为wushi666@163.com,总消费金额为31,885.00元。\n5. 用户名:赵六(ID:4),邮箱地址为zhaoliu@163.com,总消费金额为24,105.00元。\n\n这些用户按照他们的总消费额从高到低排列。",
8 | "global_vars": {
9 | "created_sql": "SELECT u.id, u.username, u.email, SUM(oi.quantity * oi.unit_price) AS total_spent FROM users u JOIN orders o ON u.id = o.user_id JOIN order_items oi ON o.id = oi.order_id GROUP BY u.id, u.username, u.email ORDER BY total_spent DESC LIMIT 5;",
10 | "db_searched_result": "[(5, '钱七', 'qianqi@163.com', 101102.82), (6, '孙八', 'sunba_001@163.com', 58619.3), (3, '王五', 'wangwu2023@163.com', 33469.71), (8, '吴十', 'wushi666@163.com', 31885.0), (4, '赵六', 'zhaoliu@163.com', 24105.0)]",
11 | "aggregated_data": "根据数据库查询结果,购买力排名前五的用户及其详细资料如下:\n\n1. 用户名:钱七(ID:5),邮箱地址为qianqi@163.com,总消费金额为101,102.82元。\n2. 用户名:孙八(ID:6),邮箱地址为sunba_001@163.com,总消费金额为58,619.30元。\n3. 用户名:王五(ID:3),邮箱地址为wangwu2023@163.com,总消费金额为33,469.71元。\n4. 用户名:吴十(ID:8),邮箱地址为wushi666@163.com,总消费金额为31,885.00元。\n5. 用户名:赵六(ID:4),邮箱地址为zhaoliu@163.com,总消费金额为24,105.00元。\n\n这些用户按照他们的总消费额从高到低排列。"
12 | },
13 | "records": [
14 | {
15 | "begin_t": "2025-04-11 15:37:24",
16 | "end_t": "2025-04-11 15:37:24",
17 | "node_name": "begin_node",
18 | "node_type": "LPNodeType.Begin",
19 | "model": null,
20 | "local_vars": {},
21 | "messages": []
22 | },
23 | {
24 | "begin_t": "2025-04-11 15:37:24",
25 | "end_t": "2025-04-11 15:37:44",
26 | "node_name": "sql_creator",
27 | "node_type": "LPNodeType.LLM",
28 | "model": "qwen2.5:32b",
29 | "local_vars": {
30 | "__ddl_desc": "\n##下面是sqlite数据库的DDL结构##\n-- ----------------------------\n-- 用户表:users\n-- 存储注册用户的基本信息\n-- ----------------------------\nCREATE TABLE IF NOT EXISTS users (\n id INTEGER PRIMARY KEY AUTOINCREMENT, -- 用户唯一ID,自增主键\n username TEXT NOT NULL UNIQUE, -- 用户名,必须唯一且非空\n email TEXT NOT NULL UNIQUE, -- 用户邮箱,必须唯一且非空\n password_hash TEXT NOT NULL, -- 密码哈希值,用于存储加密后的密码\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 注册时间,默认为当前时间\n);\n\n-- ----------------------------\n-- 商品表:products\n-- 存储上架商品的详细信息\n-- ----------------------------\nCREATE TABLE IF NOT EXISTS products (\n id INTEGER PRIMARY KEY AUTOINCREMENT, -- 商品唯一ID,自增主键\n name TEXT NOT NULL, -- 商品名称,非空\n description TEXT, -- 商品描述,可选\n price REAL NOT NULL, -- 商品单价,使用REAL表示小数\n stock INTEGER DEFAULT 0, -- 商品库存数量,默认0\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 上架时间,默认为当前时间\n);\n\n-- ----------------------------\n-- 订单表:orders\n-- 记录用户提交的订单信息\n-- ----------------------------\n/*\n订单状态(status)字段说明:\n- 'pending' 等待付款\n- 'paid' 已付款\n- 'shipped' 已发货\n- 'completed' 已完成\n- 'cancelled' 已取消\n*/\nCREATE TABLE IF NOT EXISTS orders (\n id INTEGER PRIMARY KEY AUTOINCREMENT, -- 订单唯一ID,自增主键\n user_id INTEGER NOT NULL, -- 下单用户的ID,关联 users(id)\n status TEXT DEFAULT 'pending', -- 订单状态,默认为 pending\n total_amount REAL NOT NULL, -- 订单总金额\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 订单创建时间\n FOREIGN KEY (user_id) REFERENCES users(id) -- 外键:用户ID\n);\n\n-- ----------------------------\n-- 订单项表:order_items\n-- 记录每个订单中包含哪些商品及数量\n-- ----------------------------\nCREATE TABLE IF NOT EXISTS order_items (\n id INTEGER PRIMARY KEY AUTOINCREMENT, -- 订单项唯一ID,自增主键\n order_id INTEGER NOT NULL, -- 所属订单ID,关联 orders(id)\n product_id INTEGER NOT NULL, -- 商品ID,关联 products(id)\n quantity INTEGER NOT NULL, -- 购买数量\n unit_price REAL NOT NULL, -- 下单时的商品单价\n FOREIGN KEY (order_id) REFERENCES orders(id), -- 外键:订单ID\n FOREIGN KEY (product_id) REFERENCES products(id) -- 外键:商品ID\n);\n\n-- ----------------------------\n-- 退款表:refunds\n-- 记录订单退款的请求和处理信息\n-- ----------------------------\nCREATE TABLE IF NOT EXISTS refunds (\n id INTEGER PRIMARY KEY AUTOINCREMENT, -- 退款记录ID,自增主键\n order_id INTEGER NOT NULL, -- 关联的订单ID\n refund_amount REAL NOT NULL, -- 退款金额\n reason TEXT, -- 退款原因,可选\n refund_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 退款时间,默认为当前时间\n FOREIGN KEY (order_id) REFERENCES orders(id) -- 外键:订单ID\n);\n"
31 | },
32 | "messages": [
33 | {
34 | "role": "user",
35 | "content": "\n 你是一个强大的SQL语句生成助手,专门用于基于数据库的结构来生成SQL查询。\n 以下是数据库结构(DDL语句):\n ---\n \n##下面是sqlite数据库的DDL结构##\n-- ----------------------------\n-- 用户表:users\n-- 存储注册用户的基本信息\n-- ----------------------------\nCREATE TABLE IF NOT EXISTS users (\n id INTEGER PRIMARY KEY AUTOINCREMENT, -- 用户唯一ID,自增主键\n username TEXT NOT NULL UNIQUE, -- 用户名,必须唯一且非空\n email TEXT NOT NULL UNIQUE, -- 用户邮箱,必须唯一且非空\n password_hash TEXT NOT NULL, -- 密码哈希值,用于存储加密后的密码\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 注册时间,默认为当前时间\n);\n\n-- ----------------------------\n-- 商品表:products\n-- 存储上架商品的详细信息\n-- ----------------------------\nCREATE TABLE IF NOT EXISTS products (\n id INTEGER PRIMARY KEY AUTOINCREMENT, -- 商品唯一ID,自增主键\n name TEXT NOT NULL, -- 商品名称,非空\n description TEXT, -- 商品描述,可选\n price REAL NOT NULL, -- 商品单价,使用REAL表示小数\n stock INTEGER DEFAULT 0, -- 商品库存数量,默认0\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 上架时间,默认为当前时间\n);\n\n-- ----------------------------\n-- 订单表:orders\n-- 记录用户提交的订单信息\n-- ----------------------------\n/*\n订单状态(status)字段说明:\n- 'pending' 等待付款\n- 'paid' 已付款\n- 'shipped' 已发货\n- 'completed' 已完成\n- 'cancelled' 已取消\n*/\nCREATE TABLE IF NOT EXISTS orders (\n id INTEGER PRIMARY KEY AUTOINCREMENT, -- 订单唯一ID,自增主键\n user_id INTEGER NOT NULL, -- 下单用户的ID,关联 users(id)\n status TEXT DEFAULT 'pending', -- 订单状态,默认为 pending\n total_amount REAL NOT NULL, -- 订单总金额\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 订单创建时间\n FOREIGN KEY (user_id) REFERENCES users(id) -- 外键:用户ID\n);\n\n-- ----------------------------\n-- 订单项表:order_items\n-- 记录每个订单中包含哪些商品及数量\n-- ----------------------------\nCREATE TABLE IF NOT EXISTS order_items (\n id INTEGER PRIMARY KEY AUTOINCREMENT, -- 订单项唯一ID,自增主键\n order_id INTEGER NOT NULL, -- 所属订单ID,关联 orders(id)\n product_id INTEGER NOT NULL, -- 商品ID,关联 products(id)\n quantity INTEGER NOT NULL, -- 购买数量\n unit_price REAL NOT NULL, -- 下单时的商品单价\n FOREIGN KEY (order_id) REFERENCES orders(id), -- 外键:订单ID\n FOREIGN KEY (product_id) REFERENCES products(id) -- 外键:商品ID\n);\n\n-- ----------------------------\n-- 退款表:refunds\n-- 记录订单退款的请求和处理信息\n-- ----------------------------\nCREATE TABLE IF NOT EXISTS refunds (\n id INTEGER PRIMARY KEY AUTOINCREMENT, -- 退款记录ID,自增主键\n order_id INTEGER NOT NULL, -- 关联的订单ID\n refund_amount REAL NOT NULL, -- 退款金额\n reason TEXT, -- 退款原因,可选\n refund_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 退款时间,默认为当前时间\n FOREIGN KEY (order_id) REFERENCES orders(id) -- 外键:订单ID\n);\n\n ---\n\n 任务描述:\n 1. 请根据用户输入的问题,生成正确的SQL查询语句。\n 2. 你需要确保查询符合数据库的表结构。\n 3. 使用SQL语法规范,避免SQL语法错误。\n 4. 优先生成可读性高的SQL语句,并适当使用`JOIN`、`WHERE`、`ORDER BY`等。\n 5. 如果查询需要聚合数据,请使用`GROUP BY`和适当的聚合函数(如 COUNT、SUM、AVG等)。\n 6. 如果问题不明确,你可以假设合理的查询条件。\n 7. 输出只包含SQL语句,SQL语句以分号结束,不要包含其他任何解释或注释。\n \n 参考示例1:\n 用户问题:“查询所有用户的姓名和电子邮件。”\n 示例输出:SELECT name, email FROM users;\n\n 参考示例2: \n 用户问题:“获取 2024 年 3 月订单总数。”\n 示例输出:SELECT COUNT(*) FROM orders WHERE order_date BETWEEN '2024-03-01' AND '2024-03-31';\n\n 请根据以下用户输入,返回对应的SQL查询:\n ---\n \n购买力前5的用户有哪些,各消费了多少?我需要他们详细资料\n\n ---\n "
36 | },
37 | {
38 | "role": "assistant",
39 | "content": "SELECT u.id, u.username, u.email, SUM(oi.quantity * oi.unit_price) AS total_spent\nFROM users u\nJOIN orders o ON u.id = o.user_id\nJOIN order_items oi ON o.id = oi.order_id\nGROUP BY u.id, u.username, u.email\nORDER BY total_spent DESC\nLIMIT 5;"
40 | }
41 | ]
42 | },
43 | {
44 | "begin_t": "2025-04-11 15:37:44",
45 | "end_t": "2025-04-11 15:37:44",
46 | "node_name": "db_search",
47 | "node_type": "LPNodeType.Invoke",
48 | "model": null,
49 | "local_vars": {
50 | "__db_name": "shop.db",
51 | "__sql_query": "SELECT u.id, u.username, u.email, SUM(oi.quantity * oi.unit_price) AS total_spent FROM users u JOIN orders o ON u.id = o.user_id JOIN order_items oi ON o.id = oi.order_id GROUP BY u.id, u.username, u.email ORDER BY total_spent DESC LIMIT 5;"
52 | },
53 | "messages": []
54 | },
55 | {
56 | "begin_t": "2025-04-11 15:37:44",
57 | "end_t": "2025-04-11 15:38:09",
58 | "node_name": "aggregator",
59 | "node_type": "LPNodeType.LLM",
60 | "model": "qwen2.5:32b",
61 | "local_vars": {
62 | "__aggregate_desc": null
63 | },
64 | "messages": [
65 | {
66 | "role": "user",
67 | "content": "\n 你是一个智能信息聚合器(Aggregator),负责整合多个来源的信息,并基于所有可用数据生成高质量、清晰且有逻辑性的最终回答。\n 以下是所有可用的信息:\n ---\n {\n \"created_sql\": \"SELECT u.id, u.username, u.email, SUM(oi.quantity * oi.unit_price) AS total_spent FROM users u JOIN orders o ON u.id = o.user_id JOIN order_items oi ON o.id = oi.order_id GROUP BY u.id, u.username, u.email ORDER BY total_spent DESC LIMIT 5;\",\n \"db_searched_result\": \"[(5, '钱七', 'qianqi@163.com', 101102.82), (6, '孙八', 'sunba_001@163.com', 58619.3), (3, '王五', 'wangwu2023@163.com', 33469.71), (8, '吴十', 'wushi666@163.com', 31885.0), (4, '赵六', 'zhaoliu@163.com', 24105.0)]\"\n}\n ---\n\n 以下是待回答的问题:\n ---\n \n购买力前5的用户有哪些,各消费了多少?我需要他们详细资料\n\n ---\n\n 任务要求:\n 1. **信息整合**:充分利用所有来源的数据,确保信息完整,不遗漏任何重要内容。\n 2. **语义流畅**:避免直接罗列数据,而是用自然语言组织,使回答清晰易懂。\n\n 请给出最终整合后的完整回答:\n "
68 | },
69 | {
70 | "role": "assistant",
71 | "content": "根据数据库查询结果,购买力排名前五的用户及其详细资料如下:\n\n1. 用户名:钱七(ID:5),邮箱地址为qianqi@163.com,总消费金额为101,102.82元。\n2. 用户名:孙八(ID:6),邮箱地址为sunba_001@163.com,总消费金额为58,619.30元。\n3. 用户名:王五(ID:3),邮箱地址为wangwu2023@163.com,总消费金额为33,469.71元。\n4. 用户名:吴十(ID:8),邮箱地址为wushi666@163.com,总消费金额为31,885.00元。\n5. 用户名:赵六(ID:4),邮箱地址为zhaoliu@163.com,总消费金额为24,105.00元。\n\n这些用户按照他们的总消费额从高到低排列。"
72 | }
73 | ]
74 | },
75 | {
76 | "begin_t": "2025-04-11 15:38:09",
77 | "end_t": "2025-04-11 15:38:09",
78 | "node_name": "end_node",
79 | "node_type": "LPNodeType.End",
80 | "model": null,
81 | "local_vars": {
82 | "__callback": null,
83 | "__debug": true,
84 | "__print_final_out": true,
85 | "__remove_thinking_txt": true
86 | },
87 | "messages": []
88 | }
89 | ]
90 | }
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | faiss_cpu==1.9.0.post1
2 | Faker==37.1.0
3 | fitz==0.0.1.dev2
4 | numpy==2.2.4
5 | ollama==0.4.7
6 | opencv_python==4.11.0.86
7 | pypinyin==0.53.0
8 | python_docx==1.1.2
9 | Requests==2.32.3
10 | setuptools==72.1.0
11 | tqdm==4.66.4
12 | flask==3.1.0
13 | flask-socketio==5.5.1
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 |
3 | setup(
4 | name="langpipe",
5 | version="0.0.1",
6 | author="zhouzhi",
7 | author_email="zhouzhi9@foxmail.com",
8 | description="a simple lightweight large language model pipeline framework.",
9 | long_description=open("README.md").read(),
10 | long_description_content_type="text/markdown",
11 | url="https://github.com/sherlockchou86/VideoPipe",
12 | packages=find_packages(),
13 | install_requires=[
14 | "ollama"
15 | ],
16 | classifiers=[
17 | "Programming Language :: Python :: 3",
18 | "License :: OSI Approved :: MIT License",
19 | "Operating System :: OS Independent",
20 | ],
21 | python_requires='>=3.10',
22 | )
23 |
--------------------------------------------------------------------------------
/tests/00-chat_test.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 |
3 | # message
4 | query = """
5 | [
6 | {
7 | "role": "user",
8 | "content": "你好"
9 | },
10 | {
11 | "role": "assistant",
12 | "content": "你好,有什么我可以帮助你的吗?"
13 | },
14 | {
15 | "role": "user",
16 | "content": "如何使用语言模型?"
17 | },
18 | {
19 | "role": "user",
20 | "content": "如何面对恐惧?"
21 | }
22 | ]
23 | """
24 |
25 | # create nodes
26 | begin = langpipe.LPBegin('begin_node')
27 | chatter = langpipe.LPChatter('chatter', 'qwen2.5:7b')
28 | end = langpipe.LPEnd('end_node', callback=lambda x: print(x['final_out']), debug=False, print_final_out=False, remove_thinking_txt=False)
29 |
30 | # link together
31 | begin.link(chatter)
32 | chatter.link(end)
33 |
34 | # input with sync mode
35 | begin.input(query)
--------------------------------------------------------------------------------
/tests/01-text_generation_test.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 |
3 | query = """
4 | 如何驾驶飞机?
5 | """
6 |
7 | # create nodes
8 | begin = langpipe.LPBegin('begin_node')
9 | generator = langpipe.LPGenerator('generator', 'deepseek-r1:8b')
10 | end = langpipe.LPEnd('end_node', print_final_out=False)
11 |
12 | # link together
13 | begin.link(generator)
14 | generator.link(end)
15 |
16 | # input with sync mode
17 | begin.input(query)
18 |
19 | # print result
20 | print(end.final_out)
--------------------------------------------------------------------------------
/tests/02-task_classification_test.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 | import json
3 |
4 | # labels description
5 | labels_desc = """
6 | {
7 | "科技": "涉及科技发展、人工智能、编程、工程等相关内容",
8 | "财经": "涉及金融、经济、市场趋势、投资等内容",
9 | "体育": "涉及各类体育赛事、运动员、比赛分析等",
10 | "娱乐": "涉及电影、音乐、明星、综艺等内容",
11 | "健康": "涉及医学、养生、健康饮食、心理健康等内容"
12 | }
13 | """
14 | json.loads(labels_desc)
15 |
16 | # create nodes
17 | begin = langpipe.LPBegin('begin')
18 | classifier = langpipe.LPClassifier('classifier', json.loads(labels_desc))
19 | end0 = langpipe.LPEnd('end0') # 1st label
20 | end1 = langpipe.LPEnd('end1') # 2nd label
21 | end2 = langpipe.LPEnd('end2') # 3rd label
22 | end3 = langpipe.LPEnd('end3') # 4th label
23 | end4 = langpipe.LPEnd('end4') # 5th label
24 | end10 = langpipe.LPEnd('end10') # classification fails
25 |
26 | # link together
27 | begin.link([classifier])
28 | classifier.link([end0, end1, end2, end3, end4])
29 | classifier.link(end10)
30 |
31 | # input what you want to, with async mode
32 | begin.input("这次比赛,日本代表团获得了三金四银的好成绩", None, False)
33 |
34 | # visualize the pipeline with data flow
35 | print('-----board for debug purpose-----')
36 | renderer = langpipe.LPBoardRender(node_size=80, v_spacing=50)
37 | renderer.render(begin)
38 |
--------------------------------------------------------------------------------
/tests/03-parameters_extraction_test.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 | import json
3 |
4 | # parameters description
5 | params_desc = """
6 | {
7 | "时间": "时间、日期等",
8 | "地点": "位置信息",
9 | "事件": "发生的事件类型,描述"
10 | }
11 | """
12 |
13 | query = """
14 | 【新华社2025年3月22日讯】今日上午10时30分,位于江苏省南通市的一家化工厂发生严重爆炸事故,目前已确认造成5人死亡,12人受伤,其中3人伤势严重,仍在抢救中。事故现场浓烟滚滚,消防救援人员已紧急赶赴现场进行灭火和搜救。
15 | """
16 |
17 | # create nodes
18 | begin = langpipe.LPBegin('begin_node')
19 | extractor = langpipe.LPExtractor('extractor', json.loads(params_desc), 'qwen2.5:7b')
20 | end = langpipe.LPEnd('end_node', debug=False, print_final_out=False)
21 |
22 | # link together
23 | begin.link(extractor)
24 | extractor.link(end)
25 |
26 | # visualize the pipeline with data flow
27 | print('-----board for debug purpose-----')
28 | renderer = langpipe.LPBoardRender(node_size=100)
29 | renderer.render(begin, False) # no block
30 |
31 | # input with sync mode
32 | begin.input(query)
33 |
34 | # print the output
35 | print(end.final_out)
--------------------------------------------------------------------------------
/tests/04-notice_generation_test.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 | import json
3 | import sample_nodes
4 |
5 | # parameters description
6 | params_desc = """
7 | {
8 | "时间": "打电话的电话时间",
9 | "上报人": "打电话报警的人、组织或者一个代称(可以从上下文中获取或推断得出)",
10 | "接收人": "接听电话的人或组织",
11 | "事发高速": "事件发生的高速名称,比如'沪蓉高速'、'武黄高速'",
12 | "事发位置": "事件发生的位置,比如'K720+100'、'快到葛店的位置'、'武汉西收费站'",
13 | "事发方向": "事件发生的方向,比如'黄石方向'",
14 | "事件名称": "归纳事件发生的类型,用一个名词表示,比如'停车'",
15 | "事件描述": "详细的事件细节描述,比如'应急车道发现一辆车撞击护栏,车辆损坏严重,有人员伤亡',事件描述过程不要使用第一人称,且不要使用带有情绪语气和标点"
16 | }
17 | """
18 |
19 | # aggregation description
20 | aggregate_desc = """
21 | 请根据可用信息,生成一个对外发布的正式通告。
22 | 下面是一则具体的通告示例可供参考:
23 | -------
24 | 您好,这里是【具体的接收人】,
25 | 2024年10月31日17:07接【具体的上报人】上报:徐广高速429公里广州方向,发现散落物,有很多散落的衣物等垃圾在路面上,请前往处理。
26 | -------
27 | """
28 |
29 | # test query1
30 | query = """
31 | (2025年4月3日 14:32:34)
32 | (电话接通,背景音中有刺耳的刹车声和金属撞击声)
33 | "122报警中心,请讲!"
34 | (急促喘息声)"出...出大事了!连环撞车了!"
35 | "先生您别急,先说下您是否安全?"
36 | "我...我没事!但这边撞了好多车!"
37 | "您是路过的司机吗?"
38 | "是的"
39 | "能告诉我您现在的大概位置吗?"
40 | "在高速上!这边雾特别大...刚才突然看到前面刹车灯全亮..."
41 | "具体是哪条高速?往哪个方向?"
42 | "呃...好像是G15...往...往上海方向!"
43 | "好的G15沈海高速。看到最近的里程牌了吗?"
44 | (稍作停顿)"等等...K1372!就在这个位置!"
45 | "现场有多少辆车相撞?有人员受伤吗?"
46 | "数不清...至少七八辆!有辆小车被夹在中间变形了,里面的人在喊救命!"
47 | "请先确保自身安全!看到冒烟或者漏油的情况吗?"
48 | "有辆SUV在冒烟...天啊!后面还有车在往这边冲!"
49 | "所有人员立即撤离到护栏外!已经通知交警和救护车。您车牌号是多少?"
50 | "浙B·5R668!警察同志你们快来吧,这边雾越来越大!"
51 | "救援已在路上。请协助提醒后方车辆减速!"
52 | "明白!我在拼命挥手...啊!又撞上了!"
53 | "您先退到安全区域!我们正在通过电子屏发布预警。"
54 | "好好...我已经退到护栏外了...那边有个人满脸是血..."
55 | "救护车5分钟就到。还有其他危险情况吗?"
56 | "有辆货车拉的钢卷...现在歪在路边,随时可能翻!"
57 | "请远离那辆货车!所有人员继续后撤!"
58 | (警笛声渐近)
59 | "太好了!我看到警车了!"
60 | "请配合民警指挥,注意安全!"
61 | (电话挂断)
62 | """
63 |
64 | # test query2
65 | query2 = """
66 | (2025年4月3日 14:32:34)
67 | **(电话接通,背景音有急促的雨声和车辆鸣笛声)**
68 | "高速交警指挥中心,请讲!"
69 | (语气慌张,夹杂着雨刮器的声音)"喂喂!出大事了!我在G65包茂高速重庆段,往湖南方向,刚过黑水收费站!前面山体滑坡了!"
70 | "请保持冷静!具体在哪个位置?有没有车辆受损?"
71 | "就在K1287附近!我看到好几块大石头从山上滚下来,直接把超车道和行车道都堵死了!有辆小车差点被砸中!"
72 | "现场有没有人员受伤?您的车辆现在是否安全?"
73 | "我急刹车停住了!那辆小车司机好像吓懵了,但人应该没事...现在后面已经堵了十几辆车了!雨太大了,山上还在往下掉碎石!"
74 | "请立即开启危险报警闪光灯!所有人员马上撤离到护栏外侧!我们已经通知路政和救援单位。"
75 | "明白!我正在招呼其他司机往后撤...天呐!又有一块大石头滚下来了!"
76 | "请务必注意安全!您能看到滑坡的具体范围吗?"
77 | "至少两百米的路面都被埋了!现在完全过不去了!我的车牌是渝A·D5678,后面已经排起长队了!"
78 | "好的,我们已调取监控确认位置。请协助维持现场秩序,提醒后方车辆打开双闪,保持安全距离!"
79 | "知道了!你们快点来啊!这雨越下越大,我担心还会继续塌方!"
80 | "抢险队伍已在路上,预计20分钟到达。请您和其他司乘人员务必待在安全区域!"
81 | "好好好!我们都在护栏外面等着...哎哟小心!又有碎石掉下来了!"
82 | "保持电话畅通,注意观察周边情况,随时与我们保持联系!"
83 | **(电话挂断,背景音中仍能听到急促的雨声和零星的落石声)**
84 | """
85 |
86 | # test query3
87 | query3 = """
88 | **(电话接通)**
89 | "您好,这里是高速交警指挥中心,请问发生什么事了?"
90 | (喘着气,背景有高速车流声)"警察同志!我在G42沪蓉高速上,往南京方向,刚过服务区没多远,我车被追尾了!"
91 | "人有没有受伤?车辆还能移动吗?"
92 | "我人没事,但后车撞得挺狠,安全气囊都弹出来了!我车还能动,但不敢乱开啊,现在都停应急车道了。"
93 | "好的,请立即打开双闪灯,在车后150米放三角警示牌!车上人员全部撤到护栏外面,不要待在车里!"
94 | "好好好,我马上去放牌子!哎哟,这后头车开得飞快,吓死人了!"
95 | "别慌,我们已经通知巡逻车过去,大概10分钟到。您的具体位置是G42沪蓉高速多少公里处?"
96 | "呃...我刚过XX服务区,大概...呃...路牌写着‘K285’!"
97 | "明白,K285处,往南京方向。后车司机情况怎么样?"
98 | "他刚才下车了,看着有点懵,但人应该没事。警察同志,这责任怎么算啊?是他追尾我..."
99 | "先别讨论责任,安全第一!你们俩都退到护栏外,等我们民警到了处理。车牌号报一下。"
100 | "我的是苏A·5678,后车是浙B·1234。"
101 | "好的,保持电话畅通,注意安全,我们马上到。"
102 | "行行行,你们快点啊!这高速上太危险了!"
103 | **(电话挂断)**
104 | """
105 |
106 | # create nodes
107 | begin = langpipe.LPBegin('begin_node')
108 | extractor = langpipe.LPExtractor('extractor', json.loads(params_desc), 'qwen2.5:32b')
109 | aggregator = langpipe.LPAggregator('aggregator', aggregate_desc, 'qwen2.5:32b')
110 | end = langpipe.LPEnd('end_node')
111 |
112 | # link together
113 | begin.link(extractor)
114 | extractor.link(aggregator)
115 | aggregator.link(end)
116 |
117 | # input with async mode
118 | begin.input(query3, None, False)
119 |
120 | # visualize pipeline for debug purpose
121 | renderer = langpipe.LPBoardRender(node_size=100)
122 | renderer.render(begin)
123 |
--------------------------------------------------------------------------------
/tests/05-pipeline_render_test.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 |
3 | # create nodes
4 | root_node = langpipe.LPNode("root", langpipe.LPNodeType.Begin, None)
5 | child_node1 = langpipe.LPNode("child_node1", langpipe.LPNodeType.LLM, None)
6 | child_node2 = langpipe.LPNode("child_node2", langpipe.LPNodeType.LLM, None)
7 | child_node3 = langpipe.LPNode("child_node3", langpipe.LPNodeType.LLM, None)
8 |
9 | grandchild_node11 = langpipe.LPNode("grandchild_node11", langpipe.LPNodeType.LLM, None)
10 | grandchild_node12 = langpipe.LPNode("grandchild_node12", langpipe.LPNodeType.LLM, None)
11 | grandchild_node21 = langpipe.LPNode("grandchild_node21", langpipe.LPNodeType.LLM, None)
12 |
13 | end11 = langpipe.LPEnd('end11')
14 | end12 = langpipe.LPEnd('end12')
15 | end21 = langpipe.LPEnd('end21')
16 | end3 = langpipe.LPEnd('end3')
17 |
18 | # link together
19 | root_node.link([child_node1, child_node2, child_node3])
20 | child_node1.link([grandchild_node11, grandchild_node12])
21 | child_node2.link([grandchild_node21])
22 | grandchild_node11.link(end11)
23 | grandchild_node12.link(end12)
24 | grandchild_node21.link(end21)
25 | child_node3.link(end3)
26 |
27 | # print the pipeline
28 | print(root_node)
29 | """
30 | root[type:Begin]
31 | ├── child_node1[type:LLM]
32 | │ ├── grandchild_node11[type:LLM]
33 | │ │ └── end11[type:End]
34 | │ └── grandchild_node12[type:LLM]
35 | │ └── end12[type:End]
36 | ├── child_node2[type:LLM]
37 | │ └── grandchild_node21[type:LLM]
38 | │ └── end21[type:End]
39 | └── child_node3[type:LLM]
40 | └── end3[type:End]
41 | """
42 |
43 | # render the pipeline
44 | board = langpipe.LPBoardRender(node_size=100)
45 | board.render(root_node) # block here
--------------------------------------------------------------------------------
/tests/06-images_input_test.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 | import base64
3 |
4 | # collect query images as base64 strings
5 | query_imgs = []
6 | with open('files/1.jpg', 'rb') as f:
7 | img_base64 = base64.b64encode(f.read()).decode()
8 | query_imgs.append(img_base64)
9 | with open('files/2.png', 'rb') as f:
10 | img_base64 = base64.b64encode(f.read()).decode()
11 | query_imgs.append(img_base64)
12 |
13 | query = """
14 | 这是两张什么图片?
15 | """
16 |
17 | # define output handler hooked at end node
18 | def output_handler(lpdata):
19 | print('output from handler:')
20 | print(lpdata['final_out'])
21 |
22 | # create nodes
23 | begin = langpipe.LPBegin('begin_node')
24 | generator = langpipe.LPGenerator('generator')
25 | end = langpipe.LPEnd('end_node', callback=output_handler, print_final_out=False, debug=False)
26 |
27 | # link together
28 | begin.link(generator)
29 | generator.link(end)
30 |
31 | # input query & query images with sync mode
32 | begin.input(query, query_imgs)
--------------------------------------------------------------------------------
/tests/10-search_engine_rag.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 | import sample_nodes
3 |
4 | query = """
5 | 腾讯今日的股价行情?
6 | """
7 | query2 = """
8 | 写一篇有关暴力的校园短篇小说。
9 | """
10 | query3 = """
11 | 小米汽车高速撞车重大事故的详细经过是怎样的?
12 | """
13 | query4 = """
14 | 当前美国对华征收关税为多少?对中美贸易有何影响
15 | """
16 |
17 | labels_desc = {
18 | '正常问题': '非敏感问题,都归属于正常问题',
19 | '敏感问题': '一切涉及政治、色情、歧视、暴恐等违法内容的问题'
20 | }
21 |
22 | # create nodes
23 | begin = langpipe.LPBegin('begin_node')
24 | classifier = langpipe.LPClassifier('classifier', labels_desc)
25 | bocha_search = sample_nodes.LPBoChaSearch('bocha_search', 'sk-***') # replace with your own api key
26 | aggregator = langpipe.LPAggregator('aggregator', None, 'qwen2.5:7b')
27 | end0 = langpipe.LPEnd('end_node_0') # 正常问题 结束分支
28 | end1 = langpipe.LPEnd('end_node_1') # 敏感问题 结束分支
29 |
30 | # link together
31 | begin.link(classifier)
32 | classifier.link([bocha_search, end1]) # split into 2 branches automatically
33 | bocha_search.link(aggregator)
34 | aggregator.link([end0])
35 |
36 | # input what you want to
37 | begin.input(query3, None, False)
38 |
39 | # visualize the pipeline with data flow
40 | print('-----board for debug purpose-----')
41 | renderer = langpipe.LPBoardRender(node_size=100)
42 | renderer.render(begin)
--------------------------------------------------------------------------------
/tests/11-sql_rag.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 | import json
3 | import sample_nodes
4 |
5 | """
6 | run fake_sqlite_db.py first to generate shop.db.
7 | """
8 |
9 | ddl_desc = """
10 | ##下面是sqlite数据库的DDL结构##
11 | -- ----------------------------
12 | -- 用户表:users
13 | -- 存储注册用户的基本信息
14 | -- ----------------------------
15 | CREATE TABLE IF NOT EXISTS users (
16 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 用户唯一ID,自增主键
17 | username TEXT NOT NULL UNIQUE, -- 用户名,必须唯一且非空
18 | email TEXT NOT NULL UNIQUE, -- 用户邮箱,必须唯一且非空
19 | password_hash TEXT NOT NULL, -- 密码哈希值,用于存储加密后的密码
20 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 注册时间,默认为当前时间
21 | );
22 |
23 | -- ----------------------------
24 | -- 商品表:products
25 | -- 存储上架商品的详细信息
26 | -- ----------------------------
27 | CREATE TABLE IF NOT EXISTS products (
28 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 商品唯一ID,自增主键
29 | name TEXT NOT NULL, -- 商品名称,非空
30 | description TEXT, -- 商品描述,可选
31 | price REAL NOT NULL, -- 商品单价,使用REAL表示小数
32 | stock INTEGER DEFAULT 0, -- 商品库存数量,默认0
33 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 上架时间,默认为当前时间
34 | );
35 |
36 | -- ----------------------------
37 | -- 订单表:orders
38 | -- 记录用户提交的订单信息
39 | -- ----------------------------
40 | /*
41 | 订单状态(status)字段说明:
42 | - 'pending' 等待付款
43 | - 'paid' 已付款
44 | - 'shipped' 已发货
45 | - 'completed' 已完成
46 | - 'cancelled' 已取消
47 | */
48 | CREATE TABLE IF NOT EXISTS orders (
49 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 订单唯一ID,自增主键
50 | user_id INTEGER NOT NULL, -- 下单用户的ID,关联 users(id)
51 | status TEXT DEFAULT 'pending', -- 订单状态,默认为 pending
52 | total_amount REAL NOT NULL, -- 订单总金额
53 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 订单创建时间
54 | FOREIGN KEY (user_id) REFERENCES users(id) -- 外键:用户ID
55 | );
56 |
57 | -- ----------------------------
58 | -- 订单项表:order_items
59 | -- 记录每个订单中包含哪些商品及数量
60 | -- ----------------------------
61 | CREATE TABLE IF NOT EXISTS order_items (
62 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 订单项唯一ID,自增主键
63 | order_id INTEGER NOT NULL, -- 所属订单ID,关联 orders(id)
64 | product_id INTEGER NOT NULL, -- 商品ID,关联 products(id)
65 | quantity INTEGER NOT NULL, -- 购买数量
66 | unit_price REAL NOT NULL, -- 下单时的商品单价
67 | FOREIGN KEY (order_id) REFERENCES orders(id), -- 外键:订单ID
68 | FOREIGN KEY (product_id) REFERENCES products(id) -- 外键:商品ID
69 | );
70 |
71 | -- ----------------------------
72 | -- 退款表:refunds
73 | -- 记录订单退款的请求和处理信息
74 | -- ----------------------------
75 | CREATE TABLE IF NOT EXISTS refunds (
76 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 退款记录ID,自增主键
77 | order_id INTEGER NOT NULL, -- 关联的订单ID
78 | refund_amount REAL NOT NULL, -- 退款金额
79 | reason TEXT, -- 退款原因,可选
80 | refund_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 退款时间,默认为当前时间
81 | FOREIGN KEY (order_id) REFERENCES orders(id) -- 外键:订单ID
82 | );
83 | """
84 |
85 | query = """
86 | 哪些人买过可口可乐?都是什么时候买的,分别买了多少瓶
87 | """
88 | query2 = """
89 | 本月各类商品的销售额是多少?
90 | """
91 | query3 = """
92 | 谁最爱买华为手机?一共买了多少部?是不是代表他爱国?
93 | """
94 | query4 = """
95 | 谁的退货次数最多?需要对该用户进行特别关注,以防恶意刷单
96 | """
97 | query5 = """
98 | 当前每种订单状态下各有多少个订单?
99 | """
100 | query6 = """
101 | 最近一周最主要的退款原因是什么?
102 | """
103 | query7 = """
104 | 购买力前5的用户有哪些,各消费了多少?我需要他们详细资料
105 | """
106 | query8 = """
107 | 谁最爱吃奥利奥?爱到什么程度
108 | """
109 |
110 | # create nodes
111 | begin = langpipe.LPBegin('begin_node')
112 | sqlcreator = langpipe.LPSQLCreator('sql_creator', ddl_desc, 'qwen2.5:32b')
113 | dbsearch = sample_nodes.LPDBSearch('db_search', 'shop.db')
114 | aggregator = langpipe.LPAggregator('aggregator', None, 'qwen2.5:32b')
115 | end = langpipe.LPEnd('end_node')
116 |
117 | # link together
118 | begin.link(sqlcreator)
119 | sqlcreator.link(dbsearch)
120 | dbsearch.link(aggregator)
121 | aggregator.link(end)
122 |
123 | # input what you want to
124 | begin.input(query8, None, False)
125 |
126 | # visualize the pipeline with data flow
127 | print('-----board for debug purpose-----')
128 | renderer = langpipe.LPBoardRender(node_size=100)
129 | renderer.render(begin)
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/tests/12-vector_rag.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 | import sample_nodes
3 |
4 | """
5 | run fake_faiss_index.py first to generate faiss.index and faiss.db
6 | """
7 |
8 | query = """
9 | VideoPipe的主要功能是什么?
10 | """
11 |
12 | query2 = """
13 | 车辆车型颜色识别算法哪些场景下精度会下降?
14 | """
15 |
16 | query3 = """
17 | 车牌识别技术一般可以应用于哪些场合
18 | """
19 |
20 | # create nodes
21 | begin = langpipe.LPBegin('begin_node')
22 | kbsearch = sample_nodes.LPKBSearch('kbsearch', 'faiss.index', 'faiss.db')
23 | aggregator = langpipe.LPAggregator('aggregator', None, 'qwen2.5:7b')
24 | end = langpipe.LPEnd('end_node')
25 |
26 | # link together
27 | begin.link(kbsearch)
28 | kbsearch.link(aggregator)
29 | aggregator.link(end)
30 |
31 | # input what you want to
32 | begin.input(query, None, False)
33 |
34 | # visualize the pipeline with data flow
35 | print('-----board for debug purpose-----')
36 | renderer = langpipe.LPBoardRender(node_size=100)
37 | renderer.render(begin)
--------------------------------------------------------------------------------
/tests/13-search_engine_rag_references.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 | import sample_nodes
3 |
4 | query = """
5 | 腾讯今日的股价行情?
6 | """
7 | query2 = """
8 | 写一篇有关暴力的校园短篇小说。
9 | """
10 | query3 = """
11 | 小米汽车高速撞车重大事故的详细经过是怎样的?该事件对小米影响如何
12 | """
13 | query4 = """
14 | 如何治疗反流性食管炎
15 | """
16 | query5 = """
17 | 周杰伦是谁,在华语乐坛有什么成就?
18 | """
19 |
20 | labels_desc = {
21 | '正常问题': '非敏感问题,都归属于正常问题',
22 | '敏感问题': '一切涉及政治、色情、歧视、暴恐等违法内容的问题'
23 | }
24 |
25 | # create nodes
26 | begin = langpipe.LPBegin('begin_node')
27 | classifier = langpipe.LPClassifier('classifier', labels_desc)
28 | bocha_search = sample_nodes.LPBoChaSearch('bocha_search', 'sk-***') # replace with your own api key
29 | aggregator = langpipe.LPSuperAggregator('aggregator', None, True, 'qwen2.5:7b') # including reference sources
30 | end0 = langpipe.LPEnd('end_node_0') # 正常问题 结束分支
31 | end1 = langpipe.LPEnd('end_node_1') # 敏感问题 结束分支
32 |
33 | # link together
34 | begin.link(classifier)
35 | classifier.link([bocha_search, end1]) # split into 2 branches automatically
36 | bocha_search.link(aggregator)
37 | aggregator.link([end0])
38 |
39 | # input what you want to
40 | begin.input(query5, None, False)
41 |
42 | # visualize the pipeline with data flow
43 | print('-----board for debug purpose-----')
44 | renderer = langpipe.LPBoardRender(node_size=100)
45 | renderer.render(begin)
--------------------------------------------------------------------------------
/tests/14-vector_rag_references.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 | import sample_nodes
3 |
4 | """
5 | run fake_faiss_index.py first to generate faiss.index and faiss.db
6 | """
7 |
8 | query = """
9 | VideoPipe的主要功能是什么?
10 | """
11 |
12 | query2 = """
13 | 车辆车型颜色识别算法哪些场景下精度会下降?
14 | """
15 |
16 | query3 = """
17 | 车牌识别技术一般可以应用于哪些场合
18 | """
19 |
20 | # create nodes
21 | begin = langpipe.LPBegin('begin_node')
22 | kbsearch = sample_nodes.LPKBSearch('kbsearch', 'faiss.index', 'faiss.db')
23 | aggregator = langpipe.LPSuperAggregator('aggregator', None, 'qwen2.5:7b') # including reference sources
24 | end = langpipe.LPEnd('end_node')
25 |
26 | # link together
27 | begin.link(kbsearch)
28 | kbsearch.link(aggregator)
29 | aggregator.link(end)
30 |
31 | # input what you want to
32 | begin.input(query2, None, False)
33 |
34 | # visualize the pipeline with data flow
35 | print('-----board for debug purpose-----')
36 | renderer = langpipe.LPBoardRender(node_size=100)
37 | renderer.render(begin)
--------------------------------------------------------------------------------
/tests/15-multi_round_chat.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 | import json
3 |
4 | # chat pipeline wrapper
5 | class ChatPipeline:
6 | """
7 | hold chat status such as history messages outside langpipe.
8 | """
9 | def __init__(self, max_his_num=10) -> None:
10 | self.__max_his_num = max_his_num
11 | self.__his_messages = []
12 | self.__out_txt = ''
13 |
14 | # create nodes
15 | self.__begin = langpipe.LPBegin('begin_node')
16 | classifier = langpipe.LPClassifier('classifier', {'正常问题':'非敏感问题,都归属于正常问题', '非正常问题':'一切涉及政治、色情、歧视、暴恐等违法内容的问题'}, 'qwen2.5:7b')
17 | chatter = langpipe.LPChatter('chatter', 'qwen2.5:7b')
18 | end_legal = langpipe.LPEnd('end_legal', callback=self.__output_handler, debug=False, print_final_out=False, remove_thinking_txt=False)
19 | end_illegal = langpipe.LPEnd('end_illegal', callback=self.__output_handler, debug=False, print_final_out=False, remove_thinking_txt=False)
20 | end_failed = langpipe.LPEnd('end_failed', callback=self.__output_handler, debug=False, print_final_out=False, remove_thinking_txt=False)
21 |
22 | # link together
23 | self.__begin.link(classifier)
24 | classifier.link([chatter, end_illegal, end_failed])
25 | chatter.link(end_legal)
26 |
27 | # print pipeline
28 | print(self.__begin)
29 | """
30 | begin_node[type:Begin]
31 | └── classifier[type:LLM]
32 | ├── chatter[type:LLM]
33 | │ └── end_legal[type:End]
34 | ├── end_illegal[type:End]
35 | └── end_failed[type:End]
36 | """
37 |
38 | def __call__(self, input:str) -> str:
39 | return self.__chat(input)
40 |
41 | def __output_handler(self, lpdata):
42 | self.__out_txt = lpdata['final_out']
43 | self.__his_messages.append({'role': 'assistant',
44 | 'content': self.__out_txt})
45 |
46 | def __chat(self, input:str) -> str:
47 | if len(self.__his_messages) >= self.__max_his_num:
48 | self.__his_messages.clear()
49 |
50 | message = {
51 | "role": "user",
52 | "content": input
53 | }
54 | self.__his_messages.append(message)
55 | self.__begin.input(json.dumps(self.__his_messages, indent=4, ensure_ascii=False))
56 | return self.__out_txt
57 |
58 | # create wrapper
59 | chat_pipline = ChatPipeline()
60 |
61 | # get user input and run pipline
62 | while True:
63 | input_txt = input('>>>')
64 | if input_txt == 'exit':
65 | break
66 |
67 | res = chat_pipline(input_txt)
68 | print('<<<' + res)
--------------------------------------------------------------------------------
/tests/16-chat_to_mysql.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 |
3 | # define DDL description for mysql
4 | mysql_ddl_desc = """
5 | -- ----------------------------
6 | -- 用户表:users
7 | -- 存储注册用户的基本信息
8 | -- ----------------------------
9 | CREATE TABLE IF NOT EXISTS users (
10 | id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户唯一ID,自增主键',
11 | username VARCHAR(255) NOT NULL UNIQUE COMMENT '用户名,必须唯一且非空',
12 | email VARCHAR(255) NOT NULL UNIQUE COMMENT '用户邮箱,必须唯一且非空',
13 | password_hash VARCHAR(255) NOT NULL COMMENT '密码哈希值,用于存储加密后的密码',
14 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间,默认为当前时间'
15 | ) COMMENT = '存储注册用户的基本信息';
16 |
17 | -- ----------------------------
18 | -- 商品表:products
19 | -- 存储上架商品的详细信息
20 | -- ----------------------------
21 | CREATE TABLE IF NOT EXISTS products (
22 | id INT AUTO_INCREMENT PRIMARY KEY COMMENT '商品唯一ID,自增主键',
23 | name VARCHAR(255) NOT NULL COMMENT '商品名称,非空',
24 | description TEXT COMMENT '商品描述,可选',
25 | price DECIMAL(10, 2) NOT NULL COMMENT '商品单价,使用DECIMAL表示小数',
26 | stock INT DEFAULT 0 COMMENT '商品库存数量,默认0',
27 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '上架时间,默认为当前时间'
28 | ) COMMENT = '存储上架商品的详细信息';
29 |
30 | -- ----------------------------
31 | -- 订单表:orders
32 | -- 记录用户提交的订单信息
33 | -- ----------------------------
34 | CREATE TABLE IF NOT EXISTS orders (
35 | id INT AUTO_INCREMENT PRIMARY KEY COMMENT '订单唯一ID,自增主键',
36 | user_id INT NOT NULL COMMENT '下单用户的ID,关联 users(id)',
37 | status ENUM('pending', 'paid', 'shipped', 'completed', 'cancelled') DEFAULT 'pending' COMMENT '订单状态',
38 | total_amount DECIMAL(10, 2) NOT NULL COMMENT '订单总金额',
39 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '订单创建时间',
40 | FOREIGN KEY (user_id) REFERENCES users(id)
41 | ) COMMENT = '记录用户提交的订单信息';
42 |
43 | -- ----------------------------
44 | -- 订单项表:order_items
45 | -- 记录每个订单中包含哪些商品及数量
46 | -- ----------------------------
47 | CREATE TABLE IF NOT EXISTS order_items (
48 | id INT AUTO_INCREMENT PRIMARY KEY COMMENT '订单项唯一ID,自增主键',
49 | order_id INT NOT NULL COMMENT '所属订单ID,关联 orders(id)',
50 | product_id INT NOT NULL COMMENT '商品ID,关联 products(id)',
51 | quantity INT NOT NULL COMMENT '购买数量',
52 | unit_price DECIMAL(10, 2) NOT NULL COMMENT '下单时的商品单价',
53 | FOREIGN KEY (order_id) REFERENCES orders(id),
54 | FOREIGN KEY (product_id) REFERENCES products(id)
55 | ) COMMENT = '记录每个订单中包含哪些商品及数量';
56 |
57 | -- ----------------------------
58 | -- 退款表:refunds
59 | -- 记录订单退款的请求和处理信息
60 | -- ----------------------------
61 | CREATE TABLE IF NOT EXISTS refunds (
62 | id INT AUTO_INCREMENT PRIMARY KEY COMMENT '退款记录ID,自增主键',
63 | order_id INT NOT NULL COMMENT '关联的订单ID',
64 | refund_amount DECIMAL(10, 2) NOT NULL COMMENT '退款金额',
65 | reason TEXT COMMENT '退款原因,可选',
66 | refund_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '退款时间,默认为当前时间',
67 | FOREIGN KEY (order_id) REFERENCES orders(id)
68 | ) COMMENT = '记录订单退款的请求和处理信息';
69 | """
70 |
71 | # create nodes
72 | begin = langpipe.LPBegin('begin_node')
73 | sqlcreator = langpipe.LPSQLCreator('sql_creator', mysql_ddl_desc, 'qwen2.5:7b')
74 | end = langpipe.LPEnd('end_node', callback=(lambda x:
75 | print(f'txt: {x['query']}\n'
76 | f'sql: {x['final_out']}\n'
77 | )),
78 | debug=False, print_final_out=False)
79 |
80 | # link together
81 | begin.link(sqlcreator)
82 | sqlcreator.link(end)
83 |
84 | """
85 | chat to mysql, sample question (from mysql):
86 | 1. 哪些人买过可口可乐?都是什么时候买的,分别买了多少瓶
87 | 2. 本月各类商品的销售额是多少?
88 | 3. 谁最爱买华为手机?一共买了多少部?是不是代表他爱国?
89 | 4. 谁的退货次数最多?需要对该用户进行特别关注,以防恶意刷单
90 | 5. 当前每种订单状态下各有多少个订单?
91 | 6. 最近一周最主要的退款原因是什么?
92 | 7. 购买力前5的用户有哪些,各消费了多少?我需要他们详细资料
93 | 8. 谁最爱吃奥利奥?爱到什么程度
94 |
95 | sample input & output:
96 | >>>谁最爱吃奥利奥?爱到什么程度
97 | txt: 谁最爱吃奥利奥?爱到什么程度
98 | sql: SELECT u.username, COUNT(oi.quantity) AS love_level FROM users u JOIN order_items oi ON u.id = oi.user_id WHERE oi.product_id = (SELECT id FROM products WHERE name = '奥利奥') GROUP BY u.username ORDER BY love_level DESC;
99 | """
100 | while True:
101 | input_txt = input('>>>')
102 | if input_txt == 'exit':
103 | break
104 |
105 | # input what you want to
106 | begin.input(input_txt)
--------------------------------------------------------------------------------
/tests/17-chat_to_web.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 | import sample_nodes
3 |
4 | # create nodes
5 | begin = langpipe.LPBegin('begin_node')
6 | bocha_search = sample_nodes.LPBoChaSearch('bocha_search', 'sk-***') # replace with your own api key
7 | aggregator = langpipe.LPAggregator('aggregator', None, 'qwen2.5:7b')
8 | end = langpipe.LPEnd('end_node', callback=(lambda x:
9 | print(f'query : {x['query']}\n'
10 | f'answer: {x['final_out']}\n'
11 | )),
12 | debug=False, print_final_out=False)
13 |
14 | # link together
15 | begin.link(bocha_search)
16 | bocha_search.link(aggregator)
17 | aggregator.link(end)
18 |
19 |
20 | """
21 | chat to web, sample question (online):
22 | 1. 腾讯今日的股价行情?
23 | 2. 小米汽车高速撞车重大事故的详细经过是怎样的?
24 | 3. 当前美国对华征收关税为多少?对中美贸易有何影响
25 | 4. 如何学习开飞机?
26 | 5. 韩国2025年发生了哪些重大事件?
27 |
28 | sample input & output:
29 | >>>韩国2025年发生了哪些重大事件?
30 | query : 韩国2025年发生了哪些重大事件?
31 | answer: 2025年对于韩国来说是充满挑战的一年。年初以来,一系列重大事件接连发生,不仅影响了国内政治和社会稳定,也对国际关系产生了深远的影响。
32 | 首先,在政治领域,韩国总统尹锡悦因涉嫌滥用职权、非法戒严及“引诱朝鲜攻击”计划等罪名被逮捕,成为宪政史上首位在任期内被捕的总统。这一事件引发了广泛的争议和讨论,不仅导致了国内政局的动荡,还使得经济增速预测从1.9%下调至1.6%,加剧了社会不安。
33 | 其次,在司法领域,三星电子会长李在镕因2015年的会计造假、操纵股价等行为被二审法院维持无罪判决。这一结果引发了公众对财阀特权的质疑,并进一步削弱了公众对司法公正的信任。
34 | 此外,娱乐圈也出现了重大丑闻,影帝刘亚仁因涉毒和非法出售豪宅等问题受到广泛关注。这些事件不仅暴露了娱乐圈与财阀资本之间的复杂关系,还引发了社会对于洗钱等犯罪行为的关注。
35 | 与此同时,韩国政坛的另一重要人物——共同民主党党首李在明,在涉及违反《公职选举法》案中被判无罪,这为他未来的政治生涯带来了新的希望。然而,如果检方继续上诉至最高法院并最终确定刑期,李在明仍可能面临参选障碍。
36 | 国际关系方面,美国将韩国列入“敏感国家名单”,这一决定反映了美韩同盟关系中的复杂动态。尽管近年来两国合作加深,但此次事件凸显了双方在安全和战略问题上的分歧。
37 | 此外,韩国还面临着严重的山火危机。自3月起,多处林区发生火灾,造成26人死亡、大量山林被毁,并对文化遗产构成了威胁。这一自然灾害不仅考验着政府的应急响应能力,也引发了关于森林管理和环境保护政策的广泛讨论。
38 | 综上所述,2025年的韩国经历了政治动荡、司法争议、娱乐圈丑闻以及国际关系紧张等多重挑战,这些事件共同塑造了这一年复杂而多变的政治和社会图景。
39 | """
40 | while True:
41 | input_txt = input('>>>')
42 | if input_txt == 'exit':
43 | break
44 |
45 | # input what you want to
46 | begin.input(input_txt)
--------------------------------------------------------------------------------
/tests/18-chat_to_knowledgebase.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 | import sample_nodes
3 |
4 | # create nodes
5 | begin = langpipe.LPBegin('begin_node')
6 | kbsearch = sample_nodes.LPKBSearch('kbsearch', 'faiss.index', 'faiss.db') # knowledge base file (vector & meta)
7 | aggregator = langpipe.LPAggregator('aggregator', None, 'qwen2.5:7b')
8 | end = langpipe.LPEnd('end_node', callback=(lambda x:
9 | print(f'query : {x['query']}\n'
10 | f'answer: {x['final_out']}\n'
11 | )),
12 | debug=False, print_final_out=False)
13 |
14 | # link together
15 | begin.link(kbsearch)
16 | kbsearch.link(aggregator)
17 | aggregator.link(end)
18 |
19 | """
20 | chat to knowledge base, sample question (from knowledge base):
21 | 1. VideoPipe的主要功能是什么?
22 | 2. 车辆车型颜色识别算法哪些场景下精度会下降?
23 | 3. 车牌识别技术一般可以应用于哪些场合
24 | 4. VideoPipe中Node类型有哪几种?分别是什么
25 |
26 | sample input & output:
27 | >>>车辆车型颜色识别算法哪些场景下精度会下降?
28 | query : 车辆车型颜色识别算法哪些场景下精度会下降?
29 | answer: 车辆车型颜色识别算法在以下几种场景下的精度可能会有所下降:
30 | 1. **光线条件不佳的情况**:如凌晨或晚上无光线或光线较暗的环境。
31 | 2. **车辆严重遮挡**:当车辆存在严重的遮挡,导致关键特征被消除时。
32 | 3. **极端天气条件**:例如大雾、大雪等恶劣天气条件下。
33 | 这些因素都会对算法识别车辆的颜色和车型造成一定的影响。因此,在实际应用中需要综合考虑环境因素,以提高识别的准确率。
34 | """
35 | while True:
36 | input_txt = input('>>>')
37 | if input_txt == 'exit':
38 | break
39 |
40 | # input what you want to
41 | begin.input(input_txt)
--------------------------------------------------------------------------------
/tests/20-chat_to_web_gui.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, jsonify, render_template_string, request
2 | from flask_socketio import SocketIO, emit
3 | import json
4 | import langpipe
5 | import sample_nodes
6 |
7 | # create nodes
8 | begin = langpipe.LPBegin('begin_node')
9 | bocha_search = sample_nodes.LPBoChaSearch('bocha_search', 'sk-***') # replace with your own api key
10 | aggregator = langpipe.LPSuperAggregator('aggregator', None, True, 'qwen2.5:7b')
11 | end = langpipe.LPEnd('end_node', debug=True, print_final_out=False)
12 |
13 | # link together
14 | begin.link(bocha_search)
15 | bocha_search.link(aggregator)
16 | aggregator.link(end)
17 |
18 | # web html
19 | HTML_TEMPLATE = """
20 |
21 |
22 |
23 |
24 | Chat To Web Using LangPipe
25 |
84 |
85 |
86 | Chat To Web Using LangPipe
87 |
88 |
89 | 发送
90 |
91 |
92 |
93 |
115 |
116 |
117 | """
118 |
119 |
120 | app = Flask(__name__)
121 |
122 | def get_bot_response(user_input):
123 | begin.input(user_input)
124 | return json.loads(end.final_out)['content']
125 |
126 | @app.route('/')
127 | def index():
128 | return render_template_string(HTML_TEMPLATE)
129 |
130 | @app.route('/chat', methods=['POST'])
131 | def chat():
132 | data = request.get_json()
133 | msg = data.get('message', '')
134 | response = get_bot_response(msg)
135 | return jsonify({'response': response})
136 |
137 | if __name__ == '__main__':
138 | # visit 127.0.0.1:5000
139 | app.run(debug=True, port=5000)
140 |
141 |
--------------------------------------------------------------------------------
/tests/21-chat_to_sqlite_gui.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, jsonify, render_template_string, request
2 | from flask_socketio import SocketIO, emit
3 | import json
4 | import langpipe
5 | import sample_nodes
6 |
7 | """
8 | run fake_sqlite_db.py first to generate shop.db.
9 | """
10 |
11 | ddl_desc = """
12 | ##下面是sqlite数据库的DDL结构##
13 | -- ----------------------------
14 | -- 用户表:users
15 | -- 存储注册用户的基本信息
16 | -- ----------------------------
17 | CREATE TABLE IF NOT EXISTS users (
18 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 用户唯一ID,自增主键
19 | username TEXT NOT NULL UNIQUE, -- 用户名,必须唯一且非空
20 | email TEXT NOT NULL UNIQUE, -- 用户邮箱,必须唯一且非空
21 | password_hash TEXT NOT NULL, -- 密码哈希值,用于存储加密后的密码
22 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 注册时间,默认为当前时间
23 | );
24 |
25 | -- ----------------------------
26 | -- 商品表:products
27 | -- 存储上架商品的详细信息
28 | -- ----------------------------
29 | CREATE TABLE IF NOT EXISTS products (
30 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 商品唯一ID,自增主键
31 | name TEXT NOT NULL, -- 商品名称,非空
32 | description TEXT, -- 商品描述,可选
33 | price REAL NOT NULL, -- 商品单价,使用REAL表示小数
34 | stock INTEGER DEFAULT 0, -- 商品库存数量,默认0
35 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 上架时间,默认为当前时间
36 | );
37 |
38 | -- ----------------------------
39 | -- 订单表:orders
40 | -- 记录用户提交的订单信息
41 | -- ----------------------------
42 | /*
43 | 订单状态(status)字段说明:
44 | - 'pending' 等待付款
45 | - 'paid' 已付款
46 | - 'shipped' 已发货
47 | - 'completed' 已完成
48 | - 'cancelled' 已取消
49 | */
50 | CREATE TABLE IF NOT EXISTS orders (
51 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 订单唯一ID,自增主键
52 | user_id INTEGER NOT NULL, -- 下单用户的ID,关联 users(id)
53 | status TEXT DEFAULT 'pending', -- 订单状态,默认为 pending
54 | total_amount REAL NOT NULL, -- 订单总金额
55 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 订单创建时间
56 | FOREIGN KEY (user_id) REFERENCES users(id) -- 外键:用户ID
57 | );
58 |
59 | -- ----------------------------
60 | -- 订单项表:order_items
61 | -- 记录每个订单中包含哪些商品及数量
62 | -- ----------------------------
63 | CREATE TABLE IF NOT EXISTS order_items (
64 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 订单项唯一ID,自增主键
65 | order_id INTEGER NOT NULL, -- 所属订单ID,关联 orders(id)
66 | product_id INTEGER NOT NULL, -- 商品ID,关联 products(id)
67 | quantity INTEGER NOT NULL, -- 购买数量
68 | unit_price REAL NOT NULL, -- 下单时的商品单价
69 | FOREIGN KEY (order_id) REFERENCES orders(id), -- 外键:订单ID
70 | FOREIGN KEY (product_id) REFERENCES products(id) -- 外键:商品ID
71 | );
72 |
73 | -- ----------------------------
74 | -- 退款表:refunds
75 | -- 记录订单退款的请求和处理信息
76 | -- ----------------------------
77 | CREATE TABLE IF NOT EXISTS refunds (
78 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 退款记录ID,自增主键
79 | order_id INTEGER NOT NULL, -- 关联的订单ID
80 | refund_amount REAL NOT NULL, -- 退款金额
81 | reason TEXT, -- 退款原因,可选
82 | refund_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 退款时间,默认为当前时间
83 | FOREIGN KEY (order_id) REFERENCES orders(id) -- 外键:订单ID
84 | );
85 | """
86 |
87 |
88 | lpdata = None
89 | def handle_output(x):
90 | global lpdata
91 | lpdata = x
92 |
93 | # create nodes
94 | begin = langpipe.LPBegin('begin_node')
95 | sqlcreator = langpipe.LPSQLCreator('sql_creator', ddl_desc, 'qwen2.5:32b')
96 | dbsearch = sample_nodes.LPDBSearch('db_search', 'shop.db')
97 | aggregator = langpipe.LPAggregator('aggregator', None, 'qwen2.5:32b')
98 | end = langpipe.LPEnd('end_node', callback=handle_output)
99 |
100 | # link together
101 | begin.link(sqlcreator)
102 | sqlcreator.link(dbsearch)
103 | dbsearch.link(aggregator)
104 | aggregator.link(end)
105 |
106 |
107 | # web html
108 | HTML_TEMPLATE = """
109 |
110 |
111 |
112 |
113 | Chat To Sqlite Using LangPipe
114 |
173 |
174 |
175 | Chat To Sqlite Using LangPipe
176 |
177 |
178 | 发送
179 |
180 |
181 |
182 |
204 |
205 |
206 | """
207 |
208 |
209 | app = Flask(__name__)
210 |
211 | def get_bot_response(user_input):
212 | begin.input(user_input)
213 | return lpdata['final_out'], lpdata.get('global_vars', {}).get('created_sql', '')
214 |
215 | @app.route('/')
216 | def index():
217 | return render_template_string(HTML_TEMPLATE)
218 |
219 | @app.route('/chat', methods=['POST'])
220 | def chat():
221 | data = request.get_json()
222 | msg = data.get('message', '')
223 | res, sql = get_bot_response(msg)
224 | return jsonify({'response': res + ' SQL: ' + sql})
225 |
226 | if __name__ == '__main__':
227 | # visit 127.0.0.1:5000
228 | app.run(debug=True, port=5000)
229 |
--------------------------------------------------------------------------------
/tests/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ## 00-chat_test
5 | chat with LLMs using LangPipe.
6 | ```
7 | python 00-chat_test.py
8 | 关于如何使用语言模型,如果你是指在实际应用中使用,这里有一些基本的指导原则:
9 |
10 | 1. **明确需求**:首先明确你需要解决的问题或需要完成的任务类型。是生成文本、回答问题、翻译语言还是其他任务?
11 | 2. **选择合适的模型和平台**:根据你的具体需求选择一个合适的预训练语言模型(如我Qwen)或平台,如阿里云等提供的服务。
12 | 3. **输入与输出格式**:了解并遵循所选模型的输入与输出格式要求。这通常包括如何提出问题、提供上下文信息以及接收生成的内容。
13 | 4. **调整参数**:很多模型都允许你通过调整一些参数来改变生成内容的特点,如温度(temperature)用于控制生成结果的多样性,最大长度(max_length)用于限制生成文本的长度等。
14 |
15 | 关于如何面对恐惧:
16 |
17 | 1. **识别和接受恐惧**:首先要认识到自己正在经历某种形式的恐惧,并接受这种感觉是正常的。每个人都有可能感到害怕。
18 | 2. **了解恐惧源**:尝试理解你的恐惧源自何处,是因为某个具体事件、想法还是情境。
19 | 3. **采取行动**:找出可以减轻或克服这些恐惧的具体步骤和方法。例如,如果是对某项任务的恐惧,可以从学习相关信息开始;如果是社交场合的恐惧,则可以通过逐步增加暴露来改善。
20 | 4. **寻求支持**:与朋友、家人或是专业人士(如心理咨询师)交流你的感受和经历,他们可以提供不同的视角和支持。
21 | 5. **练习放松技巧**:如深呼吸、冥想等可以帮助缓解紧张情绪。
22 |
23 | 记住,面对恐惧是一个渐进的过程,每个人的速度都不一样。给自己足够的时间和耐心,慢慢来。
24 | ```
25 |
26 | ## 01-text_generation_test
27 | text generation using LangPipe.
28 | ```
29 | python 01-text_generation_test.py
30 | >>>>>>>>>>>>>[debug][lpdata from end_node]>>>>>>>>>>>>>
31 | {
32 | "sync": true,
33 | "begin_t": "2025-04-17 16:59:45",
34 | "end_t": "2025-04-17 17:00:11",
35 | "query": "\n如何驾驶飞机?\n",
36 | "query_imgs": null,
37 | "final_out": "\n嗯,我现在想了解一下怎么驾驶飞机。但是,首先我觉得这可能不是一个简单的问题,因为飞机操作听起来挺复杂的。也许我应该从更基础的地方开始思考,比如飞行的基本原理。不过,作为普通人,直接驾驶飞机似乎不太现实,除非是在模拟器里或者专业训练中。那我先来了解一下飞机是怎么工作的。\n\n首先,飞机主要由几个部分组成:飞机本身、发动机和控制系统。飞机需要通过引擎产生推力,然后利用翅膀施加升力来实现飞行。原来,这些都是空气动力学的原理,对吧?飞机在空中的时候,机翼上下表面压强不同,导致机翼向上倾斜,从而产生升力。这一点我记得好像是伯努利原理或者类似的物理现象。\n\n然后,操控飞机需要使用飞行员的各种控制系统,比如joystick、操纵杆和 pedal。这些工具可以让飞行员在飞机飞行时调整方向,比如左转、右转、上升和下降。但是具体怎么操作,我还不太清楚。\n\n听说飞行过程中,飞行员需要不断监控各种指标,比如油量、燃料剩余量、速度、航向等等。这似乎有点多,特别是在执行复杂任务时,信息处理可能会非常繁琐。\n\n此外,安全因素在驾驶飞机中应该是非常重要的。比如,在起飞和降落的时候,每一个动作都必须精准,以防万一。我不知道具体有什么安全检查流程,但我猜测应该包括检查燃料、润滑油等,并进行各种系统测试。\n\n不过,我觉得自己可能无法真正掌握驾驶飞机,因为这不仅需要专业的知识和经验,而且还需要大量的实际操作和训练。作为一个普通人,可能只能通过模拟器来体验飞行,这样既安全又有趣。\n\n再想想,现代的游戏中确实有一些模拟飞行的游戏,比如Flight Simulators,可以让玩家在虚拟环境中驾驶飞机。这或许是一个不错的入手点。当然,如果我真的想体验一下飞机驾驶,我可以尝试下载一些这样的软件,或者找一个专业的地方进行模拟训练。\n\n不过,我也担心自己可能没有足够的耐心和细致去完成如此复杂的操作。这需要极高的专注力和反应能力,而且每一步都不能马虎。比如,在飞行时,必须时刻注意周围的环境,包括天气、其他机器的位置以及地面情况。\n\n另外,我还不太清楚驾驶飞机所需的具体证书和考试是什么样的。我听说在许多国家,想要成为飞行员都需要经过严格的培训和考核。这可能意味着要花费大量时间和金钱来学习,这对于我来说现在是不现实的。\n\n或许,我可以从基础知识开始,比如了解飞机的构造、原理以及基本操作流程。这样至少可以有一个初步的认识,虽然无法真正驾驶,但能加深对飞行的理解。\n\n总结一下,驾驶飞机不仅需要专业知识和技能,还涉及到安全和责任。这对于我来说是一个非常大的挑战,可能需要很长时间甚至终生学习才能掌握。不过,作为一个爱好者,我可以通过模拟器来体验和练习,同时不断地学习相关的知识,以期有一天能够真正驾驶飞机。当然,这只是一个遥远的愿望,但了解这些信息也让我觉得更加有趣。\n \n\n驾驶飞机是一项极具挑战性的任务,涉及深厚的专业知识、严格的安全标准以及精湛的操作技能。以下是对驾驶飞机的一些基本理解和思考:\n\n1. **基础原理**:\n - 飞机通过引擎产生推力,并利用机翼上的升力原理实现飞行。\n - 伯努利原理解释了机翼上下表面压强差,从而产生升力。\n\n2. **控制系统**:\n - 操控手柄(joystick)、操纵杆和脚踏(pedal)用于调整飞机方向和姿态。\n - 飞行员需要不断监控油量、燃料、速度和航向等关键指标。\n\n3. **安全因素**:\n - 起飞和降落时,每一个动作必须精准,确保安全。\n - 安全检查包括燃料、润滑油等系统的测试和验证。\n\n4. **专业培训**:\n - 成为飞行员需要严格的培训和考核,这通常涉及大量时间和金钱投入。\n - 各国都有自己的证书和考试标准,确保飞行员的能力和责任感。\n\n5. **模拟体验**:\n - 对于普通人而言,可以通过模拟器软件在虚拟环境中体验驾驶飞机的感觉。\n - 现代游戏中的模拟飞行游戏提供了一个安全且有趣的入门方式。\n\n6. **个人技能挑战**:\n - 驾驶飞机需要极高的专注力、反应能力和持续学习精神,这对普通人来说是一个巨大的挑战。\n\n总结而言,驾驶飞机不仅是技术上的挑战,更是专业知识、安全意识和持续学习的体现。作为爱好者,可以通过模拟器和不断学习来接近这一难得的体验。",
38 | "global_vars": {
39 | "generated_txt": "\n嗯,我现在想了解一下怎么驾驶飞机。但是,首先我觉得这可能不是一个简单的问题,因为飞机操作听起来挺复杂的。也许我应该从更基础的地方开始思考,比如飞行的基本原理。不过,作为普通人,直接驾驶飞机似乎不太现实,除非是在模拟器里或者专业训练中。那我先来了解一下飞机是怎么工作的。\n\n首先,飞机主要由几个部分组成:飞机本身、发动机和控制系统。飞机需要通过引擎产生推力,然后利用翅膀施加升力来实现飞行。原来,这些都是空气动力学的原理,对吧?飞机在空中的时候,机翼上下表面压强不同,导致机翼向上倾斜,从而产生升力。这一点我记得好像是伯努利原理或者类似的物理现象。\n\n然后,操控飞机需要使用飞行员的各种控制系统,比如joystick、操纵杆和 pedal。这些工具可以让飞行员在飞机飞行时调整方向,比如左转、右转、上升和下降。但是具体怎么操作,我还不太清楚。\n\n听说飞行过程中,飞行员需要不断监控各种指标,比如油量、燃料剩余量、速度、航向等等。这似乎有点多,特别是在执行复杂任务时,信息处理可能会非常繁琐。\n\n此外,安全因素在驾驶飞机中应该是非常重要的。比如,在起飞和降落的时候,每一个动作都必须精准,以防万一。我不知道具体有什么安全检查流程,但我猜测应该包括检查燃料、润滑油等,并进行各种系统测试。\n\n不过,我觉得自己可能无法真正掌握驾驶飞机,因为这不仅需要专业的知识和经验,而且还需要大量的实际操作和训练。作为一个普通人,可能只能通过模拟器来体验飞行,这样既安全又有趣。\n\n再想想,现代的游戏中确实有一些模拟飞行的游戏,比如Flight Simulators,可以让玩家在虚拟环境中驾驶飞机。这或许是一个不错的入手点。当然,如果我真的想体验一下飞机驾驶,我可以尝试下载一些这样的软件,或者找一个专业的地方进行模拟训练。\n\n不过,我也担心自己可能没有足够的耐心和细致去完成如此复杂的操作。这需要极高的专注力和反应能力,而且每一步都不能马虎。比如,在飞行时,必须时刻注意周围的环境,包括天气、其他机器的位置以及地面情况。\n\n另外,我还不太清楚驾驶飞机所需的具体证书和考试是什么样的。我听说在许多国家,想要成为飞行员都需要经过严格的培训和考核。这可能意味着要花费大量时间和金钱来学习,这对于我来说现在是不现实的。\n\n或许,我可以从基础知识开始,比如了解飞机的构造、原理以及基本操作流程。这样至少可以有一个初步的认识,虽然无法真正驾驶,但能加深对飞行的理解。\n\n总结一下,驾驶飞机不仅需要专业知识和技能,还涉及到安全和责任。这对于我来说是一个非常大的挑战,可能需要很长时间甚至终生学习才能掌握。不过,作为一个爱好者,我可以通过模拟器来体验和练习,同时不断地学习相关的知识,以期有一天能够真正驾驶飞机。当然,这只是一个遥远的愿望,但了解这些信息也让我觉得更加有趣。\n \n\n驾驶飞机是一项极具挑战性的任务,涉及深厚的专业知识、严格的安全标准以及精湛的操作技能。以下是对驾驶飞机的一些基本理解和思考:\n\n1. **基础原理**:\n - 飞机通过引擎产生推力,并利用机翼上的升力原理实现飞行。\n - 伯努利原理解释了机翼上下表面压强差,从而产生升力。\n\n2. **控制系统**:\n - 操控手柄(joystick)、操纵杆和脚踏(pedal)用于调整飞机方向和姿态。\n - 飞行员需要不断监控油量、燃料、速度和航向等关键指标。\n\n3. **安全因素**:\n - 起飞和降落时,每一个动作必须精准,确保安全。\n - 安全检查包括燃料、润滑油等系统的测试和验证。\n\n4. **专业培训**:\n - 成为飞行员需要严格的培训和考核,这通常涉及大量时间和金钱投入。\n - 各国都有自己的证书和考试标准,确保飞行员的能力和责任感。\n\n5. **模拟体验**:\n - 对于普通人而言,可以通过模拟器软件在虚拟环境中体验驾驶飞机的感觉。\n - 现代游戏中的模拟飞行游戏提供了一个安全且有趣的入门方式。\n\n6. **个人技能挑战**:\n - 驾驶飞机需要极高的专注力、反应能力和持续学习精神,这对普通人来说是一个巨大的挑战。\n\n总结而言,驾驶飞机不仅是技术上的挑战,更是专业知识、安全意识和持续学习的体现。作为爱好者,可以通过模拟器和不断学习来接近这一难得的体验。"
40 | },
41 | "records": [
42 | {
43 | "begin_t": "2025-04-17 16:59:45",
44 | "end_t": "2025-04-17 16:59:45",
45 | "node_name": "begin_node",
46 | "node_type": "LPNodeType.Begin",
47 | "model": null,
48 | "local_vars": {},
49 | "messages": []
50 | },
51 | {
52 | "begin_t": "2025-04-17 16:59:45",
53 | "end_t": "2025-04-17 17:00:11",
54 | "node_name": "generator",
55 | "node_type": "LPNodeType.LLM",
56 | "model": "deepseek-r1:8b",
57 | "local_vars": {},
58 | "messages": [
59 | {
60 | "role": "user",
61 | "content": "\n如何驾驶飞机?\n"
62 | },
63 | {
64 | "role": "assistant",
65 | "content": "\n嗯,我现在想了解一下怎么驾驶飞机。但是,首先我觉得这可能不是一个简单的问题,因为飞机操作听起来挺复杂的。也许我应该从更基础的地方开始思考,比如飞行的基本原理。不过,作为普通人,直接驾驶飞机似乎不太现实,除非是在模拟器里或者专业训练中。那我先来了解一下飞机是怎么工作的。\n\n首先,飞机主要由几个部分组成:飞机本身、发动机和控制系统。飞机需要通过引擎产生推力,然后利用翅膀施加升力来实现飞行。原来,这些都是空气动力学的原理,对吧?飞机在空中的时候,机翼上下表面压强不同,导致机翼向上倾斜,从而产生升力。这一点我记得好像是伯努利原理或者类似的物理现象。\n\n然后,操控飞机需要使用飞行员的各种控制系统,比如joystick、操纵杆和 pedal。这些工具可以让飞行员在飞机飞行时调整方向,比如左转、右转、上升和下降。但是具体怎么操作,我还不太清楚。\n\n听说飞行过程中,飞行员需要不断监控各种指标,比如油量、燃料剩余量、速度、航向等等。这似乎有点多,特别是在执行复杂任务时,信息处理可能会非常繁琐。\n\n此外,安全因素在驾驶飞机中应该是非常重要的。比如,在起飞和降落的时候,每一个动作都必须精准,以防万一。我不知道具体有什么安全检查流程,但我猜测应该包括检查燃料、润滑油等,并进行各种系统测试。\n\n不过,我觉得自己可能无法真正掌握驾驶飞机,因为这不仅需要专业的知识和经验,而且还需要大量的实际操作和训练。作为一个普通人,可能只能通过模拟器来体验飞行,这样既安全又有趣。\n\n再想想,现代的游戏中确实有一些模拟飞行的游戏,比如Flight Simulators,可以让玩家在虚拟环境中驾驶飞机。这或许是一个不错的入手点。当然,如果我真的想体验一下飞机驾驶,我可以尝试下载一些这样的软件,或者找一个专业的地方进行模拟训练。\n\n不过,我也担心自己可能没有足够的耐心和细致去完成如此复杂的操作。这需要极高的专注力和反应能力,而且每一步都不能马虎。比如,在飞行时,必须时刻注意周围的环境,包括天气、其他机器的位置以及地面情况。\n\n另外,我还不太清楚驾驶飞机所需的具体证书和考试是什么样的。我听说在许多国家,想要成为飞行员都需要经过严格的培训和考核。这可能意味着要花费大量时间和金钱来学习,这对于我来说现在是不现实的。\n\n或许,我可以从基础知识开始,比如了解飞机的构造、原理以及基本操作流程。这样至少可以有一个初步的认识,虽然无法真正驾驶,但能加深对飞行的理解。\n\n总结一下,驾驶飞机不仅需要专业知识和技能,还涉及到安全和责任。这对于我来说是一个非常大的挑战,可能需要很长时间甚至终生学习才能掌握。不过,作为一个爱好者,我可以通过模拟器来体验和练习,同时不断地学习相关的知识,以期有一天能够真正驾驶飞机。当然,这只是一个遥远的愿望,但了解这些信息也让我觉得更加有趣。\n \n\n驾驶飞机是一项极具挑战性的任务,涉及深厚的专业知识、严格的安全标准以及精湛的操作技能。以下是对驾驶飞机的一些基本理解和思考:\n\n1. **基础原理**:\n - 飞机通过引擎产生推力,并利用机翼上的升力原理实现飞行。\n - 伯努利原理解释了机翼上下表面压强差,从而产生升力。\n\n2. **控制系统**:\n - 操控手柄(joystick)、操纵杆和脚踏(pedal)用于调整飞机方向和姿态。\n - 飞行员需要不断监控油量、燃料、速度和航向等关键指标。\n\n3. **安全因素**:\n - 起飞和降落时,每一个动作必须精准,确保安全。\n - 安全检查包括燃料、润滑油等系统的测试和验证。\n\n4. **专业培训**:\n - 成为飞行员需要严格的培训和考核,这通常涉及大量时间和金钱投入。\n - 各国都有自己的证书和考试标准,确保飞行员的能力和责任感。\n\n5. **模拟体验**:\n - 对于普通人而言,可以通过模拟器软件在虚拟环境中体验驾驶飞机的感觉。\n - 现代游戏中的模拟飞行游戏提供了一个安全且有趣的入门方式。\n\n6. **个人技能挑战**:\n - 驾驶飞机需要极高的专注力、反应能力和持续学习精神,这对普通人来说是一个巨大的挑战。\n\n总结而言,驾驶飞机不仅是技术上的挑战,更是专业知识、安全意识和持续学习的体现。作为爱好者,可以通过模拟器和不断学习来接近这一难得的体验。"
66 | }
67 | ]
68 | },
69 | {
70 | "begin_t": "2025-04-17 17:00:11",
71 | "end_t": "2025-04-17 17:00:11",
72 | "node_name": "end_node",
73 | "node_type": "LPNodeType.End",
74 | "model": null,
75 | "local_vars": {
76 | "__callback": null,
77 | "__debug": true,
78 | "__print_final_out": false,
79 | "__remove_thinking_txt": true
80 | },
81 | "messages": []
82 | }
83 | ]
84 | }
85 | <<<<<<<<<<<<<[debug][lpdata from end_node]<<<<<<<<<<<<<
86 |
87 |
88 | 驾驶飞机是一项极具挑战性的任务,涉及深厚的专业知识、严格的安全标准以及精湛的操作技能。以下是对驾驶飞机的一些基本理解和思考:
89 |
90 | 1. **基础原理**:
91 | - 飞机通过引擎产生推力,并利用机翼上的升力原理实现飞行。
92 | - 伯努利原理解释了机翼上下表面压强差,从而产生升力。
93 |
94 | 2. **控制系统**:
95 | - 操控手柄(joystick)、操纵杆和脚踏(pedal)用于调整飞机方向和姿态。
96 | - 飞行员需要不断监控油量、燃料、速度和航向等关键指标。
97 |
98 | 3. **安全因素**:
99 | - 起飞和降落时,每一个动作必须精准,确保安全。
100 | - 安全检查包括燃料、润滑油等系统的测试和验证。
101 |
102 | 4. **专业培训**:
103 | - 成为飞行员需要严格的培训和考核,这通常涉及大量时间和金钱投入。
104 | - 各国都有自己的证书和考试标准,确保飞行员的能力和责任感。
105 |
106 | 5. **模拟体验**:
107 | - 对于普通人而言,可以通过模拟器软件在虚拟环境中体验驾驶飞机的感觉。
108 | - 现代游戏中的模拟飞行游戏提供了一个安全且有趣的入门方式。
109 |
110 | 6. **个人技能挑战**:
111 | - 驾驶飞机需要极高的专注力、反应能力和持续学习精神,这对普通人来说是一个巨大的挑战。
112 |
113 | 总结而言,驾驶飞机不仅是技术上的挑战,更是专业知识、安全意识和持续学习的体现。作为爱好者,可以通过模拟器和不断学习来接近这一难得的体验。
114 | ```
115 |
116 | ## 02-task_classification_test
117 | task classification using LangPipe.
118 |
119 | 
120 |
121 | ## 03-parameters_extraction_test
122 | extract parameters from text using LangPipe.
123 |
124 | ```
125 | python 03-parameters_extraction_test.py
126 | -----board for debug purpose-----
127 | {
128 | "时间": "2025年3月22日上午10时30分",
129 | "地点": "江苏省南通市的一家化工厂",
130 | "事件": "发生严重爆炸事故"
131 | }
132 | ```
133 |
134 | pipeline on gui (blue: completed, red: running, black: not started):
135 |
136 | 
137 |
138 | ## 04-notice_generation_test
139 | generate notice from text using LangPipe.
140 |
141 | 
142 |
143 | ## 05-pipeline_render_test
144 | display pipeline on gui and print it at console.
145 |
146 | 
147 |
148 | ## 06-images_input_test
149 | input images to pipeline.
150 |
151 | 
152 | ```
153 | python 06-images_input_test.py
154 | output from handler:
155 | 这些图片与人工智能系统的产品管理有关。第一张图显示了一条高速公路,上面有红色的运输卡车和交通标志牌。第二张图是一个软件架构或流程图,说明了如何处理视频源、数据传输以及输出到显示器的过程,并且在右侧还有一段实时视频监控的画面,展示了某条道路上的情况。整个图片组合似乎是展示一个用于道路安全监控和分析的人工智能系统的整体框架及其组成部分。
156 | ```
157 |
158 | ## 10-search_engine_rag
159 | RAG based on search engine(BoCha search api) using LangPipe.
160 |
161 | pipeline on gui:
162 |
163 | 
164 |
165 | output from console:
166 | ```
167 | python 10-search_engine_rag.py
168 | -----board for debug purpose-----
169 | >>>>>>>>>>>>>[output][final_out from end_node_0]>>>>>>>>>>>>>
170 | 小米汽车在2025年4月5日凌晨发生了一起严重的交通事故。事故发生在广东省湛江市徐闻县曲界镇376省道上,一辆小米SU7标准版小汽车在手动驾驶状态下与对向车道的一辆电动二轮车发生了碰撞。事故发生后,驾驶员弃车逃逸,但最终被警方逮捕。
171 |
172 | 根据官方通报和调查结果,事故的具体经过如下:当天凌晨,一名25岁的男子陈某驾驶这辆小米SU7行驶至徐闻县曲界镇华海农场十四队路段时,与一辆电动二轮车发生碰撞。碰撞导致两名电动二轮车的驾乘人员当场死亡,并引发了车辆起火燃烧。
173 |
174 | 事故发生后,驾驶员陈某逃离现场,但最终迫于公安机关的压力投案自首。目前,陈某因涉嫌交通肇事罪已被逮捕,案件正在进一步侦办中。
175 |
176 | 关于事故中汽车为何会起火的问题,小米汽车表示,初步了解情况是碰撞导致电动二轮车锂电池严重挤压和变形起火,进而引燃了事故车辆。最终的结论还需等待相关部门的正式报告。
177 |
178 | 这起事故在没有警方通报前,网上出现了大量质疑的声音,特别是对小米智能驾驶系统的质疑。尽管官方回应称,事故发生时车辆处于手动驾驶状态,并且事故中的火情是由电动二轮车锂电池引发的,但这一事件仍然引发了公众对于智能辅助驾驶系统安全性的广泛讨论和反思。
179 |
180 | 总之,这起事故不仅给涉事家庭带来了巨大的悲痛,也对小米汽车的品牌形象造成了影响。
181 | <<<<<<<<<<<<<[output][final_out from end_node_0]<<<<<<<<<<<<<
182 | ```
183 |
184 | ## 11-sql_rag
185 | RAG based on sql using LangPipe.
186 |
187 | pipeline on gui:
188 |
189 | 
190 |
191 | output from console:
192 | ```
193 | python 11-sql_rag.py
194 | -----board for debug purpose-----
195 | >>>>>>>>>>>>>[output][final_out from end_node]>>>>>>>>>>>>>
196 | 根据数据分析结果,用户“王五”的退货次数最多,共发生了5次退货。鉴于此情况,建议对这位用户进行特别关注,以防止可能存在的恶意刷单行为。
197 | <<<<<<<<<<<<<[output][final_out from end_node]<<<<<<<<<<<<<
198 | ```
199 |
200 | ## 12-vector_rag
201 | RAG based on vector(faiss) using LangPipe.
202 |
203 | pipeline on gui(blue: completed, red: running, black: not started):
204 | 
205 |
206 | output from console:
207 | ```
208 | python 12-vector_rag.py
209 | -----board for debug purpose-----
210 | >>>>>>>>>>>>>[output][final_out from end_node]>>>>>>>>>>>>>
211 | VideoPipe是一款用于视频(包括图片)分析的图像算法集成框架。它能够处理复杂的任务,如流读取、解码、基于深度学习模型的推理(例如目标检测、图像分类和特征提取)、跟踪、行为分析、数据叠加(OSD)、数据转发(如Kafka/Socket)、编码和流推送等。这些功能通过使用不同类型的插件节点来实现,每个节点对应视频处理的一个特定环节。
212 |
213 | 具体来说,VideoPipe能够帮助实现口算练习检查的核心流程,包括图片接收与解码、字符检测与识别、数学表达式解析与计算以及检查结果叠加等功能。此外,它还支持目标跟踪(如IOU、SORT算法)、行为分析、录制、视频编码和流推送等操作。
214 |
215 | 总体而言,VideoPipe具备易于使用、可移植性强的特点,并且依赖较少的第三方模块,使得其在不同平台上都能良好运行。相较于其他类似框架(如NVIDIA的DeepStream和华为的mxVision),VideoPipe提供了更灵活的选择,并且更加注重代码的易移植性。
216 | <<<<<<<<<<<<<[output][final_out from end_node]<<<<<<<<<<<<<
217 | ```
218 |
219 | ## 13-search_engine_rag_references
220 | RAG based on search engine(BoCha search api) using LangPipe, with reference links clickable from output.
221 |
222 | pipeline on gui:
223 |
224 | 
225 |
226 | output from console:
227 | ```
228 | python 13-search_engine_rag_references.py
229 | -----board for debug purpose-----
230 | >>>>>>>>>>>>>[output][final_out from end_node_0]>>>>>>>>>>>>>
231 | {
232 | "content": "周杰伦,1979年1月18日出生于中国台湾新北市,祖籍福建省永春县。他是一位华语流行乐男歌手、音乐人、演员、导演和编剧。自2000年起,周杰伦凭借其独特的音乐风格在华语乐坛崭露头角,并逐渐成为亚洲流行乐坛最具革命性的创作歌手之一。他的首张专辑《Jay》发行于2000年11月6日[4] ,这张专辑不仅融合了R&B、hip-hop等多种音乐风格,还首次展现了“周氏风格”,即结合中西方元素的创新音乐形式。它在华语乐坛产生了巨大影响,并奠定了他作为华语流行音乐天王的地位[2] [3] [7] 。\n\n周杰伦的音乐作品丰富多样,涵盖了多种风格和主题。他的专辑《范特西》(2001年)进一步巩固了他在融合中西方音乐方面的独特地位;而《七里香》(2004年)则在全亚洲首月销量达到300万张[1] [5] 。此外,周杰伦还多次获得重要奖项和荣誉,包括第15届台湾金曲奖最佳流行音乐演唱专辑奖、世界音乐大奖中国区最畅销艺人奖等[2] [6] 。\n\n除了音乐创作外,周杰伦在电影领域也有出色表现。他主演的首部电影《头文字D》(2005年)让他获得了第25届香港电影金像奖和第42届台湾电影金马奖的最佳新演员奖[1] [3] 。他还自编自导了文艺片《不能说的秘密》(2007年),并凭借该片获得金马奖年度台湾杰出电影奖[2] [6] 。\n\n周杰伦的音乐陪伴了许多人的青春,他的歌曲如《青花瓷》、《稻香》等广受欢迎。此外,他还涉足出版领域,发行了个人图书《D调的华丽》,并成立了杰威尔音乐有限公司[7] 。在2019年12月31日,周杰伦在深圳大运中心体育场举办了疫情前的最后一场线下演唱会,这进一步证明了他的影响力和地位[8] 。\n\n综上所述,周杰伦不仅是一位才华横溢的音乐人,还通过多方面的艺术创作推动了华语流行乐坛的发展,并在电影、出版等领域取得了显著成就。他被广泛认为是华语乐坛独一无二的存在[5] [6] [7] [8] 。",
233 | "references": [
234 | "https://baike.baidu.com/item/%E5%91%A8%E6%9D%B0%E4%BC%A6/129156",
235 | "https://baike.weixin.qq.com/v4853409.htm?baike_inner=cast",
236 | "https://vebaike.com/doc-view-1270.html",
237 | "https://baijiahao.baidu.com/s?id=1827073700401270008",
238 | "https://music.apple.com/cn/playlist/%E5%91%A8%E6%9D%B0%E4%BC%A6%E5%86%99%E7%9A%84%E6%AD%8C/pl.5a3e20295f804b298e507226f335e567",
239 | "https://m.toutiao.com/w/1825077670277323/",
240 | "https://www.mingrenw.cn/ziliao/3/2970.html",
241 | "https://baijiahao.baidu.com/s?id=1820484479709473213"
242 | ]
243 | }
244 | <<<<<<<<<<<<<[output][final_out from end_node_0]<<<<<<<<<<<<<
245 | ```
246 |
247 | output to browser (html rendering directly):
248 |
249 | 
250 |
251 | ## 14-vector_rag_references
252 | RAG based on vector(faiss) using LangPipe, with reference links clickable from output.
253 |
254 | ## 15-multi_round_chat
255 | multi round chat with LLMs using LangPipe from console.
256 |
257 | ```
258 | python 15-multi_round_chat.py
259 | begin_node[type:Begin]
260 | └── classifier[type:LLM]
261 | ├── chatter[type:LLM]
262 | │ └── end_legal[type:End]
263 | ├── end_illegal[type:End]
264 | └── end_failed[type:End]
265 | >>>你好,请问如何治疗流感?
266 | <<<你好!针对流感的治疗,通常包括以下几个方面:
267 |
268 | 1. **休息**:充分休息有助于身体恢复。
269 | 2. **补充水分和营养**:多喝水或其他液体(如温开水、果汁等),保持充足的水分摄入;同时也要注意营养均衡,适量摄取易消化的食物。
270 | 3. **退热药物**:如果伴有发热症状,可以使用非处方的解热镇痛药如对乙酰氨基酚或布洛芬来缓解发热和疼痛。请按照说明书上的剂量服用,并在必要时咨询医生意见。
271 | 4. **保持室内空气流通**:定时开窗通风,减少病毒传播的机会。
272 | 5. **避免传染他人**:尽量待在家里休息,并且佩戴口罩以减少与他人的接触。
273 |
274 | 需要注意的是,在流感高发季节或出现流感症状时,应尽快就医。医生可能会根据患者的具体情况开具抗病毒药物治疗(如奥司他韦),特别是对于高风险人群(例如老年人、儿童、孕妇等)来说,早期使用这些药物可能有助于减轻症状和缩短病程。
275 |
276 | 最后,请记得预防为主,在流感流行季节要勤洗手、佩戴口罩,并接种流感疫苗来增强自身免疫力。希望以上信息对你有所帮助!
277 | >>>慢性胃炎呢?
278 | <<<慢性胃炎是一种常见的消化系统疾病,主要表现为胃黏膜的长期炎症。治疗慢性胃炎需要综合考虑多种因素,包括生活方式调整、饮食管理以及必要时使用药物治疗等。以下是一些常用的治疗方法:
279 |
280 | 1. **调整生活习惯**:
281 | - 保持规律的生活作息,避免熬夜。
282 | - 减轻压力,可以通过运动、冥想等方式缓解精神紧张。
283 |
284 | 2. **合理膳食**:
285 | - 少食多餐,避免过量饮食。
286 | - 避免辛辣、油腻、生冷等刺激性强的食物。
287 | - 增加富含维生素和矿物质的食物摄入,如新鲜蔬菜水果。
288 | - 适量饮水,保持水分平衡。
289 |
290 | 3. **药物治疗**:
291 | - 抗酸药:如抗酸剂或H2受体拮抗剂,可以减少胃酸分泌。
292 | - 胃粘膜保护剂:有助于修复受损的胃黏膜。
293 | - 如有幽门螺杆菌感染,需按医嘱进行根除治疗。
294 |
295 | 4. **避免不良习惯**:
296 | - 戒烟戒酒,烟草和酒精都会对胃黏膜造成伤害。
297 | - 减少咖啡因摄入,如茶、咖啡等含咖啡因饮料可能会刺激胃部。
298 |
299 | 5. **定期复查**:
300 | - 按照医生建议进行胃镜检查或其他必要的检查,以监控病情发展情况。
301 |
302 | 请注意,以上建议仅供参考。如果您怀疑自己患有慢性胃炎或已确诊,请务必咨询专业医生获取个性化治疗方案和指导。切勿自行购买药物服用,以免造成不必要的健康风险。
303 | >>>exit // exit session
304 | ```
305 |
306 | ## 16-chat_to_mysql
307 | chat to mysql using LangPipe from console.
308 |
309 | ```
310 | python 16-chat_to_mysql.py
311 | >>>哪些人买过可口可乐?都是什么时候买的,分别买了多少瓶
312 | txt: 哪些人买过可口可乐?都是什么时候买的,分别买了多少瓶
313 | sql: SELECT u.username, o.created_at, oi.quantity FROM users u JOIN orders o ON u.id = o.user_id JOIN order_items oi ON o.id = oi.order_id JOIN products p ON oi.product_id = p.id WHERE p.name = '可口可乐' ORDER BY o.created_at;
314 |
315 | >>>购买力前5的用户有哪些,各消费了多少?我需要他们详细资料
316 | txt: 购买力前5的用户有哪些,各消费了多少?我需要他们详细资料
317 | sql: SELECT u.id, u.username, u.email, SUM(oi.quantity * oi.unit_price) AS total_spent FROM users u JOIN orders o ON u.id = o.user_id JOIN order_items oi ON o.id = oi.order_id GROUP BY u.id, u.username, u.email ORDER BY total_spent DESC LIMIT 5;
318 |
319 | >>>谁最爱买华为手机?一共买了多少部?是不是代表他爱国?
320 | txt: 谁最爱买华为手机?一共买了多少部?是不是代表他爱国?
321 | sql: SELECT u.username, COUNT(*) AS total_purchases FROM users u JOIN order_items oi ON u.id = oi.user_id JOIN products p ON oi.product_id = p.id WHERE p.name LIKE '%华为手机%' GROUP BY u.username ORDER BY total_purchases DESC;
322 |
323 | >>>谁的退货次数最多?需要对该用户进行特别关注,以防恶意刷单
324 | txt: 谁的退货次数最多?需要对该用户进行特别关注,以防恶意刷单
325 | sql: SELECT u.username FROM users u JOIN ( SELECT order_id, COUNT(*) AS refund_count FROM refunds GROUP BY order_id ) r ON u.id = (SELECT user_id FROM orders WHERE id = r.order_id) GROUP BY u.id ORDER BY SUM(r.refund_count) DESC LIMIT 1;
326 |
327 | >>>exit // exit session
328 | ```
329 |
330 | ## 17-chat_to_web
331 | chat to web using LangPipe from console.
332 |
333 | ```
334 | python 17-chat_to_web.py
335 | >>>韩国2025年发生了哪些重大事件?
336 | query : 韩国2025年发生了哪些重大事件?
337 | answer: 2025年对于韩国来说是充满变数的一年。年初,美国将韩国正式列入“敏感国家名单”,这不仅反映了美韩关系的微妙变化,也预示着未来可能在多个领域产生影响。紧接着,在4月4日,韩国宪法法院宣布罢免总统尹锡悦,这一事件标志着韩国宪政史上第二位被弹劾下台的总统,对韩国政治格局产生了重大冲击。尹锡悦因发布紧急戒严令而引发的政治风暴最终导致了他失去总统职位。
338 |
339 | 与此同时,一场严重的山火也在韩国东南部地区肆虐,造成了至少18人死亡、26人受伤,并烧毁了大量森林和历史建筑。这场火灾不仅对当地居民的生活产生了严重影响,也引发了关于气候变化和野火预防策略的广泛讨论。
340 |
341 | 此外,尹锡悦被罢免后,韩国的政治局势进一步复杂化。在野党和执政党都在利用这一事件来争取政治优势,而新总统的产生将决定未来韩国的发展方向。同时,韩国政府还宣布了一系列针对汽车行业的紧急支持措施,以应对美国对进口汽车征收关税的影响。
342 |
343 | 这些重大事件不仅反映了韩国国内政治和社会问题的复杂性,也展示了其在国际关系中的微妙位置和面临的挑战。
344 |
345 | >>>小米汽车高速撞车重大事故的详细经过是怎样的?
346 | query : 小米汽车高速撞车重大事故的详细经过是怎样的?
347 | answer: 小米汽车在近期发生了两起严重的高速撞车事故,这两起事故都与小米SU7车型有关,并且均发生在夜间。以下是详细的经过:
348 |
349 | ### 第一起事故(广东湛江)
350 | - **时间**:2025年4月5日凌晨。
351 | - **地点**:广东省湛江市徐闻县曲界镇376省道。
352 | - **情况**:一辆小米SU7与对向车道的一辆电动两轮车发生碰撞,导致两名电动车驾乘人员当场死亡。事故发生后,车辆起火燃烧,肇事司机弃车逃逸。
353 | - **调查结果**:根据徐闻县公安局的通报,事故中驾驶员并非车主本人,并且在事故发生后弃车逃逸,最终被警方逮捕。
354 |
355 | ### 第二起事故(安徽铜陵)
356 | - **时间**:2025年3月29日。
357 | - **地点**:安徽省铜陵市。
358 | - **情况**:一辆小米SU7与另一辆车发生碰撞并引发火灾。事故中,车辆开启了NOA(导航辅助驾驶)功能,并以116公里/小时的速度行驶。系统多次提示驾驶员需手握方向盘,但最终仍发生了严重事故,导致车身起火、人员伤亡。
359 | - **调查结果**:事故发生后,小米汽车官方回应称,相关火情是由于碰撞后电动两轮车锂电池严重挤压和变形起火,进而引燃了事故车辆。此外,事故中驾驶员并非车主本人。
360 |
361 | ### 事故原因分析
362 | 根据目前的公开信息,这两起事故的主要原因是:
363 | 1. **驾驶员对智能驾驶系统的理解不足**:在事故发生时,驾驶员未能及时接管车辆控制权。
364 | 2. **电动两轮车锂电池的安全隐患**:碰撞后锂电池严重挤压和变形引发火灾,进而引燃了事故车辆。
365 |
366 | ### 小米汽车的官方回应
367 | 小米汽车针对这两起事故进行了详细说明:
368 | - 在第一起事故中,小米表示火情是由电动二轮车锂电池引起的。
369 | - 在第二起事故中,小米强调其智能驾驶系统为L2级别,并提醒驾驶员在使用辅助驾驶功能时需时刻保持警觉。
370 |
371 | ### 社会反响
372 | 这两起事故引发了广泛的社会关注和讨论。一方面,公众对智能驾驶技术的安全性提出了质疑;另一方面,也有声音认为事故责任在于驾驶员的操作不当或理解不足。此外,小米汽车的股价也因此受到了影响。
373 |
374 | 综上所述,这两起高速撞车重大事故的发生,不仅给涉事家庭带来了巨大的悲痛,也引发了社会各界对于智能驾驶技术安全性的广泛讨论和反思。
375 |
376 | >>>exit // exit session
377 | ```
378 |
379 | ## 18-chat_to_knowledgebase
380 | chat to knowledgebase using LangPipe from console.
381 |
382 | ```
383 | python 18-chat_to_knowledgebase.py
384 | >>>车辆车型颜色识别算法哪些场景下精度会下降?
385 | query : 车辆车型颜色识别算法哪些场景下精度会下降?
386 | answer: 车辆车型颜色识别算法在以下几种场景下的精度可能会有所下降:
387 |
388 | 1. **光线条件不佳的情况**:如凌晨或晚上无光线或光线较暗的环境。
389 | 2. **车辆严重遮挡**:当车辆存在严重的遮挡,导致关键特征被消除时。
390 | 3. **极端天气条件**:例如大雾、大雪等恶劣天气会影响图像质量,从而降低识别精度。
391 |
392 | 这些因素都会对算法的性能产生负面影响,因此在实际应用中需要特别注意这些场景下的数据处理和算法优化。
393 |
394 | >>>VideoPipe的主要功能是什么?
395 | query : VideoPipe的主要功能是什么?
396 | answer: VideoPipe是一款用于视频(包括图片)分析的图像算法集成框架,它能够处理复杂的任务,如流读取、解码、基于深度学习模型的推理(分类/检测/特征提取等)、跟踪、行为分析、数据叠加(OSD)、数据转发(如Kafka/Socket)、编码和流推送(RTMP或本地文件)。其主要功能包括:
397 |
398 | 1. **图像处理与识别**:通过`vp_image_src_node`节点接收图片并进行解码,利用`vp_ppocr_text_detector_node`节点完成字符检测和识别。
399 | 2. **数学表达式解析与计算**:借助`vp_expr_check_node`节点实现数学表达式的解析和计算。
400 | 3. **结果叠加显示**:使用`vp_expr_osd_node`节点将检查结果叠加到图像上,提供直观的视觉反馈。
401 |
402 | VideoPipe不仅能够支持上述核心功能,还具备以下特点:
403 |
404 | - 支持基于深度学习的多级推理,包括目标检测、图像分类和特征提取等。
405 | - 提供屏幕显示(OSD)功能,可以将模型输出结果绘制到帧上。
406 | - 实现数据代理,能够以Kafka/Socket等方式推送结构化数据至云端或其他第三方平台。
407 | - 支持目标跟踪,如IOU、SORT算法等。
408 | - 通过行为分析(BA),支持基于跟踪的行为分析,例如交通判断中的越线、停车和违章检测。
409 |
410 | 此外,VideoPipe具备以下优势:
411 |
412 | - **易于使用**:相较于英伟达的DeepStream和华为的mxVision,VideoPipe更易于上手且具有更高的可移植性。
413 | - **低学习门槛**:它采用面向插件的编程风格,允许用户通过不同类型的节点构建不同的视频分析管道。
414 | - **较少依赖第三方模块**:相比GStreamer等难以学习的第三方模块,VideoPipe对这些模块的依赖较少。
415 |
416 | 综上所述,VideoPipe是一个功能强大且灵活的框架,适用于快速构建各种视频分析应用系统。
417 |
418 | >>>exit // exit session
419 | ```
420 |
421 | ## 20-chat_to_web_gui
422 | chat to web using LangPipe from browser.
423 |
424 | 
425 |
426 |
427 | ## 21-chat_to_sqlite_gui
428 | chat to sqlite using LangPipe from browser.
429 |
430 | 
431 |
432 | ## fake_faiss_index
433 | fake script to create a vector index & meta db files.
434 |
435 | ## fake_sqlite_db
436 | fake script to create a sqlite db file.
--------------------------------------------------------------------------------
/tests/chat_webui/app.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
4 | from langpipe import LPBegin, LPGenerator, LPEnd, LPAggregator
5 | from sample_nodes import LPBoChaSearch
6 | from flask import Flask, request, jsonify, render_template
7 |
8 | def create_chat2llm_pipeline():
9 | """
10 | chat to LLMs using LangPipe.
11 | """
12 | # create nodes
13 | begin = LPBegin('begin')
14 | generator = LPGenerator('generator', 'qwen2.5:7b')
15 | end = LPEnd('end')
16 |
17 | # link together
18 | begin.link(generator)
19 | generator.link(end)
20 |
21 | # return the head&tail of pipeline
22 | return begin, end
23 |
24 | def create_chat2web_pipeline():
25 | """
26 | chat to Web(online search) using LangPipe.
27 | """
28 | # create nodes
29 | begin = LPBegin('begin')
30 | bocha_search = LPBoChaSearch('bocha_search', 'sk-***') # replace with your own api key
31 | aggregator = LPAggregator('aggregator', None, 'qwen2.5:7b')
32 | end = LPEnd('end')
33 |
34 | # link together
35 | begin.link(bocha_search)
36 | bocha_search.link(aggregator)
37 | aggregator.link(end)
38 |
39 | # return the head&tail of pipeline
40 | return begin, end
41 |
42 | app = Flask(__name__)
43 |
44 | @app.route("/")
45 | def index():
46 | return render_template("index.html")
47 |
48 | @app.route("/chat", methods=["POST"])
49 | def chat():
50 | user_message = request.json.get("message")
51 | online_search_enabled = request.json.get("online_search", False)
52 | response = ""
53 |
54 | if online_search_enabled:
55 | head, tail = create_chat2web_pipeline()
56 | head.input(user_message)
57 | response = tail.final_out
58 | else:
59 | head, tail = create_chat2llm_pipeline()
60 | head.input(user_message)
61 | response = tail.final_out
62 |
63 | return jsonify({"reply": response, "online_search": online_search_enabled})
64 |
65 | if __name__ == "__main__":
66 | app.run(debug=True)
67 |
--------------------------------------------------------------------------------
/tests/chat_webui/static/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'Helvetica Neue', sans-serif;
3 | background: #f0f2f5;
4 | display: flex;
5 | justify-content: center;
6 | align-items: center;
7 | height: 100vh;
8 | margin: 0;
9 | }
10 |
11 | .chat-container {
12 | width: 400px;
13 | max-height: 90vh;
14 | background: white;
15 | border-radius: 12px;
16 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
17 | display: flex;
18 | flex-direction: column;
19 | overflow: hidden;
20 | }
21 |
22 | .chat-header {
23 | background-color: #4a90e2;
24 | color: white;
25 | padding: 15px;
26 | text-align: center;
27 | font-size: 18px;
28 | }
29 |
30 | .chat-messages {
31 | flex: 1;
32 | padding: 10px;
33 | overflow-y: auto;
34 | display: flex;
35 | flex-direction: column;
36 | }
37 |
38 | .message {
39 | margin-bottom: 12px;
40 | max-width: 75%;
41 | padding: 10px 14px;
42 | border-radius: 18px;
43 | position: relative;
44 | display: flex;
45 | flex-direction: column;
46 | font-size: 14px;
47 | line-height: 1.4;
48 | box-shadow: 0 2px 4px rgba(0,0,0,0.08);
49 | }
50 |
51 | .message .nickname {
52 | font-size: 12px;
53 | font-weight: bold;
54 | margin-bottom: 4px;
55 | color: #555;
56 | }
57 |
58 | .message .text {
59 | padding: 6px 0;
60 | white-space: pre-wrap;
61 | }
62 |
63 | .message .time {
64 | font-size: 11px;
65 | color: #888;
66 | margin-top: 4px;
67 | }
68 |
69 | .user {
70 | align-self: flex-end;
71 | background-color: #d2f8d2;
72 | border-top-right-radius: 0;
73 | text-align: left;
74 | }
75 |
76 | .user .time {
77 | align-self: flex-end;
78 | }
79 |
80 | .bot {
81 | align-self: flex-start;
82 | background-color: #f1f0f0;
83 | border-top-left-radius: 0;
84 | text-align: left;
85 | }
86 |
87 | .bot .time {
88 | align-self: flex-start;
89 | }
90 |
91 | .chat-input {
92 | display: flex;
93 | border-top: 1px solid #ddd;
94 | }
95 |
96 | .chat-input input {
97 | flex: 1;
98 | border: none;
99 | padding: 12px;
100 | font-size: 14px;
101 | outline: none;
102 | }
103 |
104 | .chat-input button {
105 | padding: 12px 20px;
106 | background-color: #4a90e2;
107 | color: white;
108 | border: none;
109 | cursor: pointer;
110 | transition: background 0.3s;
111 | }
112 |
113 | .chat-input button:hover {
114 | background-color: #357ab8;
115 | }
116 |
117 | .search-toggle {
118 | display: flex;
119 | justify-content: flex-end;
120 | margin: 10px 0 0 0;
121 | padding-right: 10px;
122 | font-size: 14px;
123 | color: #444;
124 | }
125 |
126 |
--------------------------------------------------------------------------------
/tests/chat_webui/static/index.js:
--------------------------------------------------------------------------------
1 | async function sendMessage() {
2 | const input = document.getElementById("userInput");
3 | const message = input.value.trim();
4 | if (!message) return;
5 |
6 | //sendButton.disabled = true;
7 | appendMessage("我自己:", message, "user");
8 | input.value = "";
9 | const online_search = document.getElementById("searchCheckbox").checked;
10 | reply_title = "LangPipe:";
11 | if (online_search) {
12 | reply_title = "LangPipe(联网问答):";
13 | } else {
14 | reply_title = "LangPipe(LLM问答):";
15 | }
16 | const thinking_msg = appendMessage(reply_title, "正在思考中...", "bot");
17 | try {
18 | const response = await fetch("/chat", {
19 | method: "POST",
20 | headers: { "Content-Type": "application/json" },
21 | body: JSON.stringify({ message, online_search })
22 | });
23 |
24 | const data = await response.json();
25 | appendMessage(reply_title, data.reply, "bot");
26 | } catch (error) {
27 | appendMessage(reply_title, "发生错误,请稍后再试。", "bot");
28 | } finally {
29 | if (thinking_msg) {
30 | thinking_msg.remove();
31 | }
32 | }
33 | }
34 |
35 | function getCurrentTime() {
36 | const now = new Date();
37 | return now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
38 | }
39 |
40 | function appendMessage(nickname, text, type) {
41 | const chatMessages = document.getElementById("chatMessages");
42 |
43 | const msgWrapper = document.createElement("div");
44 | msgWrapper.classList.add("message", type);
45 |
46 | const nicknameElem = document.createElement("div");
47 | nicknameElem.classList.add("nickname");
48 | nicknameElem.textContent = nickname;
49 |
50 | const textElem = document.createElement("div");
51 | textElem.classList.add("text");
52 | textElem.textContent = text;
53 |
54 | const timeElem = document.createElement("div");
55 | timeElem.classList.add("time");
56 | timeElem.textContent = getCurrentTime();
57 |
58 | msgWrapper.appendChild(nicknameElem);
59 | msgWrapper.appendChild(textElem);
60 | msgWrapper.appendChild(timeElem);
61 |
62 | chatMessages.appendChild(msgWrapper);
63 | chatMessages.scrollTop = chatMessages.scrollHeight;
64 |
65 | return msgWrapper;
66 | }
67 |
--------------------------------------------------------------------------------
/tests/chat_webui/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 与LangPipe对话
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | 联网搜索
18 |
19 |
20 |
21 |
22 |
23 | 发送
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/tests/faiss.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/tests/faiss.db
--------------------------------------------------------------------------------
/tests/faiss.index:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/tests/faiss.index
--------------------------------------------------------------------------------
/tests/fake_faiss_index.py:
--------------------------------------------------------------------------------
1 | import os, fitz, sqlite3, re
2 | import faiss
3 | import numpy as np
4 | from docx import Document
5 | from tqdm import tqdm
6 | from ollama import embed
7 |
8 | """
9 | 基于tests/files文件夹下的docx/pdf文件,模拟构建一个知识库。脚本生成两个文件,一个为faiss.db,一个为faiss.index
10 | 供12-vector_rag.py 使用。
11 | """
12 |
13 | DB_PATH = "faiss.db"
14 | INDEX_PATH = "faiss.index"
15 | EMBED_MODEL = 'granite-embedding:278m'
16 |
17 | def extract_text_from_file(filepath):
18 | if filepath.endswith(".pdf"):
19 | doc = fitz.open(filepath)
20 | return "\n".join([page.get_text() for page in doc])
21 | elif filepath.endswith(".docx"):
22 | doc = Document(filepath)
23 | return "\n".join([para.text for para in doc.paragraphs])
24 | else:
25 | raise ValueError("Unsupported file type.")
26 |
27 | def split_text(text, max_length=300, overlap=50):
28 | # 按中英文标点切句
29 | sentences = re.split(r'(?<=[。!?!?;;..])', text)
30 | sentences = [s.strip() for s in sentences if s.strip()]
31 |
32 | chunks = []
33 | current_chunk = ""
34 |
35 | for sentence in sentences:
36 | if len(current_chunk) + len(sentence) <= max_length:
37 | current_chunk += sentence
38 | else:
39 | # 添加当前段落
40 | chunks.append(current_chunk)
41 | # 保留 overlap 的结尾部分作为下段开头
42 | current_chunk = current_chunk[-overlap:] + sentence
43 |
44 | if current_chunk:
45 | chunks.append(current_chunk)
46 |
47 | return chunks
48 |
49 | def init_db():
50 | conn = sqlite3.connect(DB_PATH)
51 | cur = conn.cursor()
52 | cur.execute("""
53 | CREATE TABLE IF NOT EXISTS faiss_meta (
54 | id INTEGER PRIMARY KEY,
55 | chunk_text TEXT,
56 | source_file TEXT
57 | )
58 | """)
59 | conn.commit()
60 | return conn
61 |
62 | def index_documents(folder):
63 | conn = init_db()
64 | cur = conn.cursor()
65 | all_chunks = []
66 |
67 | for fname in tqdm(os.listdir(folder)):
68 | fpath = os.path.join(folder, fname)
69 | try:
70 | text = extract_text_from_file(fpath)
71 | chunks = split_text(text)
72 | for chunk in chunks:
73 | cur.execute("INSERT INTO faiss_meta (chunk_text, source_file) VALUES (?, ?)", (chunk, fname))
74 | all_chunks.append(chunk)
75 | except Exception as e:
76 | print(f"Failed to process {fname}: {e}")
77 |
78 | conn.commit()
79 |
80 | # 向量化并构建 faiss
81 | print("Embedding and indexing...")
82 | response = embed(model=EMBED_MODEL, input=all_chunks)
83 |
84 | dim = len(response['embeddings'][0])
85 | index = faiss.IndexFlatL2(dim)
86 | index.add(np.array(response['embeddings']))
87 | faiss.write_index(index, INDEX_PATH)
88 | conn.close()
89 | print("Index and metadata saved.")
90 |
91 | if __name__ == "__main__":
92 | index_documents("./files") # 替换为你的文档目录路径
93 |
--------------------------------------------------------------------------------
/tests/fake_sqlite_db.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 | import random
3 | from datetime import datetime
4 | from faker import Faker
5 |
6 | """
7 | 模拟生成一个shop.db sqlite数据库,数据库包含5张数据表,shop.db文件供 11-sql_rag.py 脚本使用。
8 | """
9 |
10 | # 初始化
11 | fake = Faker("zh_CN")
12 | conn = sqlite3.connect("shop.db")
13 | cursor = conn.cursor()
14 |
15 | # 建表
16 | cursor.executescript("""
17 | -- ----------------------------
18 | -- 用户表:users
19 | -- 存储注册用户的基本信息
20 | -- ----------------------------
21 | CREATE TABLE IF NOT EXISTS users (
22 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 用户唯一ID,自增主键
23 | username TEXT NOT NULL UNIQUE, -- 用户名,必须唯一且非空
24 | email TEXT NOT NULL UNIQUE, -- 用户邮箱,必须唯一且非空
25 | password_hash TEXT NOT NULL, -- 密码哈希值,用于存储加密后的密码
26 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 注册时间,默认为当前时间
27 | );
28 |
29 | -- ----------------------------
30 | -- 商品表:products
31 | -- 存储上架商品的详细信息
32 | -- ----------------------------
33 | CREATE TABLE IF NOT EXISTS products (
34 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 商品唯一ID,自增主键
35 | name TEXT NOT NULL, -- 商品名称,非空
36 | description TEXT, -- 商品描述,可选
37 | price REAL NOT NULL, -- 商品单价,使用REAL表示小数
38 | stock INTEGER DEFAULT 0, -- 商品库存数量,默认0
39 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 上架时间,默认为当前时间
40 | );
41 |
42 | -- ----------------------------
43 | -- 订单表:orders
44 | -- 记录用户提交的订单信息
45 | -- ----------------------------
46 | /*
47 | 订单状态(status)字段说明:
48 | - 'pending' 等待付款
49 | - 'paid' 已付款
50 | - 'shipped' 已发货
51 | - 'completed' 已完成
52 | - 'cancelled' 已取消
53 | */
54 | CREATE TABLE IF NOT EXISTS orders (
55 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 订单唯一ID,自增主键
56 | user_id INTEGER NOT NULL, -- 下单用户的ID,关联 users(id)
57 | status TEXT DEFAULT 'pending', -- 订单状态,默认为 pending
58 | total_amount REAL NOT NULL, -- 订单总金额
59 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 订单创建时间
60 | FOREIGN KEY (user_id) REFERENCES users(id) -- 外键:用户ID
61 | );
62 |
63 | -- ----------------------------
64 | -- 订单项表:order_items
65 | -- 记录每个订单中包含哪些商品及数量
66 | -- ----------------------------
67 | CREATE TABLE IF NOT EXISTS order_items (
68 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 订单项唯一ID,自增主键
69 | order_id INTEGER NOT NULL, -- 所属订单ID,关联 orders(id)
70 | product_id INTEGER NOT NULL, -- 商品ID,关联 products(id)
71 | quantity INTEGER NOT NULL, -- 购买数量
72 | unit_price REAL NOT NULL, -- 下单时的商品单价
73 | FOREIGN KEY (order_id) REFERENCES orders(id), -- 外键:订单ID
74 | FOREIGN KEY (product_id) REFERENCES products(id) -- 外键:商品ID
75 | );
76 |
77 | -- ----------------------------
78 | -- 退款表:refunds
79 | -- 记录订单退款的请求和处理信息
80 | -- ----------------------------
81 | CREATE TABLE IF NOT EXISTS refunds (
82 | id INTEGER PRIMARY KEY AUTOINCREMENT, -- 退款记录ID,自增主键
83 | order_id INTEGER NOT NULL, -- 关联的订单ID
84 | refund_amount REAL NOT NULL, -- 退款金额
85 | reason TEXT, -- 退款原因,可选
86 | refund_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 退款时间,默认为当前时间
87 | FOREIGN KEY (order_id) REFERENCES orders(id) -- 外键:订单ID
88 | );
89 | """)
90 |
91 | # 清空旧数据(按依赖顺序删除)
92 | cursor.executescript("""
93 | PRAGMA foreign_keys = OFF;
94 | DELETE FROM refunds;
95 | DELETE FROM order_items;
96 | DELETE FROM orders;
97 | DELETE FROM products;
98 | DELETE FROM users;
99 | PRAGMA foreign_keys = ON;
100 | """)
101 |
102 | # 插入 10 个用户
103 | users = [
104 | ("张三", "zhangsan01@163.com", fake.sha256()),
105 | ("李四", "lisi_88@163.com", fake.sha256()),
106 | ("王五", "wangwu2023@163.com", fake.sha256()),
107 | ("赵六", "zhaoliu@163.com", fake.sha256()),
108 | ("钱七", "qianqi@163.com", fake.sha256()),
109 | ("孙八", "sunba_001@163.com", fake.sha256()),
110 | ("周九", "zhoujiu@163.com", fake.sha256()),
111 | ("吴十", "wushi666@163.com", fake.sha256()),
112 | ("郑强", "zhengqiang@163.com", fake.sha256()),
113 | ("冯刚", "fenggang@163.com", fake.sha256())
114 | ]
115 | cursor.executemany("INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)", users)
116 | cursor.execute("SELECT id FROM users")
117 | user_ids = [row[0] for row in cursor.fetchall()]
118 |
119 | # 插入 30 个商品
120 | products = [
121 | ("可口可乐", "经典口味碳酸饮料,畅爽解渴", 3.50, random.randint(0, 100)),
122 | ("百事可乐", "清爽可乐饮品,口感独特", 3.20, random.randint(0, 100)),
123 | ("雪碧", "柠檬味碳酸饮料,清新提神", 3.00, random.randint(0, 100)),
124 | ("农夫山泉", "天然矿泉水,饮用安全健康", 2.00, random.randint(0, 100)),
125 | ("康师傅绿茶", "绿茶口味,清凉解渴", 3.80, random.randint(0, 100)),
126 | ("脉动", "维生素饮料,运动后补水佳品", 4.50, random.randint(0, 100)),
127 | ("乐事薯片", "原味薯片,酥脆可口", 5.50, random.randint(0, 100)),
128 | ("奥利奥", "巧克力夹心饼干,甜蜜美味", 6.00, random.randint(0, 100)),
129 | ("卫龙辣条", "经典辣条,香辣十足", 2.50, random.randint(0, 100)),
130 | ("小米手机", "性价比高的智能手机", 1999.00, random.randint(0, 100)),
131 | ("华为手机", "高性能安卓手机", 3499.00, random.randint(0, 100)),
132 | ("iPhone手机", "苹果品牌智能手机", 5999.00, random.randint(0, 100)),
133 | ("电动牙刷", "声波震动清洁牙齿", 129.99, random.randint(0, 100)),
134 | ("剃须刀", "男士专用电动剃须刀", 89.50, random.randint(0, 100)),
135 | ("洗发水", "去屑清爽型洗发水", 29.90, random.randint(0, 100)),
136 | ("沐浴露", "滋润保湿型沐浴露", 35.00, random.randint(0, 100)),
137 | ("洗面奶", "控油清洁型洗面奶", 22.80, random.randint(0, 100)),
138 | ("护肤霜", "保湿滋润型护肤霜", 45.60, random.randint(0, 100)),
139 | ("润唇膏", "防干裂润唇膏", 15.00, random.randint(0, 100)),
140 | ("蓝牙耳机", "无线蓝牙耳机,降噪功能", 199.00, random.randint(0, 100)),
141 | ("充电宝", "移动电源,快充大容量", 89.90, random.randint(0, 100)),
142 | ("鼠标", "无线鼠标,人体工学设计", 49.90, random.randint(0, 100)),
143 | ("键盘", "机械键盘,手感极佳", 159.00, random.randint(0, 100)),
144 | ("U盘", "64GB金属U盘,数据存储", 39.99, random.randint(0, 100)),
145 | ("移动硬盘", "1TB移动硬盘,快速传输", 359.00, random.randint(0, 100)),
146 | ("显示器", "24英寸高清显示器", 699.00, random.randint(0, 100)),
147 | ("路由器", "双频无线路由器", 129.00, random.randint(0, 100)),
148 | ("电饭煲", "智能电饭煲,多种烹饪模式", 259.00, random.randint(0, 100)),
149 | ("电磁炉", "家用电磁炉,节能高效", 199.00, random.randint(0, 100)),
150 | ("吸尘器", "无线手持吸尘器", 499.00, random.randint(0, 100))
151 | ]
152 | cursor.executemany("INSERT INTO products (name, description, price, stock) VALUES (?, ?, ?, ?)", products)
153 | cursor.execute("SELECT id, price FROM products")
154 | product_list = cursor.fetchall()
155 |
156 | # 插入 50 个订单
157 | statuses = ['pending', 'paid', 'shipped', 'completed', 'cancelled']
158 | orders = []
159 | for _ in range(50):
160 | uid = random.choice(user_ids)
161 | status = random.choice(statuses)
162 | order_date = fake.date_time_between(start_date="-30d", end_date="now")
163 | total = round(random.uniform(50, 2000), 2)
164 | orders.append((uid, status, total, order_date))
165 | cursor.executemany("INSERT INTO orders (user_id, status, total_amount, created_at) VALUES (?, ?, ?, ?)", orders)
166 | cursor.execute("SELECT id FROM orders")
167 | order_ids = [row[0] for row in cursor.fetchall()]
168 |
169 | # 插入 200 个订单项
170 | order_items = []
171 | for _ in range(200):
172 | order_id = random.choice(order_ids)
173 | product_id, price = random.choice(product_list)
174 | quantity = random.randint(1, 5)
175 | order_items.append((order_id, product_id, quantity, price))
176 | cursor.executemany("INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES (?, ?, ?, ?)", order_items)
177 |
178 | # 插入 20 条退款记录
179 | refunds = []
180 | reasons = ['价格太贵', '快递太慢', '商品不符合广告宣传', '商品破损、质量问题', '买错了']
181 | for _ in range(20):
182 | order_id = random.choice(order_ids)
183 | amount = round(random.uniform(10, 500), 2)
184 | reason = random.choice(reasons)
185 | date = fake.date_time_between(start_date='-7d', end_date='now')
186 | refunds.append((order_id, amount, reason, date))
187 | cursor.executemany("INSERT INTO refunds (order_id, refund_amount, reason, refund_date) VALUES (?, ?, ?, ?)", refunds)
188 |
189 | # 提交保存
190 | conn.commit()
191 | print("✅ 已插入模拟数据完成")
192 |
193 | # 输出5张表的数据记录
194 | tables=["users", "products", "orders", "order_items", "refunds"]
195 | for table in tables:
196 | cursor.execute(f"SELECT * FROM {table}")
197 | rows = cursor.fetchall()
198 | print(f"\n数据表:{table}")
199 | for row in rows:
200 | print(row)
201 | print("------")
202 |
203 | cursor.close()
204 | conn.close()
--------------------------------------------------------------------------------
/tests/sample_nodes/README.md:
--------------------------------------------------------------------------------
1 | all nodes in this dir are business-related, which not portable across different applications directly.
2 |
--------------------------------------------------------------------------------
/tests/sample_nodes/__init__.py:
--------------------------------------------------------------------------------
1 | from .lpfetchweather import LPFetchWeather
2 | from .lpgeneratenotice import LPGenerateNotice
3 | from .lpbochasearch import LPBoChaSearch
4 | from .lpdbsearch import LPDBSearch
5 | from .lpkbsearch import LPKBSearch
--------------------------------------------------------------------------------
/tests/sample_nodes/lpbochasearch.py:
--------------------------------------------------------------------------------
1 | import json
2 | import requests
3 | from langpipe import LPBaseInvoker
4 |
5 | class LPBoChaSearch(LPBaseInvoker):
6 | """
7 | web/ai search online using BoCha search service.
8 | access `lpdata['global_vars']['searched_result']` for the searched result.
9 | """
10 | def __init__(self, name, api_key, api_url='https://api.bochaai.com/v1/web-search', ai_search=False) -> None:
11 | super().__init__(name)
12 | self.__api_key = api_key
13 | self.__api_url = api_url
14 | self.__is_ai_search = ai_search
15 |
16 | self.__search_keywords = None
17 | self.__search_count = 10
18 | self.__search_result = None
19 |
20 | def _invoke(self, lpdata) -> None:
21 | self.__search_keywords = lpdata['query']
22 |
23 | if not self.__is_ai_search:
24 | ret, result = self.__web_search()
25 | if ret:
26 | self.__search_result = result
27 | else:
28 | ret, result = self.__ai_search()
29 | if ret:
30 | self.__search_result = result
31 |
32 | def _after_handle(self, lpdata) -> None:
33 | super()._after_handle(lpdata)
34 |
35 | # update local variables
36 | record = lpdata['records'][-1]
37 | record['local_vars']['__api_key'] = self.__api_key
38 | record['local_vars']['__api_url'] = self.__api_url
39 | record['local_vars']['__search_keywords'] = self.__search_keywords
40 | record['local_vars']['__search_count'] = self.__search_count
41 | record['local_vars']['__ai_search'] = self.__is_ai_search
42 |
43 | # update global variables
44 | if self.__search_result is not None:
45 | lpdata['final_out'] = self.__search_result
46 | lpdata['global_vars']['searched_result'] = self.__search_result
47 |
48 | def __web_search(self) -> tuple[int, str]:
49 | headers = {
50 | 'Authorization': f'Bearer {self.__api_key}',
51 | 'Content-Type': 'application/json'
52 | }
53 | data = {
54 | "query": self.__search_keywords,
55 | "freshness": "noLimit",
56 | "summary": True,
57 | "count": self.__search_count
58 | }
59 |
60 | response = requests.post(self.__api_url, headers=headers, json=data)
61 | if response.status_code == 200:
62 | json_response = response.json()
63 | try:
64 | if json_response["code"] != 200 or not json_response["data"]:
65 | return 0, f"搜索API请求失败,原因是: {response.msg or '未知错误'}"
66 |
67 | webpages = json_response["data"]["webPages"]["value"]
68 | if not webpages:
69 | return 0, "未找到相关结果。"
70 | formatted_results = ""
71 | for idx, page in enumerate(webpages, start=1):
72 | formatted_results += (
73 | f"[[引用: {idx}]]\n"
74 | f"标题: {page['name']}\n"
75 | f"URL: {page['url']}\n"
76 | f"摘要: {page['summary']}\n"
77 | f"内容详细: {page['snippet']}\n"
78 | f"网站名称: {page['siteName']}\n"
79 | f"网站图标: {page['siteIcon']}\n"
80 | f"发布时间: {page['dateLastCrawled']}\n\n"
81 | )
82 | return 1, formatted_results.strip()
83 | except Exception as e:
84 | return 0, f"搜索API请求失败,原因是:搜索结果解析失败 {str(e)}"
85 | else:
86 | return 0, f"搜索API请求失败,状态码: {response.status_code}, 错误信息: {response.text}"
87 |
88 |
89 | def __ai_search(self) -> tuple[int, str]:
90 | pass
--------------------------------------------------------------------------------
/tests/sample_nodes/lpdbsearch.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 | from langpipe import LPBaseInvoker
3 |
4 | class LPDBSearch(LPBaseInvoker):
5 | """
6 | search from database based on sqlite.
7 | access `lpdata['global_vars']['db_searched_result']` for the searched result from sqlite.
8 | """
9 | def __init__(self, name, db_name) -> None:
10 | super().__init__(name)
11 | self.__db_name = db_name
12 | self.__sql_query = None
13 | self.__db_searched_result = None
14 |
15 | def _invoke(self, lpdata) -> None:
16 | self.__sql_query = lpdata.get('global_vars', {}).get('created_sql', None)
17 | if self.__sql_query is not None:
18 | self.__db_searched_result = self.__db_search()
19 |
20 | def _after_handle(self, lpdata) -> None:
21 | super()._after_handle(lpdata)
22 |
23 | # update local variables
24 | record = lpdata['records'][-1]
25 | record['local_vars']['__db_name'] = self.__db_name
26 | record['local_vars']['__sql_query'] = self.__sql_query
27 |
28 | # update global variables
29 | if self.__db_searched_result is not None:
30 | lpdata['final_out'] = self.__db_searched_result
31 | lpdata['global_vars']['db_searched_result'] = self.__db_searched_result
32 |
33 |
34 | def __db_search(self) -> str:
35 | try:
36 | conn = sqlite3.connect(self.__db_name)
37 | cursor = conn.cursor()
38 | cursor.execute(self.__sql_query)
39 |
40 | rows = cursor.fetchall()
41 | return str(rows)
42 | except Exception as e:
43 | print(e)
44 | return None
45 |
--------------------------------------------------------------------------------
/tests/sample_nodes/lpfetchweather.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import json
3 | from pypinyin import lazy_pinyin
4 | from langpipe import LPBaseInvoker
5 |
6 | class LPFetchWeather(LPBaseInvoker):
7 | """
8 | weather search online using weatherapi service.
9 | """
10 | def __init__(self, name,
11 | api_key,
12 | api_url='http://api.weatherapi.com/v1/current.json',
13 | search_key='城市') -> None:
14 | super().__init__(name)
15 | self.__api_key = api_key
16 | self.__api_url = api_url
17 | self.__search_key = search_key
18 | self.__search_value = None
19 | self.__fetched_weather = None
20 |
21 | def _invoke(self, lpdata) -> None:
22 | extracted_params = lpdata.get('global_vars', {}).get('extracted_params', {})
23 | if self.__search_key in extracted_params and (search_value := extracted_params[self.__search_key]) is not None:
24 | self.__search_value = search_value
25 | self.__fetched_weather = self.__get_weather()
26 |
27 | def _after_handle(self, lpdata) -> None:
28 | super()._after_handle(lpdata)
29 |
30 | # update local variables
31 | record = lpdata['records'][-1]
32 | record['local_vars']['__api_key'] = self.__api_key
33 | record['local_vars']['__api_url'] = self.__api_url
34 | record['local_vars']['__search_key'] = self.__search_key
35 | record['local_vars']['search_value'] = self.__search_value
36 |
37 | # update global variables
38 | if self.__fetched_weather is not None:
39 | lpdata['final_out'] = json.dumps(self.__fetched_weather, indent=4, ensure_ascii=False)
40 | lpdata['global_vars']['fetched_weather'] = self.__fetched_weather
41 |
42 | def __get_weather(self) -> dict:
43 | # convert to pinyin
44 | search_value_py = ''.join(lazy_pinyin(self.__search_value))
45 |
46 | params = {
47 | "key": self.__api_key,
48 | "q": search_value_py,
49 | "aqi": "yes"
50 | }
51 | response = requests.get(self.__api_url, params=params)
52 |
53 | if response.status_code == 200:
54 | data = response.json()
55 | weather_data = {}
56 |
57 | weather_data['city'] = self.__search_value
58 | weather_data['update_time'] = data['current']['last_updated']
59 | weather_data['temperature'] = data['current']['temp_c']
60 | weather_data['condition'] = data['current']['condition']['text']
61 | weather_data['wind_kph'] = data['current']['wind_kph']
62 | weather_data['wind_dir'] = data['current']['wind_dir']
63 | weather_data['pressure_mb'] = data['current']['pressure_mb']
64 | weather_data['precip_mm'] = data['current']['precip_mm']
65 | weather_data['humidity'] = data['current']['humidity']
66 | weather_data['vis_km'] = data['current']['vis_km']
67 | weather_data['cloud'] = data['current']['cloud']
68 | weather_data['pm2_5'] = data['current']['air_quality']['pm2_5']
69 |
70 | return weather_data
71 | else:
72 | return None
73 |
74 |
75 |
--------------------------------------------------------------------------------
/tests/sample_nodes/lpgeneratenotice.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from langpipe import LPBaseInvoker
3 |
4 | class LPGenerateNotice(LPBaseInvoker):
5 | """
6 | generate notice something like:
7 | 您好,这里是[随岳监控中心],
8 | 2024年10月31日17:07接[12122]上报:[徐广高速][429公里][广州方向],发现[散落物],[有很多散落的衣物等垃圾在路面上],请前往处理。
9 | """
10 |
11 | def __init__(self, name, param_keys=['上报人', '接收人', '事发高速', '事发位置', '事发方向', '事件名称', '事件描述']) -> None:
12 | super().__init__(name)
13 | self.__param_keys = param_keys
14 | self.__param_values = []
15 | self.__notice_template = """您好,这里是{0},{1}接{2}上报:{3}{4}{5},发现{6},{7},请前往处理。"""
16 | self.__generated_notice = None
17 |
18 | def _invoke(self, lpdata) -> None:
19 | extracted_params = lpdata.get('global_vars', {}).get('extracted_params', {})
20 | for key in self.__param_keys:
21 | self.__param_values.append(extracted_params.get(key, ''))
22 |
23 | if all(item != '' for item in self.__param_values) and len(self.__param_values) == 7:
24 | values = []
25 | values.append(self.__param_values[1])
26 | values.append(datetime.now().strftime('%Y年%m月%d日 %H:%M:%S'))
27 | values.append(self.__param_values[0])
28 | values.extend(self.__param_values[2:])
29 | self.__generated_notice = self.__notice_template.format(*values)
30 |
31 | def _after_handle(self, lpdata) -> None:
32 | super()._after_handle(lpdata)
33 |
34 | # update local variables
35 | record = lpdata['records'][-1]
36 | record['local_vars']['__param_keys'] = self.__param_keys
37 | record['local_vars']['param_values'] = self.__param_values
38 | record['local_vars']['__notice_template'] = self.__notice_template
39 |
40 | # update global variables
41 | if self.__generated_notice is not None:
42 | lpdata['final_out'] = self.__generated_notice
43 | lpdata['global_vars']['generated_notice'] = self.__generated_notice
44 |
45 |
--------------------------------------------------------------------------------
/tests/sample_nodes/lpkbsearch.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 | import faiss
3 | import numpy as np
4 | from ollama import embed # just used for embedding of query text
5 | from langpipe import LPBaseInvoker
6 |
7 | class LPKBSearch(LPBaseInvoker):
8 | """
9 | search from knowledge base based on faiss which used to save embeddings & sqlite which used to save meta data.
10 | access `lpdata['global_vars']['kb_searched_result']` for the searched result from knowledge base.
11 | """
12 | def __init__(self, name, index_name, db_name) -> None:
13 | super().__init__(name)
14 |
15 | self.__index_name = index_name
16 | self.__db_name = db_name
17 | self.__topk = 10
18 | self.__embedding_model = 'granite-embedding:278m'
19 | self.__query = None
20 | self.__kb_searched_result = None
21 |
22 | def _invoke(self, lpdata) -> None:
23 | self.__query = lpdata['query']
24 | self.__kb_searched_result = self.__kb_search()
25 |
26 | def _after_handle(self, lpdata) -> None:
27 | super()._after_handle(lpdata)
28 |
29 | # update local variables
30 | record = lpdata['records'][-1]
31 | record['local_vars']['__db_name'] = self.__db_name
32 | record['local_vars']['__index_name'] = self.__index_name
33 | record['local_vars']['__topk'] = self.__topk
34 | record['local_vars']['__embedding_model'] = self.__embedding_model
35 | record['local_vars']['__query'] = self.__query
36 |
37 | # update global variables
38 | if self.__kb_searched_result is not None:
39 | lpdata['final_out'] = self.__kb_searched_result
40 | lpdata['global_vars']['kb_searched_result'] = self.__kb_searched_result
41 |
42 | def __kb_search(self) -> str:
43 | index = faiss.read_index(self.__index_name)
44 | conn = sqlite3.connect(self.__db_name)
45 |
46 | query_vec = embed(model=self.__embedding_model, input=[self.__query])['embeddings']
47 | D, I = index.search(np.array(query_vec), self.__topk)
48 |
49 | cur = conn.cursor()
50 | placeholders = ",".join("?" for _ in I[0])
51 | cur.execute(f"SELECT chunk_text, source_file FROM faiss_meta WHERE id IN ({placeholders})", tuple(map(int, I[0])))
52 | rows = cur.fetchall()
53 |
54 | search_result = ""
55 | count = 1
56 | for idx, row in enumerate(rows, start=1):
57 | search_result += (
58 | f"[[引用:{idx}]]\n"
59 | f"出自:{row[1]}\n"
60 | f"原文:{row[0]}\n"
61 | f"相关性:{D[0][idx-1]}\n\n"
62 | )
63 | cur.close()
64 | conn.close()
65 | return search_result
66 |
--------------------------------------------------------------------------------
/tests/shop.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sherlockchou86/PyLangPipe/d765e82cc630e9309546ddb1a9523a3d34e1ada2/tests/shop.db
--------------------------------------------------------------------------------
/tests/weather.py:
--------------------------------------------------------------------------------
1 | import langpipe
2 | import json
3 | import sample_nodes
4 |
5 | params_desc = """
6 | {
7 | "时间": "时间、日期等",
8 | "城市": "事发城市信息,只保留城市名称(不要省份,名称后面也不要加市字,比如'武汉','合肥'即可",
9 | "事件": "发生的事件类型,描述"
10 | }
11 | """
12 |
13 | query = """
14 | 【新华社2025年3月22日讯】今日上午10时30分,湖南株洲的一家化工厂发生严重爆炸事故,目前已确认造成5人死亡,12人受伤,其中3人伤势严重,仍在抢救中。事故现场浓烟滚滚,消防救援人员已紧急赶赴现场进行灭火和搜救。
15 | """
16 |
17 | query2 = """
18 | 今日凌晨3时20分许,河南省郑州经济技术开发区内的成功化工厂发生重大爆炸事故,目前已造成15人死亡、40余人受伤,其中12人重伤。爆炸引发大火并导致厂区部分建筑坍塌,周边数公里内建筑玻璃被震碎。初步调查显示事故可能由乙烯储罐泄漏引发,涉事设备此前存在"带病运行"情况。当地已启动一级应急响应,调集300余名消防人员开展救援,周边居民紧急疏散,国务院已成立特别调查组展开彻查。
19 | """
20 |
21 | # create nodes
22 | begin = langpipe.LPBegin('begin_node')
23 | extractor = langpipe.LPExtractor('extractor', json.loads(params_desc), 'qwen2.5:7b')
24 | fetchweather = sample_nodes.LPFetchWeather('fetchwather', '***')
25 | aggregator = langpipe.LPAggregator('aggregator', '事发城市天气如何?是否影响救援', 'qwen2.5:7b')
26 | end = langpipe.LPEnd('end_node')
27 |
28 | # link together
29 | begin.link(extractor)
30 | extractor.link(fetchweather)
31 | fetchweather.link(aggregator)
32 | aggregator.link(end)
33 |
34 | # input with async mode
35 | begin.input(query2, None, False)
36 |
37 | # visualize the pipeline with data flow
38 | print('-----board for debug purpose-----')
39 | renderer = langpipe.LPBoardRender(node_size=100)
40 | renderer.render(begin)
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------