Skip to content

feat: implement LLM-driven voting strategy with structured tools#9

Merged
leslieo2 merged 3 commits into
masterfrom
refactor-implement-LLM-driven-voting-strategy-with-structured-tools
Oct 31, 2025
Merged

feat: implement LLM-driven voting strategy with structured tools#9
leslieo2 merged 3 commits into
masterfrom
refactor-implement-LLM-driven-voting-strategy-with-structured-tools

Conversation

@leslieo2
Copy link
Copy Markdown
Owner

@leslieo2 leslieo2 commented Oct 31, 2025

  • Replaces hardcoded voting logic with LLM-powered decision making
  • Adds two voting strategy tools: decide_player_vote and decide_player_vote_second_best
  • Implements structured output for vote decisions using LangChain
  • Refactors player vote node to use new llm_decide_vote function
  • Adds comprehensive context building and prompt formatting for voting
  • Updates tests to mock the new LLM voting interface

  - 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
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 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".

Comment on lines +198 to +240
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),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge 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
@leslieo2
Copy link
Copy Markdown
Owner Author

@codex reivew

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 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".

Comment on lines +218 to +229
# 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,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge 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
@leslieo2
Copy link
Copy Markdown
Owner Author

@codex reivew

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 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".

Comment on lines +248 to +266
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]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge 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 👍 / 👎.

@leslieo2
Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

ℹ️ 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".

@leslieo2 leslieo2 merged commit 17b5e6c into master Oct 31, 2025
1 check passed
@leslieo2 leslieo2 deleted the refactor-implement-LLM-driven-voting-strategy-with-structured-tools branch October 31, 2025 09:47
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.

1 participant