Skip to content

feat(chat): add relationship state engine#15

Open
Natsuki521 wants to merge 3 commits into
kawayiYokami:mainfrom
Natsuki521:feat/relationship-state-engine
Open

feat(chat): add relationship state engine#15
Natsuki521 wants to merge 3 commits into
kawayiYokami:mainfrom
Natsuki521:feat/relationship-state-engine

Conversation

@Natsuki521

@Natsuki521 Natsuki521 commented May 31, 2026

Copy link
Copy Markdown

Summary

Added Relationship State Engine v1 to replace the old reward_engine prototype.

The new engine keeps relationship state per conversation + agent, analyzes the latest user message into a structured InteractionEvent, updates relationship dimensions through a StateReducer, and appends a natural-language relationship state block to the latest user message instead of changing the system prompt.

Main changes

  • Added relationship_state backend module.
  • Added default dimensions: affection, trust, tension, sadness, playfulness, attachment.
  • Added LLM-based InteractionAnalyzer with fallback heuristic analyzer.
  • Added StateReducer with dynamic delta, damping, decay, last_event and recent_events.
  • Stored relationship state on Conversation as relationship_state, isolated by agent.
  • Injected <relationship_state> into latest user extra blocks, not system prompt.
  • Added RelationshipPanel for debugging: dimensions, recent events, block preview, raw JSON, simulated events.
  • Added relationship_rules.json with default generation and hot reload.
  • Removed old reward_engine prototype files.

Validation

  • pnpm typecheck
  • cargo check
  • cargo test relationship_analyzer_tests
  • cargo test relationship_state_reducer_tests
  • cargo test relationship_rules_tests
  • cargo test build_prompt_should_put_relationship_state_in_latest_user_extra_not_system
  • cargo test build_prompt_should_not_duplicate_relationship_state_block

Notes

  • The analyzer currently reuses the model configuration from the successful chat response.
  • The relationship state block is appended to the latest user message extra blocks to avoid changing the system prompt and breaking prompt cache.
  • Default dimensions do not include custom/NSFW-specific dimensions.

Summary by CodeRabbit

  • 新功能

    • 上线 Relationship State Engine v1,按会话+助手维度追踪六项关系维度(affection/trust/tension/sadness/playfulness/attachment)
    • 自动分析交互产出结构化事件并更新关系状态;将生成的关系状态块追加到最新用户消息(不污染 system prompt)
    • 新增 Relationship Panel:维度进度、最后/最近事件、预览与原始 JSON、定期刷新与开发者控制(模拟事件)
    • 支持关系规则文件热加载且首次自动生成默认配置
  • 其他

    • 移除旧的 reward_engine 原型

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4296a1fe-5e1b-4a5f-9f82-64ef3c7d9eb8

📥 Commits

Reviewing files that changed from the base of the PR and between 6dbd5f8 and 08ec39c.

📒 Files selected for processing (20)
  • CHANGELOG.md
  • src-tauri/src/features/chat/conversation.rs
  • src-tauri/src/features/chat/message_store/store.rs
  • src-tauri/src/features/chat/tests.rs
  • src-tauri/src/features/config/tests.rs
  • src-tauri/src/features/core/domain/types_chat.rs
  • src-tauri/src/features/remote_im/tests.rs
  • src-tauri/src/features/system/commands/archive_pipeline.rs
  • src-tauri/src/features/system/commands/chat_and_runtime/core_send_inner.rs
  • src-tauri/src/features/system/commands/config_and_persona/unarchived_conversations.rs
  • src-tauri/src/features/system/commands/prompt_assembly.rs
  • src-tauri/src/features/system/commands/tool_review.rs
  • src-tauri/src/features/system/tests.rs
  • src-tauri/src/features/system/tools/terminal/exec.rs
  • src-tauri/src/features/system/tools/terminal/workspace.rs
  • src-tauri/src/main.rs
  • src/features/chat/components/RelationshipPanel.vue
  • src/features/chat/views/ChatView.vue
  • src/features/shell/components/AppWindowContent.vue
  • src/features/sidebar/views/ChatViewWrapper.vue
💤 Files with no reviewable changes (4)
  • src/features/shell/components/AppWindowContent.vue
  • src/features/sidebar/views/ChatViewWrapper.vue
  • src/features/chat/views/ChatView.vue
  • src/features/chat/components/RelationshipPanel.vue
