2 Achegas 12462d4260 ... 6c0e1be49e

Autor SHA1 Mensaxe Data
  jiayongqiang 6c0e1be49e Merge branch 'master' of http://git.yangzhiqiang.tech/jiayq/ai-tagging hai 13 horas
  jiayongqiang 517ff40ee3 打标接口增加参数 hai 13 horas

+ 10 - 4
agent/build.sh → agent/build.md

@@ -9,10 +9,16 @@ rm -rf release
 mkdir -p release
 pip download -r requirements.txt -d release/ --prefer-binary
 
-# 4. 构建并加入自己的项目
-# pdm build
-# cp dist/*.whl release/
+# 4. 导出pdm wheel
+pip download pdm -d ./release
 
 # 5. 打包
 rm release.tar.gz
-tar -czvf release.tar.gz release/
+tar -czvf release.tar.gz release/
+
+# 6. 在目标机上安装pdm
+pip install --no-index --find-links ./release pdm
+
+# 7. 修改 PDM 配置,设置 find-links
+pdm config pypi.url file://$(pwd)/release
+pdm install

+ 4 - 4
agent/config.ini

@@ -1,9 +1,9 @@
 [database]
-host = 10.192.72.13
-port = 6780
+host = 10.192.72.11
+port = 4321
 user = root
-password = 123456
-database = fjnx
+password = admin
+database = ai_tagging
 
 [llm]
 model = Qwen3-4B-Instruct

+ 16 - 463
agent/pdm.lock

@@ -5,7 +5,7 @@
 groups = ["default"]
 strategy = ["inherit_metadata"]
 lock_version = "4.5.0"
-content_hash = "sha256:b1461fe2ef5ca4d7887f30aedb0610d929a1b1f21128a9da6d005052a8ab9aa8"
+content_hash = "sha256:6733268f6b19c3ef998cc0e93e0c7287e601b0ce3a4de4d63bcb10731f9ab579"
 
 [[metadata.targets]]
 requires_python = "==3.10.*"
@@ -55,21 +55,6 @@ files = [
 ]
 
 [[package]]
-name = "anysqlite"
-version = "0.0.5"
-requires_python = ">=3.8"
-summary = ""
-groups = ["default"]
-marker = "python_version == \"3.10\""
-dependencies = [
-    "anyio>3.4.0",
-]
-files = [
-    {file = "anysqlite-0.0.5-py3-none-any.whl", hash = "sha256:cb345dc4f76f6b37f768d7a0b3e9cf5c700dfcb7a6356af8ab46a11f666edbe7"},
-    {file = "anysqlite-0.0.5.tar.gz", hash = "sha256:9dfcf87baf6b93426ad1d9118088c41dbf24ef01b445eea4a5d486bac2755cce"},
-]
-
-[[package]]
 name = "asyncer"
 version = "0.0.17"
 requires_python = ">=3.10"
@@ -87,18 +72,6 @@ files = [
 ]
 
 [[package]]
-name = "blinker"
-version = "1.9.0"
-requires_python = ">=3.9"
-summary = "Fast, simple object-to-object and broadcast signaling"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"},
-    {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"},
-]
-
-[[package]]
 name = "certifi"
 version = "2026.1.4"
 requires_python = ">=3.7"
@@ -166,44 +139,6 @@ files = [
 ]
 
 [[package]]
-name = "dbutils"
-version = "3.1.2"
-requires_python = ">=3.7"
-summary = "Database connections for multi-threaded environments."
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "dbutils-3.1.2-py3-none-any.whl", hash = "sha256:0cb388a89eeecf04089aef113a7007c3fac9199e9580c8549829f954870c403a"},
-    {file = "dbutils-3.1.2.tar.gz", hash = "sha256:160b5788154f1adeddc61080daff1530b4df2ba0d45af1c3bfbac76db24186b3"},
-]
-
-[[package]]
-name = "dep-logic"
-version = "0.5.2"
-requires_python = ">=3.8"
-summary = "Python dependency specifications supporting logical operations"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-dependencies = [
-    "packaging>=22",
-]
-files = [
-    {file = "dep_logic-0.5.2-py3-none-any.whl", hash = "sha256:0e72ab6676afd32fe8702896a8b9886bddd6ccd457b77ba360e7b6c4c95ce4c0"},
-    {file = "dep_logic-0.5.2.tar.gz", hash = "sha256:f8dc4a74d1bad0d35a45c236572cf5d6534b5c2e84de87f2a354c849eec7e562"},
-]
-
-[[package]]
-name = "distlib"
-version = "0.4.0"
-summary = "Distribution utilities"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"},
-    {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"},
-]
-
-[[package]]
 name = "distro"
 version = "1.9.0"
 requires_python = ">=3.6"
@@ -280,34 +215,6 @@ files = [
 ]
 
 [[package]]
