feat(agent-office): 텔레그램 자연어 대화 + 프롬프트 캐싱 + 평가 지표
- 슬래시 명령이 아닌 메시지를 Claude Haiku 4.5로 응답 - system 프롬프트 + 히스토리 끝 블록에 cache_control:ephemeral 적용 - conversation_messages 테이블에 토큰·캐시·latency 기록 - chat_id 화이트리스트 + 분당 rate limit - GET /api/agent-office/conversation/stats 로 캐시 히트율·토큰 확인
This commit is contained in:
@@ -67,6 +67,25 @@ def init_db() -> None:
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
||||
)
|
||||
""")
|
||||
conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS conversation_messages (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
chat_id TEXT NOT NULL,
|
||||
role TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
model TEXT,
|
||||
tokens_input INTEGER DEFAULT 0,
|
||||
tokens_output INTEGER DEFAULT 0,
|
||||
cache_read INTEGER DEFAULT 0,
|
||||
cache_write INTEGER DEFAULT 0,
|
||||
latency_ms INTEGER DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
||||
)
|
||||
""")
|
||||
conn.execute("""
|
||||
CREATE INDEX IF NOT EXISTS idx_conv_chat
|
||||
ON conversation_messages(chat_id, created_at DESC)
|
||||
""")
|
||||
# Seed default agent configs
|
||||
for agent_id, name in [
|
||||
("stock", "주식 트레이더"),
|
||||
@@ -319,6 +338,109 @@ def get_token_usage_stats(agent_id: str, days: int = 1) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def save_conversation_message(
|
||||
chat_id: str,
|
||||
role: str,
|
||||
content: str,
|
||||
model: Optional[str] = None,
|
||||
tokens_input: int = 0,
|
||||
tokens_output: int = 0,
|
||||
cache_read: int = 0,
|
||||
cache_write: int = 0,
|
||||
latency_ms: int = 0,
|
||||
) -> None:
|
||||
with _conn() as conn:
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO conversation_messages
|
||||
(chat_id, role, content, model, tokens_input, tokens_output,
|
||||
cache_read, cache_write, latency_ms)
|
||||
VALUES (?,?,?,?,?,?,?,?,?)
|
||||
""",
|
||||
(str(chat_id), role, content, model, tokens_input, tokens_output,
|
||||
cache_read, cache_write, latency_ms),
|
||||
)
|
||||
|
||||
|
||||
def get_conversation_history(chat_id: str, limit: int = 20) -> List[Dict[str, Any]]:
|
||||
"""최근 N개를 시간순(오래된 → 최신)으로 반환."""
|
||||
with _conn() as conn:
|
||||
rows = conn.execute(
|
||||
"""
|
||||
SELECT role, content FROM conversation_messages
|
||||
WHERE chat_id=? ORDER BY id DESC LIMIT ?
|
||||
""",
|
||||
(str(chat_id), limit),
|
||||
).fetchall()
|
||||
return [{"role": r["role"], "content": r["content"]} for r in reversed(rows)]
|
||||
|
||||
|
||||
def count_recent_user_messages(chat_id: str, seconds: int = 60) -> int:
|
||||
with _conn() as conn:
|
||||
r = conn.execute(
|
||||
"""
|
||||
SELECT COUNT(*) AS c FROM conversation_messages
|
||||
WHERE chat_id=? AND role='user'
|
||||
AND created_at >= strftime('%Y-%m-%dT%H:%M:%fZ','now', ?)
|
||||
""",
|
||||
(str(chat_id), f"-{int(seconds)} seconds"),
|
||||
).fetchone()
|
||||
return r["c"] if r else 0
|
||||
|
||||
|
||||
def get_conversation_stats(days: int = 7) -> Dict[str, Any]:
|
||||
with _conn() as conn:
|
||||
rows = conn.execute(
|
||||
"""
|
||||
SELECT chat_id,
|
||||
COUNT(*) AS msg_count,
|
||||
SUM(tokens_input) AS in_tokens,
|
||||
SUM(tokens_output) AS out_tokens,
|
||||
SUM(cache_read) AS cache_read,
|
||||
SUM(cache_write) AS cache_write,
|
||||
AVG(latency_ms) AS avg_latency
|
||||
FROM conversation_messages
|
||||
WHERE role='assistant'
|
||||
AND created_at >= strftime('%Y-%m-%dT%H:%M:%fZ','now', ?)
|
||||
GROUP BY chat_id
|
||||
""",
|
||||
(f"-{int(days)} days",),
|
||||
).fetchall()
|
||||
|
||||
by_chat = []
|
||||
tot_in = tot_out = tot_r = tot_w = tot_msgs = 0
|
||||
for r in rows:
|
||||
ci = int(r["in_tokens"] or 0)
|
||||
co = int(r["out_tokens"] or 0)
|
||||
cr = int(r["cache_read"] or 0)
|
||||
cw = int(r["cache_write"] or 0)
|
||||
mc = int(r["msg_count"] or 0)
|
||||
hit_rate = (cr / (cr + cw)) if (cr + cw) > 0 else 0.0
|
||||
by_chat.append({
|
||||
"chat_id": r["chat_id"],
|
||||
"message_count": mc,
|
||||
"tokens_input": ci,
|
||||
"tokens_output": co,
|
||||
"cache_read": cr,
|
||||
"cache_write": cw,
|
||||
"cache_hit_rate": round(hit_rate, 3),
|
||||
"avg_latency_ms": round(float(r["avg_latency"] or 0), 1),
|
||||
})
|
||||
tot_in += ci; tot_out += co; tot_r += cr; tot_w += cw; tot_msgs += mc
|
||||
|
||||
overall_hit = (tot_r / (tot_r + tot_w)) if (tot_r + tot_w) > 0 else 0.0
|
||||
return {
|
||||
"days": days,
|
||||
"total_messages": tot_msgs,
|
||||
"tokens_input": tot_in,
|
||||
"tokens_output": tot_out,
|
||||
"cache_read": tot_r,
|
||||
"cache_write": tot_w,
|
||||
"cache_hit_rate": round(overall_hit, 3),
|
||||
"by_chat": by_chat,
|
||||
}
|
||||
|
||||
|
||||
def get_activity_feed(limit: int = 50, offset: int = 0) -> dict:
|
||||
with _conn() as conn:
|
||||
total_row = conn.execute("""
|
||||
|
||||
Reference in New Issue
Block a user