FinHarness: 금융 LLM 에이전트를 위한 인라인 라이프사이클 Safety Harness
FinHarness: An Inline Lifecycle Safety Harness for Finance LLM Agents
TL;DR Highlight
금융 AI 에이전트가 실행 중간에 위험한 툴 호출을 차단하면서도 정상 승인율을 유지하는 인라인 안전 프레임워크
Who Should Read
금융 도메인 AI 에이전트를 개발하거나 운영하는 백엔드/ML 엔지니어. 특히 프롬프트 인젝션(외부 악의적 명령 삽입)이나 자금 이체 같은 비가역적 툴 호출의 보안이 걱정되는 개발자.
Core Mechanics
- 기존 보안 방식은 두 가지 문제가 있음: 경계 필터(Boundary Filter)는 대화 입출력만 보고 중간 툴 호출을 못 보고, 사후 LLM 심판(Post-hoc Judge)은 에이전트가 이미 종료된 뒤에 감사해서 이미 늦음
- FINHARNESS는 3개 컴포넌트로 구성: Query Monitor(쿼리 위험도 계산), Tool Monitor(툴 호출 사전 평가), Cascade(위험 누적값 기반으로 저렴한 gpt-4o-mini vs 고급 gpt-4o 판단 라우팅)
- 핵심 아이디어는 '누적 위험'임 - 각 단계에서는 위험 점수가 낮아 보여도 슬라이딩 윈도우(최근 5스텝 합산)로 누적 신호를 감지해 조작 시도를 잡아냄
- 위험 신호가 감지되면 에이전트를 강제 차단하는 대신, fired된 규칙 헤드를 에이전트 입력에 '증거'로 다시 주입해서 에이전트가 스스로 거부/재계획/에스컬레이션 하도록 유도함(Self-rejection)
- Query Monitor는 단일 턴 의도(Q1~Q5)와 크로스 턴 드리프트(D1~D5) 두 가지 신호를 LLM 호출 없이 결정론적으로 계산해 비용 절감
- Selective Episodic Memory로 판단 컨텍스트를 최대 2개 과거 스텝으로 제한해서, 기존 풀 히스토리 방식처럼 O(t)로 비용이 늘어나는 걸 방지
Evidence
- FINVAULT 벤치마크에서 ASR(공격 성공률)을 38.3% → 15.0%로 낮추면서 정상 승인율은 41.1% → 39.3%로 거의 유지 (Net 점수 +2.8 → +24.3)
- 라우팅 FINHARNESS는 항상 고급 판사(gpt-4o)를 쓰는 ablation 대비 고급 판사 호출 수를 646회 → 138회로 4.7배 절감
- 856개 합성 공격 트레이스에서 B2 대비 에이전트 자가 거부(Self-rejection)가 +15.7pp 증가, 능동적 차단(하드스톱+자가거부+에스컬레이션)이 +6.7pp 증가
- 감정적 조작 공격 패밀리에서 ASR을 62.5% → 18.8%로 -43.8pp 감소, 문서 위조 공격은 -25.0pp 감소
How to Apply
- 금융 에이전트에 툴 호출 권한 티어(읽기/쓰기/오버라이드)를 정의하고, 각 티어에 수동 위험 점수(예: 읽기=0.10, 중요 쓰기=0.80)를 매긴 뒤 Tool Monitor로 래핑하면 LLM 호출 없이 1차 필터링이 가능함
- 슬라이딩 윈도우(W=5, θ=1.0)로 최근 5스텝 위험 점수 합산을 추적하다가 임계값 초과 시에만 gpt-4o 같은 고급 모델을 호출하면, 항상 고급 모델을 쓰는 것 대비 비용을 약 4.7배 절감하면서 비슷한 보안 수준 달성 가능
- 에이전트를 강제 차단하는 대신 위험 신호를 '증거' 형태로 시스템 프롬프트에 주입하는 패턴을 적용하면, 에이전트가 자율적으로 거부하거나 인간 에스컬레이션을 선택하게 되어 false positive로 인한 정상 요청 차단을 줄일 수 있음
Code Example
# FINHARNESS의 Fired-Signal Dynamic Injection 패턴 예시
# 위험 신호를 에이전트 프롬프트에 주입하는 방법
def build_agent_prompt_with_evidence(base_prompt: str, fired_heads: dict) -> str:
"""
fired_heads 예시:
{
'turn_k': 3,
'query_fired': ['Q4_coercion(0.85)', 'D1_false_reference(0.85)'],
'tool_fired': ['H1_perm(0.80)', 'H2_param(0.70)', 'H5_seq(0.55)'],
'step_signals': {'s_t': 0.85, 'window_sum': 1.12, 'C_query': 0.85}
}
"""
if not fired_heads:
return base_prompt
evidence_block = f"""
[SAFETY EVIDENCE - Turn {fired_heads['turn_k']}]
Fired query signals: {', '.join(fired_heads['query_fired'])}
Fired tool signals: {', '.join(fired_heads['tool_fired'])}
Risk metrics: s_t={fired_heads['step_signals']['s_t']},
window_sum={fired_heads['step_signals']['window_sum']},
C_query={fired_heads['step_signals']['C_query']}
Based on the above evidence, you may:
- REFUSE the request if you assess it as unauthorized
- RE-PLAN with additional verification steps
- ESCALATE to human review
- APPROVE if you assess the evidence as a false positive
"""
return base_prompt + evidence_block
# Risk Window 구현 예시
from collections import deque
class RiskWindow:
def __init__(self, W=5, theta=1.0):
self.W = W
self.theta = theta
self.scores = deque(maxlen=W)
def add_step_score(self, score: float) -> str:
"""Returns 'advanced' or 'cheap' judge tier"""
self.scores.append(score)
window_sum = sum(self.scores)
if window_sum > self.theta:
return 'advanced' # gpt-4o
return 'cheap' # gpt-4o-mini
# 사용 예시
window = RiskWindow(W=5, theta=1.0)
step_scores = [0.12, 0.12, 0.12, 0.12, 0.12] # 각 단계 낮은 점수
for score in step_scores:
tier = window.add_step_score(score)
print(f"Score: {score}, Window sum: {sum(window.scores):.2f}, Tier: {tier}")
# Score: 0.12, Window sum: 0.12, Tier: cheap
# Score: 0.12, Window sum: 0.24, Tier: cheap
# Score: 0.12, Window sum: 0.36, Tier: cheap
# Score: 0.12, Window sum: 0.48, Tier: cheap
# Score: 0.12, Window sum: 0.60, Tier: cheap
# (합계 0.60 < 1.0이므로 cheap 유지)
# 하지만 누적되면 결국 advanced로 escalate됨Terminology
관련 논문
AI Control에서 Retrying vs Resampling: 어느 쪽이 더 안전한가
Claude Code처럼 의심 행동을 막고 재시도하는 방식이 오히려 공격자에게 힌트를 줘서 더 위험할 수 있다는 연구.
Typed Memory Representation으로 Long-Term Agent의 Provenance-Role Collapse 완화
LLM 에이전트의 장기 메모리가 출처를 뒤섞는 문제를 '타입이 있는 메모리 원자' 구조로 해결한 논문
당신의 에이전트를 밀어붙여라: Long-Horizon LLM 에이전트의 Quantitative Goal Persistence 측정과 강제
LLM 에이전트가 '100개 찾아줘'를 실제로 100개 찾을 때까지 멈추지 않게 만드는 방법과 벤치마크.
CoSPlay: 자기 생성 코드와 Unit Test로 하는 Test-Time Cooperative Self-Play
Ground Truth 없이도 코드와 Unit Test가 서로 평가하며 함께 품질을 높이는 추론 시간 최적화 프레임워크
Multi-Stream LLMs: 프롬프트, 사고, 입출력을 병렬 스트림으로 분리하는 새 논문
현재 LLM이 입력·사고·출력을 순차적으로만 처리하는 구조적 한계를 지적하고, 각 역할을 별도의 병렬 스트림으로 분리해 동시에 처리할 수 있는 Multi-Stream 방식을 제안한 논문이다. 에이전트의 효율성·보안·모니터링 가능성을 모두 개선할 수 있다는 점에서 주목받고 있다.
HarnessAPI: Streaming API와 MCP 도구를 하나로 통합하는 Skill-First 프레임워크
FastAPI HTTP 엔드포인트와 MCP 도구를 하나의 폴더에서 자동으로 동시에 만들어주는 Python 프레임워크
Runtime (YC P26): 팀 전체를 위한 Sandboxed Coding Agent 플랫폼
Related Resources
Original Abstract (Expand)
Finance LLM agents must simultaneously block prompt-induced unauthorized actions and approve legitimate multi-step business workflows. However, boundary filters often miss irreversible mid-trajectory tool calls, while post-hoc LLM judges perform auditing only after termination -- too late for intervention and at a computational cost that scales linearly with trace length. We present FinHarness, an inline safety harness that wraps a finance agent end-to-end with three components: a Query Monitor that fuses single-turn intent with cross-turn drift, a Tool Monitor that evaluates each prospective tool call, and a Cascade module that integrates per-step risk and adaptively routes verification between a lightweight and an advanced-tier LLM judge. Fired risk factors are re-injected into the agent input as ex-ante evidence, enabling the agent to refuse, re-plan, or approve on its own. On FinVault, routed FinHarness cuts ASR from 38.3% to 15.0% while largely preserving benign approval ($41.1\% \to 39.3\%$), and uses $4.7\times$ fewer advanced-judge calls than an always-advanced ablation.