microsoft / teams-ai

SDK focused on building AI based applications and extensions for Microsoft Teams and other Bot Framework channels
MIT License
408 stars 177 forks source link

[Bug]: Chinese is stored as a unicode escape character and sent to AI, resulting in confusing replies #2065

Open jamiesun opened 1 week ago

jamiesun commented 1 week ago

Language

Python

Version

latest

Description

image image image

My Application


config = Config()

#### Create AI components ##########################################
model: OpenAIModel
model = OpenAIModel(
    AzureOpenAIModelOptions(
        api_key=config.AZURE_OPENAI_API_KEY,
        api_version="2024-05-01-preview",
        default_model=config.AZURE_OPENAI_MODEL_DEPLOYMENT_NAME,
        endpoint=config.AZURE_OPENAI_ENDPOINT,
    )
)

#### Create the prompt manager #####################################
prompts_folder = f"{os.path.dirname(os.path.abspath(__file__))}/prompts"
prompts = PromptManager(PromptManagerOptions(prompts_folder=prompts_folder))

#### Define the system prompt message function #####################
@prompts.function("get_system_prompt_message")
async def get_system_prompt_message(
    _context: TurnContext,
    state: AppTurnState,
    _functions: PromptFunctions,
    _tokenizer: Tokenizer,
    _args: List[str],
):
    bot = state.conversation.bot
    if bot and bot.system_message:
        message = bot.system_message
    else:
        message = "You are an AI assistant that helps people find information."
    log.info(f"get_system_prompt_message: {message}")
    return message

#### Define the default prompt #####################################
async def get_default_prompt(
    context: TurnContext, state: AppTurnState, planner: ActionPlanner
) -> PromptTemplate:
    prompt = await prompts.get_prompt("chat")
    prompt.config.completion.model = "gpt-4o"
    if Config.AZURE_AI_SEARCH_ENABLED and config.AZURE_AI_SEARCH_ENDPOINT:
        prompt.config.completion.data_sources = [
            {
                "type": "azure_search",
                "parameters": {
                    "endpoint": Config.AZURE_AI_SEARCH_ENDPOINT,
                    "index_name": Config.AZURE_AI_SEARCH_INDEX,
                    "semantic_configuration": "default",
                    "query_type": "simple",
                    "fields_mapping": {},
                    "in_scope": True,
                    "strictness": 3,
                    "top_n_documents": 5,
                    "role_information": Path(
                        os.path.join(prompts_folder, "chat/skprompt.txt")
                    ).read_text(encoding="utf-8"),
                    "authentication": {
                        "type": "api_key",
                        "key": Config.AZURE_AI_SEARCH_API_KEY,
                    },
                },
            }
        ]

    return prompt

#### Create the action planner #####################################
planner = ActionPlanner(
    ActionPlannerOptions(
        model=model, prompts=prompts, default_prompt=get_default_prompt, logger=log
    )
)

#### Define storage and application ################################
storage = MemoryStorage()
bot_app = Application[AppTurnState](
    ApplicationOptions(
        bot_app_id=config.APP_ID,
        storage=storage,
        adapter=TeamsAdapter(config, logger=log),
        ai=AIOptions(planner=planner, enable_feedback_loop=True),
        logger=log,
        auth=(
            AuthOptions(
                default="graph",
                auto=False,
                settings={
                    "graph": OAuthOptions(
                        connection_name="graph-connection",
                        title="Sign In",
                        text="please sign in",
                        end_on_invalid_message=True,
                        enable_sso=True,
                    ),
                },
            )
            if Config.OAUTH_ENABLED
            else None
        ),
    )
)

from typing import Optional

from botbuilder.core import Storage, TurnContext
from teams.state import TurnState, ConversationState, UserState, TempState
from models import Teamsbot

class AppConversationState(ConversationState):
    debug: bool = False
    bot: Teamsbot = None
    message_handled: bool = False

    @classmethod
    async def load(cls, context: TurnContext, storage: Optional[Storage] = None) -> "AppConversationState":
        state = await super().load(context, storage)
        return cls(**state)

class AppTurnState(TurnState[AppConversationState, UserState, TempState]):

    conversation: AppConversationState

    @classmethod
    async def load(cls, context: TurnContext, storage: Optional[Storage] = None) -> "AppTurnState":
        return cls(
            conversation=await AppConversationState.load(context, storage),
            user=await UserState.load(context, storage),
            temp=await TempState.load(context, storage),
        )

Reproduction Steps

1. Start the debugging trace and power off
2. Test non-English messages
jamiesun commented 2 days ago
image

After tracing and debugging, we found that the to_string function will escape normal Chinese text, resulting in all non-Chinese text being stored as escaped characters, and llm is not able to handle escaped characters intelligently, so the response also returns escaped characters.