-name = "filelock"
-version = "3.24.3"
-requires_python = ">=3.10"
-summary = "A platform independent file lock."
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "filelock-3.24.3-py3-none-any.whl", hash = "sha256:426e9a4660391f7f8a810d71b0555bce9008b0a1cc342ab1f6947d37639e002d"},
-    {file = "filelock-3.24.3.tar.gz", hash = "sha256:011a5644dc937c22699943ebbfc46e969cdde3e171470a6e40b9533e5a72affa"},
-]
-
-[[package]]
-name = "findpython"
-version = "0.7.1"
-requires_python = ">=3.8"
-summary = "A utility to find python versions on your system"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-dependencies = [
-    "packaging>=20",
-    "platformdirs>=4.3.6",
-]
-files = [
-    {file = "findpython-0.7.1-py3-none-any.whl", hash = "sha256:1b78b1ff6e886cbddeffe80f8ecdbf2b8b061169bbd18b673070e26b644c51ac"},
-    {file = "findpython-0.7.1.tar.gz", hash = "sha256:9f29e6a3dabdb75f2b39c949772c0ed26eab15308006669f3478cdab0d867c78"},
-]
-
-[[package]]
 name = "h11"
 version = "0.16.0"
 requires_python = ">=3.8"
@@ -320,41 +227,6 @@ files = [
 ]
 
 [[package]]
-name = "hishel"
-version = "1.1.9"
-requires_python = ">=3.10"
-summary = "Elegant HTTP Caching for Python"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-dependencies = [
-    "msgpack>=1.1.2",
-    "typing-extensions>=4.14.1",
-]
-files = [
-    {file = "hishel-1.1.9-py3-none-any.whl", hash = "sha256:6b6f294cb7593f170a9bf874849cc85330ff81f5e35d2ca189548498fed10806"},
-    {file = "hishel-1.1.9.tar.gz", hash = "sha256:47248a50e4cff4fbaa141832782d8c07b2169914916f4bd792f37449176dfa23"},
-]
-
-[[package]]
-name = "hishel"
-version = "1.1.9"
-extras = ["httpx"]
-requires_python = ">=3.10"
-summary = "Elegant HTTP Caching for Python"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-dependencies = [
-    "anyio>=4.9.0",
-    "anysqlite>=0.0.5",
-    "hishel==1.1.9",
-    "httpx>=0.28.1",
-]
-files = [
-    {file = "hishel-1.1.9-py3-none-any.whl", hash = "sha256:6b6f294cb7593f170a9bf874849cc85330ff81f5e35d2ca189548498fed10806"},
-    {file = "hishel-1.1.9.tar.gz", hash = "sha256:47248a50e4cff4fbaa141832782d8c07b2169914916f4bd792f37449176dfa23"},
-]
-
-[[package]]
 name = "httpcore"
 version = "1.0.9"
 requires_python = ">=3.8"
@@ -389,38 +261,6 @@ files = [
 ]
 
 [[package]]
-name = "httpx"
-version = "0.28.1"
-extras = ["socks"]
-requires_python = ">=3.8"
-summary = "The next generation HTTP client."
-groups = ["default"]
-marker = "python_version == \"3.10\""
-dependencies = [
-    "httpx==0.28.1",
-    "socksio==1.*",
-]
-files = [
-    {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"},
-    {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"},
-]
-
-[[package]]
-name = "id"
-version = "1.6.1"
-requires_python = ">=3.9"
-summary = "A tool for generating OIDC identities"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-dependencies = [
-    "urllib3<3,>=2",
-]
-files = [
-    {file = "id-1.6.1-py3-none-any.whl", hash = "sha256:f5ec41ed2629a508f5d0988eda142e190c9c6da971100612c4de9ad9f9b237ca"},
-    {file = "id-1.6.1.tar.gz", hash = "sha256:d0732d624fb46fd4e7bc4e5152f00214450953b9e772c182c1c22964def1a069"},
-]
-
-[[package]]
 name = "idna"
 version = "3.11"
 requires_python = ">=3.8"
@@ -433,18 +273,6 @@ files = [
 ]
 
 [[package]]
-name = "installer"
-version = "0.7.0"
-requires_python = ">=3.7"
-summary = "A library for installing Python wheels."
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "installer-0.7.0-py3-none-any.whl", hash = "sha256:05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53"},
-    {file = "installer-0.7.0.tar.gz", hash = "sha256:a26d3e3116289bb08216e0d0f7d925fcef0b0194eedfa0c944bcaaa106c4b631"},
-]
-
-[[package]]
 name = "jiter"
 version = "0.13.0"
 requires_python = ">=3.9"
@@ -642,52 +470,6 @@ files = [
 ]
 
 [[package]]