✅ Files skipped from review due to trivial changes (5)
  • src-tauri/src/features/system/tools/terminal/workspace.rs
  • src-tauri/src/features/system/tools/terminal/exec.rs
  • src-tauri/src/features/system/commands/archive_pipeline.rs
  • src-tauri/src/features/system/commands/tool_review.rs
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (11)
  • src-tauri/src/features/system/commands/prompt_assembly.rs
  • src-tauri/src/features/config/tests.rs
  • src-tauri/src/features/chat/conversation.rs
  • src-tauri/src/features/core/domain/types_chat.rs
  • src-tauri/src/features/system/commands/config_and_persona/unarchived_conversations.rs
  • src-tauri/src/features/system/tests.rs
  • src-tauri/src/features/chat/message_store/store.rs
  • src-tauri/src/features/chat/tests.rs
  • src-tauri/src/main.rs
  • src-tauri/src/features/remote_im/tests.rs
  • src-tauri/src/features/system/commands/chat_and_runtime/core_send_inner.rs

Walkthrough

新增 Relationship State Engine v1,实现关系状态的结构化定义、LLM 驱动分析、动态归约更新;在 Conversation 中增加 relationship_state 字段并通过提示词注入避免系统提示缓存失效;前端新增 RelationshipPanel 面板展示维度与事件;并将生成的关系规则配置自动初始化与热加载。

Changes

Relationship State Engine v1

Layer / File(s) Summary
关系状态数据结构与基础读写
src-tauri/src/features/relationship_state/types.rs, src-tauri/src/features/relationship_state/state.rs
定义 RelationshipStateRootAgentRelationshipStateRelationshipDimensionsInteractionEventStateDeltaRelationshipRulesRelationshipPanelSnapshot 等 Serde 可序列化结构;实现状态与 JSON 双向转换、agent 维度访问、ID 规范化、时间戳生成。
交互事件分析与 LLM 集成
src-tauri/src/features/relationship_state/analyzer.rs, src-tauri/src/features/relationship_state/llm_analyzer_runtime.rs
实现 LLM 分析器系统/用户提示、严格 JSON 解析与启发式兜底分析;提供 LLM 分析运行时与近期对话上下文构建。
状态归约与关系块生成
src-tauri/src/features/relationship_state/reducer.rs, src-tauri/src/features/relationship_state/block.rs
实现衰减、缩放因子、分段阻尼、维度裁剪的状态归约流程;生成自然语言关系状态块。
模块聚合、缓存与 Tauri 命令
src-tauri/src/features/relationship_state/mod.rs, src-tauri/src/main.rs
实现关系规则加载、内存缓存、失效机制;实现事件应用与面板快照构建;暴露五个 Tauri 命令(获取快照、预览块、刷新规则、重置状态、模拟事件);在主入口注册模块与命令。
Conversation 结构扩展与提示词注入
src-tauri/src/features/core/domain/types_chat.rs, src-tauri/src/features/chat/conversation.rs, src-tauri/src/features/chat/conversation_prompt_service.rs
Conversation 新增 relationship_state: Option<serde_json::Value> 字段;在会话快照与构建 latest user payload 时注入关系状态块,避免重复和系统提示污染。
消息持久化与关系状态引擎调用
src-tauri/src/features/system/commands/chat_and_runtime/core_send_inner.rs
在 assistant message 持久化流程中调用 LLM analyzer 获取交互事件,失败时回退启发式分析,将事件应用回 conversation.relationship_state 并随会话持久化。
前端关系面板与集成
src/features/chat/components/RelationshipPanel.vue, src/features/chat/views/ChatView.vue, src/features/shell/components/AppWindowContent.vue
新增 RelationshipPanel 组件展示维度进度条、最近事件、原始 JSON;支持刷新、重置、模拟事件;集成到 ChatView 并通过 AppWindowContent 传递 agentId。
文档与测试补齐
CHANGELOG.md, plan/relationship-state-engine-v1-pr.md, 多个 **/tests.rs 文件
更新 CHANGELOG 与 PR 规划文档;在所有 Conversation 构造中补齐 relationship_state: None 字段;为提示词构建添加两条新测试验证关系块注入;更新 message_store meta 的持久化字段链路与断言。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 一只兔子在修补感情,

