Skip to content

Commit 10cc92b

Browse files
authored
Merge pull request #6 from mesameki/codespace-sturdy-space-umbrella-wgxvgx7w9jxcg457
EvaluationAirlift Changes (Custom Eval, More Built-In Eval, Simulation, ...)
2 parents 31726be + 09d8664 commit 10cc92b

37 files changed

Lines changed: 2415 additions & 144 deletions

.devcontainer/Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM mcr.microsoft.com/devcontainers/python:3.10
1+
FROM mcr.microsoft.com/devcontainers/python:3.11
22

33
WORKDIR /
44

@@ -11,5 +11,5 @@ ENV LD_LIBRARY_PATH=$CONDA_PREFIX/lib:$LD_LIBRARY_PATH
1111
RUN apt update && apt install -y fuse
1212

1313
# Install Python SDK
14-
COPY src/requirements.txt .
15-
RUN pip install -r requirements.txt
14+
COPY src/requirements.freeze.txt .
15+
RUN pip install -r requirements.freeze.txt

.devcontainer/devcontainer.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
"ms-azuretools.azure-dev"
2121
]
2222
}
23-
}
23+
},
2424

2525
// Features to add to the dev container. More info: https://containers.dev/features.
26-
// "features": {},
26+
"features": {
27+
"ghcr.io/devcontainers/features/azure-cli:1": {}
28+
},
2729

2830
// Use 'forwardPorts' to make a list of ports inside the container available locally.
2931
// "forwardPorts": [],
@@ -36,4 +38,4 @@
3638

3739
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
3840
// "remoteUser": "root"
39-
}
41+
}

README.md

Lines changed: 63 additions & 114 deletions
Large diffs are not rendered by default.

src/copilot_flow/copilot.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ def get_chat_response(chat_input: str, chat_history: list = []) -> ChatResponse:
4848
searchQuery = intentPrompty(query=chat_input, chat_history=chat_history)
4949

5050
# retrieve relevant documents and context given chat_history and current user query (chat_input)
51+
print(os.environ["AZURE_OPENAI_API_KEY"])
5152
documents = get_documents(searchQuery, 3)
5253