-name = "markdown-it-py"
-version = "4.0.0"
-requires_python = ">=3.10"
-summary = "Python port of markdown-it. Markdown parsing, done right!"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-dependencies = [
-    "mdurl~=0.1",
-]
-files = [
-    {file = "markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"},
-    {file = "markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"},
-]
-
-[[package]]
-name = "mdurl"
-version = "0.1.2"
-requires_python = ">=3.7"
-summary = "Markdown URL utilities"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
-    {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
-]
-
-[[package]]
-name = "msgpack"
-version = "1.1.2"
-requires_python = ">=3.9"
-summary = "MessagePack serializer"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "msgpack-1.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0051fffef5a37ca2cd16978ae4f0aef92f164df86823871b5162812bebecd8e2"},
-    {file = "msgpack-1.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a605409040f2da88676e9c9e5853b3449ba8011973616189ea5ee55ddbc5bc87"},
-    {file = "msgpack-1.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b696e83c9f1532b4af884045ba7f3aa741a63b2bc22617293a2c6a7c645f251"},
-    {file = "msgpack-1.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:365c0bbe981a27d8932da71af63ef86acc59ed5c01ad929e09a0b88c6294e28a"},
-    {file = "msgpack-1.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:41d1a5d875680166d3ac5c38573896453bbbea7092936d2e107214daf43b1d4f"},
-    {file = "msgpack-1.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:354e81bcdebaab427c3df4281187edc765d5d76bfb3a7c125af9da7a27e8458f"},
-    {file = "msgpack-1.1.2-cp310-cp310-win32.whl", hash = "sha256:e64c8d2f5e5d5fda7b842f55dec6133260ea8f53c4257d64494c534f306bf7a9"},
-    {file = "msgpack-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:db6192777d943bdaaafb6ba66d44bf65aa0e9c5616fa1d2da9bb08828c6b39aa"},
-    {file = "msgpack-1.1.2.tar.gz", hash = "sha256:3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e"},
-]
-
-[[package]]
 name = "numpy"
 version = "2.2.6"
 requires_python = ">=3.10"
@@ -790,65 +572,25 @@ files = [
 ]
 
 [[package]]
-name = "pbs-installer"
-version = "2026.2.11"
-requires_python = ">=3.8"
-summary = "Installer for Python Build Standalone"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "pbs_installer-2026.2.11-py3-none-any.whl", hash = "sha256:0a1eb8bc6c0a53f381b8dc09c18c0d7aa9e6a2495b0bf02b27d48af6b6b4d01f"},
-    {file = "pbs_installer-2026.2.11.tar.gz", hash = "sha256:7eb2730aaa8e2a9aa51db3871e494d058dbab64328deec1fc7bdbbc68578167f"},
-]
-
-[[package]]
-name = "pdm"
-version = "2.26.6"
+name = "psycopg2-binary"
+version = "2.9.11"
 requires_python = ">=3.9"
-summary = "A modern Python package and dependency manager supporting the latest PEP standards"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-dependencies = [
-    "blinker",
-    "certifi>=2024.8.30",
-    "dep-logic>=0.5",
-    "filelock>=3.13",
-    "findpython<1.0.0a0,>=0.7.0",
-    "hishel[httpx]>=1.0.0",
-    "httpcore>=1.0.6",
-    "httpx[socks]<1,>0.20",
-    "id>=1.5.0",
-    "importlib-metadata>=3.6; python_version < \"3.10\"",
-    "installer<0.8,>=0.7",
-    "packaging>22.0",
-    "pbs-installer>=2025.10.7",
-    "platformdirs",
-    "pyproject-hooks",
-    "python-dotenv>=0.15",
-    "resolvelib>=1.1",
-    "rich>=12.3.0",
-    "shellingham>=1.3.2",
-    "tomli>=1.1.0; python_version < \"3.11\"",
-    "tomlkit<1,>=0.11.1",
-    "truststore>=0.10.4; python_version >= \"3.10\"",
-    "unearth>=0.17.5",
-    "virtualenv>=20",
-]
-files = [
-    {file = "pdm-2.26.6-py3-none-any.whl", hash = "sha256:39583edee738cc62b9ec2d5b4e434f44e501b55f609401a89732b5e8a451944f"},
-    {file = "pdm-2.26.6.tar.gz", hash = "sha256:771f95b9a484f9eb34dcf8d851be6ff95333e4f3c46189f9004cfd5cc2e925f9"},
-]
-
-[[package]]
-name = "platformdirs"
-version = "4.9.2"
-requires_python = ">=3.10"
-summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
+summary = "psycopg2 - Python-PostgreSQL Database Adapter"
 groups = ["default"]
 marker = "python_version == \"3.10\""
 files = [
-    {file = "platformdirs-4.9.2-py3-none-any.whl", hash = "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd"},
-    {file = "platformdirs-4.9.2.tar.gz", hash = "sha256:9a33809944b9db043ad67ca0db94b14bf452cc6aeaac46a88ea55b26e2e9d291"},
+    {file = "psycopg2-binary-2.9.11.tar.gz", hash = "sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c"},
+    {file = "psycopg2_binary-2.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d6fe6b47d0b42ce1c9f1fa3e35bb365011ca22e39db37074458f27921dca40f2"},
+    {file = "psycopg2_binary-2.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a6c0e4262e089516603a09474ee13eabf09cb65c332277e39af68f6233911087"},
+    {file = "psycopg2_binary-2.9.11-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c47676e5b485393f069b4d7a811267d3168ce46f988fa602658b8bb901e9e64d"},
+    {file = "psycopg2_binary-2.9.11-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a28d8c01a7b27a1e3265b11250ba7557e5f72b5ee9e5f3a2fa8d2949c29bf5d2"},
+    {file = "psycopg2_binary-2.9.11-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f3f2732cf504a1aa9e9609d02f79bea1067d99edf844ab92c247bbca143303b"},
+    {file = "psycopg2_binary-2.9.11-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:865f9945ed1b3950d968ec4690ce68c55019d79e4497366d36e090327ce7db14"},
+    {file = "psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:91537a8df2bde69b1c1db01d6d944c831ca793952e4f57892600e96cee95f2cd"},
+    {file = "psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4dca1f356a67ecb68c81a7bc7809f1569ad9e152ce7fd02c2f2036862ca9f66b"},
+    {file = "psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0da4de5c1ac69d94ed4364b6cbe7190c1a70d325f112ba783d83f8440285f152"},
+    {file = "psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37d8412565a7267f7d79e29ab66876e55cb5e8e7b3bbf94f8206f6795f8f7e7e"},
+    {file = "psycopg2_binary-2.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:c665f01ec8ab273a61c62beeb8cce3014c214429ced8a308ca1fc410ecac3a39"},
 ]
 
 [[package]]
