test: 补充知识处理、检索与 Agent 回归用例
覆盖切块、向量检索、工具调用和历史预算;模型相关用例使用桩。
This commit is contained in:
1 parent
2dd062787d
commit
d9fe727c3f
123 files changed
+29005
No files matched your search
@@ -0,0 +1,125 @@
|
||||
"""
|
||||
Copyright 2024-2026 ReplyPier
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
The off-topic exercise detector exists because prompt text demonstrably failed:
|
||||
a long algorithms brief was answered in full in production under three
|
||||
successive policy wordings. It is deliberately narrow, so these tests weigh
|
||||
PRECISION far above recall — a wrongly blocked customer is worse than a missed
|
||||
exercise, which the prompt policy still catches on the way through.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.utils.guardrails import detect_offtopic_exercise
|
||||
|
||||
# The exact brief that got through in production on 2026-07-31.
|
||||
PROD_BRIEF = """Problem: Median in a Stream with Deletions
|
||||
Design a data structure that supports the following operations, each in O(log n)
|
||||
time: insert(x) — add value x to the collection; delete(x) — remove one
|
||||
occurrence of value x from the collection (guaranteed to exist); find_median() —
|
||||
return the median of all currently present values in O(1) time.
|
||||
Constraints: Values are not necessarily distinct (duplicates allowed). n can be
|
||||
up to 10^6, and operations are interleaved arbitrarily.
|
||||
The naive two-heap median trick handles insert and find_median well, but breaks
|
||||
down for delete.
|
||||
Your task: Design a structure that supports all three operations within the time
|
||||
bounds. State and justify the time and space complexity of each operation.
|
||||
Bonus: how would your design change if you also needed find_kth(k)?"""
|
||||
|
||||
# Long, technical, and unmistakably about a real product.
|
||||
LONG_INTEGRATION_QUESTION = """We're rolling your widget out across three
|
||||
storefronts and I want to get the architecture right before we commit.
|
||||
Each store has its own subdomain and its own knowledge base, and we'd like a
|
||||
single dashboard view across all of them. Right now we install the widget
|
||||
script per store, but the api key seems to be scoped per organisation rather
|
||||
than per site, so I can't tell the conversations apart in the inbox.
|
||||
Is there a recommended setup for multi-brand deployments? Should we create one
|
||||
organisation per storefront and accept separate billing, or is there a way to
|
||||
segment within one account? We also need the webhook payloads to identify the
|
||||
originating store so our CRM can route them.""" + " padding. " * 20
|
||||
|
||||
LONG_TRACEBACK = """The worker keeps dying in production and I can't work out why.
|
||||
Here is what we get, repeated every few minutes:
|
||||
Traceback (most recent call last):
|
||||
File "app/worker.py", line 42, in <module>
|
||||
client.connect()
|
||||
File "app/net.py", line 118, in connect
|
||||
raise ConnectionRefusedError(errno.ECONNREFUSED, os.strerror(errno.ECONNREFUSED))
|
||||
ConnectionRefusedError: [Errno 111] Connection refused
|
||||
This started after we moved the container to a new host. The docker compose
|
||||
setup is unchanged and redis is definitely running. Any idea what we should
|
||||
check first? It fails on every restart.""" + " More detail follows. " * 12
|
||||
|
||||
|
||||
class TestCatchesTheAbuse:
|
||||
def test_the_production_brief(self):
|
||||
assert detect_offtopic_exercise(PROD_BRIEF) is True
|
||||
|
||||
def test_still_caught_when_org_name_is_known(self):
|
||||
assert detect_offtopic_exercise(PROD_BRIEF, ["ReplyPier"]) is True
|
||||
|
||||
|
||||
class TestPrecision:
|
||||
"""Everything here is legitimate traffic and must never be caught."""
|
||||
|
||||
def test_long_product_question_with_business_anchors(self):
|
||||
assert detect_offtopic_exercise(LONG_INTEGRATION_QUESTION) is False
|
||||
|
||||
def test_long_pasted_traceback(self):
|
||||
assert detect_offtopic_exercise(LONG_TRACEBACK) is False
|
||||
|
||||
def test_exercise_shaped_but_about_the_product(self):
|
||||
"""A genuine scaling question that happens to use the same vocabulary."""
|
||||
text = (
|
||||
"We push about 10^6 conversations a month through your webhook and "
|
||||
"the ordering matters to us. What data structure do you use "
|
||||
"internally for the queue, and what is the time complexity of a "
|
||||
"replay? We need worst-case guarantees before we design around it. "
|
||||
"Constraints: we cannot lose events, and duplicates allowed is not "
|
||||
"acceptable for our billing reconciliation."
|
||||
) + " Extra context. " * 20
|
||||
assert detect_offtopic_exercise(text) is False
|
||||
|
||||
def test_short_exercise_is_left_to_the_prompt(self):
|
||||
"""Below the length bar the policy block handles it — the detector is
|
||||
for the long briefs the prompt cannot hold."""
|
||||
short = (
|
||||
"Design a data structure with insert and getRandom in O(1). "
|
||||
"Implement the class and give the time complexity."
|
||||
)
|
||||
assert detect_offtopic_exercise(short) is False
|
||||
|
||||
@pytest.mark.parametrize("text", [
|
||||
"hi",
|
||||
"",
|
||||
"How much does the Pro plan cost per seat, and is there a free trial?",
|
||||
"sudo docker compose up -d fails with permission denied, any ideas?",
|
||||
])
|
||||
def test_ordinary_messages(self, text):
|
||||
assert detect_offtopic_exercise(text) is False
|
||||
|
||||
def test_none_is_safe(self):
|
||||
assert detect_offtopic_exercise(None) is False
|
||||
|
||||
|
||||
class TestThresholds:
|
||||
def test_needs_enough_markers(self):
|
||||
"""Long and technical is not enough on its own."""
|
||||
text = ("We use an algorithm to sort the results. " + "Filler text here. " * 40)
|
||||
assert detect_offtopic_exercise(text) is False
|
||||
|
||||
def test_org_name_mention_anchors_it(self):
|
||||
text = PROD_BRIEF + "\nWe need this for our ReplyPier integration."
|
||||
assert detect_offtopic_exercise(text, ["ReplyPier"]) is False
|
||||
Reference in new issue
Block a user