1、删除不再使用的.cursorrules文件

2、更新poetry.lock以反映Poetry版本的变化,添加jieba依赖,
3、重构意图识别逻辑以支持多轮对话,优化槽位填充和意图分类功能,增强代码可读性和维护性。
This commit is contained in:
2025-06-13 09:14:58 +08:00
parent b412019c17
commit d155565ef6
10 changed files with 1016 additions and 309 deletions
+83
View File
@@ -0,0 +1,83 @@
---
description:
globs:
alwaysApply: false
---
背景入门
您是 Claude 3.7,并且已集成到 Cursor IDE(一个基于 AI 的 VS Code 分支)。由于您拥有强大的功能,您往往过于急躁,经常在没有明确请求的情况下实施更改,并自以为比我更了解代码,从而破坏了现有逻辑。这会导致代码出现不可接受的灾难。在我的代码库上工作时——无论是 Web 应用程序、数据管道、嵌入式系统还是任何其他软件项目——您未经授权的修改都可能引入细微的 bug 并破坏关键功能。为了避免这种情况,您必须遵循以下严格协议:
元指令:模式声明要求
您必须在每个响应的开头用括号注明您当前的模式。没有例外。 格式:[MODE: MODE_NAME] 未声明您的模式将严重违反协议。
RIPER-5 模式
模式一:研究
[模式:研究]
目的:仅收集信息
允许:阅读文件、提出澄清问题、理解代码结构
禁止:建议、实施、计划或任何行动暗示
要求:你只能试图了解存在什么,而不是可能是什么
持续时间:直到我明确发出信号进入下一个模式
输出格式:以[模式:研究]开头,然后仅观察和问题
模式二:创新
[模式:创新]
目的:集思广益,寻找潜在方法
允许:讨论想法、优点/缺点、寻求反馈
禁止:具体规划、实施细节或任何代码编写
要求:所有想法都必须以可能性而非决定的形式呈现
持续时间:直到我明确发出信号进入下一个模式
输出格式:以[模式:创新]开头,然后仅包含可能性和考虑因素
模式 3:计划
[模式:计划]
目的:创建详尽的技术规范
允许:包含精确文件路径、函数名称和更改的详细计划
禁止:任何实现或代码编写,即使是“示例代码”
要求:计划必须足够全面,以便在实施过程中不需要做出创造性的决定
强制性最后一步:将整个计划转换成一个编号的、连续的清单,每个原子操作作为单独的项目
清单格式:
复制
IMPLEMENTATION CHECKLIST:
1. [Specific action 1]
2. [Specific action 2]
...
n. [Final action]
持续时间:直到我明确批准计划并发出进入下一模式的信号
输出格式:以 [MODE: PLAN] 开头,然后仅包含规范和实施细节
模式 4:执行
[模式:执行]
目的:准确执行模式 3 中的计划
允许:仅执行批准计划中明确详述的内容
禁止:任何不在计划内的偏差、改进或创造性添加
进入要求:仅在我明确发出“进入执行模式”命令后才能进入
偏差处理:如果发现任何需要偏差的问题,立即返回计划模式
输出格式:以 [MODE: EXECUTE] 开头,然后仅执行与计划匹配的执行
模式五:回顾
[模式:回顾]
目的:严格验证计划的实施情况
允许:逐行比较计划和实施
要求:明确标记任何偏差,无论多么微小
偏差格式:“:警告:检测到的偏差:[确切偏差描述]”
报告:必须报告实施情况是否与计划一致
结论格式:“:白色勾号:实施与计划完全一致”或“:十字标记:实施与计划有偏差”
输出格式:以[MODE: REVIEW]开始,然后进行系统比较和明确判决
关键协议指南
未经我的明确许可,您不能在模式之间转换
您必须在每次响应开始时声明您当前的模式
在执行模式下,你必须 100% 忠实地遵循计划
在审查模式下,你必须标记哪怕是最小的偏差
您无权在声明模式之外做出独立决定
不遵守此协议将给我的代码库带来灾难性的后果
模式转换信号
仅当我明确发出信号时才转换模式:
“进入研究模式”
“进入创新模式”
“进入计划模式”
“进入执行模式”
“进入审核模式”
如果没有这些确切的信号,请保持当前模式。
-105
View File
@@ -1,105 +0,0 @@
# Role Definition
- You are a **Python master**, a highly experienced **tutor**, a **world-renowned ML engineer**, and a **talented data scientist**.
- You possess exceptional coding skills and a deep understanding of Python's best practices, design patterns, and idioms.
- You are adept at identifying and preventing potential errors, and you prioritize writing efficient and maintainable code.
- You are skilled in explaining complex concepts in a clear and concise manner, making you an effective mentor and educator.
- You are recognized for your contributions to the field of machine learning and have a strong track record of developing and deploying successful ML models.
- As a talented data scientist, you excel at data analysis, visualization, and deriving actionable insights from complex datasets.
# Technology Stack
- **Python Version:** Python 3.10+
- **Dependency Management:** Poetry / Rye
- **Code Formatting:** Ruff (replaces `black`, `isort`, `flake8`)
- **Type Hinting:** Strictly use the `typing` module. All functions, methods, and class members must have type annotations.
- **Testing Framework:** `pytest`
- **Documentation:** Google style docstring
- **Environment Management:** `conda` / `venv`
- **Containerization:** `docker`, `docker-compose`
- **Asynchronous Programming:** Prefer `async` and `await`
- **Web Framework:** `fastapi`
- **Demo Framework:** `gradio`, `streamlit`
- **LLM Framework:** `langchain`, `transformers`
- **Vector Database:** `faiss`, `chroma` (optional)
- **Experiment Tracking:** `mlflow`, `tensorboard` (optional)
- **Hyperparameter Optimization:** `optuna`, `hyperopt` (optional)
- **Data Processing:** `pandas`, `numpy`, `dask` (optional), `pyspark` (optional)
- **Version Control:** `git`
- **Server:** `gunicorn`, `uvicorn` (with `nginx` or `caddy`)
- **Process Management:** `systemd`, `supervisor`
# Coding Guidelines
## 1. Pythonic Practices
- **Elegance and Readability:** Strive for elegant and Pythonic code that is easy to understand and maintain.
- **PEP 8 Compliance:** Adhere to PEP 8 guidelines for code style, with Ruff as the primary linter and formatter.
- **Explicit over Implicit:** Favor explicit code that clearly communicates its intent over implicit, overly concise code.
- **Zen of Python:** Keep the Zen of Python in mind when making design decisions.
## 2. Modular Design
- **Single Responsibility Principle:** Each module/file should have a well-defined, single responsibility.
- **Reusable Components:** Develop reusable functions and classes, favoring composition over inheritance.
- **Package Structure:** Organize code into logical packages and modules.
## 3. Code Quality
- **Comprehensive Type Annotations:** All functions, methods, and class members must have type annotations, using the most specific types possible.
- **Detailed Docstrings:** All functions, methods, and classes must have Google-style docstrings, thoroughly explaining their purpose, parameters, return values, and any exceptions raised. Include usage examples where helpful.
- **Thorough Unit Testing:** Aim for high test coverage (90% or higher) using `pytest`. Test both common cases and edge cases.
- **Robust Exception Handling:** Use specific exception types, provide informative error messages, and handle exceptions gracefully. Implement custom exception classes when needed. Avoid bare `except` clauses.
- **Logging:** Employ the `logging` module judiciously to log important events, warnings, and errors.
## 4. ML/AI Specific Guidelines
- **Experiment Configuration:** Use `hydra` or `yaml` for clear and reproducible experiment configurations.
- **Data Pipeline Management:** Employ scripts or tools like `dvc` to manage data preprocessing and ensure reproducibility.
- **Model Versioning:** Utilize `git-lfs` or cloud storage to track and manage model checkpoints effectively.
- **Experiment Logging:** Maintain comprehensive logs of experiments, including parameters, results, and environmental details.
- **LLM Prompt Engineering:** Dedicate a module or files for managing Prompt templates with version control.
- **Context Handling:** Implement efficient context management for conversations, using suitable data structures like deques.
## 5. Performance Optimization
- **Asynchronous Programming:** Leverage `async` and `await` for I/O-bound operations to maximize concurrency.
- **Caching:** Apply `functools.lru_cache`, `@cache` (Python 3.9+), or `fastapi.Depends` caching where appropriate.
- **Resource Monitoring:** Use `psutil` or similar to monitor resource usage and identify bottlenecks.
- **Memory Efficiency:** Ensure proper release of unused resources to prevent memory leaks.
- **Concurrency:** Employ `concurrent.futures` or `asyncio` to manage concurrent tasks effectively.
- **Database Best Practices:** Design database schemas efficiently, optimize queries, and use indexes wisely.
## 6. API Development with FastAPI
- **Data Validation:** Use Pydantic models for rigorous request and response data validation.
- **Dependency Injection:** Effectively use FastAPI's dependency injection for managing dependencies.
- **Routing:** Define clear and RESTful API routes using FastAPI's `APIRouter`.
- **Background Tasks:** Utilize FastAPI's `BackgroundTasks` or integrate with Celery for background processing.
- **Security:** Implement robust authentication and authorization (e.g., OAuth 2.0, JWT).
- **Documentation:** Auto-generate API documentation using FastAPI's OpenAPI support.
- **Versioning:** Plan for API versioning from the start (e.g., using URL prefixes or headers).
- **CORS:** Configure Cross-Origin Resource Sharing (CORS) settings correctly.
# Code Example Requirements
- All functions must include type annotations.
- Must provide clear, Google-style docstrings.
- Key logic should be annotated with comments.
- Provide usage examples (e.g., in the `tests/` directory or as a `__main__` section).
- Include error handling.
- Use `ruff` for code formatting.
# Others
- **Prioritize new features in Python 3.10+.**
- **When explaining code, provide clear logical explanations and code comments.**
- **When making suggestions, explain the rationale and potential trade-offs.**
- **If code examples span multiple files, clearly indicate the file name.**
- **Do not over-engineer solutions. Strive for simplicity and maintainability while still being efficient.**
- **Favor modularity, but avoid over-modularization.**
- **Use the most modern and efficient libraries when appropriate, but justify their use and ensure they don't add unnecessary complexity.**
- **When providing solutions or examples, ensure they are self-contained and executable without requiring extensive modifications.**
- **If a request is unclear or lacks sufficient information, ask clarifying questions before proceeding.**
- **Always consider the security implications of your code, especially when dealing with user inputs and external data.**
- **Actively use and promote best practices for the specific tasks at hand (LLM app development, data cleaning, demo creation, etc.).**
Generated
+125 -21
View File
@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. # This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
[[package]] [[package]]
name = "aiohappyeyeballs" name = "aiohappyeyeballs"
@@ -6,6 +6,7 @@ version = "2.6.1"
description = "Happy Eyeballs for asyncio" description = "Happy Eyeballs for asyncio"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"},
{file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"},
@@ -22,6 +23,7 @@ version = "3.11.18"
description = "Async http client/server framework (asyncio)" description = "Async http client/server framework (asyncio)"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "aiohttp-3.11.18-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96264854fedbea933a9ca4b7e0c745728f01380691687b7365d18d9e977179c4"}, {file = "aiohttp-3.11.18-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96264854fedbea933a9ca4b7e0c745728f01380691687b7365d18d9e977179c4"},
{file = "aiohttp-3.11.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9602044ff047043430452bc3a2089743fa85da829e6fc9ee0025351d66c332b6"}, {file = "aiohttp-3.11.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9602044ff047043430452bc3a2089743fa85da829e6fc9ee0025351d66c332b6"},
@@ -116,7 +118,7 @@ propcache = ">=0.2.0"
yarl = ">=1.17.0,<2.0" yarl = ">=1.17.0,<2.0"
[package.extras] [package.extras]
speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""]
[package.source] [package.source]
type = "legacy" type = "legacy"
@@ -129,6 +131,7 @@ version = "1.3.2"
description = "aiosignal: a list of registered asynchronous callbacks" description = "aiosignal: a list of registered asynchronous callbacks"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"},
{file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"},
@@ -148,6 +151,7 @@ version = "0.7.0"
description = "Reusable constraint types to use with typing.Annotated" description = "Reusable constraint types to use with typing.Annotated"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
@@ -164,6 +168,7 @@ version = "4.9.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations" description = "High level compatibility layer for multiple asynchronous event loop implementations"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"}, {file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"},
{file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"}, {file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"},
@@ -176,7 +181,7 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""}
[package.extras] [package.extras]
doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"]
test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""]
trio = ["trio (>=0.26.1)"] trio = ["trio (>=0.26.1)"]
[package.source] [package.source]
@@ -190,18 +195,19 @@ version = "25.3.0"
description = "Classes Without Boilerplate" description = "Classes Without Boilerplate"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"},
{file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"},
] ]
[package.extras] [package.extras]
benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"]
tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""]
[package.source] [package.source]
type = "legacy" type = "legacy"
@@ -214,6 +220,7 @@ version = "4.13.4"
description = "Screen-scraping library" description = "Screen-scraping library"
optional = false optional = false
python-versions = ">=3.7.0" python-versions = ">=3.7.0"
groups = ["main"]
files = [ files = [
{file = "beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"}, {file = "beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"},
{file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"}, {file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"},
@@ -241,6 +248,7 @@ version = "1.9.0"
description = "Fast, simple object-to-object and broadcast signaling" description = "Fast, simple object-to-object and broadcast signaling"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"},
{file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"},
@@ -257,6 +265,7 @@ version = "0.0.2"
description = "Dummy package for Beautiful Soup (beautifulsoup4)" description = "Dummy package for Beautiful Soup (beautifulsoup4)"
optional = false optional = false
python-versions = "*" python-versions = "*"
groups = ["main"]
files = [ files = [
{file = "bs4-0.0.2-py2.py3-none-any.whl", hash = "sha256:abf8742c0805ef7f662dce4b51cca104cffe52b835238afc169142ab9b3fbccc"}, {file = "bs4-0.0.2-py2.py3-none-any.whl", hash = "sha256:abf8742c0805ef7f662dce4b51cca104cffe52b835238afc169142ab9b3fbccc"},
{file = "bs4-0.0.2.tar.gz", hash = "sha256:a48685c58f50fe127722417bae83fe6badf500d54b55f7e39ffe43b798653925"}, {file = "bs4-0.0.2.tar.gz", hash = "sha256:a48685c58f50fe127722417bae83fe6badf500d54b55f7e39ffe43b798653925"},
@@ -276,6 +285,7 @@ version = "2025.4.26"
description = "Python package for providing Mozilla's CA Bundle." description = "Python package for providing Mozilla's CA Bundle."
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
groups = ["main"]
files = [ files = [
{file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"}, {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"},
{file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"}, {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"},
@@ -292,6 +302,8 @@ version = "1.17.1"
description = "Foreign Function Interface for Python calling C code." description = "Foreign Function Interface for Python calling C code."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
markers = "(sys_platform == \"win32\" or platform_python_implementation == \"PyPy\") and (platform_python_implementation == \"CPython\" or platform_python_implementation == \"PyPy\")"
files = [ files = [
{file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
{file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
@@ -376,6 +388,7 @@ version = "3.4.2"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
groups = ["main"]
files = [ files = [
{file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"},
{file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"},
@@ -482,6 +495,7 @@ version = "8.2.1"
description = "Composable command line interface toolkit" description = "Composable command line interface toolkit"
optional = false optional = false
python-versions = ">=3.10" python-versions = ">=3.10"
groups = ["main"]
files = [ files = [
{file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"},
{file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"},
@@ -501,6 +515,8 @@ version = "0.4.6"
description = "Cross-platform colored terminal text." description = "Cross-platform colored terminal text."
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
groups = ["main"]
markers = "platform_system == \"Windows\""
files = [ files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
@@ -517,6 +533,7 @@ version = "0.6.7"
description = "Easily serialize dataclasses to and from JSON." description = "Easily serialize dataclasses to and from JSON."
optional = false optional = false
python-versions = ">=3.7,<4.0" python-versions = ">=3.7,<4.0"
groups = ["main"]
files = [ files = [
{file = "dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a"}, {file = "dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a"},
{file = "dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0"}, {file = "dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0"},
@@ -537,6 +554,7 @@ version = "1.9.0"
description = "Distro - an OS platform information API" description = "Distro - an OS platform information API"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
groups = ["main"]
files = [ files = [
{file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"},
{file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"},
@@ -553,6 +571,7 @@ version = "2.0.0"
description = "An implementation of lxml.xmlfile for the standard library" description = "An implementation of lxml.xmlfile for the standard library"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa"}, {file = "et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa"},
{file = "et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54"}, {file = "et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54"},
@@ -569,6 +588,7 @@ version = "1.11.0"
description = "A library for efficient similarity search and clustering of dense vectors." description = "A library for efficient similarity search and clustering of dense vectors."
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "faiss_cpu-1.11.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1995119152928c68096b0c1e5816e3ee5b1eebcf615b80370874523be009d0f6"}, {file = "faiss_cpu-1.11.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1995119152928c68096b0c1e5816e3ee5b1eebcf615b80370874523be009d0f6"},
{file = "faiss_cpu-1.11.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:788d7bf24293fdecc1b93f1414ca5cc62ebd5f2fecfcbb1d77f0e0530621c95d"}, {file = "faiss_cpu-1.11.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:788d7bf24293fdecc1b93f1414ca5cc62ebd5f2fecfcbb1d77f0e0530621c95d"},
@@ -613,6 +633,7 @@ version = "3.1.1"
description = "A simple framework for building complex web applications." description = "A simple framework for building complex web applications."
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "flask-3.1.1-py3-none-any.whl", hash = "sha256:07aae2bb5eaf77993ef57e357491839f5fd9f4dc281593a81a9e4d79a24f295c"}, {file = "flask-3.1.1-py3-none-any.whl", hash = "sha256:07aae2bb5eaf77993ef57e357491839f5fd9f4dc281593a81a9e4d79a24f295c"},
{file = "flask-3.1.1.tar.gz", hash = "sha256:284c7b8f2f58cb737f0cf1c30fd7eaf0ccfcde196099d24ecede3fc2005aa59e"}, {file = "flask-3.1.1.tar.gz", hash = "sha256:284c7b8f2f58cb737f0cf1c30fd7eaf0ccfcde196099d24ecede3fc2005aa59e"},
@@ -641,6 +662,7 @@ version = "1.6.0"
description = "A list-like structure which implements collections.abc.MutableSequence" description = "A list-like structure which implements collections.abc.MutableSequence"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "frozenlist-1.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e6e558ea1e47fd6fa8ac9ccdad403e5dd5ecc6ed8dda94343056fa4277d5c65e"}, {file = "frozenlist-1.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e6e558ea1e47fd6fa8ac9ccdad403e5dd5ecc6ed8dda94343056fa4277d5c65e"},
{file = "frozenlist-1.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4b3cd7334a4bbc0c472164f3744562cb72d05002cc6fcf58adb104630bbc352"}, {file = "frozenlist-1.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4b3cd7334a4bbc0c472164f3744562cb72d05002cc6fcf58adb104630bbc352"},
@@ -759,6 +781,7 @@ version = "25.5.1"
description = "Coroutine-based network library" description = "Coroutine-based network library"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "gevent-25.5.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8e5a0fab5e245b15ec1005b3666b0a2e867c26f411c8fe66ae1afe07174a30e9"}, {file = "gevent-25.5.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8e5a0fab5e245b15ec1005b3666b0a2e867c26f411c8fe66ae1afe07174a30e9"},
{file = "gevent-25.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7b80a37f2fb45ee4a8f7e64b77dd8a842d364384046e394227b974a4e9c9a52"}, {file = "gevent-25.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7b80a37f2fb45ee4a8f7e64b77dd8a842d364384046e394227b974a4e9c9a52"},
@@ -808,11 +831,11 @@ greenlet = {version = ">=3.2.2", markers = "platform_python_implementation == \"
"zope.interface" = "*" "zope.interface" = "*"
[package.extras] [package.extras]
dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] dnspython = ["dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\""]
docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"]
monitor = ["psutil (>=5.7.0)"] monitor = ["psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\""]
recommended = ["cffi (>=1.17.1)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] recommended = ["cffi (>=1.17.1) ; platform_python_implementation == \"CPython\"", "dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\"", "psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\""]
test = ["cffi (>=1.17.1)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] test = ["cffi (>=1.17.1) ; platform_python_implementation == \"CPython\"", "coverage (>=5.0) ; sys_platform != \"win32\"", "dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\"", "objgraph", "psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\"", "requests"]
[package.source] [package.source]
type = "legacy" type = "legacy"
@@ -825,6 +848,8 @@ version = "3.2.2"
description = "Lightweight in-process concurrent programming" description = "Lightweight in-process concurrent programming"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_python_implementation == \"CPython\""
files = [ files = [
{file = "greenlet-3.2.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:c49e9f7c6f625507ed83a7485366b46cbe325717c60837f7244fc99ba16ba9d6"}, {file = "greenlet-3.2.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:c49e9f7c6f625507ed83a7485366b46cbe325717c60837f7244fc99ba16ba9d6"},
{file = "greenlet-3.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3cc1a3ed00ecfea8932477f729a9f616ad7347a5e55d50929efa50a86cb7be7"}, {file = "greenlet-3.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3cc1a3ed00ecfea8932477f729a9f616ad7347a5e55d50929efa50a86cb7be7"},
@@ -898,6 +923,7 @@ version = "23.0.0"
description = "WSGI HTTP Server for UNIX" description = "WSGI HTTP Server for UNIX"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
groups = ["main"]
files = [ files = [
{file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"},
{file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"},
@@ -924,6 +950,7 @@ version = "0.16.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"},
{file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"},
@@ -940,6 +967,7 @@ version = "1.0.9"
description = "A minimal low-level HTTP client." description = "A minimal low-level HTTP client."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"},
{file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"},
@@ -966,6 +994,7 @@ version = "0.28.1"
description = "The next generation HTTP client." description = "The next generation HTTP client."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"},
{file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"},
@@ -978,7 +1007,7 @@ httpcore = "==1.*"
idna = "*" idna = "*"
[package.extras] [package.extras]
brotli = ["brotli", "brotlicffi"] brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""]
cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
http2 = ["h2 (>=3,<5)"] http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"] socks = ["socksio (==1.*)"]
@@ -995,6 +1024,7 @@ version = "0.4.0"
description = "Consume Server-Sent Event (SSE) messages with HTTPX." description = "Consume Server-Sent Event (SSE) messages with HTTPX."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721"}, {file = "httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721"},
{file = "httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f"}, {file = "httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f"},
@@ -1011,6 +1041,7 @@ version = "3.10"
description = "Internationalized Domain Names in Applications (IDNA)" description = "Internationalized Domain Names in Applications (IDNA)"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
groups = ["main"]
files = [ files = [
{file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
{file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
@@ -1030,6 +1061,7 @@ version = "2.2.0"
description = "Safely pass data to untrusted environments and back." description = "Safely pass data to untrusted environments and back."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"},
{file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"},
@@ -1040,12 +1072,29 @@ type = "legacy"
url = "http://mirrors.aliyun.com/pypi/simple" url = "http://mirrors.aliyun.com/pypi/simple"
reference = "ali-mirrors" reference = "ali-mirrors"
[[package]]
name = "jieba"
version = "0.42.1"
description = "Chinese Words Segmentation Utilities"
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "jieba-0.42.1.tar.gz", hash = "sha256:055ca12f62674fafed09427f176506079bc135638a14e23e25be909131928db2"},
]
[package.source]
type = "legacy"
url = "http://mirrors.aliyun.com/pypi/simple"
reference = "ali-mirrors"
[[package]] [[package]]
name = "jinja2" name = "jinja2"
version = "3.1.6" version = "3.1.6"
description = "A very fast and expressive template engine." description = "A very fast and expressive template engine."
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
groups = ["main"]
files = [ files = [
{file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
{file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
@@ -1068,6 +1117,7 @@ version = "0.9.0"
description = "Fast iterable JSON parser." description = "Fast iterable JSON parser."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "jiter-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:816ec9b60fdfd1fec87da1d7ed46c66c44ffec37ab2ef7de5b147b2fce3fd5ad"}, {file = "jiter-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:816ec9b60fdfd1fec87da1d7ed46c66c44ffec37ab2ef7de5b147b2fce3fd5ad"},
{file = "jiter-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b1d3086f8a3ee0194ecf2008cf81286a5c3e540d977fa038ff23576c023c0ea"}, {file = "jiter-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b1d3086f8a3ee0194ecf2008cf81286a5c3e540d977fa038ff23576c023c0ea"},
@@ -1158,6 +1208,7 @@ version = "1.33"
description = "Apply JSON-Patches (RFC 6902) " description = "Apply JSON-Patches (RFC 6902) "
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*"
groups = ["main"]
files = [ files = [
{file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"}, {file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"},
{file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"}, {file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"},
@@ -1177,6 +1228,7 @@ version = "3.0.0"
description = "Identify specific nodes in a JSON document (RFC 6901) " description = "Identify specific nodes in a JSON document (RFC 6901) "
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
groups = ["main"]
files = [ files = [
{file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"},
{file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"},
@@ -1193,6 +1245,7 @@ version = "0.3.25"
description = "Building applications with LLMs through composability" description = "Building applications with LLMs through composability"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "langchain-0.3.25-py3-none-any.whl", hash = "sha256:931f7d2d1eaf182f9f41c5e3272859cfe7f94fc1f7cef6b3e5a46024b4884c21"}, {file = "langchain-0.3.25-py3-none-any.whl", hash = "sha256:931f7d2d1eaf182f9f41c5e3272859cfe7f94fc1f7cef6b3e5a46024b4884c21"},
{file = "langchain-0.3.25.tar.gz", hash = "sha256:a1d72aa39546a23db08492d7228464af35c9ee83379945535ceef877340d2a3a"}, {file = "langchain-0.3.25.tar.gz", hash = "sha256:a1d72aa39546a23db08492d7228464af35c9ee83379945535ceef877340d2a3a"},
@@ -1237,6 +1290,7 @@ version = "0.3.24"
description = "Community contributed LangChain integrations." description = "Community contributed LangChain integrations."
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "langchain_community-0.3.24-py3-none-any.whl", hash = "sha256:b6cdb376bf1c2f4d2503aca20f8f35f2d5b3d879c52848277f20ce1950e7afaf"}, {file = "langchain_community-0.3.24-py3-none-any.whl", hash = "sha256:b6cdb376bf1c2f4d2503aca20f8f35f2d5b3d879c52848277f20ce1950e7afaf"},
{file = "langchain_community-0.3.24.tar.gz", hash = "sha256:62d9e8cf9aadf35182ec3925f9ec1c8e5e84fb4f199f67a01aee496d289dc264"}, {file = "langchain_community-0.3.24.tar.gz", hash = "sha256:62d9e8cf9aadf35182ec3925f9ec1c8e5e84fb4f199f67a01aee496d289dc264"},
@@ -1267,6 +1321,7 @@ version = "0.3.59"
description = "Building applications with LLMs through composability" description = "Building applications with LLMs through composability"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "langchain_core-0.3.59-py3-none-any.whl", hash = "sha256:9686baaff43f2c8175535da13faf40e6866769015e93130c3c1e4243e7244d70"}, {file = "langchain_core-0.3.59-py3-none-any.whl", hash = "sha256:9686baaff43f2c8175535da13faf40e6866769015e93130c3c1e4243e7244d70"},
{file = "langchain_core-0.3.59.tar.gz", hash = "sha256:052a37cf298c505144f007e5aeede6ecff2dc92c827525d1ef59101eb3a4551c"}, {file = "langchain_core-0.3.59.tar.gz", hash = "sha256:052a37cf298c505144f007e5aeede6ecff2dc92c827525d1ef59101eb3a4551c"},
@@ -1295,6 +1350,7 @@ version = "0.3.16"
description = "An integration package connecting OpenAI and LangChain" description = "An integration package connecting OpenAI and LangChain"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "langchain_openai-0.3.16-py3-none-any.whl", hash = "sha256:eae74a6758d38a26159c5fde5abf8ef313e6400efb01a08f12dd7410c9f4fd0f"}, {file = "langchain_openai-0.3.16-py3-none-any.whl", hash = "sha256:eae74a6758d38a26159c5fde5abf8ef313e6400efb01a08f12dd7410c9f4fd0f"},
{file = "langchain_openai-0.3.16.tar.gz", hash = "sha256:4e423e39d072f1432adc9430f2905fe635cc019f01ad1bdffa5ed8d0dda32149"}, {file = "langchain_openai-0.3.16.tar.gz", hash = "sha256:4e423e39d072f1432adc9430f2905fe635cc019f01ad1bdffa5ed8d0dda32149"},
@@ -1316,6 +1372,7 @@ version = "0.3.8"
description = "LangChain text splitting utilities" description = "LangChain text splitting utilities"
optional = false optional = false
python-versions = "<4.0,>=3.9" python-versions = "<4.0,>=3.9"
groups = ["main"]
files = [ files = [
{file = "langchain_text_splitters-0.3.8-py3-none-any.whl", hash = "sha256:e75cc0f4ae58dcf07d9f18776400cf8ade27fadd4ff6d264df6278bb302f6f02"}, {file = "langchain_text_splitters-0.3.8-py3-none-any.whl", hash = "sha256:e75cc0f4ae58dcf07d9f18776400cf8ade27fadd4ff6d264df6278bb302f6f02"},
{file = "langchain_text_splitters-0.3.8.tar.gz", hash = "sha256:116d4b9f2a22dda357d0b79e30acf005c5518177971c66a9f1ab0edfdb0f912e"}, {file = "langchain_text_splitters-0.3.8.tar.gz", hash = "sha256:116d4b9f2a22dda357d0b79e30acf005c5518177971c66a9f1ab0edfdb0f912e"},
@@ -1335,6 +1392,7 @@ version = "0.3.42"
description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform."
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "langsmith-0.3.42-py3-none-any.whl", hash = "sha256:18114327f3364385dae4026ebfd57d1c1cb46d8f80931098f0f10abe533475ff"}, {file = "langsmith-0.3.42-py3-none-any.whl", hash = "sha256:18114327f3364385dae4026ebfd57d1c1cb46d8f80931098f0f10abe533475ff"},
{file = "langsmith-0.3.42.tar.gz", hash = "sha256:2b5cbc450ab808b992362aac6943bb1d285579aa68a3a8be901d30a393458f25"}, {file = "langsmith-0.3.42.tar.gz", hash = "sha256:2b5cbc450ab808b992362aac6943bb1d285579aa68a3a8be901d30a393458f25"},
@@ -1369,6 +1427,7 @@ version = "0.13.1"
description = "Convert HTML to markdown." description = "Convert HTML to markdown."
optional = false optional = false
python-versions = "*" python-versions = "*"
groups = ["main"]
files = [ files = [
{file = "markdownify-0.13.1-py3-none-any.whl", hash = "sha256:1d181d43d20902bcc69d7be85b5316ed174d0dda72ff56e14ae4c95a4a407d22"}, {file = "markdownify-0.13.1-py3-none-any.whl", hash = "sha256:1d181d43d20902bcc69d7be85b5316ed174d0dda72ff56e14ae4c95a4a407d22"},
{file = "markdownify-0.13.1.tar.gz", hash = "sha256:ab257f9e6bd4075118828a28c9d02f8a4bfeb7421f558834aa79b2dfeb32a098"}, {file = "markdownify-0.13.1.tar.gz", hash = "sha256:ab257f9e6bd4075118828a28c9d02f8a4bfeb7421f558834aa79b2dfeb32a098"},
@@ -1389,6 +1448,7 @@ version = "3.0.2"
description = "Safely add untrusted strings to HTML/XML markup." description = "Safely add untrusted strings to HTML/XML markup."
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
@@ -1464,6 +1524,7 @@ version = "3.26.1"
description = "A lightweight library for converting complex datatypes to and from native Python datatypes." description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"}, {file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"},
{file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"}, {file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"},
@@ -1488,6 +1549,7 @@ version = "6.4.3"
description = "multidict implementation" description = "multidict implementation"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "multidict-6.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32a998bd8a64ca48616eac5a8c1cc4fa38fb244a3facf2eeb14abe186e0f6cc5"}, {file = "multidict-6.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32a998bd8a64ca48616eac5a8c1cc4fa38fb244a3facf2eeb14abe186e0f6cc5"},
{file = "multidict-6.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a54ec568f1fc7f3c313c2f3b16e5db346bf3660e1309746e7fccbbfded856188"}, {file = "multidict-6.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a54ec568f1fc7f3c313c2f3b16e5db346bf3660e1309746e7fccbbfded856188"},
@@ -1606,6 +1668,7 @@ version = "1.1.0"
description = "Type system extensions for programs checked with the mypy type checker." description = "Type system extensions for programs checked with the mypy type checker."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"},
{file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
@@ -1622,6 +1685,7 @@ version = "1.26.4"
description = "Fundamental package for array computing in Python" description = "Fundamental package for array computing in Python"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"},
{file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"},
@@ -1672,6 +1736,7 @@ version = "1.78.1"
description = "The official Python library for the openai API" description = "The official Python library for the openai API"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "openai-1.78.1-py3-none-any.whl", hash = "sha256:7368bf147ca499804cc408fe68cdb6866a060f38dec961bbc97b04f9d917907e"}, {file = "openai-1.78.1-py3-none-any.whl", hash = "sha256:7368bf147ca499804cc408fe68cdb6866a060f38dec961bbc97b04f9d917907e"},
{file = "openai-1.78.1.tar.gz", hash = "sha256:8b26b364531b100df1b961d03560042e5f5be11301d7d49a6cd1a2b9af824dca"}, {file = "openai-1.78.1.tar.gz", hash = "sha256:8b26b364531b100df1b961d03560042e5f5be11301d7d49a6cd1a2b9af824dca"},
@@ -1703,6 +1768,7 @@ version = "3.1.5"
description = "A Python library to read/write Excel 2010 xlsx/xlsm files" description = "A Python library to read/write Excel 2010 xlsx/xlsm files"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2"}, {file = "openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2"},
{file = "openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050"}, {file = "openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050"},
@@ -1722,6 +1788,8 @@ version = "3.10.18"
description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
markers = "platform_python_implementation != \"PyPy\""
files = [ files = [
{file = "orjson-3.10.18-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a45e5d68066b408e4bc383b6e4ef05e717c65219a9e1390abc6155a520cac402"}, {file = "orjson-3.10.18-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a45e5d68066b408e4bc383b6e4ef05e717c65219a9e1390abc6155a520cac402"},
{file = "orjson-3.10.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be3b9b143e8b9db05368b13b04c84d37544ec85bb97237b3a923f076265ec89c"}, {file = "orjson-3.10.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be3b9b143e8b9db05368b13b04c84d37544ec85bb97237b3a923f076265ec89c"},
@@ -1808,6 +1876,7 @@ version = "24.2"
description = "Core utilities for Python packages" description = "Core utilities for Python packages"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
{file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
@@ -1824,6 +1893,7 @@ version = "2.2.3"
description = "Powerful data structures for data analysis, time series, and statistics" description = "Powerful data structures for data analysis, time series, and statistics"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"},
{file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"},
@@ -1914,6 +1984,7 @@ version = "0.3.1"
description = "Accelerated property cache" description = "Accelerated property cache"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "propcache-0.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f27785888d2fdd918bc36de8b8739f2d6c791399552333721b58193f68ea3e98"}, {file = "propcache-0.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f27785888d2fdd918bc36de8b8739f2d6c791399552333721b58193f68ea3e98"},
{file = "propcache-0.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4e89cde74154c7b5957f87a355bb9c8ec929c167b59c83d90654ea36aeb6180"}, {file = "propcache-0.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4e89cde74154c7b5957f87a355bb9c8ec929c167b59c83d90654ea36aeb6180"},
@@ -2026,6 +2097,7 @@ version = "2.9.10"
description = "psycopg2 - Python-PostgreSQL Database Adapter" description = "psycopg2 - Python-PostgreSQL Database Adapter"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "psycopg2-2.9.10-cp310-cp310-win32.whl", hash = "sha256:5df2b672140f95adb453af93a7d669d7a7bf0a56bcd26f1502329166f4a61716"}, {file = "psycopg2-2.9.10-cp310-cp310-win32.whl", hash = "sha256:5df2b672140f95adb453af93a7d669d7a7bf0a56bcd26f1502329166f4a61716"},
{file = "psycopg2-2.9.10-cp310-cp310-win_amd64.whl", hash = "sha256:c6f7b8561225f9e711a9c47087388a97fdc948211c10a4bccbf0ba68ab7b3b5a"}, {file = "psycopg2-2.9.10-cp310-cp310-win_amd64.whl", hash = "sha256:c6f7b8561225f9e711a9c47087388a97fdc948211c10a4bccbf0ba68ab7b3b5a"},
@@ -2050,6 +2122,8 @@ version = "2.22"
description = "C parser in Python" description = "C parser in Python"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
markers = "(sys_platform == \"win32\" or platform_python_implementation == \"PyPy\") and (platform_python_implementation == \"CPython\" or platform_python_implementation == \"PyPy\")"
files = [ files = [
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
@@ -2066,6 +2140,7 @@ version = "2.11.4"
description = "Data validation using Python type hints" description = "Data validation using Python type hints"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb"}, {file = "pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb"},
{file = "pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d"}, {file = "pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d"},
@@ -2079,7 +2154,7 @@ typing-inspection = ">=0.4.0"
[package.extras] [package.extras]
email = ["email-validator (>=2.0.0)"] email = ["email-validator (>=2.0.0)"]
timezone = ["tzdata"] timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""]
[package.source] [package.source]
type = "legacy" type = "legacy"
@@ -2092,6 +2167,7 @@ version = "2.33.2"
description = "Core functionality for Pydantic validation and serialization" description = "Core functionality for Pydantic validation and serialization"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"},
{file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"},
@@ -2208,6 +2284,7 @@ version = "2.9.1"
description = "Settings management using Pydantic" description = "Settings management using Pydantic"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef"}, {file = "pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef"},
{file = "pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268"}, {file = "pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268"},
@@ -2236,6 +2313,7 @@ version = "1.1.1"
description = "Pure Python MySQL Driver" description = "Pure Python MySQL Driver"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
groups = ["main"]
files = [ files = [
{file = "PyMySQL-1.1.1-py3-none-any.whl", hash = "sha256:4de15da4c61dc132f4fb9ab763063e693d521a80fd0e87943b9a453dd4c19d6c"}, {file = "PyMySQL-1.1.1-py3-none-any.whl", hash = "sha256:4de15da4c61dc132f4fb9ab763063e693d521a80fd0e87943b9a453dd4c19d6c"},
{file = "pymysql-1.1.1.tar.gz", hash = "sha256:e127611aaf2b417403c60bf4dc570124aeb4a57f5f37b8e95ae399a42f904cd0"}, {file = "pymysql-1.1.1.tar.gz", hash = "sha256:e127611aaf2b417403c60bf4dc570124aeb4a57f5f37b8e95ae399a42f904cd0"},
@@ -2256,6 +2334,7 @@ version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module" description = "Extensions to the standard Python datetime module"
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
groups = ["main"]
files = [ files = [
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
@@ -2275,6 +2354,7 @@ version = "1.1.0"
description = "Read key-value pairs from a .env file and set them as environment variables" description = "Read key-value pairs from a .env file and set them as environment variables"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d"}, {file = "python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d"},
{file = "python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5"}, {file = "python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5"},
@@ -2294,6 +2374,7 @@ version = "2025.2"
description = "World timezone definitions, modern and historical" description = "World timezone definitions, modern and historical"
optional = false optional = false
python-versions = "*" python-versions = "*"
groups = ["main"]
files = [ files = [
{file = "pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"}, {file = "pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"},
{file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"}, {file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"},
@@ -2310,6 +2391,7 @@ version = "6.0.2"
description = "YAML parser and emitter for Python" description = "YAML parser and emitter for Python"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
@@ -2377,6 +2459,7 @@ version = "2024.11.6"
description = "Alternative regular expression module, to replace re." description = "Alternative regular expression module, to replace re."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"},
{file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"},
@@ -2485,6 +2568,7 @@ version = "2.32.3"
description = "Python HTTP for Humans." description = "Python HTTP for Humans."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
{file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
@@ -2511,6 +2595,7 @@ version = "1.0.0"
description = "A utility belt for advanced users of python-requests" description = "A utility belt for advanced users of python-requests"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
groups = ["main"]
files = [ files = [
{file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"},
{file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"},
@@ -2530,19 +2615,20 @@ version = "80.9.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages" description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"},
{file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"},
] ]
[package.extras] [package.extras]
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""]
core = ["importlib_metadata (>=6)", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"]
cover = ["pytest-cov"] cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
enabler = ["pytest-enabler (>=2.2)"] enabler = ["pytest-enabler (>=2.2)"]
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"]
[package.source] [package.source]
type = "legacy" type = "legacy"
@@ -2555,6 +2641,7 @@ version = "1.17.0"
description = "Python 2 and 3 compatibility utilities" description = "Python 2 and 3 compatibility utilities"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
groups = ["main"]
files = [ files = [
{file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
@@ -2571,6 +2658,7 @@ version = "1.3.1"
description = "Sniff out which async library your code is running under" description = "Sniff out which async library your code is running under"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
groups = ["main"]
files = [ files = [
{file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
@@ -2587,6 +2675,7 @@ version = "2.7"
description = "A modern CSS selector implementation for Beautiful Soup." description = "A modern CSS selector implementation for Beautiful Soup."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"}, {file = "soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"},
{file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}, {file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"},
@@ -2603,6 +2692,7 @@ version = "2.0.41"
description = "Database Abstraction Library" description = "Database Abstraction Library"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
groups = ["main"]
files = [ files = [
{file = "SQLAlchemy-2.0.41-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6854175807af57bdb6425e47adbce7d20a4d79bbfd6f6d6519cd10bb7109a7f8"}, {file = "SQLAlchemy-2.0.41-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6854175807af57bdb6425e47adbce7d20a4d79bbfd6f6d6519cd10bb7109a7f8"},
{file = "SQLAlchemy-2.0.41-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05132c906066142103b83d9c250b60508af556982a385d96c4eaa9fb9720ac2b"}, {file = "SQLAlchemy-2.0.41-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05132c906066142103b83d9c250b60508af556982a385d96c4eaa9fb9720ac2b"},
@@ -2703,6 +2793,7 @@ version = "9.1.2"
description = "Retry code until it succeeds" description = "Retry code until it succeeds"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138"}, {file = "tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138"},
{file = "tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb"}, {file = "tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb"},
@@ -2723,6 +2814,7 @@ version = "0.9.0"
description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "tiktoken-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:586c16358138b96ea804c034b8acf3f5d3f0258bd2bc3b0227af4af5d622e382"}, {file = "tiktoken-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:586c16358138b96ea804c034b8acf3f5d3f0258bd2bc3b0227af4af5d622e382"},
{file = "tiktoken-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9c59ccc528c6c5dd51820b3474402f69d9a9e1d656226848ad68a8d5b2e5108"}, {file = "tiktoken-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9c59ccc528c6c5dd51820b3474402f69d9a9e1d656226848ad68a8d5b2e5108"},
@@ -2775,6 +2867,7 @@ version = "4.67.1"
description = "Fast, Extensible Progress Meter" description = "Fast, Extensible Progress Meter"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
groups = ["main"]
files = [ files = [
{file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"},
{file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"},
@@ -2801,6 +2894,7 @@ version = "4.13.2"
description = "Backported and Experimental Type Hints for Python 3.8+" description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"},
{file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"},
@@ -2817,6 +2911,7 @@ version = "0.9.0"
description = "Runtime inspection utilities for typing module." description = "Runtime inspection utilities for typing module."
optional = false optional = false
python-versions = "*" python-versions = "*"
groups = ["main"]
files = [ files = [
{file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"},
{file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"},
@@ -2837,6 +2932,7 @@ version = "0.4.0"
description = "Runtime typing introspection tools" description = "Runtime typing introspection tools"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f"}, {file = "typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f"},
{file = "typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122"}, {file = "typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122"},
@@ -2856,6 +2952,7 @@ version = "2025.2"
description = "Provider of IANA time zone data" description = "Provider of IANA time zone data"
optional = false optional = false
python-versions = ">=2" python-versions = ">=2"
groups = ["main"]
files = [ files = [
{file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"},
{file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"},
@@ -2872,13 +2969,14 @@ version = "2.4.0"
description = "HTTP library with thread-safe connection pooling, file post, and more." description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"},
{file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"},
] ]
[package.extras] [package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
h2 = ["h2 (>=4,<5)"] h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"] zstd = ["zstandard (>=0.18.0)"]
@@ -2894,6 +2992,7 @@ version = "3.1.3"
description = "The comprehensive WSGI web application library." description = "The comprehensive WSGI web application library."
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"},
{file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"},
@@ -2916,6 +3015,7 @@ version = "3.2.3"
description = "A Python module for creating Excel XLSX files." description = "A Python module for creating Excel XLSX files."
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
groups = ["main"]
files = [ files = [
{file = "XlsxWriter-3.2.3-py3-none-any.whl", hash = "sha256:593f8296e8a91790c6d0378ab08b064f34a642b3feb787cf6738236bd0a4860d"}, {file = "XlsxWriter-3.2.3-py3-none-any.whl", hash = "sha256:593f8296e8a91790c6d0378ab08b064f34a642b3feb787cf6738236bd0a4860d"},
{file = "xlsxwriter-3.2.3.tar.gz", hash = "sha256:ad6fd41bdcf1b885876b1f6b7087560aecc9ae5a9cc2ba97dcac7ab2e210d3d5"}, {file = "xlsxwriter-3.2.3.tar.gz", hash = "sha256:ad6fd41bdcf1b885876b1f6b7087560aecc9ae5a9cc2ba97dcac7ab2e210d3d5"},
@@ -2932,6 +3032,7 @@ version = "1.20.0"
description = "Yet another URL library" description = "Yet another URL library"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"]
files = [ files = [
{file = "yarl-1.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f1f6670b9ae3daedb325fa55fbe31c22c8228f6e0b513772c2e1c623caa6ab22"}, {file = "yarl-1.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f1f6670b9ae3daedb325fa55fbe31c22c8228f6e0b513772c2e1c623caa6ab22"},
{file = "yarl-1.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85a231fa250dfa3308f3c7896cc007a47bc76e9e8e8595c20b7426cac4884c62"}, {file = "yarl-1.20.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85a231fa250dfa3308f3c7896cc007a47bc76e9e8e8595c20b7426cac4884c62"},
@@ -3055,6 +3156,7 @@ version = "5.0"
description = "Very basic event publishing system" description = "Very basic event publishing system"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
groups = ["main"]
files = [ files = [
{file = "zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26"}, {file = "zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26"},
{file = "zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"}, {file = "zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"},
@@ -3078,6 +3180,7 @@ version = "7.2"
description = "Interfaces for Python" description = "Interfaces for Python"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "zope.interface-7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2"}, {file = "zope.interface-7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2"},
{file = "zope.interface-7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a"}, {file = "zope.interface-7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a"},
@@ -3137,6 +3240,7 @@ version = "0.23.0"
description = "Zstandard bindings for Python" description = "Zstandard bindings for Python"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"]
files = [ files = [
{file = "zstandard-0.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf0a05b6059c0528477fba9054d09179beb63744355cab9f38059548fedd46a9"}, {file = "zstandard-0.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf0a05b6059c0528477fba9054d09179beb63744355cab9f38059548fedd46a9"},
{file = "zstandard-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fc9ca1c9718cb3b06634c7c8dec57d24e9438b2aa9a0f02b8bb36bf478538880"}, {file = "zstandard-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fc9ca1c9718cb3b06634c7c8dec57d24e9438b2aa9a0f02b8bb36bf478538880"},
@@ -3249,6 +3353,6 @@ url = "http://mirrors.aliyun.com/pypi/simple"
reference = "ali-mirrors" reference = "ali-mirrors"
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.1"
python-versions = ">=3.11,<3.13" python-versions = ">=3.11,<3.13"
content-hash = "b227dc263d83a0d98c3903fc0bca5763858aaeda6f6c14f83294afddd05679b4" content-hash = "0b608c0456c4f231d1ce887cace758d19553b3c2c1ec423134ab87cec8776f40"
+1
View File
@@ -26,6 +26,7 @@ gunicorn = "^23.0.0"
gevent = "^25.5.1" gevent = "^25.5.1"
pymysql = "^1.1.1" pymysql = "^1.1.1"
sqlalchemy = "^2.0.41" sqlalchemy = "^2.0.41"
jieba = "^0.42.1"
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
+36 -14
View File
@@ -8,6 +8,7 @@ Description: 意图识别和问题改写示例
import os import os
from dotenv import load_dotenv from dotenv import load_dotenv
from regex import F
from rag2_0.intent_recognition import IntentRecognizer from rag2_0.intent_recognition import IntentRecognizer
import pandas as pd import pandas as pd
import logging import logging
@@ -16,6 +17,7 @@ import concurrent.futures
from tqdm import tqdm from tqdm import tqdm
import time import time
import sys import sys
from typing import List, Dict
# 加载环境变量 # 加载环境变量
load_dotenv() load_dotenv()
@@ -42,7 +44,7 @@ def load_questions_from_excel(file_path=None):
logging.error(f"读取Excel文件时出错: {e}") logging.error(f"读取Excel文件时出错: {e}")
return [] return []
def process_query(recognizer, query): def process_query(recognizer: IntentRecognizer, query: str, conversation_context: str = "", chat_history: List[Dict[str, str]] = None, previous_slots: Dict[str, str] = None):
""" """
处理单个查询,支持重试机制,并包含槽位填充 处理单个查询,支持重试机制,并包含槽位填充
@@ -59,8 +61,8 @@ def process_query(recognizer, query):
while retry_count <= max_retries: while retry_count <= max_retries:
try: try:
# 使用新的process_query_with_slots方法处理查询 # 使用新的process_query_with_slots方法处理查询
result = recognizer.process_query_with_slots(query) # result = recognizer.process_query_with_slots(query)
result = recognizer.process_query(query, conversation_context=conversation_context, chat_history=chat_history, previous_slots=previous_slots)
# 提取分类信息 # 提取分类信息
classification = result["classification"] classification = result["classification"]
@@ -101,7 +103,6 @@ def process_query(recognizer, query):
"检索的关键词": keywords_str, "检索的关键词": keywords_str,
"槽位填充": slot_filling_str "槽位填充": slot_filling_str
} }
except Exception as e: except Exception as e:
retry_count += 1 retry_count += 1
@@ -174,10 +175,30 @@ def save_results_to_excel(results, output_file, is_final=False):
logging.info(f"已保存{len(valid_results)}条结果至: {temp_output_file}") logging.info(f"已保存{len(valid_results)}条结果至: {temp_output_file}")
# 示例查询 # 示例查询
examples_query = """"锁标签号:811621005858, 注册单位:惠州电力勘察设计院有限公司,软件名称:广东迁改导则2022, 注册号:BW278-83834-58155-58339.迁改导则是要另外下载安装软件吗?" examples_query = """那西藏软件呢"""
conversation_context=""
""" chat_history=[
{
"user": "怎么新建工程啊"
},
{
"assistant": "您好!为了更准确地帮助您解决问题,请提供以下信息:\n\n- **软件名称**:请确认您使用的软件名称,可选值如下:\n - 配网工程计价通D3软件\n - 新型储能电站建设计价通C1软件\n - 西藏电力工程计价通Z1软件\n - 技改检修工程计价通T1软件\n - 技改检修清单计价通T1软件\n - 主网电力建设计价通软件\n\n请补充以上信息,以便我们能够更好地指导您如何新建工程。"
},
{
"user": "配网工程计价通D3软件"
},
{
"assistant": """好的,针对您使用**配网工程计价通D3软件**新建工程的问题,以下是具体的操作步骤:\n\n## 新建工程\n\n### 功能入口\n【主页】界面——"新建"按钮\n\n### 操作步骤\n1. 在"新建窗口"选择对应工程模板。\n2. 设置工程名称。\n3. 确定后根据新建向导完善"电压等级""地区类型"等参数。\n4. 点击"确定"即可完成新建工程。\n\n![进入新建工程窗口](https://172.20.0.145/files/362491a3-69c3-45b0-a037-b5f96b91bf73/image-preview)\n\n希望这些步骤能帮助您顺利完成新建工程。如果还有其他问题,欢迎随时提问!\n"""
}
]
previous_slots={
"software_name": "配网工程计价通D3软件",
"function_name": "新建工程",
"operation": "如何新建工程",
"project_type": None,
"software_version": None,
"operation_steps": None
}
def main(): def main():
""" """
意图识别和问题改写示例 意图识别和问题改写示例
@@ -193,18 +214,19 @@ def main():
# 读取提问数据 # 读取提问数据
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
data_file = os.path.join(current_dir, "..", "..", "data", "excel", "历史提问数据(dislike)_提问明确.xlsx") data_file = os.path.join(current_dir, "..", "..", "data", "excel", "历史提问数据(like)_提问明确.xlsx")
output_file = os.path.join(current_dir, "..", "..", "data", "excel", "测试提问数据_槽位填充结果.xlsx") output_file = os.path.join(current_dir, "..", "..", "data", "excel", "测试提问数据_槽位填充结果.xlsx")
# 检测是否为调试模式,调试模式下使用examples_query,否则从Excel读取 # 检测是否为调试模式,调试模式下使用examples_query,否则从Excel读取
is_debug = hasattr(sys, 'gettrace') and sys.gettrace() is not None is_debug = hasattr(sys, 'gettrace') and sys.gettrace() is not None
# is_debug = False
if is_debug: if is_debug:
examples = examples_query.strip().split("\n") examples = examples_query.strip().split("\n")
else: else:
examples = load_questions_from_excel(data_file) examples = load_questions_from_excel(data_file)
if not is_debug: if not is_debug:
max_workers = 20 # 减少并发数以避免API限制 max_workers = 40 # 减少并发数以避免API限制
logging.info(f"共有 {len(examples)} 个问题需要处理,使用 {max_workers} 个并发线程") logging.info(f"共有 {len(examples)} 个问题需要处理,使用 {max_workers} 个并发线程")
# 创建一个与输入顺序相同的结果列表 # 创建一个与输入顺序相同的结果列表
@@ -229,9 +251,9 @@ def main():
completed += 1 completed += 1
# 每处理batch_size条数据保存一次 # 每处理batch_size条数据保存一次
if completed % batch_size == 0: # if completed % batch_size == 0:
logging.info(f"已完成 {completed}/{len(examples)} 条,保存中间结果...") # logging.info(f"已完成 {completed}/{len(examples)} 条,保存中间结果...")
save_results_to_excel(results, output_file, is_final=False) # save_results_to_excel(results, output_file, is_final=False)
# 处理完所有数据后,保存最终结果 # 处理完所有数据后,保存最终结果
save_results_to_excel(results, output_file, is_final=True) save_results_to_excel(results, output_file, is_final=True)
@@ -240,7 +262,7 @@ def main():
for idx, query in enumerate(examples): for idx, query in enumerate(examples):
if query.strip() == "": if query.strip() == "":
continue continue
process_query(recognizer, query) process_query(recognizer, query, conversation_context, chat_history, previous_slots)
def setup_logging(): def setup_logging():
# 配置日志输出到控制台 # 配置日志输出到控制台
+6 -3
View File
@@ -36,6 +36,9 @@ def intent_recognize():
try: try:
data = request.get_json(force=True) data = request.get_json(force=True)
query = data.get('query') query = data.get('query')
conversation_context = data.get('conversation_context', "")
chat_history = data.get('chat_history', None)
previous_slots = data.get('previous_slots', None)
if not query: if not query:
return Response(json.dumps({"error": "缺少query参数"}, ensure_ascii=False), content_type='application/json; charset=utf-8', status=400) return Response(json.dumps({"error": "缺少query参数"}, ensure_ascii=False), content_type='application/json; charset=utf-8', status=400)
@@ -43,7 +46,7 @@ def intent_recognize():
# 获取单例实例并使用线程锁保护关键操作 # 获取单例实例并使用线程锁保护关键操作
recognizer = RecognizerSingleton.get_instance() recognizer = RecognizerSingleton.get_instance()
result = recognizer.process_query_with_slots(query) result = recognizer.process_query(query, conversation_context, chat_history, previous_slots)
end_time = time.time() end_time = time.time()
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S %z") current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S %z")
@@ -60,8 +63,8 @@ def intent_recognize():
for term in keywords["terms"]: for term in keywords["terms"]:
term_info = { term_info = {
"名称": term["name"], "名称": term["name"],
"同义词": ";".join(term["synonymous"]) if term["synonymous"] else "", # "同义词": ";".join(term["synonymous"]) if term["synonymous"] else "",
"描述": term["description"] # "描述": term["description"]
} }
term_details.append(term_info) term_details.append(term_info)
keywords_str = term_details keywords_str = term_details
+177 -61
View File
@@ -7,8 +7,8 @@ Date: 2025-05-13
Description: 提取和分类的数据模型 Description: 提取和分类的数据模型
""" """
from pydantic import BaseModel, Field from pydantic import BaseModel, Field, field_validator
from typing import List, Optional, Dict, Tuple from typing import List, Optional, Dict, Tuple, Union, Any
from enum import Enum from enum import Enum
class SoftwareName(str, Enum): class SoftwareName(str, Enum):
@@ -31,6 +31,61 @@ class SoftwareName(str, Enum):
MAIN: "别名包括:主网软件、电力建设软件、主网建设软件、博微电力建设计价通等其他类似称呼" MAIN: "别名包括:主网软件、电力建设软件、主网建设软件、博微电力建设计价通等其他类似称呼"
} }
# 构建别名到标准名称的映射
def build_alias_mapping() -> Dict[str, SoftwareName]:
"""构建从别名到标准软件名称的映射字典"""
alias_map = {}
# 配网工程计价通D3软件的别名映射
alias_map["配网D3"] = SoftwareName.D3
alias_map["D3软件"] = SoftwareName.D3
alias_map["配网工程软件"] = SoftwareName.D3
alias_map["配网软件"] = SoftwareName.D3
# 新型储能电站建设计价通C1软件的别名映射
alias_map["储能C1"] = SoftwareName.C1
alias_map["C1软件"] = SoftwareName.C1
alias_map["储能电站软件"] = SoftwareName.C1
alias_map["储能软件"] = SoftwareName.C1
# 西藏电力工程计价通Z1软件的别名映射
alias_map["西藏Z1"] = SoftwareName.Z1
alias_map["Z1软件"] = SoftwareName.Z1
alias_map["西藏电力软件"] = SoftwareName.Z1
# 技改检修工程计价通T1软件的别名映射
alias_map["技改T1"] = SoftwareName.T1
alias_map["T1软件"] = SoftwareName.T1
alias_map["技改检修软件"] = SoftwareName.T1
# 技改检修清单计价通T1软件的别名映射
alias_map["技改清单T1"] = SoftwareName.T1_LIST
alias_map["T1清单软件"] = SoftwareName.T1_LIST
alias_map["技改检修清单软件"] = SoftwareName.T1_LIST
# 主网电力建设计价通软件的别名映射
alias_map["主网软件"] = SoftwareName.MAIN
alias_map["电力建设软件"] = SoftwareName.MAIN
alias_map["主网建设软件"] = SoftwareName.MAIN
alias_map["博微电力建设计价通"] = SoftwareName.MAIN
alias_map["主网计价通"] = SoftwareName.MAIN
alias_map["主网计价通软件"] = SoftwareName.MAIN
alias_map["计价通软件"] = SoftwareName.MAIN
alias_map["电力计价通软件"] = SoftwareName.MAIN
alias_map["计价通"] = SoftwareName.MAIN
# 添加标准名称映射
alias_map[SoftwareName.D3.value] = SoftwareName.D3
alias_map[SoftwareName.C1.value] = SoftwareName.C1
alias_map[SoftwareName.Z1.value] = SoftwareName.Z1
alias_map[SoftwareName.T1.value] = SoftwareName.T1
alias_map[SoftwareName.T1_LIST.value] = SoftwareName.T1_LIST
alias_map[SoftwareName.MAIN.value] = SoftwareName.MAIN
return alias_map
# 全局别名映射字典
SOFTWARE_NAME_ALIAS_MAP = build_alias_mapping()
# 定义输出模型 # 定义输出模型
class Term(BaseModel): class Term(BaseModel):
name: str = Field(description="专业名词") name: str = Field(description="专业名词")
@@ -55,140 +110,201 @@ class Classification(BaseModel):
class QueryRewrite(BaseModel): class QueryRewrite(BaseModel):
rewrite:str = Field(description="问题改写") rewrite:str = Field(description="问题改写")
##########################槽位模型###########################
class SlotBase(BaseModel):
"""槽位基础模型"""
def check_required_slots(self) -> Tuple[bool, Dict[str, str]]:
"""检查必填槽位是否都存在"""
raise NotImplementedError("子类必须实现check_required_slots方法")
@field_validator('software_name', mode='before', check_fields=False)
@classmethod
def validate_software_name(cls, v):
"""验证并转换软件名称,支持别名"""
if v is None or v == "":
return ""
# 如果已经是枚举类型,直接返回其值
if isinstance(v, SoftwareName):
return v.value
# 如果是字符串,尝试转换
if isinstance(v, str):
# 直接匹配枚举值
for software in SoftwareName:
if v == software.value:
return software.value
# 尝试通过别名匹配
if v in SOFTWARE_NAME_ALIAS_MAP:
return SOFTWARE_NAME_ALIAS_MAP[v].value
# 如果无法匹配,返回原值用于错误提示
return v
return v
# 1. 软件问题 # 1. 软件问题
# 1.1 软件功能 # 1.1 软件功能
class SoftwareFunction(BaseModel): class SoftwareFunctionSlots(SlotBase):
software_name: SoftwareName = Field(description="软件名称,只能从给定的范围中取值") software_name: str = Field(default="", description="软件名称")
function_name: str = Field(description="具体功能名称") function_name: str = Field(default="", description="具体功能名称")
operation: str = Field(description="用户操作意图(如何使用功能、功能入口、功能使用场景)") operation: str = Field(default="", description="用户操作意图(如何使用功能、功能入口、功能使用场景)")
software_version: Optional[str] = Field(None, description="软件版本") project_type: Optional[str] = Field(default="单工程", description="工程类型(单工程、多工程、批次工程)")
operation_steps: Optional[str] = Field(None, description="操作步骤描述") software_version: Optional[str] = Field(default="", description="软件版本")
operation_steps: Optional[str] = Field(default="", description="操作步骤描述")
def check_required_slots(self) -> Tuple[bool, Dict[str, str]]: def check_required_slots(self) -> Tuple[bool, Dict[str, str]]:
"""检查必填槽位是否都存在""" """检查必填槽位是否都存在"""
missing_slots = {} missing_slots = {}
if not self.software_name: if not self.software_name:
missing_slots["software_name"] = f"{SoftwareFunction.model_fields['software_name'].description},可选值:{', '.join([name.value for name in SoftwareName if name not in [SoftwareName.UNKNOWN, SoftwareName.ALIASES]])}" missing_slots["software_name"] = f"{SoftwareFunctionSlots.model_fields['software_name'].description},可选值:{', '.join([name.value for name in SoftwareName if name not in [SoftwareName.UNKNOWN, SoftwareName.ALIASES]])}"
if not self.function_name: if not self.function_name:
missing_slots["function_name"] = SoftwareFunction.model_fields["function_name"].description missing_slots["function_name"] = SoftwareFunctionSlots.model_fields["function_name"].description
if not self.operation: if not self.operation:
missing_slots["operation"] = SoftwareFunction.model_fields["operation"].description missing_slots["operation"] = SoftwareFunctionSlots.model_fields["operation"].description
return len(missing_slots) == 0, missing_slots return len(missing_slots) == 0, missing_slots
# 1.2 故障排查 # 1.2 故障排查
class TroubleShooting(BaseModel): class SoftwareTroubleShootingSlots(SlotBase):
software_name: SoftwareName = Field(description="软件名称,只能从给定的范围中取值") software_name: str = Field(default="", description="软件名称")
function_name: str = Field(description="具体功能名称/操作描述") function_name: str = Field(default="", description="具体功能名称/操作描述")
error_message: str = Field(description="报错信息/异常现象") error_message: str = Field(default="", description="报错信息/异常现象")
software_version: Optional[str] = Field(None, description="软件版本") software_version: Optional[str] = Field(default="", description="软件版本")
os_version: Optional[str] = Field(None, description="操作系统及版本") os_version: Optional[str] = Field(default="", description="操作系统及版本")
reproduction_steps: Optional[str] = Field(None, description="故障重现步骤") reproduction_steps: Optional[str] = Field(default="", description="故障重现步骤")
project_type: Optional[str] = Field(default="单工程", description="工程类型(单工程、多工程、批次工程)")
def check_required_slots(self) -> Tuple[bool, Dict[str, str]]: def check_required_slots(self) -> Tuple[bool, Dict[str, str]]:
"""检查必填槽位是否都存在""" """检查必填槽位是否都存在"""
missing_slots = {} missing_slots = {}
if not self.software_name: if not self.software_name:
missing_slots["software_name"] = f"{TroubleShooting.model_fields['software_name'].description},可选值:{', '.join([name.value for name in SoftwareName if name not in [SoftwareName.UNKNOWN, SoftwareName.ALIASES]])}" missing_slots["software_name"] = f"{SoftwareTroubleShootingSlots.model_fields['software_name'].description},可选值:{', '.join([name.value for name in SoftwareName if name not in [SoftwareName.UNKNOWN, SoftwareName.ALIASES]])}"
if not self.function_name: if not self.function_name:
missing_slots["function_name"] = TroubleShooting.model_fields["function_name"].description missing_slots["function_name"] = SoftwareTroubleShootingSlots.model_fields["function_name"].description
if not self.error_message: if not self.error_message:
missing_slots["error_message"] = TroubleShooting.model_fields["error_message"].description missing_slots["error_message"] = SoftwareTroubleShootingSlots.model_fields["error_message"].description
return len(missing_slots) == 0, missing_slots return len(missing_slots) == 0, missing_slots
# 2. 业务问题 # 2. 业务问题
# 2.1 专业咨询 # 2.1 专业咨询
class ProfessionalConsulting(BaseModel): class ProfessionalConsultingSlots(SlotBase):
scene_subject: str = Field(description="场景主体") scene_subject: str = Field(default="", description="场景主体")
business_scene: str = Field(description="业务场景描述") business_scene: str = Field(default="", description="业务场景描述")
software_name: Optional[SoftwareName] = Field(None, description="软件名称") software_name: Optional[str] = Field(default="", description="软件名称")
def check_required_slots(self) -> Tuple[bool, Dict[str, str]]: def check_required_slots(self) -> Tuple[bool, Dict[str, str]]:
"""检查必填槽位是否都存在""" """检查必填槽位是否都存在"""
missing_slots = {} missing_slots = {}
if not self.scene_subject: if not self.scene_subject:
missing_slots["scene_subject"] = ProfessionalConsulting.model_fields["scene_subject"].description missing_slots["scene_subject"] = ProfessionalConsultingSlots.model_fields["scene_subject"].description
if not self.business_scene: if not self.business_scene:
missing_slots["business_scene"] = ProfessionalConsulting.model_fields["business_scene"].description missing_slots["business_scene"] = ProfessionalConsultingSlots.model_fields["business_scene"].description
return len(missing_slots) == 0, missing_slots return len(missing_slots) == 0, missing_slots
# 2.2 数据问题 # 2.2 数据问题
class DataProblem(BaseModel): class DataProblemSlots(SlotBase):
expense_type: str = Field(description="费用类型") expense_type: str = Field(default="", description="费用类型")
operation_purpose: str = Field(description="操作目的") operation_purpose: str = Field(default="", description="操作目的")
software_name: Optional[SoftwareName] = Field(None, description="软件名称") software_name: Optional[str] = Field(default="", description="软件名称")
project_type: Optional[str] = Field(None, description="工程类型") project_type: Optional[str] = Field(default="", description="工程类型")
def check_required_slots(self) -> Tuple[bool, Dict[str, str]]: def check_required_slots(self) -> Tuple[bool, Dict[str, str]]:
"""检查必填槽位是否都存在""" """检查必填槽位是否都存在"""
missing_slots = {} missing_slots = {}
if not self.expense_type: if not self.expense_type:
missing_slots["expense_type"] = DataProblem.model_fields["expense_type"].description missing_slots["expense_type"] = DataProblemSlots.model_fields["expense_type"].description
if not self.operation_purpose: if not self.operation_purpose:
missing_slots["operation_purpose"] = DataProblem.model_fields["operation_purpose"].description missing_slots["operation_purpose"] = DataProblemSlots.model_fields["operation_purpose"].description
return len(missing_slots) == 0, missing_slots return len(missing_slots) == 0, missing_slots
# 3. 安装下载注册 # 3. 安装下载注册
# 3.1 后缀名咨询 # 3.1 后缀名咨询
class FileExtensionConsulting(BaseModel): class FileExtensionConsultingSlots(SlotBase):
file_extension: str = Field(description="文件后缀名") file_extension: str = Field(default="", description="文件后缀名")
operation_purpose: str = Field(description="操作目的") operation_purpose: str = Field(default="", description="操作目的(了解对应软件,对应工程)")
file_source: Optional[str] = Field(None, description="文件来源场景") file_source: Optional[str] = Field(default="", description="文件来源场景")
related_software: Optional[str] = Field(None, description="相关软件名称") related_software: Optional[str] = Field(default="", description="相关软件名称")
def check_required_slots(self) -> Tuple[bool, Dict[str, str]]: def check_required_slots(self) -> Tuple[bool, Dict[str, str]]:
"""检查必填槽位是否都存在""" """检查必填槽位是否都存在"""
missing_slots = {} missing_slots = {}
if not self.file_extension: if not self.file_extension:
missing_slots["file_extension"] = FileExtensionConsulting.model_fields["file_extension"].description missing_slots["file_extension"] = FileExtensionConsultingSlots.model_fields["file_extension"].description
if not self.operation_purpose: if not self.operation_purpose:
missing_slots["operation_purpose"] = FileExtensionConsulting.model_fields["operation_purpose"].description missing_slots["operation_purpose"] = FileExtensionConsultingSlots.model_fields["operation_purpose"].description
return len(missing_slots) == 0, missing_slots return len(missing_slots) == 0, missing_slots
# 3.2 软件锁类 # 3.2 软件锁类
class SoftwareLock(BaseModel): class SoftwareLockSlots(SlotBase):
lock_type: str = Field(description="锁类型") lock_type: str = Field(default="", description="锁类型")
operation_purpose: str = Field(description="操作目的") operation_purpose: str = Field(default="", description="操作目的")
lock_number: Optional[str] = Field(None, description="软件锁编号/注册号") lock_number: Optional[str] = Field(default="", description="软件锁编号/注册号")
def check_required_slots(self) -> Tuple[bool, Dict[str, str]]: def check_required_slots(self) -> Tuple[bool, Dict[str, str]]:
"""检查必填槽位是否都存在""" """检查必填槽位是否都存在"""
missing_slots = {} missing_slots = {}
if not self.lock_type: if not self.lock_type:
missing_slots["lock_type"] = SoftwareLock.model_fields["lock_type"].description missing_slots["lock_type"] = SoftwareLockSlots.model_fields["lock_type"].description
if not self.operation_purpose: if not self.operation_purpose:
missing_slots["operation_purpose"] = SoftwareLock.model_fields["operation_purpose"].description missing_slots["operation_purpose"] = SoftwareLockSlots.model_fields["operation_purpose"].description
return len(missing_slots) == 0, missing_slots return len(missing_slots) == 0, missing_slots
# 3.3 安装下载类 # 3.3 安装下载类
class InstallationDownload(BaseModel): class InstallationDownloadSlots(SlotBase):
software_name: str = Field(description="软件/插件名称,与file_name二选一") software_name: str = Field(default="", description="软件/插件名称,与file_name二选一")
file_name: str = Field(description="文件名,与software_name二选一") file_name: str = Field(default="", description="文件名,与software_name二选一")
operation_stage: str = Field(description="操作阶段") operation_stage: str = Field(default="", description="操作阶段(下载、安装等)")
os_version: Optional[str] = Field(None, description="操作系统版本") os_version: Optional[str] = Field(default="", description="操作系统版本")
package_source: Optional[str] = Field(None, description="安装包来源/版本号") package_source: Optional[str] = Field(default="", description="安装包来源/版本号")
def check_required_slots(self) -> Tuple[bool, Dict[str, str]]: def check_required_slots(self) -> Tuple[bool, Dict[str, str]]:
"""检查必填槽位是否都存在""" """检查必填槽位是否都存在"""
missing_slots = {} missing_slots = {}
if not self.software_name and not self.file_name: if not self.software_name and not self.file_name:
missing_slots["software_name"] = f"{InstallationDownload.model_fields['software_name'].description}," missing_slots["software_name"] = f"{InstallationDownloadSlots.model_fields['software_name'].description},"
f"可选值:{', '.join([name.value for name in SoftwareName if name not in [SoftwareName.UNKNOWN, SoftwareName.ALIASES]])}" f"可选值:{', '.join([name.value for name in SoftwareName if name not in [SoftwareName.UNKNOWN, SoftwareName.ALIASES]])}"
missing_slots["file_name"] = InstallationDownload.model_fields["file_name"].description missing_slots["file_name"] = InstallationDownloadSlots.model_fields["file_name"].description
if not self.operation_stage: if not self.operation_stage:
missing_slots["operation_stage"] = InstallationDownload.model_fields["operation_stage"].description missing_slots["operation_stage"] = InstallationDownloadSlots.model_fields["operation_stage"].description
return len(missing_slots) == 0, missing_slots return len(missing_slots) == 0, missing_slots
# 3.4 问题排查类 # 3.4 问题排查类
class ProblemDiagnosis(BaseModel): class ProblemDiagnosisSlots(SlotBase):
error_message: str = Field(description="报错信息/异常现象") error_message: str = Field(default="", description="报错信息/异常现象")
software_name: Optional[SoftwareName] = Field(None, description="软件名称,只能从给定的范围中取值") software_name: Optional[str] = Field(default="", description="软件名称")
os_version: Optional[str] = Field(None, description="操作系统版本") os_version: Optional[str] = Field(default="", description="操作系统版本")
def check_required_slots(self) -> Tuple[bool, Dict[str, str]]: def check_required_slots(self) -> Tuple[bool, Dict[str, str]]:
"""检查必填槽位是否都存在""" """检查必填槽位是否都存在"""
missing_slots = {} missing_slots = {}
if not self.error_message: if not self.error_message:
missing_slots["error_message"] = ProblemDiagnosis.model_fields["error_message"].description missing_slots["error_message"] = ProblemDiagnosisSlots.model_fields["error_message"].description
return len(missing_slots) == 0, missing_slots return len(missing_slots) == 0, missing_slots
class OtherSlots(SlotBase):
"""其他类型槽位"""
content_type: str = Field(default="", description="内容类型(必填)")
intent: Optional[str] = Field(default="", description="用户意图(选填)")
def check_required_slots(self) -> Tuple[bool, Dict[str, str]]:
missing_slots = {}
if not self.content_type:
missing_slots["content_type"] = OtherSlots.model_fields["content_type"].description
return len(missing_slots) == 0, missing_slots
class IntentAndSlotResult(BaseModel):
"""意图槽位填充结果"""
classification: Classification
slots: Union[
SoftwareFunctionSlots,
SoftwareTroubleShootingSlots,
ProfessionalConsultingSlots,
DataProblemSlots,
FileExtensionConsultingSlots,
SoftwareLockSlots,
InstallationDownloadSlots,
ProblemDiagnosisSlots,
OtherSlots
]
+230 -104
View File
@@ -7,18 +7,27 @@ Date: 2025-05-13
Description: 意图分类、改写核心逻辑 Description: 意图分类、改写核心逻辑
""" """
import logging
import os import os
from langchain_openai import ChatOpenAI
from langchain.output_parsers import PydanticOutputParser from langchain.output_parsers import PydanticOutputParser
import json import json
from typing import List, Tuple, Dict, Any, Optional, Union from typing import List, Tuple, Dict, Any, Optional
import re import re
from .PromptTemplates import classification_prompt, query_rewrite_prompt, extract_nouns_prompt, classification_info, slot_filling_prompt import jieba
from .PromptTemplates import (classification_prompt, query_rewrite_prompt,
extract_nouns_prompt, classification_info,
slot_filling_prompt)
from .Multi_PromptTemplates import (
intent_and_slot_prompt, output_example,
generate_slot_mapping_doc, query_rewrite_prompt_pro,
)
from .DataModels import ( from .DataModels import (
Classification, QueryRewrite, Term, TermList, Classification, QueryRewrite, Term, TermList,
SoftwareFunction, TroubleShooting, ProfessionalConsulting, SoftwareFunctionSlots, SoftwareTroubleShootingSlots, ProfessionalConsultingSlots,
DataProblem, FileExtensionConsulting, SoftwareLock, DataProblemSlots, FileExtensionConsultingSlots, SoftwareLockSlots,
InstallationDownload, ProblemDiagnosis InstallationDownloadSlots, ProblemDiagnosisSlots, OtherSlots, IntentAndSlotResult
) )
from .ProfessionalNounVector import ProfessionalNounRetriever from .ProfessionalNounVector import ProfessionalNounRetriever
from rag2_0.tool.ModelTool import XinferenceReRankerModel, OpenAiLLM, SiliconFlowReRankerModel from rag2_0.tool.ModelTool import XinferenceReRankerModel, OpenAiLLM, SiliconFlowReRankerModel
@@ -52,22 +61,13 @@ class IntentRecognizer:
if base_url: if base_url:
llm_params["base_url"] = base_url llm_params["base_url"] = base_url
self.llm = OpenAiLLM(**llm_params) self._llm = OpenAiLLM(**llm_params)
# 准备分类解析器
self.classification_parser = PydanticOutputParser(pydantic_object=Classification)
# 准备问题改写解析器
self.query_rewrite_parser = PydanticOutputParser(pydantic_object=QueryRewrite)
# 准备术语列表解析器
self.terms_list_parser = PydanticOutputParser(pydantic_object=TermList)
# 加载suffix关键词 # 加载suffix关键词
self.suffix_keywords = self._load_suffix_keywords() self._suffix_keywords = self._load_suffix_keywords()
# 初始化向量检索器 # 初始化向量检索器
self.noun_retriever = ProfessionalNounRetriever(api_key=api_key, index_dir=vector_index_dir) self._noun_retriever = ProfessionalNounRetriever(api_key=api_key, index_dir=vector_index_dir)
def _load_suffix_keywords(self, filepath: str = None) -> List[str]: def _load_suffix_keywords(self, filepath: str = None) -> List[str]:
""" """
@@ -95,7 +95,7 @@ class IntentRecognizer:
except Exception as e: except Exception as e:
raise RuntimeError(f"加载后缀关键词失败: {e}") from e raise RuntimeError(f"加载后缀关键词失败: {e}") from e
def classify_intent(self, query: str, keywords: TermList) -> Classification: def _classify_intent(self, query: str) -> Classification:
""" """
对用户输入进行意图分类 对用户输入进行意图分类
@@ -106,49 +106,85 @@ class IntentRecognizer:
Returns: Returns:
分类结果 分类结果
""" """
classification_parser = PydanticOutputParser(pydantic_object=Classification)
formatted_prompt = classification_prompt.format(user_input=query, formatted_prompt = classification_prompt.format(user_input=query,
classification_info=classification_info, classification_info=classification_info,
output_format=self.classification_parser.get_format_instructions()) output_format=classification_parser.get_format_instructions())
# 将关键词列表转换为JSON字符串
terms_dict = [term.model_dump() for term in keywords.terms]
keywords_str = json.dumps(terms_dict, ensure_ascii=False)
formatted_prompt = formatted_prompt.replace("{keywords}", keywords_str)
# 调用LLM # 调用LLM
response = self.llm.invoke(formatted_prompt, False) response = self._llm.invoke(formatted_prompt, False)
# 解析输出 # 解析输出
try: try:
# 尝试直接解析JSON响应 # 尝试直接解析JSON响应
parsed_output = self.classification_parser.parse(response.content.strip()) parsed_output = classification_parser.parse(response.content.strip())
return parsed_output return parsed_output
except Exception as e: except Exception as e:
raise RuntimeError(f"解析分类结果时出错: {e}") from e raise RuntimeError(f"解析分类结果时出错: {e}") from e
def extract_keywords_with_llm(self, query: str) -> List[Term]: def _tokenize_with_jieba(self, query: str) -> List[str]:
""" """
使用LLM从用户查询中提取专业关键 使用jieba分词器对查询进行分
Args: Args:
query: 用户查询 query: 用户查询
Returns:
分词后的词语列表
"""
# 使用jieba进行分词
seg_list = jieba.cut(query, cut_all=False)
# 过滤掉停用词和标点符号
filtered_tokens = []
for token in seg_list:
# 过滤掉空格和标点符号
if token.strip() and not re.match(r'^[^\w\s]$', token):
filtered_tokens.append(token)
return filtered_tokens
def _extract_keywords_with_llm(self, query: str, use_jieba: bool = False) -> List[Term]:
"""
使用LLM从用户查询中提取专业关键词
Args:
query: 用户查询
use_jieba: 是否使用jieba分词辅助提取关键词
Returns: Returns:
提取的术语列表 提取的术语列表
""" """
# 准备提示词
formatted_prompt = extract_nouns_prompt.replace("{content}", query)
formatted_prompt = formatted_prompt.replace("{output_format}", self.terms_list_parser.get_format_instructions())
# 调用LLM
response = self.llm.invoke(formatted_prompt, False)
try: try:
# 尝试使用Pydantic解析器解析TermList # 如果使用jieba分词
parsed_output = self.terms_list_parser.parse(response.content) if use_jieba:
return parsed_output.terms # 先使用jieba分词
tokens = self._tokenize_with_jieba(query)
# 构建术语列表
terms = []
for token in tokens:
if len(token) > 1: # 过滤掉单字词
terms.append(Term(name=token, synonymous=[], description=""))
return terms
else:
# 使用LLM提取关键词
# 准备提示词
formatted_prompt = extract_nouns_prompt.replace("{content}", query)
terms_list_parser = PydanticOutputParser(pydantic_object=TermList)
formatted_prompt = formatted_prompt.replace("{output_format}", terms_list_parser.get_format_instructions())
# 调用LLM
response = self._llm.invoke(formatted_prompt, False)
# 尝试使用Pydantic解析器解析TermList
parsed_output = terms_list_parser.parse(response.content)
return parsed_output.terms
except Exception as e: except Exception as e:
raise RuntimeError(f"无法解析LLM关键词提取响应: {e}") from e raise RuntimeError(f"无法解析LLM关键词提取响应: {e}") from e
def rerank_matched_terms(self, query_key: str, matched_terms: set, top_k: int = 2) -> List[Term]: def _rerank_matched_terms(self, query_key: str, matched_terms: set, top_k: int = 2) -> List[Term]:
""" """
对召回的专业术语进行重排序,按与用户查询的相关性排序 对召回的专业术语进行重排序,按与用户查询的相关性排序
@@ -182,31 +218,32 @@ class IntentRecognizer:
except Exception as e: except Exception as e:
raise RuntimeError(f"SiliconFlowReRankerModel重排失败:{e}") from e raise RuntimeError(f"SiliconFlowReRankerModel重排失败:{e}") from e
def match_keywords(self, query: str) -> Tuple[TermList, List[str]]: def _match_keywords(self, query: str, use_jieba: bool = False) -> Tuple[TermList, List[str]]:
""" """
从用户问题中匹配关键词,结合LLM提取和向量检索 从用户问题中匹配关键词,结合LLM提取和向量检索
Args: Args:
query: 用户问题 query: 用户问题
use_jieba: 是否使用jieba分词辅助提取关键词
Returns: Returns:
匹配到的关键词列表 匹配到的关键词列表
""" """
query_keys=[] query_keys=[]
# 步骤2: 使用LLM提取查询中的关键词 # 步骤1: 使用LLM提取查询中的关键词
try: try:
extracted_terms = self.extract_keywords_with_llm(query) extracted_terms = self._extract_keywords_with_llm(query, use_jieba)
for term in extracted_terms: for term in extracted_terms:
query_keys.append(term.name) query_keys.append(term.name)
except Exception as e: except Exception as e:
raise RuntimeError(f"LLM关键词提取失败: {e}") from e raise RuntimeError(f"LLM关键词提取失败: {e}") from e
matched_terms = [] # 存储匹配到的Term对象 matched_terms = [] # 存储匹配到的Term对象
# 步骤3: 使用向量检索找到相似的专业名词 # 步骤2: 使用向量检索找到相似的专业名词
try: try:
# 对matched_terms中的每个关键字进行向量检索 # 对matched_terms中的每个关键字进行向量检索
for current_key in query_keys: for current_key in query_keys:
vector_results = self.noun_retriever.query(current_key, top_k=5, use_intersection=False) vector_results = self._noun_retriever.query(current_key, top_k=5, use_intersection=False)
current_key_terms = set() current_key_terms = set()
# 添加向量检索结果 # 添加向量检索结果
for result in vector_results: for result in vector_results:
@@ -218,8 +255,9 @@ class IntentRecognizer:
description=result.get('description', '') description=result.get('description', '')
) )
current_key_terms.add(term) current_key_terms.add(term)
reranked_terms = self.rerank_matched_terms(current_key, current_key_terms) if len(current_key_terms) > 0:
matched_terms.extend(reranked_terms) reranked_terms = self._rerank_matched_terms(current_key, current_key_terms)
matched_terms.extend(reranked_terms)
except Exception as e: except Exception as e:
raise RuntimeError(f"向量检索关键词时出错: {e}") from e raise RuntimeError(f"向量检索关键词时出错: {e}") from e
@@ -228,7 +266,7 @@ class IntentRecognizer:
term_list = TermList(terms=list(matched_terms)) term_list = TermList(terms=list(matched_terms))
return term_list, query_keys return term_list, query_keys
def rewrite_query(self, query: str, keywords: TermList) -> QueryRewrite: def _rewrite_query(self, query: str, keywords: TermList, chat_history: List[Dict[str, str]] = None, context: str = "") -> QueryRewrite:
""" """
对用户问题进行改写 对用户问题进行改写
@@ -242,23 +280,28 @@ class IntentRecognizer:
# 准备问题改写提示 # 准备问题改写提示
terms_dict = [term.model_dump(exclude={"description"}) for term in keywords.terms] terms_dict = [term.model_dump(exclude={"description"}) for term in keywords.terms]
keywords_str = json.dumps(terms_dict, ensure_ascii=False) keywords_str = json.dumps(terms_dict, ensure_ascii=False)
formatted_prompt = query_rewrite_prompt.format(query=query, query_rewrite_parser = PydanticOutputParser(pydantic_object=QueryRewrite)
output_format=self.query_rewrite_parser.get_format_instructions(), # formatted_prompt = query_rewrite_prompt.format(query=query,
keywords=keywords_str) # output_format=query_rewrite_parser.get_format_instructions(),
# keywords=keywords_str)
formatted_prompt = query_rewrite_prompt_pro.format(query=query,
output_format=query_rewrite_parser.get_format_instructions(),
keywords=keywords_str,
chat_history=chat_history,
context=context)
# 调用LLM # 调用LLM
response = self.llm.invoke(formatted_prompt, False) response = self._llm.invoke(formatted_prompt, False)
# 解析输出 # 解析输出
try: try:
# 尝试直接解析JSON响应 # 尝试直接解析JSON响应
parsed_output = self.query_rewrite_parser.parse(response.content) parsed_output = query_rewrite_parser.parse(response.content)
return parsed_output return parsed_output
except Exception as e: except Exception as e:
raise RuntimeError(f"解析问题改写结果时出错: {e}") from e raise RuntimeError(f"解析问题改写结果时出错: {e}") from e
def judge_define_suffix(self, input_str: str) -> Tuple[bool, List[str]]: def _judge_define_suffix(self, input_str: str) -> Tuple[bool, List[str]]:
""" """
判断输入字符串是否包含定义的后缀,并返回所有匹配到的后缀名列表 判断输入字符串是否包含定义的后缀,并返回所有匹配到的后缀名列表
@@ -270,7 +313,7 @@ class IntentRecognizer:
""" """
# 构建正则表达式模式,匹配大小写不敏感且前面可能带有. # 构建正则表达式模式,匹配大小写不敏感且前面可能带有.
pattern = r'(?:\.?)(' + '|'.join(re.escape(field.get('name')) for field in self.suffix_keywords) + r')' pattern = r'(?:\.?)(' + '|'.join(re.escape(field.get('name')) for field in self._suffix_keywords) + r')'
# 使用 re.IGNORECASE 标志来忽略大小写,findall找到所有匹配 # 使用 re.IGNORECASE 标志来忽略大小写,findall找到所有匹配
matches = re.finditer(pattern, input_str, re.IGNORECASE) matches = re.finditer(pattern, input_str, re.IGNORECASE)
@@ -278,23 +321,30 @@ class IntentRecognizer:
return bool(matched_suffixes), matched_suffixes return bool(matched_suffixes), matched_suffixes
def process_query(self, query: str) -> Tuple[Classification, TermList, QueryRewrite, List[str]]: def process_query(self, query: str, conversation_context: str = "",
chat_history: List[Dict[str, str]] = None,
previous_slots: Dict[str, Any] = None,
use_jieba: bool = False) -> Dict[str, Any]:
""" """
处理用户问题的完整流程 处理用户问题的完整流程
Args: Args:
query: 用户原始问题 query: 用户原始问题
conversation_context: 会话背景信息
chat_history: 历史对话记录,格式为[{"user": "content"}, {"assistant": "content"}]
previous_slots: 历史槽位信息
use_jieba: 是否使用jieba分词辅助提取关键词
Returns: Returns:
(意图分类结果, 匹配的关键词列表, 问题改写结果)的元组 包含分类、关键词、改写和槽位填充结果的字典
""" """
# 是否是扩展名 # 是否是扩展名
# is_suffix, matched_suffixes = self.judge_define_suffix(query) # is_suffix, matched_suffixes = self._judge_define_suffix(query)
# if is_suffix: # if is_suffix:
# # 将所有匹配到的后缀名作为Term添加到结果中 # # 将所有匹配到的后缀名作为Term添加到结果中
# suffix_terms = [] # suffix_terms = []
# for suffix in matched_suffixes: # for suffix in matched_suffixes:
# term_dict = next((item for item in self.suffix_keywords if item['name'].lower() == suffix.lower()), None) # term_dict = next((item for item in self._suffix_keywords if item['name'].lower() == suffix.lower()), None)
# if term_dict: # if term_dict:
# suffix_term = Term( # suffix_term = Term(
# name=term_dict.get('name'), # name=term_dict.get('name'),
@@ -306,26 +356,41 @@ class IntentRecognizer:
# return Classification(vertical_classification="安装下载", sub_classification="查询"), TermList(terms=suffix_terms), QueryRewrite(rewrite=query), matched_suffixes # return Classification(vertical_classification="安装下载", sub_classification="查询"), TermList(terms=suffix_terms), QueryRewrite(rewrite=query), matched_suffixes
# 步骤1: 匹配关键词 # 步骤1: 匹配关键词
keywords_terms, query_keys = self.match_keywords(query) keywords_terms, query_keys = self._match_keywords(query, use_jieba)
# 步骤2: 问题改写 # 步骤2: 问题改写
rewrite = self.rewrite_query( rewrite = self._rewrite_query(
query=query, query=query,
keywords=keywords_terms keywords=keywords_terms,
chat_history=chat_history,
context=conversation_context
) )
# 步骤3: 进行意图识别和槽位填充
result = self._process_intent_and_slot(query, conversation_context, chat_history, previous_slots)
result.update({"keywords": keywords_terms.model_dump(),
"rewrite": rewrite.model_dump(),
"query_keys": query_keys})
return result
# # 步骤3: 进行意图分类
# classification = self._classify_intent(query)
# 步骤3: 进行意图分类 # # 步骤4: 进行槽位填充
classification = self.classify_intent(query, keywords_terms) # # 如果是有效分类,进行槽位填充
if classification.vertical_classification == "其他" or classification.sub_classification == "其他": # slot_filling_result = {}
return classification, TermList(terms=[]), QueryRewrite(rewrite=query), [] # if classification.vertical_classification not in ["其他", "闲聊"] and classification.sub_classification not in ["其他", "闲聊"]:
# slot_filling_result = self._fill_slots(rewrite.rewrite, classification)
# return {
# "classification": classification.model_dump(),
# "keywords": keywords_terms.model_dump(),
# "rewrite": rewrite.model_dump(),
# "query_keys": query_keys,
# "slot_filling": slot_filling_result
# }
if classification.vertical_classification == "闲聊" or classification.sub_classification == "闲聊":
return classification, TermList(terms=[]), QueryRewrite(rewrite=query),[]
# rewrite = QueryRewrite(rewrite=query)
return classification, keywords_terms, rewrite, query_keys
def fill_slots(self, query: str, classification: Classification) -> Dict[str, Any]: def _fill_slots(self, query: str, classification: Classification) -> Dict[str, Any]:
""" """
根据分类结果对问题进行槽位填充 根据分类结果对问题进行槽位填充
@@ -340,7 +405,7 @@ class IntentRecognizer:
# 根据分类结果选择对应的数据模型 # 根据分类结果选择对应的数据模型
slot_model = self._get_slot_model(classification) slot_model = self._get_slot_model(classification)
if not slot_model: if not slot_model:
return {"error": "未找到匹配的槽位模型"} raise RuntimeError("未找到匹配的槽位模型")
# 使用LLM进行槽位填充 # 使用LLM进行槽位填充
filled_slots = self._fill_slots_with_llm(query, classification, slot_model) filled_slots = self._fill_slots_with_llm(query, classification, slot_model)
@@ -356,7 +421,7 @@ class IntentRecognizer:
def _get_slot_model(self, classification: Classification) -> Optional[type]: def _get_slot_model(self, classification: Classification) -> Optional[type]:
""" """
根据分类结果获取对应的槽位模型类 根据分类结果获取对应的槽位模型类,用于统一提示词处理
Args: Args:
classification: 意图分类结果 classification: 意图分类结果
@@ -367,31 +432,33 @@ class IntentRecognizer:
# 软件问题 # 软件问题
if classification.vertical_classification == "软件问题": if classification.vertical_classification == "软件问题":
if classification.sub_classification == "软件功能": if classification.sub_classification == "软件功能":
return SoftwareFunction return SoftwareFunctionSlots
elif classification.sub_classification == "故障排查": elif classification.sub_classification == "故障排查":
return TroubleShooting return SoftwareTroubleShootingSlots
# 业务问题 # 业务问题
elif classification.vertical_classification == "业务问题": elif classification.vertical_classification == "业务问题":
if classification.sub_classification == "专业咨询": if classification.sub_classification == "专业咨询":
return ProfessionalConsulting return ProfessionalConsultingSlots
elif classification.sub_classification == "数据问题": elif classification.sub_classification == "数据问题":
return DataProblem return DataProblemSlots
# 安装下载注册 # 安装下载注册
elif classification.vertical_classification == "安装下载注册": elif classification.vertical_classification == "安装下载注册":
if classification.sub_classification == "后缀名咨询": if classification.sub_classification == "后缀名咨询":
return FileExtensionConsulting return FileExtensionConsultingSlots
elif classification.sub_classification == "软件锁类": elif classification.sub_classification == "软件锁类":
return SoftwareLock return SoftwareLockSlots
elif classification.sub_classification == "安装下载类": elif classification.sub_classification == "安装下载类":
return InstallationDownload return InstallationDownloadSlots
elif classification.sub_classification == "问题排查类": elif classification.sub_classification == "问题排查类":
return ProblemDiagnosis return ProblemDiagnosisSlots
# 其他
elif classification.vertical_classification == "其他":
return OtherSlots
return None return None
count=1
def _fill_slots_with_llm(self, query: str, classification: Classification, slot_model_class: type) -> Any: def _fill_slots_with_llm(self, query: str, classification: Classification, slot_model_class: type) -> Any:
""" """
@@ -416,7 +483,7 @@ class IntentRecognizer:
) )
# 调用LLM # 调用LLM
response = self.llm.invoke(formatted_prompt, False) response = self._llm.invoke(formatted_prompt, False)
try: try:
# 尝试解析LLM响应 # 尝试解析LLM响应
@@ -426,29 +493,88 @@ class IntentRecognizer:
# 如果解析失败,创建一个空的模型实例 # 如果解析失败,创建一个空的模型实例
empty_instance = slot_model_class() empty_instance = slot_model_class()
return empty_instance return empty_instance
def process_query_with_slots(self, query: str) -> Dict[str, Any]: def _process_intent_and_slot(self, user_input: str, conversation_context: str = "",
chat_history: List[Dict[str, str]] = None,
previous_slots: Dict[str, Any] = None) -> Dict[str, Any]:
""" """
处理用户问题的完整流程,包括槽位填充 使用统一提示词同时进行意图识别和槽位填充
Args: Args:
query: 用户原始问题 user_input: 当前用户输入
conversation_context: 会话背景信息
chat_history: 历史对话记录,格式为[{"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}]
previous_slots: 历史槽位信息
Returns: Returns:
包含分类、关键词、改写和槽位填充结果的字典 包含意图分类和槽位填充结果的字典
""" """
# 执行基本处理流程 # 初始化默认值
classification, keywords, rewrite, query_keys = self.process_query(query) if chat_history is None:
chat_history = []
# 如果是有效分类,进行槽位填充 if previous_slots is None:
slot_filling_result = {} previous_slots = {}
if classification.vertical_classification not in ["其他", "闲聊"] and classification.sub_classification not in ["其他", "闲聊"]:
slot_filling_result = self.fill_slots(rewrite.rewrite, classification) # 生成槽位映射文档
slot_mapping_doc = generate_slot_mapping_doc()
return { # 准备提示词
"classification": classification.model_dump(), parser = PydanticOutputParser(pydantic_object=IntentAndSlotResult)
"keywords": keywords.model_dump(), formatted_prompt = intent_and_slot_prompt.format(
"rewrite": rewrite.model_dump(), conversation_context=conversation_context,
"query_keys": query_keys, chat_history=json.dumps(chat_history, ensure_ascii=False),
"slot_filling": slot_filling_result previous_slots=json.dumps(previous_slots, ensure_ascii=False),
} user_input=user_input,
slot_mapping_doc=slot_mapping_doc,
output_format=parser.get_format_instructions(),
classification_info=classification_info
)
# 调用LLM
response = self._llm.invoke(formatted_prompt + output_example, False)
try:
# 解析LLM响应为JSON
result_json = parser.parse(response.content)
classification=result_json.classification
slot_filling=result_json.slots
is_complete, missing_slots = slot_filling.check_required_slots()
expected_slot_model = self._get_slot_model(classification)
# 添加容错处理,发生概率较低,但仍需处理
if expected_slot_model is None:
# 添加容错处理,应对LLM返回错误分类信息,一级分类跟二级分类错乱
# 重新分类
classification = self._classify_intent(user_input)
fill_slots = self._fill_slots(user_input, classification)
result = {
"classification": classification.model_dump(),
"slot_filling": fill_slots
}
logging.warning(f"重新分类与槽点填充")
return result
elif expected_slot_model.__name__ != type(slot_filling).__name__:
# 添加容错处理,应对LLM槽位与分类不匹配。重新填充槽位
slot_filling = self._fill_slots(user_input, classification)
result = {
"classification": classification.model_dump(),
"slot_filling": slot_filling
}
logging.warning(f"重新填充槽点")
return result
# 构建最终结果
result = {
"classification": classification.model_dump(),
"slot_filling": {
"is_complete": is_complete,
"missing_slots": missing_slots,
"filled_data": slot_filling.model_dump()
}
}
return result
except Exception as e:
logging.error(f"process_intent_and_slot error:{e}")
raise RuntimeError(f"process_intent_and_slot error:{e}") from e
@@ -0,0 +1,357 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
File: Multi_PromptTemplates.py
Author: oyyz
Date: 2025-06-13
Description: 多轮对话下意图分类、改写核心提示词
"""
# 首版重构提示词
query_rewrite_prompt_pro_old="""
# 电力造价专业问答优化工程师(升级版)
你是一名电力造价专业问答优化工程师,负责结合历史对话背景和专业术语库,将用户的原始问题进行规范化重构,以提升知识库检索准确率。
## 核心任务
基于历史对话上下文和专业术语库,将用户的原始问题进行规范化重构,提高知识库检索的准确性和专业性,同时保持对话的连贯性和语境相关性。
## 处理流程
### 第一阶段:输入解析
1. 解析基础信息
- 原始问题(需保留核心语义){query}
- 关键词集合:{keywords}
- 历史对话记录:{chat_history}
- 当前聊天背景:{context}
2. 背景分析
- 识别历史对话中的关键主题和专业领域
- 提取上下文中的隐含信息(如软件版本、地区、具体场景等)
- 分析用户的提问模式和专业水平
### 第二阶段:上下文匹配分析
**背景匹配规则:**
1. 检查当前问题是否与历史对话存在关联性
2. 识别历史对话中提到的关键信息:
- 软件版本/系统(如Z1、D3等)
- 地区定额(如西藏、山东等)
- 具体功能模块
- 用户操作习惯
**术语匹配规则:**
1. 检查原始问题中是否包含关键词集合中的`name`字段或`synonymous`字段中的任何词汇
2. 结合历史对话,识别可能的隐含专业术语
3. 统计匹配的术语数量
4. 判断执行路径:
- 匹配术语 ≥ 1个 或 存在明显上下文关联 → 执行重构流程
- 匹配术语 = 0个 且 无明显上下文关联 → 直接输出原始问题
### 第三阶段:问题重构
**重构原则(按优先级排序):**
1. **语义保真**:严格保持原问题的核心意图和诉求
2. **上下文继承**
- 补充历史对话中的隐含信息(如软件名称、版本、地区等)
- 保持对话的连贯性和逻辑性
- 避免重复已确认的背景信息
3. **术语规范**
- 将匹配到的同义词替换为对应的标准术语(name字段)
- 对在关键词中的标准术语使用【】进行标记
- 保留在原问题中未在关键词库中的专业术语、限定词和修饰词
4. **结构优化**
- 保持原问题的语态特征5W2H
- 保持主谓宾结构清晰
- 保留时间、版本等限定条件
**术语处理规则:**
- 优先级1:基于历史对话补充缺失的背景信息
- 优先级2:保留原问题中的专业术语、限定词和修饰词(即使不在关键词库中)
- 优先级3:将同义词替换为标准术语并用【】标记
- 优先级4:对原问题中已存在的标准术语添加【】标记
**上下文处理策略:**
- 如果当前问题与历史对话高度相关,适当补充背景信息
- 如果用户使用代词(如"这个""那个"),尝试结合历史对话明确指代
- 如果历史对话中已确定软件或系统,在当前问题中适当体现
# 输出规范
{output_format}
# 示范案例库
▶ 案例1(有效匹配 + 上下文继承)
历史对话:用户之前询问过"西藏定额升级的问题"
输入:
原始问题:怎么把旧版工程转到Z1新版
关键词:【'老版本定额升级', '批量设置定额', '西藏造价软件Z1'
输出:
{{"rewrite":"【西藏造价软件Z1】如何执行【老版本定额升级】操作?"}}
▶ 案例2(无效匹配 + 无上下文关联)
历史对话:无相关内容
输入:
原始问题:程序界面文字显示过小如何处理?
关键词:【'定额升级', '工程批量导入'
输出:
{{"rewrite":"程序界面文字显示过小如何处理?"}}
▶ 案例3(部分匹配 + 上下文补充)
历史对话:用户之前询问过"D3软件的功能"
输入:
原始问题:能导出清单的计算公式吗?
关键词:【'配网工程计价通D3软件', '计算式'
输出:
{{"rewrite":"【配网工程计价通D3软件】能导出清单的【计算式】吗?"}}
▶ 案例4(代词替换 + 上下文解析)
历史对话:用户刚询问过"山东定额的问题"
输入:
原始问题:这个定额怎么批量导入?
关键词:【'批量导入定额', '山东定额'
输出:
{{"rewrite":"【山东定额】如何进行【批量导入定额】操作?"}}
## 质量检查清单
执行前请确认:
- [ ] 是否保持了原问题的核心诉求?
- [ ] 是否合理利用了历史对话中的背景信息?
- [ ] 是否正确执行了同义词替换?
- [ ] 是否保留了原问题中的专业术语和限定条件?
- [ ] 是否正确使用了【】标记?
- [ ] 重构后的问题是否自然流畅?
- [ ] 是否保持了对话的连贯性?
- [ ] 是否避免了过度补充不必要的信息?
"""
query_rewrite_prompt_pro="""
# 电力造价问答优化工程师(精简版)
**角色**:基于历史对话和专业术语库重构问题,提升知识库检索准确率。
## 核心原则
1. 语义保真 → 保持问题核心意图
2. 术语规范 → 同义词转标准词并【】标记
3. 背景继承 → 补充历史对话的隐含信息
## 处理流程
### 一、输入解析
- 原始问题(需保留核心语义){query}
- 关键词集合:{keywords}
- 历史对话记录:
<history>
{chat_history}
</history>
- 当前聊天背景:
<conversation_background>
{context}
</conversation_background>
### 二、重构决策树
```mermaid
graph TD
A[输入问题] --> B{{匹配关键词或上下文?}}
B -- 是 --> C[执行重构]
B -- 否 --> D[直接输出原始问题]
C --> E[补充缺失背景]
E --> F[同义词替换+【】标记]
F --> G[保留原生专业术语]
```
### 三、重构优先级
1. **背景补充**
- 历史对话中确定的软件/地区必须继承(例:"这软件""【配网工程D3】"
2. **术语处理**
- 同义词转标准词 → 批量设置定额
- 存在即标记 → 【计算式】
3. **结构优化**
- 保持原问题的5W2H特征
- 明确指代关系("该功能""【批量导入】功能"
## 输出规范
{output_format}
## 典型案例
| 场景 | 输入问题 | 输出结果 |
|---------------------|-----------------------------------|------------------------------------------|
| 强上下文关联 | “怎么升级旧版工程” | {{"rewrite":"【西藏Z1】如何执行【老版本定额升级】?"}} |
| 弱术语匹配 | “界面文字太小怎么办” | 原样输出 |
| 代词+背景继承 | “这个定额如何导入” | {{"rewrite":"【山东定额】如何执行【批量导入定额】?"}}|
## 质量自检
- [] 核心诉求是否保留?
- [] 背景信息是否合理补充?
- [] 术语标记是否完整【】?
- [] 语句是否自然流畅?
- [] 避免过度补充无关信息
"""
intent_and_slot_prompt = """
# 电力造价软件意图分类与槽位填充统一提示词
你是一个专业的电力造价领域智能助手,负责对用户输入进行意图分类识别和关键信息槽位填充。
{classification_info}
{slot_mapping_doc}
## 【软件名称规范】
支持的软件名称及其别名:
- **配网工程计价通D3软件**:别名包括配网D3、D3软件、配网工程软件等
- **新型储能电站建设计价通C1软件**:别名包括储能C1、C1软件、储能电站软件、储能软件等
- **西藏电力工程计价通Z1软件**:别名包括西藏Z1、Z1软件、西藏电力软件等
- **技改检修工程计价通T1软件**:别名包括技改T1、T1软件、技改检修软件等
- **技改检修清单计价通T1软件**:别名包括技改清单T1、T1清单软件、技改检修清单软件等
- **主网电力建设计价通软件**:别名包括主网软件、电力建设软件、主网建设软件、博微电力建设计价通等
## 【任务要求】
1. **会话理解**:综合考虑会话背景、历史对话和之前的槽位信息来理解当前用户输入
2. **意图分类**:准确识别用户输入属于哪个垂直领域和子分类
3. **槽位填充**:从当前用户问题中提取关键信息,并结合历史槽位信息进行补充完善
4. **信息融合**
- 优先使用当前用户输入中的明确信息
- 当前输入缺失但历史槽位存在的信息,可适当继承
- 历史对话中的上下文信息有助于理解当前输入的真实意图
5. **槽位处理**
- 对于必填槽位,必须尽力从当前输入和历史信息中提取
- 对于选填槽位,如果能从当前输入或历史信息中提取则填写
- 如果当前输入与历史信息存在冲突,以当前输入为准
6. **输出格式**:只输出符合格式的JSON数据,不要有任何额外的解释
## 【会话背景信息】
{conversation_context}
## 【历史对话记录】
{chat_history}
## 【历史槽位信息】
{previous_slots}
## 【当前用户输入】
{user_input}
## 【输出格式】
{output_format}
"""
output_example="""
## 【综合分析示例】
**示例1:利用历史对话理解当前输入**
会话背景: 用户正在咨询软件使用问题
历史对话:
- 用户: "我在使用配网D3软件"
- 助手: "好的,请问您遇到什么问题?"
历史槽位:{"software_name": "配网工程计价通D3软件"}
当前用户输入: "新建工程按钮找不到"
输出:
```json
{
"classification": {
"vertical_classification": "软件问题",
"sub_classification": "软件功能"
},
"slot_filling": {
"software_name": "配网工程计价通D3软件",
"function_name": "新建工程",
"operation": "查找新建工程按钮位置",
"software_version": null,
"operation_steps": null
}
}
```
**示例2:继承和更新槽位信息**
会话背景: 用户遇到软件报错,正在进行故障排查
历史对话:
- 用户: "西藏Z1软件报错了"
- 助手: "请详细描述一下报错信息"
历史槽位: {"software_name": "西藏电力工程计价通Z1软件"}
当前用户输入: "提示找不到许可证,是在新建工程的时候"
输出:
```json
{
"classification": {
"vertical_classification": "软件问题",
"sub_classification": "故障排查"
},
"slot_filling": {
"software_name": "西藏电力工程计价通Z1软件",
"function_name": "新建工程",
"error_message": "提示找不到许可证",
"software_version": null,
"os_version": null,
"reproduction_steps": "新建工程时出现错误"
}
}
```
**示例3:信息冲突处理**
会话背景: 用户在多个软件间切换咨询
历史对话:
- 用户: "配网D3的费用计算"
- 助手: "好的,关于配网D3的费用计算..."
历史槽位: {"software_name": "配网工程计价通D3软件"}
当前用户输入: "不对,我说的是技改T1软件的材料费怎么算"
输出:
```json
{
"classification": {
"vertical_classification": "业务问题",
"sub_classification": "数据问题"
},
"slot_filling": {
"expense_type": "材料费",
"operation_purpose": "了解费用计算方法",
"software_name": "技改检修工程计价通T1软件",
"project_type": null
}
}
```
"""
def generate_slot_mapping_doc() -> str:
"""
生成分类与槽位模型对应关系的文档
Returns:
str: 格式化的文档字符串
"""
mapping = {
"软件问题": {
"软件功能": "SoftwareFunctionSlots",
"故障排查": "SoftwareTroubleShootingSlots"
},
"业务问题": {
"专业咨询": "ProfessionalConsultingSlots",
"数据问题": "DataProblemSlots"
},
"安装下载注册": {
"后缀名咨询": "FileExtensionConsultingSlots",
"软件锁类": "SoftwareLockSlots",
"安装下载类": "InstallationDownloadSlots",
"问题排查类": "ProblemDiagnosisSlots"
},
"其他": {
"其他": "OtherSlots"
}
}
doc = ["## 【分类与槽位模型对应关系】"]
for vertical, sub_classes in mapping.items():
doc.append(f"\n{vertical}:")
for sub_class, slot_model in sub_classes.items():
doc.append(f"- {sub_class} -> {slot_model}")
doc.append("\n## 【注意事项】")
doc.append("1. 分类与槽位模型必须严格对应")
doc.append("2. 每个分类只能使用其对应的槽位模型")
doc.append("3. 不允许混用不同分类的槽位模型")
return "\n".join(doc)
+1 -1
View File
@@ -118,7 +118,7 @@ class XinferenceReRankerModel:
return [{"document": item["document"]["text"], "score": item["relevance_score"], "index": item["index"]} for item in results["results"]] return [{"document": item["document"]["text"], "score": item["relevance_score"], "index": item["index"]} for item in results["results"]]
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
logging.error(f"重排序请求失败: {str(e)}") logging.error(f"XinferenceReRankerModel重排序请求失败: {str(e)}")
return [] return []