@@ -905,70 +647,6 @@ files = [
 ]
 
 [[package]]
-name = "pygments"
-version = "2.19.2"
-requires_python = ">=3.8"
-summary = "Pygments is a syntax highlighting package written in Python."
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"},
-    {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"},
-]
-
-[[package]]
-name = "pymysql"
-version = "1.1.2"
-requires_python = ">=3.8"
-summary = "Pure Python MySQL Driver"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "pymysql-1.1.2-py3-none-any.whl", hash = "sha256:e6b1d89711dd51f8f74b1631fe08f039e7d76cf67a42a323d3178f0f25762ed9"},
-    {file = "pymysql-1.1.2.tar.gz", hash = "sha256:4961d3e165614ae65014e361811a724e2044ad3ea3739de9903ae7c21f539f03"},
-]
-
-[[package]]
-name = "pyproject-hooks"
-version = "1.2.0"
-requires_python = ">=3.7"
-summary = "Wrappers to call pyproject.toml-based build backend hooks."
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913"},
-    {file = "pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8"},
-]
-
-[[package]]
-name = "python-discovery"
-version = "1.1.0"
-requires_python = ">=3.8"
-summary = "Python interpreter discovery"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-dependencies = [
-    "filelock>=3.15.4",
-    "platformdirs<5,>=4.3.6",
-]
-files = [
-    {file = "python_discovery-1.1.0-py3-none-any.whl", hash = "sha256:a162893b8809727f54594a99ad2179d2ede4bf953e12d4c7abc3cc9cdbd1437b"},
-    {file = "python_discovery-1.1.0.tar.gz", hash = "sha256:447941ba1aed8cc2ab7ee3cb91be5fc137c5bdbb05b7e6ea62fbdcb66e50b268"},
-]
-
-[[package]]
-name = "python-dotenv"
-version = "1.2.1"
-requires_python = ">=3.9"
-summary = "Read key-value pairs from a .env file and set them as environment variables"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61"},
-    {file = "python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6"},
-]
-
-[[package]]
 name = "pyyaml"
 version = "6.0.3"
 requires_python = ">=3.8"
@@ -1050,46 +728,6 @@ files = [
 ]
 
 [[package]]
-name = "resolvelib"
-version = "1.2.1"
-requires_python = ">=3.9"
-summary = "Resolve abstract dependencies into concrete ones"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "resolvelib-1.2.1-py3-none-any.whl", hash = "sha256:fb06b66c8da04172d9e72a21d7d06186d8919e32ae5ab5cdf5b9d920be805ac2"},
-    {file = "resolvelib-1.2.1.tar.gz", hash = "sha256:7d08a2022f6e16ce405d60b68c390f054efcfd0477d4b9bd019cc941c28fad1c"},
-]
-
-[[package]]
-name = "rich"
-version = "14.3.3"
-requires_python = ">=3.8.0"
-summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-dependencies = [
-    "markdown-it-py>=2.2.0",
-    "pygments<3.0.0,>=2.13.0",
-]
-files = [
-    {file = "rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d"},
-    {file = "rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b"},
-]
-
-[[package]]
-name = "shellingham"
-version = "1.5.4"
-requires_python = ">=3.7"
-summary = "Tool to Detect Surrounding Shell"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"},
-    {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"},
-]
-
-[[package]]
 name = "sniffio"
 version = "1.3.1"
 requires_python = ">=3.7"
@@ -1102,18 +740,6 @@ files = [
 ]
 
 [[package]]
-name = "socksio"
-version = "1.0.0"
-requires_python = ">=3.6"
-summary = "Sans-I/O implementation of SOCKS4, SOCKS4A, and SOCKS5."
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3"},
-    {file = "socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac"},
-]
-
-[[package]]
 name = "starlette"
 version = "0.50.0"
 requires_python = ">=3.10"
@@ -1164,30 +790,6 @@ files = [
 ]
 
 [[package]]