维度交织,事件轻敲门。
LLM 倾听又归约,数字化温柔,
面板里旧事新意并存。
规则热载,交互继续。

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR标题'feat(chat): add relationship state engine'清晰准确地总结了主要变更:引入关系状态引擎。标题简洁明了,遵循常规提交消息格式,能让团队成员快速了解核心改动。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (2)
src-tauri/src/features/relationship_state/mod.rs (1)

1-204: 💤 Low value

建议添加代码组织注释以提升可读性。

根据编码规范 "用注释分区(// ========== xxx ==========)组织代码",建议在文件中添加分区注释,例如:

// ========== 路径与缓存 ==========
static LAST_SNAPSHOT: ...
static RULES_CACHE: ...

// ========== 规则加载与持久化 ==========
pub fn load_relationship_rules(...) -> ...
fn write_default_relationship_rules(...) -> ...

// ========== 面板快照构建 ==========
pub fn build_relationship_state_block_for_agent(...) -> ...
pub fn apply_interaction_event(...) -> ...

// ========== Tauri 命令 ==========
#[tauri::command]
pub fn get_relationship_panel_snapshot(...) -> ...

这有助于在单个文件超过 200 行时快速定位不同职责区域。

根据编码规范:单个 Rust 文件控制在 1500 行以内,函数控制在 100 行以内,用注释分区(// ========== xxx ==========)组织代码。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/features/relationship_state/mod.rs` around lines 1 - 204, The
file lacks the standardized section-divider comments; add concise "// ==========
<Section Name> ==========" comments to group related code so readers can quickly
navigate (e.g., a "路径与缓存" section above static LAST_SNAPSHOT and RULES_CACHE;
"规则加载与持久化" above load_relationship_rules and write_default_relationship_rules;
"面板快照构建" above build_relationship_state_block_for_agent, build_panel_snapshot,
snapshot_key and related helpers; "事件应用" above apply_interaction_event and
reduce-related functions; and "Tauri 命令" above get_relationship_panel_snapshot,
preview_relationship_block, refresh_relationship_rules,
reset_relationship_state, simulate_relationship_event; optionally a "测试" section
above mod relationship_rules_tests). Place each divider immediately before the
first related symbol (function/static/module) to improve readability without
changing logic.
src-tauri/src/features/chat/conversation_prompt_service.rs (1)

816-833: 💤 Low value

可选优化:避免无效的关系状态块构建

当前实现在 line 816-819 无条件构建 relationship_block,即使后续检测到重复时也不会使用。可以将构建逻辑移入条件分支,仅在需要时构建:

♻️ 建议的优化方案
                 extra_blocks.extend(self.build_remote_im_transient_profile_blocks(
                     state,
                     conversation,
                     agent,
                 ));
-                let relationship_block = relationship_state::build_relationship_state_block_for_agent(
-                    conversation.relationship_state.as_ref(),
-                    &agent.id,
-                );
                 if !extra_blocks.iter().any(|block| block.contains("<relationship_state>"))
                     && !conversation.messages.iter().rev().any(|message| {
                         prompt_role_for_message(message, &agent.id).as_deref() == Some("user")
                             && message
                                 .extra_text_blocks
                                 .iter()
                                 .any(|block| block.contains("<relationship_state>"))
                     })
                 {
+                    let relationship_block = relationship_state::build_relationship_state_block_for_agent(
+                        conversation.relationship_state.as_ref(),
+                        &agent.id,
+                    );
                     extra_blocks.push(relationship_block);
                     if let Some(log_stage) = stage_logger {
                         log_stage("prepare_context.relationship_state_ready");
                     }
                 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/features/chat/conversation_prompt_service.rs` around lines 816
- 833, The code eagerly constructs relationship_block by calling
relationship_state::build_relationship_state_block_for_agent even when it might
not be used; move the call into the conditional so you only build the block when
the duplicate checks fail (i.e., inside the if that checks extra_blocks and
conversation.messages), replace the current precomputed relationship_block
variable with a local one created only when needed, and keep the existing push
to extra_blocks and the stage_logger/log_stage invocation unchanged (refer to
relationship_state::build_relationship_state_block_for_agent,
relationship_block, extra_blocks, conversation, stage_logger, and log_stage).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src-tauri/src/features/chat/message_store/meta.rs`:
- Line 244: 不要在目录型会话重建时把 relationship_state 硬编码为 None;在 ConversationShardMeta /
ConversationPersistMeta 的序列化与反序列化逻辑中加入 relationship_state
字段的读写(保持原有字段布局兼容),并在从目录型消息存储恢复(the meta construction path where
relationship_state is currently set to None)去除该默认清空逻辑;同时补一个非空 round-trip 测试:构建含有
relationship_state 的元数据,序列化->反序列化并断言恢复后的 relationship_state 与原始值相同,确保不会被静默丢失。

In `@src-tauri/src/features/relationship_state/mod.rs`:
- Around line 21-50: load_relationship_rules and
write_default_relationship_rules perform synchronous file I/O
(std::fs::read_to_string, create_dir_all, write) which blocks the UI; change
both functions to async (pub async fn load_relationship_rules(...) and async fn
write_default_relationship_rules(...)), replace sync I/O with tokio::fs
equivalents (tokio::fs::read_to_string, tokio::fs::create_dir_all,
tokio::fs::write) and await them, propagate Result types accordingly, and update
all callers of load_relationship_rules to be async and await the call; also
ensure the RULES_CACHE locking is safe in async context (replace std Mutex with
tokio::sync::Mutex or wrap blocking lock in tokio::task::spawn_blocking) and
keep relationship_rules_path usage unchanged.
- Around line 7-15: The function relationship_data_dir currently returns a
hard-coded Windows path as a fallback which must be removed; change
relationship_data_dir to not return that dev-only path—either make it return
Result<String, String> (or Result<PathBuf, Error>) and on None return
Err("missing data_path" or a descriptive error) or keep String return type but
panic/unwrap with a clear message; update the function signature and replace the
None arm to return Err or propagate an error instead of the
r"...relationship_state" literal, and adjust callers of relationship_data_dir to
handle the Result (or possible panic) accordingly.

In `@src-tauri/src/features/system/commands/chat_and_runtime/core_send_inner.rs`:
- Around line 3122-3147: The code currently uses
relationship_state::load_relationship_rules(...)? which will propagate errors
and abort the whole send flow; change it to catch and log the error and fall
back to heuristic behavior: call relationship_state::relationship_data_dir(...)
and attempt relationship_state::load_relationship_rules inside a match (or
map_err + unwrap_or_else) so that on Err you runtime_log_warn the error and set
rules to a default with analyzer_enabled = false (or simply proceed to call
relationship_state::analyze_interaction_fallback). Keep the subsequent logic
that checks rules.analyzer_enabled and calls
relationship_state_llm_analyzer_runtime::run_relationship_interaction_analyzer(...).await,
preserving its own Err -> fallback handling, so a failure to load rules does not
return Err from the whole function.
- Around line 3117-3155: The relationship_state update (running the LLM analyzer
with fallback and then calling relationship_state::apply_interaction_event) is
only implemented inside the read_persisted_conversation success branch, so
temporary/runtime conversations created in
delegate_runtime_thread_conversation_get never advance their relationship_state;
extract that logic into a shared helper (e.g.
update_relationship_state_for_interaction or
relationship_state::handle_interaction_event_for_conversation) that
encapsulates: reading current state
(relationship_state::read_relationship_from_value,
relationship_state::agent_state), building recent_context
(relationship_state_llm_analyzer_runtime::relationship_recent_context), loading
rules (relationship_state::load_relationship_rules), running analyzer with
fallback
(relationship_state_llm_analyzer_runtime::run_relationship_interaction_analyzer
-> relationship_state::analyze_interaction_fallback), and finally calling
relationship_state::apply_interaction_event; then call this helper from both the
read_persisted_conversation branch and inside the
delegate_runtime_thread_conversation_get branch after writing the assistant
message so runtime sessions update consistently.

In `@src/features/chat/components/RelationshipPanel.vue`:
- Around line 167-205: The async handlers (refresh, reset, simulate,
refreshRules) write snapshot.value unconditionally after awaiting, which lets
stale responses from a previous conversation/agent overwrite the current panel;
fix by capturing the current identity before the await (e.g., const currentId =
props.conversationId and if applicable the current agent id/other session key),
then after the invokeTauri call compare that captured id to the live props value
and only assign snapshot.value if they still match; apply this pattern in
refresh, reset, simulate (and any polling or session-switch-triggered calls) so
late responses are ignored when the conversation/agent has changed.
- Around line 2-9: The collapsed-state trigger is currently an unfocusable div,
so update the root clickable element in RelationshipPanel.vue (the outer div
that uses collapsed and `@click`="collapsed && toggleCollapse()") to be an actual
keyboard-accessible control: either change it to a <button> when collapsed (or
always) or add tabindex="0", role="button", and keydown handler that calls
toggleCollapse() on Enter/Space; ensure the existing :class, :style bindings and
the Heart component usage remain intact and that the handler references the same
toggleCollapse() method and collapsed reactive prop.

In `@src/features/chat/views/ChatView.vue`:
- Around line 314-315: The RelationshipPanel is always mounted and overlays the
right review panel; change its rendering to depend on the right-tool state so it
doesn't display when the review tool is open. In ChatView.vue, wrap or change
the existing RelationshipPanel usage (RelationshipPanel, props
activeConversationId and activeAgentId) to render only when the local/computed
boolean toolReviewPanelOpen is false (e.g., use a v-if or equivalent conditional
on !toolReviewPanelOpen) so the fixed overlay is not mounted while the review
panel is open.

---

Nitpick comments:
In `@src-tauri/src/features/chat/conversation_prompt_service.rs`:
- Around line 816-833: The code eagerly constructs relationship_block by calling
relationship_state::build_relationship_state_block_for_agent even when it might
not be used; move the call into the conditional so you only build the block when
the duplicate checks fail (i.e., inside the if that checks extra_blocks and
conversation.messages), replace the current precomputed relationship_block
variable with a local one created only when needed, and keep the existing push
to extra_blocks and the stage_logger/log_stage invocation unchanged (refer to
relationship_state::build_relationship_state_block_for_agent,
relationship_block, extra_blocks, conversation, stage_logger, and log_stage).

In `@src-tauri/src/features/relationship_state/mod.rs`:
- Around line 1-204: The file lacks the standardized section-divider comments;
add concise "// ========== <Section Name> ==========" comments to group related
code so readers can quickly navigate (e.g., a "路径与缓存" section above static
LAST_SNAPSHOT and RULES_CACHE; "规则加载与持久化" above load_relationship_rules and
write_default_relationship_rules; "面板快照构建" above
build_relationship_state_block_for_agent, build_panel_snapshot, snapshot_key and
related helpers; "事件应用" above apply_interaction_event and reduce-related
functions; and "Tauri 命令" above get_relationship_panel_snapshot,
preview_relationship_block, refresh_relationship_rules,
reset_relationship_state, simulate_relationship_event; optionally a "测试" section
above mod relationship_rules_tests). Place each divider immediately before the
first related symbol (function/static/module) to improve readability without
changing logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9e91bbe2-43a9-49d9-b076-2d8d9b2b7939

📥 Commits

Reviewing files that changed from the base of the PR and between c3e63f0 and 5d8eef7.

📒 Files selected for processing (34)
  • CHANGELOG.md
  • plan/relationship-state-engine-v1-pr.md
  • src-tauri/src/features/chat/conversation.rs
  • src-tauri/src/features/chat/conversation_prompt_service.rs
  • src-tauri/src/features/chat/message_store/jsonl_snapshot.rs
  • src-tauri/src/features/chat/message_store/meta.rs
  • src-tauri/src/features/chat/message_store/migration.rs
  • src-tauri/src/features/chat/message_store/persist.rs
  • src-tauri/src/features/chat/message_store/store.rs
  • src-tauri/src/features/chat/tests.rs
  • src-tauri/src/features/config/tests.rs
  • src-tauri/src/features/core/domain/types_chat.rs
  • src-tauri/src/features/relationship_state/analyzer.rs
  • src-tauri/src/features/relationship_state/block.rs
  • src-tauri/src/features/relationship_state/llm_analyzer_runtime.rs
  • src-tauri/src/features/relationship_state/mod.rs
  • src-tauri/src/features/relationship_state/reducer.rs
  • src-tauri/src/features/relationship_state/state.rs
  • src-tauri/src/features/relationship_state/types.rs
  • src-tauri/src/features/remote_im/tests.rs
  • src-tauri/src/features/system/commands/archive_host_selector.rs
  • src-tauri/src/features/system/commands/archive_pipeline.rs
  • src-tauri/src/features/system/commands/chat_and_runtime/core_send_inner.rs
  • src-tauri/src/features/system/commands/config_and_persona/unarchived_conversations.rs
  • src-tauri/src/features/system/commands/prompt_assembly.rs
  • src-tauri/src/features/system/commands/tool_review.rs
  • src-tauri/src/features/system/tests.rs
  • src-tauri/src/features/system/tools/terminal/exec.rs
  • src-tauri/src/features/system/tools/terminal/workspace.rs
  • src-tauri/src/features/tests.rs
  • src-tauri/src/main.rs
  • src/features/chat/components/RelationshipPanel.vue
  • src/features/chat/views/ChatView.vue
  • src/features/shell/components/AppWindowContent.vue

Comment thread src-tauri/src/features/chat/message_store/meta.rs Outdated
Comment thread src-tauri/src/features/relationship_state/mod.rs Outdated
Comment thread src-tauri/src/features/relationship_state/mod.rs Outdated
Comment thread src-tauri/src/features/system/commands/chat_and_runtime/core_send_inner.rs Outdated
Comment thread src-tauri/src/features/system/commands/chat_and_runtime/core_send_inner.rs Outdated
Comment thread src/features/chat/components/RelationshipPanel.vue Outdated
Comment thread src/features/chat/components/RelationshipPanel.vue
Comment thread src/features/chat/views/ChatView.vue Outdated
@Natsuki521

Copy link
Copy Markdown
Author

Thanks for the review. I pushed a follow-up commit 6dbd5f86 addressing the actionable comments:

  • Removed the hardcoded local Windows fallback path from relationship_data_dir.
  • Switched relationship rules file access to async tokio::fs.
  • Preserved relationship_state in directory-based conversation meta round-trip and added test coverage.
  • Made relationship rules loading non-fatal for chat send; failures now log a warning and skip relationship state write instead of failing the whole response.
  • Applied relationship state updates to runtime delegate conversations as well.
  • Made the collapsed RelationshipPanel entry a focusable button.
  • Guarded async panel refresh/reset/simulate responses against stale request overwrites.
  • Hid RelationshipPanel while the tool review panel is open to avoid overlay conflicts.

Validation:

  • pnpm typecheck
  • cargo check
  • cargo test relationship_rules_tests
  • cargo test message_store_meta_tests

@kawayiYokami

Copy link
Copy Markdown
Owner

感谢提交 PR #15。我们看完实现后,暂时不会合并这个 PR。

这个 PR 引入了一个 Relationship State Engine,用于按 conversation + agent 维护关系状态,并在每轮回复后额外调用 LLM 分析用户消息,再把关系状态注入下一轮 prompt。这个方向目前和 PAI 的主线定位、成本模型以及现有对话架构都不匹配。

主要拒绝理由如下:

  1. 未看到明确产品价值

    当前 PR 没有说明这个关系状态系统解决了 PAI 里的哪个真实问题,也没有展示它对普通聊天、远程 IM、归档、多 agent 场景的实际收益。

    PAI 当前更偏向桌面 AI 助手、工作流助手和可控对话工具,而不是“好感度/关系数值驱动”的 AI 伴侣系统。这个方向需要产品层面先确认,不能直接进入主线。

  2. 没有质量评测或压力测试证明收益

    PR 里的测试主要证明了 JSON 解析、状态 reducer、prompt 注入等代码路径能跑通,但没有证明回答质量真的提升。

    例如缺少:

    • A/B 对比
    • 长对话稳定性评测
    • 错误分类对回答质量的影响
    • prompt 注入后是否导致模型过度拟人、过度情绪化
    • 高并发/频繁对话下的延迟与成本评估

    在没有数据前,我们不能接受一个会改变模型行为的默认功能。

  3. 默认增加额外 LLM 调用,成本和延迟不可接受

    当前 analyzer_enabled 默认是 true。这意味着每轮正常对话完成后,还会再调用一次当前聊天模型做关系分析。

    即使 analyzer prompt 比主对话短,请求次数仍然增加了,延迟和费用也会稳定上升。对 PAI 这种本地桌面应用来说,这个成本默认加到所有用户身上是不合理的。

  4. agent 概念没有解释清楚

    PR 里关系状态按 agent_id 隔离,但没有解释这里的 agent 在产品语义上是什么。

    如果 agent 是助手人格/部门角色,那么关系状态是否应该跟 agent 绑定?如果用户切换部门、切换人格、远程 IM 绑定联系人时,关系应该怎么继承?这些都没有设计说明。

  5. 关系状态绑定 conversation 不合理

    当前实现把 relationship_state 存在 Conversation 上。结果是同一个用户和同一个 agent,在不同会话里会拥有完全不同的“感情状态”。

    如果这个状态表达的是长期关系,那它不应该绑定单个会话;如果它表达的是单个会话内的氛围,那就不应该叫 relationship state,也不应该作为长期人格状态注入后续 prompt。这个数据建模目前不清晰。

  6. 会直接改变模型行为

    PR 会把 <relationship_state> 注入到最新用户消息的 extra block 中,内容包括亲近度、信任度、紧张感、失落感等,并要求模型按这些状态调整语气。

    这不是内部状态优化,而是直接改变 assistant 输出风格。它可能让模型更拟人、更情绪化,也可能污染原本用户设定的人格、系统提示和远程 IM 行为。

  7. 前端调试面板进入了正式聊天界面

    RelationshipPanel 被直接挂到聊天页,而且包含 Developer Controls、模拟事件、Raw JSON、Block preview,并且每 3 秒轮询一次。

    这些看起来更像调试工具,不适合默认进入用户界面。即使未来保留,也应该放在开发模式或实验开关后面。

  8. 部分状态操作没有真正持久化

    reset_relationship_statesimulate_relationship_event 主要更新内存里的 LAST_SNAPSHOT,没有清晰地写回对应 conversation 的真实持久化状态。这会导致面板显示状态和真实会话状态可能分叉。

  9. PR 与当前主线存在明显冲突/过期问题

    PR 当前基于较旧主线,包含过期的 CHANGELOG.md 改动,还插入了旧版本 v0.9.88 段落。当前主线已经发布到 v0.10.14,直接合并会制造发布记录冲突。

    此外,该 PR 修改了核心发送链路、prompt 拼装、会话持久化结构、前端聊天视图等多个关键模块,和近期主线上的持久化、发布、聊天链路改动存在较高冲突风险。

结论:这个 PR 暂不合并。

如果后续想继续推进这个方向,建议先重新提交设计方案,而不是直接提交实现。至少需要说明:

  • 这个功能面向什么用户场景
  • 是否默认关闭
  • 是否只作为实验功能
  • 是否允许额外 LLM 调用
  • 是否支持独立轻量 analyzer 模型
  • 状态到底绑定 user、agent、persona、conversation 还是 remote contact
  • 如何评估回答质量提升
  • 如何避免 prompt 污染和成本上升
  • 前端调试面板是否仅开发模式可见

在这些问题明确之前,PAI 主线不会引入 Relationship State Engine。

@Natsuki521

Copy link
Copy Markdown
Author

Thanks for the detailed explanation. I understand that the current Relationship State Engine is not aligned with PAI mainline as a default relationship/emotion-driven system.

Before closing this direction completely, I want to ask whether a different architecture would be worth discussing:

Instead of a Relationship State Engine, I am considering a default-off experimental Context Continuity Layer.

The goal would not be AI affection / relationship simulation, but improving long conversation and remote IM continuity in a more neutral way:

  • default disabled
  • no default extra LLM call
  • rule-based mode as the default analyzer
  • optional lightweight analyzer model only if explicitly configured
  • no affection / attachment / sadness dimensions
  • use neutral dimensions such as clarity, urgency, friction, engagement, tone, confidence
  • separate state ownership:
    • user preference profile
    • persona continuity profile
    • conversation mood snapshot
    • remote contact overlay
  • prompt injection optional and gated
  • diagnostics panel only visible in developer / experimental mode
  • include A/B and long conversation evaluation before implementation

Would this direction be acceptable as a design proposal / RFC first, before any implementation PR?

If not, I will stop this line of work and keep the current PR only as a private prototype.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants