Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by SolarSeeker694

How can I enforce strict adherence to the mail_tool rules in my LangChain agent?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

Issue Description:

I am developing a LangChain agent that uses LangChain, langchain_openai, and a custom email-sending tool called mail_tool. This tool is designed to follow a strict set of rules and a specific input format for sending emails. However, the agent occasionally ignores these rules, invents email addresses, or fails to send the correct content.

Tool Rules and Input Format:

The mail_tool must adhere to the following:

  1. Tool Usage: Use this tool only when the user explicitly requests to send an email and provides both their email address and the required content.
  2. No Invented Emails: Only use the email provided by the user, without inventing any additional addresses.
  3. Input Format: The tool expects a string that represents a dictionary (since the model outputs strings) formatted as follows:
JSON
{ 'mail': 'user_email', # The user's email address. 'content': 'content_to_send' # The information to be sent. }

Code Implementation:

Below is the code implemented so far:

PYTHON
import asyncio from langchain_openai import ChatOpenAI from langchain.tools import Tool from langgraph.checkpoint.memory import MemorySaver from langchain_core.messages import HumanMessage, AIMessage from langgraph.graph import StateGraph, START, END, MessagesState from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain.agents import create_openai_functions_agent, AgentExecutor import json # Model and tools configuration model = ChatOpenAI(model="gpt-4", openai_api_key="KEY_HERE") prompt = ChatPromptTemplate.from_messages([ ("system", "You are a friendly assistant named Axel, and you will answer questions. You only provide answers of a maximum of 25 words. You respond in Spanish"), MessagesPlaceholder(variable_name="chat_history"), ("human", "{input}"), MessagesPlaceholder(variable_name="agent_scratchpad") ]) def send_mail_tool(data: str): try: parsed_data = json.loads(data) mail = parsed_data.get("mail") content = parsed_data.get("content") if mail and content: return "Email sent" else: return "Error: 'mail' or 'content' is missing" except Exception as e: return f"Error: {str(e)}" mail_tool = Tool( name="send_mail_tool", func=send_mail_tool, description=""" FOLLOW THESE RULES STRICTLY TO USE THIS TOOL: - Use this tool only when the user requests to send information via email and provides their email address and the required content. - Only use it for this purpose. - Do not invent email addresses; only use the one provided by the user. - Always, without exception, use the Input format. - this is the Input Format: The input must be a dictionary with the following keys: { 'mail': 'user_email', # The user's email address. 'content': 'content_to_send' # The information to be sent. } """ ) tools = [mail_tool] agent = create_openai_functions_agent(llm=model, prompt=prompt, tools=tools) agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=False) workflow = StateGraph(state_schema=MessagesState) async def call_agent(state: MessagesState): user_input = state["messages"][-1].content chat_history = state["messages"][:-1] response = await agent_executor.ainvoke({ "input": user_input, "chat_history": chat_history }) return {"messages": state["messages"] + [AIMessage(content=response["output"])]} workflow.add_node("agent", call_agent) workflow.add_edge(START, "agent") memory = MemorySaver() app = workflow.compile(checkpointer=memory) config = {"configurable": {"thread_id": "user1234"}} app.update_state(config, {"messages": [ AIMessage(content="You are a friendly assistant named Axel, and you will answer questions. You only provide answers of a maximum of 25 words. You respond in Spanish") ]}) async def main(): print("Assistant Online. Type 'exit' to quit.") while True: user_input = input("\nYou: ") if user_input.lower() == "exit": print("Goodbye!") break app.update_state(config, {"messages": [HumanMessage(content=user_input)]}) state = app.get_state(config).values try: output = await app.ainvoke(state, config) except Exception as e: print("\nAssistant: Sorry, an error occurred while processing your request.") print(f"Details: {str(e)}") continue print("\nAssistant:") print(output["messages"][-1].content) if __name__ == "__main__": asyncio.run(main())

Specific Issues Encountered:

  • The agent sometimes invents email addresses instead of using the provided one.
  • The agent occasionally fails to send the correct content or follow the specified input format.

Question:

How can I modify my approach to ensure the agent consistently adheres to the rules and input format for mail_tool? Are there alternative methods or improvements for a more robust enforcement?

Additional Information:

  1. I am using GPT-4 as the language model.
  2. The send_mail_tool function is a simulation and does not send real emails, but it must follow the defined format and rules.

I appreciate any guidance or suggestions. Thank you!

0

Comments 0

Answers 2

Views 53

2 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

One effective approach is to “structure” your tool invocation so that the language model is forced to produce JSON output that exactly matches your specified format. Here are some suggestions:

  1. Use a functions–style agent
    Leverage LangChain’s support for OpenAI functions (or the structured tool concept) so that your mail_tool is wrapped as a structured tool. This forces the model to output a JSON object following the schema you define. For example, using an argument schema (with Pydantic) for the tool will have LangChain automatically validate and correct nonconforming output.

  2. Tighten your prompt instructions
    Update your system and tool descriptions with clear instructions emphasizing:

    • The tool must only be used when the user explicitly wants to send an email.
    • Only use the user-provided email address.
    • Always output a JSON string with just the keys "mail" and "content".
  3. Validate the output before execution
    Before calling your send_mail_tool, implement a separate step that parses and validates the model’s output. For example, you could try/catch a JSON parse and check that the 'mail' field is indeed syntactically correct (using regex) and that "content" is present. This additional guard can prevent acting on incorrect outputs.

  4. Consider separating the tasks
    You might keep the conversational part separate from the email–sending function. Let the language model decide when to invoke the tool by explicitly asking, “Do you want to send this information via email?” Only if the confirmation is affirmative do you then trigger the function with the validated input.

Using these adjustments improves consistency. For instance, wrapping your tool using a Pydantic schema (similar to the Infobip example in the LangChain docs) might look like this:


Example:

PYTHON
from pydantic import BaseModel, EmailStr, Field from langchain.tools import StructuredTool class MailInput(BaseModel): mail: EmailStr = Field(description="The user's email address.") content: str = Field(description="The information to be sent.") def send_mail(data: MailInput): # Here, the function assumes that `mail` is valid and content is provided. return "Email sent" mail_tool = StructuredTool.from_function( name="send_mail_tool", description=""" Use this tool ONLY when the user explicitly requests to send an email. The provided email must be used exactly, and the content must follow the given format. Please output exactly a dictionary in JSON format with keys 'mail' and 'content'. """, func=send_mail, args_schema=MailInput, )

By using a structured tool, the agent receives “guardrails” that help ensure:

  • The output must conform to the MailInput schema (failing which LangChain will try to fix it).
  • Email addresses are validated (via EmailStr) so invented values are less likely.
  • The input format is strictly enforced.

Combining these improvements with detailed system prompts and post–processing validation can greatly minimize the issues of invented email addresses or improper content format. This results in a more robust and consistent experience when the agent invokes mail_tool.

This structured approach is aligned with the LangChain documentation for tools like Infobip and AWS Lambda, where similar patterns are used to ensure the tool is invoked with the correct parameters.

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by PulsarMariner898 1 month ago

0

sometimes i find that the behavior changes and operates better when you use a better model. Try gpt-4o instead if you have not already.

No comments yet.

Discussion

No comments yet.