-name = "tomli"
-version = "2.4.0"
-requires_python = ">=3.8"
-summary = "A lil' TOML parser"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a"},
-    {file = "tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c"},
-]
-
-[[package]]
-name = "tomlkit"
-version = "0.14.0"
-requires_python = ">=3.9"
-summary = "Style preserving TOML library"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680"},
-    {file = "tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064"},
-]
-
-[[package]]
 name = "tqdm"
 version = "4.67.2"
 requires_python = ">=3.7"
@@ -1203,18 +805,6 @@ files = [
 ]
 
 [[package]]
-name = "truststore"
-version = "0.10.4"
-requires_python = ">=3.10"
-summary = "Verify certificates using native system trust stores"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-files = [
-    {file = "truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981"},
-    {file = "truststore-0.10.4.tar.gz", hash = "sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301"},
-]
-
-[[package]]
 name = "typing-extensions"
 version = "4.15.0"
 requires_python = ">=3.9"
@@ -1242,22 +832,6 @@ files = [
 ]
 
 [[package]]
-name = "unearth"
-version = "0.18.2"
-requires_python = ">=3.9"
-summary = "A utility to fetch and download python packages"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-dependencies = [
-    "httpx<1,>=0.27.0",
-    "packaging>=20",
-]
-files = [
-    {file = "unearth-0.18.2-py3-none-any.whl", hash = "sha256:31fd55d67c0e46a1ebb78993a2010568e6c4231334a3207d18d5d4a549d8d692"},
-    {file = "unearth-0.18.2.tar.gz", hash = "sha256:1e53d7f52f46dd5f875e77ff1c55b12477e215a092e4b66c9764a77df4a9b520"},
-]
-
-[[package]]
 name = "urllib3"
 version = "2.6.3"
 requires_python = ">=3.9"
@@ -1312,27 +886,6 @@ files = [
 ]
 
 [[package]]
-name = "virtualenv"
-version = "21.1.0"
-requires_python = ">=3.8"
-summary = "Virtual Python Environment builder"
-groups = ["default"]
-marker = "python_version == \"3.10\""
-dependencies = [
-    "distlib<1,>=0.3.7",
-    "filelock<4,>=3.24.2; python_version >= \"3.10\"",
-    "filelock<=3.19.1,>=3.16.1; python_version < \"3.10\"",
-    "importlib-metadata>=6.6; python_version < \"3.8\"",
-    "platformdirs<5,>=3.9.1",
-    "python-discovery>=1",
-    "typing-extensions>=4.13.2; python_version < \"3.11\"",
-]
-files = [
-    {file = "virtualenv-21.1.0-py3-none-any.whl", hash = "sha256:164f5e14c5587d170cf98e60378eb91ea35bf037be313811905d3a24ea33cc07"},
-    {file = "virtualenv-21.1.0.tar.gz", hash = "sha256:1990a0188c8f16b6b9cf65c9183049007375b26aad415514d377ccacf1e4fb44"},
-]
-
-[[package]]
 name = "xxhash"
 version = "3.6.0"
 requires_python = ">=3.7"

+ 2 - 4
agent/pyproject.toml

@@ -1,7 +1,7 @@
 [project]
 name = "agent"
 version = "0.1.0"
-description = "ai-tagging"
+description = "Default template for PDM package"
 authors = [
     {name = "jiayongqiang", email = "15936285643@163.com"},
 ]
@@ -11,12 +11,10 @@ dependencies = [
     "requests>=2.32.5",
     "langchain>=1.2.9",
     "langchain-openai>=1.1.7",
-    "dbutils>=3.1.2",
-    "pymysql>=1.1.2",
     "elasticsearch==8.9.0",
     "numpy>=2.2.6",
     "asyncer>=0.0.17",
-    "pdm>=2.26.6",
+    "psycopg2-binary>=2.9.11",
 ]
 requires-python = ">=3.10"
 readme = "README.md"

+ 4 - 1
agent/readme.md

@@ -29,7 +29,10 @@
 - URL: /api/aitag/v1/tagging
 - METHOD:POST
 - Content-Type:application/json
-- Body:{"business_attr": "test_attr","phrase": "职业:水产养殖人员 投向:海水养殖 用途:个人经营"}
+- Body:{
+  - "business_attr": "test_attr",
+  - "phrase": "职业:水产养殖人员 投向:海水养殖 用途:个人经营",
+  - "tag_category_id":"xxx"}
 
 ### 2.2 查询接口
 - URL:/api/aitag/v1/query

BIN=BIN
agent/release.tar.gz


+ 1 - 0
agent/src/agent/agent.py

@@ -27,6 +27,7 @@ class Lable(BaseModel):
     id: str = Field(description="标签ID")
     tag_code: str = Field(description="标签编码")
     tag_name: str = Field(description="标签名称")
+    tag_path: str = Field(description="标签路径")
     desc: str = Field(description="给出简要的理由说明该标签为何被保留或剔除。")
     passr:bool = Field(description="是否保留该标签,true表示保留,false表示剔除。")
 

+ 3 - 4
agent/src/agent/api_inner.py

