From 99e02d88b1e6ec45bdbbb37064fd0a1c0f46a9cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=85=E8=AF=BA=E7=8B=90?= <212194964+foxcyber907@users.noreply.github.com> Date: Tue, 9 Dec 2025 22:26:28 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=81=97=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 1 + uv.lock | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index ae1304aa7..f83ac462e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,6 +85,7 @@ dependencies = [ "jinja2>=3.1.0", "psycopg2-binary", "redis>=7.1.0", + "asyncpg>=0.31.0", ] [[tool.uv.index]] diff --git a/uv.lock b/uv.lock index a072c52d3..db1f928e0 100644 --- a/uv.lock +++ b/uv.lock @@ -250,6 +250,38 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/57/64/eff2564783bd650ca25e15938d1c5b459cda997574a510f7de69688cb0b4/asyncio-4.0.0-py3-none-any.whl", hash = "sha256:c1eddb0659231837046809e68103969b2bef8b0400d59cfa6363f6b5ed8cc88b", size = 5555, upload-time = "2025-08-05T02:51:45.767Z" }, ] +[[package]] +name = "asyncpg" +version = "0.31.0" +source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } +sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/cc/d18065ce2380d80b1bcce927c24a2642efd38918e33fd724bc4bca904877/asyncpg-0.31.0.tar.gz", hash = "sha256:c989386c83940bfbd787180f2b1519415e2d3d6277a70d9d0f0145ac73500735", size = 993667, upload-time = "2025-11-24T23:27:00.812Z" } +wheels = [ + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/17/cc02bc49bc350623d050fa139e34ea512cd6e020562f2a7312a7bcae4bc9/asyncpg-0.31.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eee690960e8ab85063ba93af2ce128c0f52fd655fdff9fdb1a28df01329f031d", size = 643159, upload-time = "2025-11-24T23:25:36.443Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a4/62/4ded7d400a7b651adf06f49ea8f73100cca07c6df012119594d1e3447aa6/asyncpg-0.31.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2657204552b75f8288de08ca60faf4a99a65deef3a71d1467454123205a88fab", size = 638157, upload-time = "2025-11-24T23:25:37.89Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d6/5b/4179538a9a72166a0bf60ad783b1ef16efb7960e4d7b9afe9f77a5551680/asyncpg-0.31.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a429e842a3a4b4ea240ea52d7fe3f82d5149853249306f7ff166cb9948faa46c", size = 2918051, upload-time = "2025-11-24T23:25:39.461Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/35/c27719ae0536c5b6e61e4701391ffe435ef59539e9360959240d6e47c8c8/asyncpg-0.31.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c0807be46c32c963ae40d329b3a686356e417f674c976c07fa49f1b30303f109", size = 2972640, upload-time = "2025-11-24T23:25:41.512Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/43/f4/01ebb9207f29e645a64699b9ce0eefeff8e7a33494e1d29bb53736f7766b/asyncpg-0.31.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e5d5098f63beeae93512ee513d4c0c53dc12e9aa2b7a1af5a81cddf93fe4e4da", size = 2851050, upload-time = "2025-11-24T23:25:43.153Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3e/f4/03ff1426acc87be0f4e8d40fa2bff5c3952bef0080062af9efc2212e3be8/asyncpg-0.31.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37fc6c00a814e18eef51833545d1891cac9aa69140598bb076b4cd29b3e010b9", size = 2962574, upload-time = "2025-11-24T23:25:44.942Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/39/cc788dfca3d4060f9d93e67be396ceec458dfc429e26139059e58c2c244d/asyncpg-0.31.0-cp311-cp311-win32.whl", hash = "sha256:5a4af56edf82a701aece93190cc4e094d2df7d33f6e915c222fb09efbb5afc24", size = 521076, upload-time = "2025-11-24T23:25:46.486Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/fc/735af5384c029eb7f1ca60ccb8fa95521dbdaeef788edf4cecfc604c3cab/asyncpg-0.31.0-cp311-cp311-win_amd64.whl", hash = "sha256:480c4befbdf079c14c9ca43c8c5e1fe8b6296c96f1f927158d4f1e750aacc047", size = 584980, upload-time = "2025-11-24T23:25:47.938Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/a6/59d0a146e61d20e18db7396583242e32e0f120693b67a8de43f1557033e2/asyncpg-0.31.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b44c31e1efc1c15188ef183f287c728e2046abb1d26af4d20858215d50d91fad", size = 662042, upload-time = "2025-11-24T23:25:49.578Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/36/01/ffaa189dcb63a2471720615e60185c3f6327716fdc0fc04334436fbb7c65/asyncpg-0.31.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0c89ccf741c067614c9b5fc7f1fc6f3b61ab05ae4aaa966e6fd6b93097c7d20d", size = 638504, upload-time = "2025-11-24T23:25:51.501Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9f/62/3f699ba45d8bd24c5d65392190d19656d74ff0185f42e19d0bbd973bb371/asyncpg-0.31.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:12b3b2e39dc5470abd5e98c8d3373e4b1d1234d9fbdedf538798b2c13c64460a", size = 3426241, upload-time = "2025-11-24T23:25:53.278Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/d1/a867c2150f9c6e7af6462637f613ba67f78a314b00db220cd26ff559d532/asyncpg-0.31.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:aad7a33913fb8bcb5454313377cc330fbb19a0cd5faa7272407d8a0c4257b671", size = 3520321, upload-time = "2025-11-24T23:25:54.982Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/1a/cce4c3f246805ecd285a3591222a2611141f1669d002163abef999b60f98/asyncpg-0.31.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3df118d94f46d85b2e434fd62c84cb66d5834d5a890725fe625f498e72e4d5ec", size = 3316685, upload-time = "2025-11-24T23:25:57.43Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/ae/0fc961179e78cc579e138fad6eb580448ecae64908f95b8cb8ee2f241f67/asyncpg-0.31.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bd5b6efff3c17c3202d4b37189969acf8927438a238c6257f66be3c426beba20", size = 3471858, upload-time = "2025-11-24T23:25:59.636Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/52/b2/b20e09670be031afa4cbfabd645caece7f85ec62d69c312239de568e058e/asyncpg-0.31.0-cp312-cp312-win32.whl", hash = "sha256:027eaa61361ec735926566f995d959ade4796f6a49d3bde17e5134b9964f9ba8", size = 527852, upload-time = "2025-11-24T23:26:01.084Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b5/f0/f2ed1de154e15b107dc692262395b3c17fc34eafe2a78fc2115931561730/asyncpg-0.31.0-cp312-cp312-win_amd64.whl", hash = "sha256:72d6bdcbc93d608a1158f17932de2321f68b1a967a13e014998db87a72ed3186", size = 597175, upload-time = "2025-11-24T23:26:02.564Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/11/97b5c2af72a5d0b9bc3fa30cd4b9ce22284a9a943a150fdc768763caf035/asyncpg-0.31.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c204fab1b91e08b0f47e90a75d1b3c62174dab21f670ad6c5d0f243a228f015b", size = 661111, upload-time = "2025-11-24T23:26:04.467Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1b/71/157d611c791a5e2d0423f09f027bd499935f0906e0c2a416ce712ba51ef3/asyncpg-0.31.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:54a64f91839ba59008eccf7aad2e93d6e3de688d796f35803235ea1c4898ae1e", size = 636928, upload-time = "2025-11-24T23:26:05.944Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2e/fc/9e3486fb2bbe69d4a867c0b76d68542650a7ff1574ca40e84c3111bb0c6e/asyncpg-0.31.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0e0822b1038dc7253b337b0f3f676cadc4ac31b126c5d42691c39691962e403", size = 3424067, upload-time = "2025-11-24T23:26:07.957Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/c6/8c9d076f73f07f995013c791e018a1cd5f31823c2a3187fc8581706aa00f/asyncpg-0.31.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bef056aa502ee34204c161c72ca1f3c274917596877f825968368b2c33f585f4", size = 3518156, upload-time = "2025-11-24T23:26:09.591Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ae/3b/60683a0baf50fbc546499cfb53132cb6835b92b529a05f6a81471ab60d0c/asyncpg-0.31.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0bfbcc5b7ffcd9b75ab1558f00db2ae07db9c80637ad1b2469c43df79d7a5ae2", size = 3319636, upload-time = "2025-11-24T23:26:11.168Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/dc/8487df0f69bd398a61e1792b3cba0e47477f214eff085ba0efa7eac9ce87/asyncpg-0.31.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:22bc525ebbdc24d1261ecbf6f504998244d4e3be1721784b5f64664d61fbe602", size = 3472079, upload-time = "2025-11-24T23:26:13.164Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/13/a1/c5bbeeb8531c05c89135cb8b28575ac2fac618bcb60119ee9696c3faf71c/asyncpg-0.31.0-cp313-cp313-win32.whl", hash = "sha256:f890de5e1e4f7e14023619399a471ce4b71f5418cd67a51853b9910fdfa73696", size = 527606, upload-time = "2025-11-24T23:26:14.78Z" }, + { url = "https://pypi.tuna.tsinghua.edu.cn/packages/91/66/b25ccb84a246b470eb943b0107c07edcae51804912b824054b3413995a10/asyncpg-0.31.0-cp313-cp313-win_amd64.whl", hash = "sha256:dc5f2fa9916f292e5c5c8b2ac2813763bcd7f58e130055b4ad8a0531314201ab", size = 596569, upload-time = "2025-11-24T23:26:16.189Z" }, +] + [[package]] name = "attrs" version = "25.4.0" @@ -2020,6 +2052,7 @@ dependencies = [ { name = "aiosqlite" }, { name = "apscheduler" }, { name = "asyncio" }, + { name = "asyncpg" }, { name = "beautifulsoup4" }, { name = "botocore" }, { name = "chromadb" }, @@ -2110,6 +2143,7 @@ requires-dist = [ { name = "aiosqlite", specifier = ">=0.21.0" }, { name = "apscheduler", specifier = ">=3.11.0" }, { name = "asyncio", specifier = ">=4.0.0" }, + { name = "asyncpg", specifier = ">=0.31.0" }, { name = "beautifulsoup4", specifier = ">=4.13.4" }, { name = "botocore", specifier = ">=1.35.0" }, { name = "chromadb", specifier = ">=1.2.0" }, From 44f85c40bf0e1a0babcc1204b4f93ab7cb1fe49a Mon Sep 17 00:00:00 2001 From: tt-P607 <68868379+tt-P607@users.noreply.github.com> Date: Tue, 9 Dec 2025 22:52:36 +0800 Subject: [PATCH 2/2] =?UTF-8?q?refactor(profile,llm):=20=E6=8F=90=E9=AB=98?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E8=B5=84=E6=96=99=E7=9A=84=E5=87=86=E7=A1=AE?= =?UTF-8?q?=E6=80=A7=E5=92=8C=E7=B3=BB=E7=BB=9F=E7=9A=84=E7=A8=B3=E5=81=A5?= =?UTF-8?q?=E6=80=A7=E3=80=82=E6=9C=AC=E6=AC=A1=E6=8F=90=E4=BA=A4=E5=BC=95?= =?UTF-8?q?=E5=85=A5=E4=BA=86=E5=A4=9A=E9=A1=B9=E9=92=88=E5=AF=B9=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E8=B5=84=E6=96=99=E7=AE=A1=E7=90=86=E5=92=8C=E5=A4=A7?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E6=A8=A1=E5=9E=8B=E4=BA=A4=E4=BA=92=E7=9A=84?= =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=8C=E7=9B=AE=E6=A0=87=E6=98=AF=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E6=9B=B4=E9=AB=98=E7=9A=84=E5=87=86=E7=A1=AE=E6=80=A7?= =?UTF-8?q?=E3=80=81=E6=9B=B4=E4=B8=A5=E6=A0=BC=E7=9A=84=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E4=BB=A5=E5=8F=8A=E6=8F=90=E5=8D=87=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E5=8F=AF=E9=9D=A0=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - **用户资料管理(`user_profile_tool.py`):** - `UserProfileTool` 的描述进行了大幅更新,明确定义了严格的使用场景和绝对禁止的行为,防止误用。 - 对 `preference_keywords` 和 `key_info` 的值实施了更严格的过滤,确保只记录具体、客观的事实和真实兴趣。 - 减少了用于上下文的最近聊天消息数量,以更关注相关性更高的近期交互。 - 修改了好感度计算逻辑,使其更加保守,不容易因日常小互动而改变,需要更有意义的交流才会产生变化。 - 印象生成提示已更新,严格禁止猜测。 并强调记录事实观察到的特征。- **关系信息显示(`relationship_fetcher.py`):** - 通过过滤掉一般交互术语来增强用户偏好显示,仅展示真实的爱好和兴趣。- 暂时注释了“关键事实”的显示,以防呈现潜在不准确或推测性的信息。- **大型语言模型交互稳定性(`base_action.py`):** - 在 `should_activate` 方法中引入了 7 秒超时的 LLM 判断调用。- 如果 LLM 判断超时,动作现在默认为“激活”,以防止系统阻塞并确保持续运行。 --- src/person_info/relationship_fetcher.py | 40 ++-- src/plugin_system/base/base_action.py | 19 +- .../tools/user_profile_tool.py | 174 +++++++++++------- 3 files changed, 145 insertions(+), 88 deletions(-) diff --git a/src/person_info/relationship_fetcher.py b/src/person_info/relationship_fetcher.py index e5df2c651..586f7573c 100644 --- a/src/person_info/relationship_fetcher.py +++ b/src/person_info/relationship_fetcher.py @@ -173,24 +173,34 @@ class RelationshipFetcher: if impression: relation_parts.append(f"\n你对{person_name}的印象:\n{impression}") - # 5. 用户偏好关键词 + # 5. 用户偏好关键词(仅显示真实兴趣爱好) if rel_data.get("preference_keywords"): keywords_list = [kw.strip() for kw in rel_data["preference_keywords"].split(",") if kw.strip()] - if keywords_list: - keywords_str = "、".join(keywords_list) - relation_parts.append(f"\n{person_name}的偏好和兴趣:{keywords_str}") + # 过滤掉明显不是兴趣爱好的词 + filtered_keywords = [] + for kw in keywords_list: + kw_lower = kw.lower() + # 排除聊天互动、情感需求等不是真实兴趣的词汇 + if not any(excluded in kw_lower for excluded in [ + '亲亲', '撒娇', '被宠', '被夸', '聊天', '互动', '关心', '专注', '需要' + ]): + filtered_keywords.append(kw) + + if filtered_keywords: + keywords_str = "、".join(filtered_keywords) + relation_parts.append(f"\n{person_name}的兴趣爱好:{keywords_str}") - # 6. 关键信息 - if rel_data.get("key_facts"): - try: - import orjson - facts = orjson.loads(rel_data["key_facts"]) - if facts and isinstance(facts, list): - facts_lines = self._format_key_facts(facts, person_name) - if facts_lines: - relation_parts.append(f"\n你记住的关于{person_name}的重要信息:\n{facts_lines}") - except Exception: - pass + # 6. 关键信息 - 暂时隐藏,防止显示不准确的推测信息 + # if rel_data.get("key_facts"): + # try: + # import orjson + # facts = orjson.loads(rel_data["key_facts"]) + # if facts and isinstance(facts, list): + # facts_lines = self._format_key_facts(facts, person_name) + # if facts_lines: + # relation_parts.append(f"\n你记住的关于{person_name}的重要信息:\n{facts_lines}") + # except Exception: + # pass except Exception as e: logger.error(f"查询UserRelationships表失败: {e}") diff --git a/src/plugin_system/base/base_action.py b/src/plugin_system/base/base_action.py index 44ef212ce..bf36bcc14 100644 --- a/src/plugin_system/base/base_action.py +++ b/src/plugin_system/base/base_action.py @@ -838,11 +838,20 @@ class BaseAction(ABC): 只需要回答"是"或"否",不要有其他内容。 """ - # 调用 LLM 进行判断 - response, _ = await llm_judge_model.generate_response_async(prompt=prompt) - response = response.strip().lower() - - should_activate = "是" in response or "yes" in response or "true" in response + # 调用 LLM 进行判断,设置7秒超时避免长时间等待 + import asyncio + response = "" # 初始化response变量 + try: + response, _ = await asyncio.wait_for( + llm_judge_model.generate_response_async(prompt=prompt), + timeout=7.0 + ) + response = response.strip().lower() + should_activate = "是" in response or "yes" in response or "true" in response + except asyncio.TimeoutError: + logger.warning(f"{self.log_prefix} LLM 判断激活超时(7秒),默认激活以避免阻塞") + # 超时时默认激活,交给后续决策系统处理 + should_activate = True logger.debug( f"{self.log_prefix} LLM 判断结果: 响应='{response}', 结果={'激活' if should_activate else '不激活'}" diff --git a/src/plugins/built_in/affinity_flow_chatter/tools/user_profile_tool.py b/src/plugins/built_in/affinity_flow_chatter/tools/user_profile_tool.py index dfa7d8f96..9fc7c016b 100644 --- a/src/plugins/built_in/affinity_flow_chatter/tools/user_profile_tool.py +++ b/src/plugins/built_in/affinity_flow_chatter/tools/user_profile_tool.py @@ -43,32 +43,34 @@ class UserProfileTool(BaseTool): """ name = "update_user_profile" - description = """记录或更新你对某个人的认识。 + description = """⚠️ 严格限制使用场景 ⚠️ -使用场景: -1. TA告诉你个人信息(生日、职业、城市等)→ 填 key_info_type 和 key_info_value -2. TA的信息有变化(搬家、换工作等)→ 会自动更新旧信息 -3. 你对TA有了新的认识或感受 → 填 impression_hint -4. 想记录TA真正的兴趣爱好 → 填 preference +记录或更新你对某个人的认识 - 仅限重要信息! -## ⛔ 别名(alias)规则: -- 只填TA明确要求被称呼的真实昵称 -- 必须是TA主动说"叫我xxx"或"我的昵称是xxx" -- 聊天中的玩笑称呼、撒娇称呼、临时戏称一律不填 -- 你给TA起的爱称不算别名 +## 📋 明确的使用场景(必须符合其中一种): +1. TA明确说出具体个人信息("我生日是3月15日"、"我在北京工作"、"我是程序员")→ 填 key_info +2. TA的重要信息发生变化("我搬到上海了"、"我换工作了")→ 更新 key_info +3. TA主动深度自我揭露重大个人经历或核心价值观 → 慎重考虑填 impression_hint +4. TA明确表达具体的现实兴趣爱好("我喜欢摄影"、"我在学编程")→ 填 preference -## ⛔ 偏好(preference)规则: -- 只填可以作为兴趣爱好的名词(如:编程、摄影、音乐、游戏) -- 必须是TA在现实中真正从事或喜欢的活动/领域 -- 聊天互动方式不是爱好(撒娇、亲亲、被夸奖等不填) -- 你们之间的私密互动不是爱好 -- 情感状态不是爱好 +## 🚫 绝对禁止的情况(常见误用): +- 一般性聊天、日常互动、开玩笑 → 绝对不用 +- 撒娇、求抱抱、情感表达 → 绝对不用 +- 描述聊天感受、互动方式 → 绝对不用 +- 状态描述("累了"、"开心"、"忙")→ 绝对不用 +- 你的推测或印象 → 绝对不用 +- 聊天话题、兴趣讨论 → 绝对不用 -## ⛔ 关键信息(key_info)规则: -- 只填客观可验证的事实信息 -- 必须是具体的值(日期、地点、职业名称) -- 你的主观感受不是TA的信息 -- 关系描述不是信息 +## ⛔ 关键信息(key_info)严格标准: +- job: 必须是具体职业("程序员"、"医生"、"学生"),不能是状态("工作很累"、"上班族") +- birthday: 具体日期("3月15日"、"1995年"),不能是模糊描述 +- location: 具体地点("北京"、"上海浦东"),不能是"在家"、"公司" +- 如果不是TA明确说出的具体事实,绝对不要记录 + +## ⛔ 印象更新(impression_hint)超严格标准: +- 只有深度心理揭露、重大人生事件、核心价值观分享才考虑 +- 聊天互动方式、日常行为表现、情感表达方式 → 绝对不记录 +- 默认策略:当有疑虑时,不要使用此工具 此工具在后台异步执行,不影响回复速度。""" parameters = [ @@ -195,7 +197,11 @@ class UserProfileTool(BaseTool): final_impression = existing_profile.get("relationship_text", "") affection_change = 0.0 # 好感度变化量 - if impression_hint or chat_history_text: + # 只有在LLM明确提供impression_hint时才更新印象(更严格) + if impression_hint and impression_hint.strip(): + # 获取最近的聊天记录用于上下文 + chat_history_text = await self._get_recent_chat_history(target_user_id) + impression_result = await self._generate_impression_with_affection( target_user_name=target_user_name, impression_hint=impression_hint, @@ -277,14 +283,31 @@ class UserProfileTool(BaseTool): if info_type not in valid_types: info_type = "other" - # 🎯 信息质量判断:过滤掉模糊的描述性内容 + # 🎯 信息质量判断:过滤掉模糊的描述性内容和状态描述 low_quality_patterns = [ + # 原有的模糊描述 "的生日", "的工作", "的位置", "的梦想", "的家人", "的宠物", "birthday", "job", "location", "unknown", "未知", "不知道", - "affectionate", "friendly", "的信息", "某个", "一个" + "affectionate", "friendly", "的信息", "某个", "一个", + # 新增:状态描述而非具体信息 + "很累", "累了", "疲惫", "忙", "很忙", "加班", "休息", + "开心", "难过", "高兴", "沮丧", "烦躁", "焦虑", + "上班", "下班", "工作中", "在家", "出差", + "感觉", "心情", "状态", "最近", "今天", "现在" ] info_value_lower = info_value.lower().strip() + # 🎯 针对job类型的特殊验证 + if info_type == "job": + job_invalid_patterns = [ + "累", "忙", "班", "工作", "上班族", "打工人", "社畜", + "很", "非常", "特别", "太", "好", "不好" + ] + for pattern in job_invalid_patterns: + if pattern in info_value_lower: + logger.warning(f"职业信息无效(状态描述而非具体职业),跳过: {info_value}") + return + # 如果值太短或包含低质量模式,跳过 if len(info_value_lower) < 2: logger.warning(f"关键信息值太短,跳过: {info_value}") @@ -362,12 +385,12 @@ class UserProfileTool(BaseTool): logger.error(f"保存关键信息失败: {e}") # 不抛出异常,因为这是后台任务 - async def _get_recent_chat_history(self, target_user_id: str, max_messages: int = 50) -> str: + async def _get_recent_chat_history(self, target_user_id: str, max_messages: int = 10) -> str: """获取最近的聊天记录 Args: target_user_id: 目标用户ID - max_messages: 最大消息数量 + max_messages: 最大消息数量(默认10条,避免传递过多历史消息) Returns: str: 格式化的聊天记录文本 @@ -499,22 +522,28 @@ class UserProfileTool(BaseTool): ## 当前好感度 {current_score:.2f} (范围0-1,0.3=普通认识,0.5=朋友,0.7=好友,0.9=挚友) -## ⚠️⚠️ 最高优先级:保持已确认信息 ⚠️⚠️ -**旧印象中已经明确的信息必须保持,绝对不能改动!** +## ⚠️⚠️ 最高优先级:严格控制信息记录 ⚠️⚠️ +**绝对禁止推测、猜想、脑补任何具体信息!** -1. **性别判断规则**(按优先级): - - 如果旧印象中已经用"他"→ 这是男生,gender="male",继续用"他" - - 如果旧印象中已经用"她"→ 这是女生,gender="female",继续用"她" - - 只有旧印象没有性别线索时,才根据聊天内容判断 +1. **不要推测身份职业**: + - 不要根据聊天话题推测工作(聊AI ≠ 是程序员) + - 不要根据时间推测身份(深夜聊天 ≠ 是学生) + - 不要根据行为推测背景(会装机 ≠ 从事相关工作) -2. **其他已确认信息**: - - 旧印象中提到的身份(学生/上班族等)→ 保持 - - 旧印象中提到的特点、爱好 → 保持或深化,不要删除 - - 你是在**补充和深化**印象,不是**重写** +2. **不要记录未确认的信息**: + - 只记录TA明确说出的事实 + - 你的推测、联想、印象都不是事实 + - 模糊的、不确定的信息不要记录 + +3. **保持旧印象中已确认的信息**: + - 如果旧印象中已经用"他"→ 这是男生,继续用"他" + - 如果旧印象中已经用"她"→ 这是女生,继续用"她" + - 其他已明确的特点、爱好要保持,不要删除 ## ⚠️ 区分虚构内容和真实信息 - 游戏剧情、小说情节、角色扮演等虚构内容 ≠ TA本人的特质 -- 印象记录的是**这个人本身**:TA的性格、TA喜欢什么、TA和你交流的方式 +- 印象记录的是**这个人本身**:TA的性格、TA和你交流的方式 +- 不要将聊天内容当作个人信息记录 ## 任务 1. **先看旧印象中的性别**,已确定就沿用,没确定才判断 @@ -552,34 +581,43 @@ class UserProfileTool(BaseTool): 当前好感度:{current_score:.2f} -**关系阶段与增速:** +**关系阶段与增速(更加保守):** | 阶段 | 分数范围 | 单次变化范围 | 说明 | |------|----------|--------------|------| -| 陌生→初识 | 0.0-0.3 | ±0.03~0.05 | 容易建立初步印象 | -| 初识→熟人 | 0.3-0.5 | ±0.02~0.04 | 逐渐熟悉的阶段 | -| 熟人→朋友 | 0.5-0.7 | ±0.01~0.03 | 需要更多互动积累 | -| 朋友→好友 | 0.7-0.85 | ±0.01~0.02 | 关系深化变慢 | -| 好友→挚友 | 0.85-1.0 | ±0.005~0.01 | 极难变化,需要重大事件 | +| 陌生→初识 | 0.0-0.3 | ±0.01~0.03 | 需要重要交流才变化 | +| 初识→熟人 | 0.3-0.5 | ±0.01~0.025 | 逐渐熟悉的阶段 | +| 熟人→朋友 | 0.5-0.7 | ±0.01~0.02 | 需要更多深入互动 | +| 朋友→好友 | 0.7-0.85 | ±0.005~0.015 | 关系深化极慢 | +| 好友→挚友 | 0.85-1.0 | ±0.002~0.005 | 极难变化,需要重大事件 | **加分情况(根据当前阶段选择合适幅度):** -- 愉快的聊天、有来有往的互动 → 小幅+(低阶段更明显) -- 分享心情、倾诉烦恼 → 中幅+ -- 主动关心、记得之前聊过的事 → 中幅+ -- 深度交流、展现信任 → 较大+ -- 在困难时寻求帮助或给予支持 → 大幅+ +- 深层情感分享、主动倾诉重要烦恼 → 小幅+(低阶段更明显) +- 在你遇到困难时主动关心或提供帮助 → 中幅+ +- 记得并主动询问你之前提到的重要事情 → 中幅+ +- 深度价值观交流、展现真实的信任 → 较大+ +- 在重大困难时寻求帮助或给予关键支持 → 大幅+ **减分情况:** -- 敷衍、冷淡的回应 → 小幅- -- 明显的不耐烦或忽视 → 中幅- -- 冲突、误解 → 较大- -- 长期不联系(关系会自然冷却)→ 缓慢- +- 长时间敷衍、多次冷淡回应 → 小幅- +- 明显的不耐烦、忽视重要话题 → 中幅- +- 直接冲突、严重误解或伤害性言论 → 较大- +- 长期不联系且无合理原因 → 缓慢- -**不变的情况:** -- 纯粹的信息询问(问时间、问天气等) +**不变的情况(大部分日常交流都应该是这种):** +- 普通的愉快聊天、日常问候 +- 一般性信息交换、轻松互动 +- 开玩笑、调侃、日常关心 +- 分享日常生活琐事、兴趣爱好 +- 寻求一般性建议或提供普通帮助 +- 纯粹的信息询问 - 机械式的对话 - 无法判断情感倾向的中性交流 -**注意:高好感度(>0.8)时要非常谨慎加分,友好互动在这个阶段是常态,不是加分项。** +**重要原则:** +- 默认倾向于"不变化",只有真正重大的交流才改变好感度 +- 普通的友好互动是维持关系,不是加深关系 +- 高好感度(>0.7)时,日常友好交流绝对不加分 +- 宁可保守不变,也不要轻易加减分 请严格按照以下JSON格式输出: {{ @@ -613,22 +651,22 @@ class UserProfileTool(BaseTool): change_reason = result.get("change_reason", "") detected_gender = result.get("gender", "unknown") - # 🎯 根据当前好感度阶段限制变化范围 + # 🎯 根据当前好感度阶段限制变化范围(更加保守) if current_score < 0.3: - # 陌生→初识:±0.05 - max_change = 0.05 - elif current_score < 0.5: - # 初识→熟人:±0.04 - max_change = 0.04 - elif current_score < 0.7: - # 熟人→朋友:±0.03 + # 陌生→初识:±0.03 max_change = 0.03 - elif current_score < 0.85: - # 朋友→好友:±0.02 + elif current_score < 0.5: + # 初识→熟人:±0.025 + max_change = 0.025 + elif current_score < 0.7: + # 熟人→朋友:±0.02 max_change = 0.02 + elif current_score < 0.85: + # 朋友→好友:±0.015 + max_change = 0.015 else: - # 好友→挚友:±0.01 - max_change = 0.01 + # 好友→挚友:±0.005 + max_change = 0.005 affection_change = max(-max_change, min(max_change, affection_change))