feat(chat): add relationship state engine#15
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (20)
💤 Files with no reviewable changes (4)
✅ Files skipped from review due to trivial changes (5)
🚧 Files skipped from review as they are similar to previous changes (11)
Walkthrough新增 Relationship State Engine v1,实现关系状态的结构化定义、LLM 驱动分析、动态归约更新;在 Conversation 中增加 relationship_state 字段并通过提示词注入避免系统提示缓存失效;前端新增 RelationshipPanel 面板展示维度与事件;并将生成的关系规则配置自动初始化与热加载。 ChangesRelationship State Engine v1
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (34)
CHANGELOG.mdplan/relationship-state-engine-v1-pr.mdsrc-tauri/src/features/chat/conversation.rssrc-tauri/src/features/chat/conversation_prompt_service.rssrc-tauri/src/features/chat/message_store/jsonl_snapshot.rssrc-tauri/src/features/chat/message_store/meta.rssrc-tauri/src/features/chat/message_store/migration.rssrc-tauri/src/features/chat/message_store/persist.rssrc-tauri/src/features/chat/message_store/store.rssrc-tauri/src/features/chat/tests.rssrc-tauri/src/features/config/tests.rssrc-tauri/src/features/core/domain/types_chat.rssrc-tauri/src/features/relationship_state/analyzer.rssrc-tauri/src/features/relationship_state/block.rssrc-tauri/src/features/relationship_state/llm_analyzer_runtime.rssrc-tauri/src/features/relationship_state/mod.rssrc-tauri/src/features/relationship_state/reducer.rssrc-tauri/src/features/relationship_state/state.rssrc-tauri/src/features/relationship_state/types.rssrc-tauri/src/features/remote_im/tests.rssrc-tauri/src/features/system/commands/archive_host_selector.rssrc-tauri/src/features/system/commands/archive_pipeline.rssrc-tauri/src/features/system/commands/chat_and_runtime/core_send_inner.rssrc-tauri/src/features/system/commands/config_and_persona/unarchived_conversations.rssrc-tauri/src/features/system/commands/prompt_assembly.rssrc-tauri/src/features/system/commands/tool_review.rssrc-tauri/src/features/system/tests.rssrc-tauri/src/features/system/tools/terminal/exec.rssrc-tauri/src/features/system/tools/terminal/workspace.rssrc-tauri/src/features/tests.rssrc-tauri/src/main.rssrc/features/chat/components/RelationshipPanel.vuesrc/features/chat/views/ChatView.vuesrc/features/shell/components/AppWindowContent.vue
|
Thanks for the review. I pushed a follow-up commit
Validation:
|
|
感谢提交 PR #15。我们看完实现后,暂时不会合并这个 PR。 这个 PR 引入了一个 Relationship State Engine,用于按 主要拒绝理由如下:
结论:这个 PR 暂不合并。 如果后续想继续推进这个方向,建议先重新提交设计方案,而不是直接提交实现。至少需要说明:
在这些问题明确之前,PAI 主线不会引入 Relationship State Engine。 |
|
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 The goal would not be AI affection / relationship simulation, but improving long conversation and remote IM continuity in a more neutral way:
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. |
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
relationship_statebackend module.relationship_state, isolated by agent.<relationship_state>into latest user extra blocks, not system prompt.RelationshipPanelfor debugging: dimensions, recent events, block preview, raw JSON, simulated events.relationship_rules.jsonwith default generation and hot reload.reward_engineprototype files.Validation
pnpm typecheckcargo checkcargo test relationship_analyzer_testscargo test relationship_state_reducer_testscargo test relationship_rules_testscargo test build_prompt_should_put_relationship_state_in_latest_user_extra_not_systemcargo test build_prompt_should_not_duplicate_relationship_state_blockNotes
Summary by CodeRabbit
新功能
其他