@@ -44,14 +44,14 @@ def load_tag_2_es(tag_ids: list[str]):
                     tti.tag_code,
                     tti.tag_remark,
                     tti.reg,
-                    tti.level,
+                    tti.tag_level,
                     tti.tag_path,
                     ttc.category_nm,
                     ttc.category_code,
                     tti.tag_prompt
-                    from aitag_tag_info tti left join aitag_tag_category  ttc 
+                    from ai_tagging.aitag_tag_info tti left join ai_tagging.aitag_tag_category  ttc 
                     on tti.category_id = ttc.id 
-                    where ttc.is_delete=0 and tti.is_delete=0 and ttc.state =  0 and tti.state = 0 and tti.`level` = ttc.visibility_level
+                    where ttc.is_delete=0 and tti.is_delete=0 and ttc.state =  0 and tti.state = 0 and tti.tag_level = ttc.visibility_level
                     and tti.id in ({placeholders})"""    
     labels = dao.query(sql,tag_ids)
     res = es.bulk_upsert([{
@@ -65,7 +65,6 @@ def load_tag_2_es(tag_ids: list[str]):
         "tag_prompt": label[9],
         "tag_vector": get_embeddings([(label[6] or '')+(label[4] or '')])[0] 
     } for label in labels])
-    print(f"bulk_upsert result: {res}")
     return labels
 
 @router.post("/synchronize_tag")

+ 20 - 14
agent/src/agent/api_outter.py

@@ -29,15 +29,18 @@ class TaggingRequest(BaseModel):
     sign: Optional[str] = Field(None, description="请求签名")
     business_attr: str = Field(..., description="业务属性")
     phrase: str = Field(..., description="需要打标签的文本")
+    tag_category_id: Optional[str] = Field(None, description="指定标签类别ID,默认为空表示不指定")
 
-async def execute_reg(id:str,phrase: str)-> list:
+async def execute_reg(log_id:str,tag_category_id:str,phrase: str)-> list:
     sql = f"""select 
                     tti.id,
                     tti.reg
-                    from aitag_tag_info tti left join aitag_tag_category  ttc 
+                    from ai_tagging.aitag_tag_info tti left join ai_tagging.aitag_tag_category  ttc 
                     on tti.category_id = ttc.id 