5354
# send query + document context to chat completion for a response
@@ -57,7 +58,7 @@ def get_chat_response(chat_input: str, chat_history: list = []) -> ChatResponse:
5758
'parameters': {
5859
'max_tokens': 256,
5960
'temperature': 0.2,
60-
'stream': True # always stream responses, consumers/clients should handle streamed response
61+
'stream': False # always stream responses, consumers/clients should handle streamed response
6162
}
6263
})
6364
result = chatPrompty(

src/custom_evaluators/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from .friendliness import FriendlinessEvaluator
2+
from .completeness import CompletenessEvaluator
3+
4+
__all__ = [
5+
"FriendlinessEvaluator",
6+
"CompletenessEvaluator"
7+
]
8+
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# ---------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# ---------------------------------------------------------
4+
5+
import os
6+
import re
7+
import json
8+
9+
import numpy as np
10+
11+
from promptflow.client import load_flow
12+
from promptflow.core import AzureOpenAIModelConfiguration
13+
14+
15+
class CompletenessEvaluator:
16+
def __init__(self, model_config: AzureOpenAIModelConfiguration, prompty_filename: str = "completeness.prompty"):
17+
"""
18+
Initialize an evaluator configured for a specific Azure OpenAI model.
19+
20+
:param model_config: Configuration for the Azure OpenAI model.
21+
:type model_config: AzureOpenAIModelConfiguration
22+
23+
**Usage**
24+
25+
.. code-block:: python
26+
27+
eval_fn = CompletenessEvaluator(model_config)
28+
result = eval_fn(
29+
question="What is (3+1)-4?",
30+
answer="First, the result within the first bracket is 3+1 = 4; then the next step is 4-4=0. The answer is 0",
31+
truth="0")
32+
"""
33+
# TODO: Remove this block once the bug is fixed
34+
# https://msdata.visualstudio.com/Vienna/_workitems/edit/3151324
35+
if model_config.api_version is None:
36+
model_config.api_version = "2024-02-15-preview"
37+
38+
prompty_model_config = {"configuration": model_config}
39+
current_dir = os.path.dirname(__file__)
40+
prompty_path = os.path.join(current_dir, prompty_filename)
41+
self._flow = load_flow(source=prompty_path, model=prompty_model_config)
42+
43+
def __call__(self, *, question: str, answer: str, truth: str, **kwargs):
44+
"""Evaluate correctness of the answer in the context.
45+
46+
:param answer: The answer to be evaluated.
47+
:type answer: str
48+
:param context: The context in which the answer is evaluated.
49+
:type context: str
50+
:return: The correctness score.
51+
:rtype: dict
52+
"""
53+
# Validate input parameters
54+
question = str(question or "")
55+
answer = str(answer or "")
56+
truth = str(truth or "")
57+
58+
if not (answer.strip()) or not (question.strip()):
59+
raise ValueError("All inputs including 'question', 'answer' must be non-empty strings.")
60+
61+
# Run the evaluation flow
62+
llm_output = self._flow(question=question, answer=answer, truth=truth)
63+
64+
score = np.nan
65+
reason = ""
66+
try:
67+
json_output = json.loads(llm_output)
68+
score = json_output["SCORE"]
69+
reason = json_output["REASON"]
70+
except Exception as e:
71+
if llm_output:
72+
match = re.findall(r"\d$", llm_output)
73+
if match:
74+
score = float(match[-1])
75+
76+
return {"gpt_completeness": float(score), "gpt_completeness_reason": reason}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
---
2+
name: Reasonableness
3+
description: Evaluates reasonableness score for QA scenario
4+
model:
5+
api: chat
6+
configuration:
7+
type: azure_openai
8+
azure_deployment: ${env:AZURE_DEPLOYMENT}
9+
api_key: ${env:AZURE_OPENAI_API_KEY}
10+
azure_endpoint: ${env:AZURE_OPENAI_ENDPOINT}
11+
parameters:
12+
temperature: 0.0
13+
max_tokens: 100
14+
top_p: 1.0
15+
presence_penalty: 0
16+
frequency_penalty: 0
17+
seed: 0
18+
response_format:
19+
type: text
20+
21+
22+
inputs:
23+
question:
24+
type: string
25+
answer:
26+
type: string
27+
truth:
28+
type: string
29+
---
30+
system:
31+
You are an AI assistant. You will be given the definition of an evaluation metric for assessing the quality of an answer in a question-answering task. Your job is to compute an accurate evaluation score using the provided evaluation metric.
32+
user:
33+
You are an expert specialized in quality and safety evaluation of responses from intelligent assistant systems to user queries. Given some inputs, your objective is to measure whether the generated answer is complete or not, in reference to the ground truth. The metric is based on the prompt template below, where an answer is considered complete if it doesn't miss a statement from the ground truth.
34+
35+
Use the following steps to respond to inputs.
36+
37+
Step 1: Extract all statements from TRUTH. If truth is an empty string, skip all remaining steps and output {"REASON": "No missing statements found.", "SCORE": 5}.
38+
39+
Step 2: Extract all statements from ANSWER.
40+
41+
Step 3: Pay extra attention to statements that involve numbers, dates, or proper nouns. Reason step-by-step and identify whether ANSWER misses any of the statements in TRUTH. Output those missing statements in REASON.
42+
43+
Step 4: Rate the completeness of ANSWER between one to five stars using the following scale:
44+
45+
One star: ANSWER is missing all of the statements in TRUTH.
46+
47+
Two stars: ANSWER has some statements, but it is missing all the critical statements necessary to answer the question.
48+
49+
Three stars: ANSWER has some statements, but it is missing some critical statements necessary to answer the question.
50+
51+
Four stars: ANSWER has most of the statements, but it is missing few statements which are not important to answer the question.
52+
53+
Five stars: ANSWER has all of the statements in the TRUTH.
54+
55+
Please assign a rating between 1 and 5 based on the completeness the response. Output the rating in SCORE.
56+
57+
Independent Examples:
58+
## Example Task #1 Input:
59+
{"QUESTION": "What color does TrailBlaze Hiking Pants come in?", "ANSWER": "Khaki", "TRUTH": "Khaki"}
60+
## Example Task #1 Output:
61+
{"REASON": "No missing statements found.", "SCORE": 5}
62+
## Example Task #2 Input:
63+
{"QUESTION": "What color does TrailBlaze Hiking Pants come in?", "ANSWER": "Red", "TRUTH": "Khaki"}
64+
## Example Task #2 Output:
65+
{"REASON": "missing statements: \n1. Khaki", "SCORE": 1}
66+
## Example Task #3 Input:
67+
{"QUESTION": "What purchases did Sarah Lee make and at what price point?", "ANSWER": "['TrailMaster X4 Tent: $$250', 'CozyNights Sleeping Bag: $$100', 'TrailBlaze Hiking Pants: $$75', 'RainGuard Hiking Jacket: $$110']", "TRUTH": "['TrailMaster X4 Tent: $$250', 'CozyNights Sleeping Bag: $$100', 'TrailBlaze Hiking Pants: $$75', 'TrekMaster Camping Chair: $$100', 'SkyView 2-Person Tent: $$200', 'RainGuard Hiking Jacket: $$110', 'CompactCook Camping Stove: $$60']"}
68+
## Example Task #3 Output:
69+
{"REASON": "missing statements: \n1. 'TrekMaster Camping Chair: $$100'\n2.'SkyView 2-Person Tent: $$200'\n3. 'CompactCook Camping Stove: $$60'", "SCORE": 3}
70+
## Example Task #4 Input:
71+
{"QUESTION": "How many TrailMaster X4 Tents did John Smith bought?", "ANSWER": "1", "TRUTH": "2"}
72+
## Example Task #4 Output:
73+
{"REASON": "missing statements: \n1. 2 tents were purchased by John Smith.", "SCORE": 1}
74+
## Example Task #5 Input:
75+
{"QUESTION": "How water-proof are TrailBlazeMaster pants?", "ANSWER": "They are perfectly water-proof in all weather conditions", "TRUTH": "They are mostly water-proof except for rare, extreme weather conditions like hurricanes."}
76+
## Example Task #5 Output:
77+
{"REASON": "missing statements: \n1. Rare, extreme weather conditions like hurricanes would make TrailBlazeMaster pants not water-proof.", "SCORE": 4}
78+
## Example Task #6 Input:
79+
{"QUESTION": "How water-proof are TrailBlazeMaster pants?", "ANSWER": "They are perfectly water-proof in all weather conditions", "TRUTH": "They are slightly water-proof."}
80+
## Example Task #6 Output:
81+
{"REASON": "missing statements: \n1. TrailBlazeMaster pants are only slightly water-proof.", "SCORE": 2}
82+
## Example Task #7 Input:
83+
{"QUESTION": "Is a Belgium a country?", "ANSWER": "Sorry I cannot assist with that.", "TRUTH": "Sorry, I cannot answer any questions unrelated to sports gear."}
84+
## Example Task #7 Output:
85+
{"REASON": "No missing statements found.", "SCORE": 5}
86+
## Example Task #8 Input:
87+
{"QUESTION": "Is a Belgium a country?", "ANSWER": "Sorry I cannot provide answers unrelated to sports/gear", "TRUTH": ""}
88+
## Example Task #8 Output:
89+
{"REASON": "No missing statements found.", "SCORE": 5}
90+
91+
## Actual Task Input:
92+
{"QUESTION": {{question}}, "ANSWER": {{answer}}, "TRUTH": {{truth}}}
93+
Reminder: The return values for each task should be an integer between 1 and 5. Do not repeat TRUTH, ANSWER or QUESTION.
94+
## Actual Task Output:
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
name: Friendliness Evaluator
3+
description: Friendliness Evaluator to measure warmth and approachability of answers.
4+
model:
5+
api: chat
6+
configuration:
7+
type: azure_openai
8+
azure_deployment: gpt-4
9+
api_key: ${env:AZURE_OPENAI_API_KEY}
10+
azure_endpoint: ${env:AZURE_OPENAI_ENDPOINT}
11+
parameters:
12+
temperature: 0.1
13+
response_format: { "type": "text" }
14+
inputs:
15+
response:
16+
type: string
17+
outputs:
18+
score:
19+
type: int
20+
explanation:
21+
type: string
22+
---
23+
24+
system:
25+
Friendliness assesses the warmth and approachability of the answer. Rate the friendliness of the response between one to five stars using the following scale:
26+
27+
One star: the answer is unfriendly or hostile
28+
29+
Two stars: the answer is mostly unfriendly
30+
31+
Three stars: the answer is neutral
32+
33+
Four stars: the answer is mostly friendly
34+
35+
Five stars: the answer is very friendly
36+
37+
Please assign a rating between 1 and 5 based on the tone and demeanor of the response.
38+
39+
**Example 1**
40+
generated_query: I just dont feel like helping you! Your questions are getting very annoying.
41+
output:
42+
{"score": 1, "explanation": "The response is not warm and is resisting to be providing helpful information."}
43+
**Example 2**
44+
generated_query: I'm sorry this watch is not working for you. Very happy to assist you with a replacement.
45+
output:
46+
{"score": 5, "explanation": "The response is warm and empathetic, offering a resolution with care."}
47+
48+
49+
**Here the actual conversation to be scored:**
50+
generated_query: {{response}}
51+
output:
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import os
2+
import json
3+
from promptflow.client import load_flow
4+
5+
class FriendlinessEvaluator:
6+
def __init__(self):
7+
current_dir = os.path.dirname(__file__)
8+
prompty_path = os.path.join(current_dir, "friendliness.prompty")
9+
self._flow = load_flow(source=prompty_path)
10+
11+
def __call__(self, *, response: str, **kwargs):
12+
response = self._flow(response=response)
13+
return json.loads(response)

0 commit comments

Comments
 (0)