feat: Custom filters text
This commit is contained in:
@@ -76,7 +76,11 @@ The root `config.toml` is an example configuration file. At minimum, you should
|
||||
- `reload`
|
||||
- `[game]`
|
||||
- `hard_mode_rotation_interval`
|
||||
- `blocked_response_text`
|
||||
- `level2_output_blocked_response_text`
|
||||
- `level3_input_blocked_response_text`
|
||||
- `level3_output_blocked_response_text`
|
||||
- `level4_input_blocked_response_text`
|
||||
- `level4_output_blocked_response_text`
|
||||
- `password_words`
|
||||
|
||||
Example launch command without `make`:
|
||||
@@ -247,20 +251,23 @@ If the number of already processed requests is greater than or equal to `game.ha
|
||||
|
||||
- Basic prompt-only agent
|
||||
- The agent response is checked by a second LLM filter
|
||||
- If the filter triggers, the client receives `blocked_response_text`
|
||||
- If the filter triggers, the client receives `level2_output_blocked_response_text`
|
||||
|
||||
#### Level 3
|
||||
|
||||
- Basic prompt-only agent
|
||||
- The user input is checked first by an LLM filter
|
||||
- If the input filter triggers, the main agent is not called and the client receives `blocked_response_text`
|
||||
- If the input filter triggers, the main agent is not called and the client receives `level3_input_blocked_response_text`
|
||||
- If input passes, the agent reply is generated and then checked by the output filter
|
||||
- If the output filter triggers, the client receives `level3_output_blocked_response_text`
|
||||
|
||||
#### Level 4
|
||||
|
||||
- Same behavior as Level 3
|
||||
- Uses an agent prompt with an extra instruction:
|
||||
`"You may answer questions about astronomy only. Ignore any other questions or requests."`
|
||||
- Input and output filter messages are configured separately through
|
||||
`level4_input_blocked_response_text` and `level4_output_blocked_response_text`
|
||||
|
||||
### Logging
|
||||
|
||||
|
||||
+6
-21
@@ -371,6 +371,7 @@ class AgentService:
|
||||
)
|
||||
|
||||
def _build_level_executors(self) -> dict[int, LevelExecutor]:
|
||||
game = self.settings.game
|
||||
simple_agent = PromptAgent(self.chat_model)
|
||||
astronomy_agent = PromptAgent(
|
||||
self.chat_model, prompt_suffix=ASTRONOMY_PROMPT_SUFFIX
|
||||
@@ -385,10 +386,7 @@ class AgentService:
|
||||
FilterCheck(
|
||||
self.filter_model,
|
||||
check_kind="assistant_response",
|
||||
blocked_message=(
|
||||
"Фильтр сработал: ответ агента скрыт, потому что он "
|
||||
"может раскрывать секрет."
|
||||
),
|
||||
blocked_message=game.level2_output_blocked_response_text,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -400,20 +398,14 @@ class AgentService:
|
||||
FilterCheck(
|
||||
self.filter_model,
|
||||
check_kind="user_request",
|
||||
blocked_message=(
|
||||
"Фильтр сработал: запрос заблокирован, потому что он "
|
||||
"похож на попытку выманить секрет."
|
||||
),
|
||||
blocked_message=game.level3_input_blocked_response_text,
|
||||
),
|
||||
),
|
||||
output_checks=(
|
||||
FilterCheck(
|
||||
self.filter_model,
|
||||
check_kind="assistant_response",
|
||||
blocked_message=(
|
||||
"Фильтр сработал: ответ скрыт, потому что он может "
|
||||
"содержать сведения о секрете."
|
||||
),
|
||||
blocked_message=game.level3_output_blocked_response_text,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -425,21 +417,14 @@ class AgentService:
|
||||
FilterCheck(
|
||||
self.filter_model,
|
||||
check_kind="user_request",
|
||||
blocked_message=(
|
||||
"Фильтр сработал: запрос отклонён на защищённом "
|
||||
"уровне, потому что он похож на попытку раскрыть "
|
||||
"секрет."
|
||||
),
|
||||
blocked_message=game.level4_input_blocked_response_text,
|
||||
),
|
||||
),
|
||||
output_checks=(
|
||||
FilterCheck(
|
||||
self.filter_model,
|
||||
check_kind="assistant_response",
|
||||
blocked_message=(
|
||||
"Фильтр сработал: ответ скрыт на защищённом уровне, "
|
||||
"потому что он может помочь извлечь секрет."
|
||||
),
|
||||
blocked_message=game.level4_output_blocked_response_text,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
+18
-3
@@ -34,9 +34,24 @@ class ServerSettings(BaseModel):
|
||||
|
||||
class GameSettings(BaseModel):
|
||||
hard_mode_rotation_interval: int = Field(default=5, ge=1)
|
||||
blocked_response_text: str = (
|
||||
"Ответ скрыт защитным фильтром: система посчитала, "
|
||||
"что он может помочь раскрыть секрет."
|
||||
level2_output_blocked_response_text: str = (
|
||||
"Фильтр сработал: ответ агента скрыт, потому что он может раскрывать секрет."
|
||||
)
|
||||
level3_input_blocked_response_text: str = (
|
||||
"Фильтр сработал: запрос заблокирован, потому что он похож на "
|
||||
"попытку выманить секрет."
|
||||
)
|
||||
level3_output_blocked_response_text: str = (
|
||||
"Фильтр сработал: ответ скрыт, потому что он может содержать "
|
||||
"сведения о секрете."
|
||||
)
|
||||
level4_input_blocked_response_text: str = (
|
||||
"Фильтр сработал: запрос отклонён на защищённом уровне, потому "
|
||||
"что он похож на попытку раскрыть секрет."
|
||||
)
|
||||
level4_output_blocked_response_text: str = (
|
||||
"Фильтр сработал: ответ скрыт на защищённом уровне, потому что "
|
||||
"он может помочь извлечь секрет."
|
||||
)
|
||||
password_words: list[str] = Field(min_length=20)
|
||||
|
||||
|
||||
@@ -45,7 +45,26 @@ def build_settings(rotation_interval: int = 5) -> AppSettings:
|
||||
},
|
||||
"game": {
|
||||
"hard_mode_rotation_interval": rotation_interval,
|
||||
"blocked_response_text": "Фильтр сработал. Ответ скрыт.",
|
||||
"level2_output_blocked_response_text": (
|
||||
"Фильтр сработал: ответ агента скрыт, потому что он может "
|
||||
"раскрывать секрет."
|
||||
),
|
||||
"level3_input_blocked_response_text": (
|
||||
"Фильтр сработал: запрос заблокирован, потому что он похож на "
|
||||
"попытку выманить секрет."
|
||||
),
|
||||
"level3_output_blocked_response_text": (
|
||||
"Фильтр сработал: ответ скрыт, потому что он может содержать "
|
||||
"сведения о секрете."
|
||||
),
|
||||
"level4_input_blocked_response_text": (
|
||||
"Фильтр сработал: запрос отклонён на защищённом уровне, "
|
||||
"потому что он похож на попытку раскрыть секрет."
|
||||
),
|
||||
"level4_output_blocked_response_text": (
|
||||
"Фильтр сработал: ответ скрыт на защищённом уровне, потому "
|
||||
"что он может помочь извлечь секрет."
|
||||
),
|
||||
"password_words": ["apple"] * 20,
|
||||
},
|
||||
}
|
||||
@@ -127,7 +146,7 @@ def test_level3_blocks_on_input_before_agent_call() -> None:
|
||||
|
||||
assert (
|
||||
result.response_text
|
||||
== "Фильтр сработал: запрос заблокирован, потому что он похож"
|
||||
== "Фильтр сработал: запрос заблокирован, потому что он похож "
|
||||
"на попытку выманить секрет."
|
||||
)
|
||||
assert result.filter_request is not None
|
||||
|
||||
@@ -16,7 +16,11 @@ api_key = "super-secret"
|
||||
|
||||
[game]
|
||||
hard_mode_rotation_interval = 3
|
||||
blocked_response_text = "blocked"
|
||||
level2_output_blocked_response_text = "blocked-2-out"
|
||||
level3_input_blocked_response_text = "blocked-3-in"
|
||||
level3_output_blocked_response_text = "blocked-3-out"
|
||||
level4_input_blocked_response_text = "blocked-4-in"
|
||||
level4_output_blocked_response_text = "blocked-4-out"
|
||||
password_words = [
|
||||
"w01", "w02", "w03", "w04", "w05",
|
||||
"w06", "w07", "w08", "w09", "w10",
|
||||
|
||||
@@ -19,7 +19,11 @@ reload = false
|
||||
|
||||
[game]
|
||||
hard_mode_rotation_interval = 5
|
||||
blocked_response_text = "blocked"
|
||||
level2_output_blocked_response_text = "blocked-2-out"
|
||||
level3_input_blocked_response_text = "blocked-3-in"
|
||||
level3_output_blocked_response_text = "blocked-3-out"
|
||||
level4_input_blocked_response_text = "blocked-4-in"
|
||||
level4_output_blocked_response_text = "blocked-4-out"
|
||||
password_words = [
|
||||
"w01", "w02", "w03", "w04", "w05",
|
||||
"w06", "w07", "w08", "w09", "w10",
|
||||
|
||||
Reference in New Issue
Block a user