-                    where ttc.is_delete=0 and tti.is_delete=0 and ttc.state =  0 and tti.state = 0 and tti.`level` = ttc.visibility_level
+                    where ttc.is_delete=0 and tti.is_delete=0 and ttc.state =  0 and tti.state = 0 and tti.tag_level = ttc.visibility_level
                     """    
+    if tag_category_id:
+        sql += f""" and ttc.id = '{tag_category_id}'"""
     labels = dao.query(sql)
     # 循环调用reg匹配phrase,匹配成功则返回标签id
     result = []
@@ -55,8 +58,8 @@ async def execute_reg(id:str,phrase: str)-> list:
         logging.error(f"Regex execution failed: {e}")
     logging.info(f"Regex filtering candidates: {result}")
     dao.execute(
-            """UPDATE aitag_tag_log SET reg_result = %s WHERE id = %s""",
-            (str(result), id)
+            """UPDATE ai_tagging.aitag_tag_log SET reg_result = %s WHERE id = %s""",
+            (str(result), log_id)
         )
     logging.info(f"Updated reg_result for log_id {id}")
     return result
@@ -66,31 +69,33 @@ def vector_similarity_search(phrase: str, ids:list)-> list:
     query = get_embeddings([phrase])[0]
     results = hybrid_search(ids, query, top_k=TOP_K)
     # return [{"id": r["_id"], "score": r["_score"], "tag_prompt": r["_source"]["tag_prompt"],"tag_name": r["_source"]["tag_name"],"tag_code": r["_source"]["tag_code"]} for r in results]
-    return [{"id": r["_id"], "tag_remark":r["_source"]["tag_remark"], "tag_prompt": r["_source"]["tag_prompt"],"tag_name": r["_source"]["tag_name"],"tag_code": r["_source"]["tag_code"]} for r in results]
+    return [{"id": r["_id"], "tag_remark":r["_source"]["tag_remark"], "tag_prompt": r["_source"]["tag_prompt"],"tag_name": r["_source"]["tag_name"],"tag_code": r["_source"]["tag_code"],"tag_path": r["_source"]["tag_path"]   } for r in results]
 
 def init_tag_log(request: TaggingRequest):
     id = uuid.uuid4().hex
     # 保证business_attr的唯一性,如果已经存在相同business_attr且状态为0(处理中)的记录,则将is_delete设为1(已删除),然后插入新记录
     dao.execute(
-        """UPDATE aitag_tag_log SET is_delete = 1 WHERE business_attr = %s""",
+        """UPDATE ai_tagging.aitag_tag_log SET is_delete = 1 WHERE business_attr = %s""",
         (request.business_attr,)
     )
+    # 业务编号如果以test开头,则tag_scope = 1,否则都是0
+    tag_scope = 1 if request.business_attr.startswith("test") else 0
     dao.execute(
-        """INSERT INTO aitag_tag_log (id,app_id, insert_time, business_attr, phrase, state) VALUES (%s, %s, %s, %s, %s, %s)""",
-        (id,request.app_id, datetime.now(), request.business_attr, request.phrase, 0)
+        """INSERT INTO ai_tagging.aitag_tag_log (id,app_id, insert_time, business_attr, phrase, state, tag_scope) VALUES (%s, %s, %s, %s, %s, %s, %s)""",
+        (id,request.app_id, datetime.now(), request.business_attr, request.phrase, 0, tag_scope)
     )
     return id
 
 def update_tag_log(id:str, result:str):
     dao.execute(
-            """UPDATE aitag_tag_log SET state = %s, result = %s WHERE id = %s""",
+            """UPDATE ai_tagging.aitag_tag_log SET state = %s, result = %s WHERE id = %s""",
             (1, result, id)
         )
 
-async def run_ai_pipeline(log_id: str, phrase: str):
+async def run_ai_pipeline(log_id: str, tag_category_id: str, phrase: str):
     try:
         # step1: 正则过滤
-        result = await execute_reg(log_id,phrase)
+        result = await execute_reg(log_id,tag_category_id,phrase)
         logging.info(f"[{log_id}] Regex filtering result: {result}")
 
         # step2: 向量检索
@@ -120,6 +125,7 @@ async def ai_tagging(request: TaggingRequest,background_tasks: BackgroundTasks):
     background_tasks.add_task(
         run_ai_pipeline,  # 后台任务函数
         log_id=id,
+        tag_category_id=request.tag_category_id,    
         phrase=request.phrase
     )
     logging.info(f"Started background task for log_id: {id}")
@@ -131,7 +137,7 @@ async def ai_tagging(request: TaggingRequest,background_tasks: BackgroundTasks):
 @router.get("/query")
 def query(business_attr: str):
     # 查询指定business_attr对应的标签信息
-    sql = """SELECT * FROM aitag_tag_log WHERE business_attr = %s and is_delete = 0"""
+    sql = """SELECT * FROM ai_tagging.aitag_tag_log WHERE business_attr = %s and is_delete = 0"""
     result = dao.query_dict(sql, (business_attr,))
     print(result)
     return {"code": 200, "message": "AI Query Endpoint", "data": result[0] if result else None}
@@ -151,7 +157,7 @@ def ai_feedback(feedback_request: FeedbackRequest):
     logging.info(f"Received feedback request: {feedback_request}")
     # 这里将用户的反馈信息保存到数据库中aitag_tag_log,供后续分析和模型优化使用
     dao.execute(
-        """update aitag_tag_log set feedback = %s, feedback_result = %s, feedback_time = %s, feedback_user_id = %s, feedback_user_nm = %s, contract_no = %s, feedback_user_org = %s, feedback_user_endpoint = %s, state = %s where business_attr = %s""",   
+        """update ai_tagging.aitag_tag_log set feedback = %s, feedback_result = %s, feedback_time = %s, feedback_user_id = %s, feedback_user_nm = %s, contract_no = %s, feedback_user_org = %s, feedback_user_endpoint = %s, state = %s where business_attr = %s""",   
         (feedback_request.feedback, feedback_request.feedback_result, datetime.now(), feedback_request.user_id, feedback_request.user_nm, feedback_request.contract_no, feedback_request.user_org, feedback_request.user_endpoint, 2, feedback_request.business_attr)
     )
     return {"code": 200, "message": "Feedback received successfully"}

+ 22 - 21
agent/src/agent/core/dao.py

@@ -1,6 +1,7 @@
-from dbutils.pooled_db import PooledDB 
-import pymysql
+import psycopg2
+from psycopg2 import pool
 import configparser
+from contextlib import contextmanager
 
 config = configparser.ConfigParser()
 config.read('config.ini')
@@ -11,32 +12,34 @@ user = config['database']['user']
 password = config['database']['password']
 database = config['database']['database']
 
-pool = PooledDB(
-    creator=pymysql,                    # 使用 PyMySQL 作为底层驱动
+pool = psycopg2.pool.SimpleConnectionPool(
+    20, 50,
     host=host,
     port=port,
-    user=user,
-    password=password,
     database=database,
-    charset='utf8mb4',
-    autocommit=True,                    # 自动提交事务(根据需求设置)
-    mincached=2,                        # 初始化时创建的空闲连接数
-    maxcached=10,                       # 最大空闲连接数
-    maxshared=10,                       # 最大共享连接数(PyMySQL 不支持共享,可忽略)
-    maxconnections=20,                  # 最大连接数
-    blocking=True,                      # 连接数满时是否阻塞等待
-    ping=1                              # 每次从池取连接前 ping 一下(0=不 ping, 1~7=不同级别)
+    user=user,
+    password=password
 )
 
+@contextmanager
+def get_db_connection():
+    conn = None
+    try:
+        conn = pool.getconn()
+        yield conn
+    finally:
+        if conn:
+            pool.putconn(conn)
+
 def query(sql, params=None):
-    with pool.connection() as conn:
+    with get_db_connection() as conn:
         with conn.cursor() as cursor:
             cursor.execute(sql, params)
             result = cursor.fetchall()
             return result
 
 def query_dict(sql, params=None):
-    with pool.connection() as conn:
+    with get_db_connection() as conn:
         with conn.cursor() as cursor:
             cursor.execute(sql, params)
             result = cursor.fetchall()
@@ -46,17 +49,15 @@ def query_dict(sql, params=None):
    
 
 def execute(sql, params=None):
-    conn = pool.connection()
-    try:
+    with get_db_connection() as conn:
         with conn.cursor() as cursor:
             cursor.execute(sql, params)
             conn.commit()
-    finally:
-        conn.close()
+     
 
 if __name__ == "__main__":
     print(host, port, user, password, database)
-    with pool.connection() as conn:
+    with get_db_connection() as conn:
         with conn.cursor() as cursor:
             cursor.execute("SELECT VERSION()")
             version = cursor.fetchone()

+ 1 - 2
agent/src/agent/core/es.py

@@ -106,5 +106,4 @@ def hybrid_search( target_doc_ids, query_vector, top_k=2):
 if __name__ == "__main__":
     results = search_all()
     print(f"Search results: {len(results)}")
-    for result in results:
-        print(result["_source"])
+    

+ 2 - 2
agent/tests/test_feedback.py

@@ -9,14 +9,14 @@ logging.basicConfig(level=logging.INFO, force=True,format='%(asctime)s - %(level
 #     user_org: Optional[str] = Field(None, description="用户所属机构")
 #     user_endpoint: Optional[str] = Field(None, description="用户所属网点")
 #     business_attr: str = Field(..., description="业务属性")
-res = requests.post("http://localhost:8080/api/aitag/v1/feedback", json={
+res = requests.post("http://localhost:9876/api/aitag/v1/feedback", json={
     "user_id": "12345",
     "user_nm": "张三",
     "feedback": "agree",
     "contract_no": "HT-2024-001",
     "user_org": "XX公司",
     "user_endpoint": "XX网点",
-    "business_attr": "99c1c7bc976c45d0912e914a602d547f"
+    "business_attr": "test_attr"
 })
 logging.info(res.text)
 

+ 6 - 16
agent/tests/test_inner_api.py

@@ -82,27 +82,17 @@ ids = [
 "20260200080",
 "20260200081",
 "20260200082",
-"20260200083"
+"20260200083",
 ]
 
-res = requests.post("http://localhost:8080/api/aitag/admin/v1/synchronize_tag", json={
+res = requests.post("http://localhost:9876/api/aitag/admin/v1/synchronize_tag", json={
     "tag_ids": ids
 })
-print(res.json())
+print(res.text)
 
-# res = requests.post("http://localhost:8080/api/aitag/admin/v1/delete_tag", json={
-#     "tag_ids": ["2","3"]
+# res = requests.post("http://localhost:9876/api/aitag/admin/v1/delete_tag", json={
+#     "tag_ids": ["20260200083"]
 # })
 
-# print(res.json())
+# print(res.text)
 
-# # 在mysql中修改标签后,调用该接口同步标签到es中
-# res = requests.post("http://10.192.72.13:9876/api/aitag/admin/v1/synchronize_tag", json={
-#     "tag_ids": ["2","3"]
-# })
-# print(res.json())
-
-# # 在mysql中删除标签后,调用该接口同步标签到es中
-# res = requests.post("http://10.192.72.13:9876/api/aitag/admin/v1/delete_tag", json={
-#     "tag_ids": ["2","3"]
-# })

+ 1 - 1
agent/tests/test_query.py

@@ -1,4 +1,4 @@
 import requests
 
-result = requests.get("http://localhost:8080/api/aitag/v1/query?business_attr=d372684dbae64b3dadd33b631480d946")
+result = requests.get("http://localhost:9876/api/aitag/v1/query?business_attr=test_attr")
 print(result.text)

+ 1 - 1
agent/tests/test_tagging.py

@@ -3,7 +3,7 @@ import logging
 logging.basicConfig(level=logging.INFO, force=True,format='%(asctime)s - %(levelname)s - %(message)s')
 logging.info("app starting!")
 
-res = requests.post("http://10.192.72.13:9876/api/aitag/v1/tagging", json={
+res = requests.post("http://localhost:9876/api/aitag/v1/tagging", json={
     # "app_id": "test_app",
     # "timestamp": 1234567890,
     # "sign": "test_sign",

+ 1 - 1
agent/tests/test_tagging_batch.py

@@ -16,7 +16,7 @@ with open("tests/test_data.txt", "r", encoding="utf-8") as f:
             phrase = "职业:"+phrase[0]+" "+"投向:"+phrase[1]+" "+"用途:"+phrase[2]
 
         logging.info("测试用例: "+phrase)
-        res = requests.post("http://localhost:8080/api/aitag/v1/tagging", json={
+        res = requests.post("http://10.192.72.13:9876/api/aitag/v1/tagging", json={
             "business_attr": uuid.uuid4().hex,
             "phrase": phrase
         })

BIN=BIN
agent/服务接口定义.xlsx