feat: implement LLM-driven voting strategy with structured tools#9
Conversation
- Create builders/ and utils/ directories for better organization - Move context_builder.py and prompt_builder.py to builders/ - Move logging_utils.py and text_utils.py to utils/ - Add serialization.py with mindset normalization utilities - Remove duplicate mindset normalization logic from player.py - Update all imports to reflect new module structure
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| def llm_decide_vote( | ||
| llm_client: Any, | ||
| state: GameState, | ||
| me: str, | ||
| my_word: str, | ||
| current_mindset: PlayerMindset, | ||
| ) -> str: | ||
| """ | ||
| Use LLM with voting tools to decide which player to vote for. | ||
|
|
||
| Args: | ||
| llm_client: Language model client | ||
| state: Current shared game state | ||
| me: Current player's ID | ||
| my_word: Player's assigned word | ||
| current_mindset: Player's latest mindset state | ||
|
|
||
| Returns: | ||
| Player ID selected as the vote target | ||
| """ | ||
| tools = vote_tools(state) | ||
| response_format = ToolStrategy( | ||
| schema=VoteDecisionModel, | ||
| tool_message_content="Vote decision captured.", | ||
| ) | ||
|
|
||
| agent = create_agent( | ||
| model=llm_client, | ||
| tools=tools, | ||
| response_format=response_format, | ||
| ) | ||
|
|
||
| alive_now = alive_players(state) | ||
| system_prompt = format_vote_system_prompt( | ||
| my_word=my_word, | ||
| alive_count=len(alive_now), | ||
| current_round=state.get("current_round", 0), | ||
| ) | ||
| vote_context = build_vote_user_context( | ||
| alive=alive_now, | ||
| me=me, | ||
| current_mindset=current_mindset, | ||
| current_round=state.get("current_round", 0), |
There was a problem hiding this comment.
Use updated mindset when executing vote tools
The new llm_decide_vote builds voting context from the freshly inferred current_mindset, but the tools created with vote_tools(state) immediately re-read the player’s mindset out of state (player_private_states). At the point player_vote calls llm_decide_vote, the state has not yet been updated with the LLM’s latest suspicions, so the tool heuristics run on stale beliefs and ignore whatever llm_update_player_mindset just produced. The previous implementation used the normalized updated_mindset_state directly when scoring players. Consider passing current_mindset into the tools or updating state before invoking them so the vote reflects the LLM’s latest reasoning.
Useful? React with 👍 / 👎.
- Create builders/ and utils/ directories for better organization - Move context_builder.py and prompt_builder.py to builders/ - Move logging_utils.py and text_utils.py to utils/ - Add serialization.py with mindset normalization utilities - Remove duplicate mindset normalization logic from player.py - Update all imports to reflect new module structure
|
@codex reivew |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # Pass the freshly inferred mindset so vote heuristics reflect the latest suspicions. | ||
| tools = vote_tools(state, mindset_overrides={me: current_mindset}) | ||
| response_format = ToolStrategy( | ||
| schema=VoteDecisionModel, | ||
| tool_message_content="Vote decision captured.", | ||
| ) | ||
|
|
||
| agent = create_agent( | ||
| model=llm_client, | ||
| tools=tools, | ||
| response_format=response_format, | ||
| ) |
There was a problem hiding this comment.
Bind player_id before exposing voting tools
The tools used for voting require a player_id argument and compute suspicions based on _get_player_mindset(player_id), but llm_decide_vote simply exports them to the agent without pre-binding me or explaining that the tool must be invoked with the caller’s own id. A model is likely to supply the suspected target as the argument (or omit it), causing the heuristic to look up the wrong player’s mindset or hit the fallback path, so the returned vote may reflect another player’s beliefs rather than the current player’s. Wrapping the tools with player_id=me (e.g., via functools.partial) or removing the parameter would make the behavior deterministic and keep votes aligned with the current player’s state.
Useful? React with 👍 / 👎.
- Create builders/ and utils/ directories for better organization - Move context_builder.py and prompt_builder.py to builders/ - Move logging_utils.py and text_utils.py to utils/ - Add serialization.py with mindset normalization utilities - Remove duplicate mindset normalization logic from player.py - Update all imports to reflect new module structure
|
@codex reivew |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| try: | ||
| result = agent.invoke( | ||
| { | ||
| "messages": [ | ||
| SystemMessage(content=system_prompt), | ||
| HumanMessage(content=vote_context), | ||
| ] | ||
| } | ||
| ) | ||
| structured = result.get("structured_response") | ||
| if structured: | ||
| if not isinstance(structured, VoteDecisionModel): | ||
| structured = VoteDecisionModel.model_validate(structured) | ||
| return structured.target | ||
| except Exception as exc: | ||
| logger.exception("LLM vote decision failed: %s", exc) | ||
|
|
||
| # Fallback: choose the first other alive player or self if alone | ||
| alternatives = [pid for pid in alive_now if pid != me] |
There was a problem hiding this comment.
Validate LLM vote result against alive roster
The new llm_decide_vote returns structured.target directly from the LLM without verifying that the chosen ID is one of alive_now. Because the agent can return arbitrary structured output (e.g., hallucinated or stale player IDs), downstream vote tallying can record votes for dead or nonexistent players, a situation that could never happen with the previous deterministic _decide_player_vote, which always selected from the current alive list. Consider checking structured.target against alive_now and falling back to a valid player before writing it into state.
Useful? React with 👍 / 👎.
|
@codex review |
|
Codex Review: Didn't find any major issues. Keep it up! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Uh oh!
There was an error while loading. Please reload this page.