feat: 记录并展示每次生成消耗的LLM调用次数(llm_calls)
This commit is contained in:
@@ -83,6 +83,7 @@ CREATE TABLE IF NOT EXISTS ai_reports (
|
|||||||
model TEXT NOT NULL,
|
model TEXT NOT NULL,
|
||||||
tokens_used INTEGER,
|
tokens_used INTEGER,
|
||||||
generation_count INTEGER NOT NULL DEFAULT 1,
|
generation_count INTEGER NOT NULL DEFAULT 1,
|
||||||
|
llm_calls INTEGER,
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime('now','localtime')),
|
created_at TEXT NOT NULL DEFAULT (datetime('now','localtime')),
|
||||||
updated_at TEXT,
|
updated_at TEXT,
|
||||||
UNIQUE(trade_date, report_type)
|
UNIQUE(trade_date, report_type)
|
||||||
@@ -121,6 +122,7 @@ def init_db():
|
|||||||
for alter_sql in (
|
for alter_sql in (
|
||||||
"ALTER TABLE ai_reports ADD COLUMN generation_count INTEGER NOT NULL DEFAULT 1",
|
"ALTER TABLE ai_reports ADD COLUMN generation_count INTEGER NOT NULL DEFAULT 1",
|
||||||
"ALTER TABLE ai_reports ADD COLUMN updated_at TEXT",
|
"ALTER TABLE ai_reports ADD COLUMN updated_at TEXT",
|
||||||
|
"ALTER TABLE ai_reports ADD COLUMN llm_calls INTEGER",
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
conn.execute(alter_sql)
|
conn.execute(alter_sql)
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ async def admin_list_reports(request: Request):
|
|||||||
try:
|
try:
|
||||||
rows = conn.execute(
|
rows = conn.execute(
|
||||||
"SELECT id, trade_date, report_type, title, summary, tools_used, model, tokens_used, "
|
"SELECT id, trade_date, report_type, title, summary, tools_used, model, tokens_used, "
|
||||||
"generation_count, created_at, updated_at "
|
"generation_count, llm_calls, created_at, updated_at "
|
||||||
"FROM ai_reports ORDER BY trade_date DESC, id DESC LIMIT 50"
|
"FROM ai_reports ORDER BY trade_date DESC, id DESC LIMIT 50"
|
||||||
).fetchall()
|
).fetchall()
|
||||||
items = []
|
items = []
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ async def list_reports():
|
|||||||
try:
|
try:
|
||||||
rows = conn.execute(
|
rows = conn.execute(
|
||||||
"SELECT id, trade_date, report_type, title, summary, tools_used, model, tokens_used, "
|
"SELECT id, trade_date, report_type, title, summary, tools_used, model, tokens_used, "
|
||||||
"generation_count, created_at, updated_at "
|
"generation_count, llm_calls, created_at, updated_at "
|
||||||
"FROM ai_reports ORDER BY trade_date DESC, id DESC LIMIT 50"
|
"FROM ai_reports ORDER BY trade_date DESC, id DESC LIMIT 50"
|
||||||
).fetchall()
|
).fetchall()
|
||||||
items = []
|
items = []
|
||||||
|
|||||||
@@ -227,11 +227,13 @@ async def collect_ai_analysis(trade_date: str) -> dict:
|
|||||||
# ── 阶段一:工具轮(只获取数据,模型若直接开写报告则丢弃,由阶段二重写) ──
|
# ── 阶段一:工具轮(只获取数据,模型若直接开写报告则丢弃,由阶段二重写) ──
|
||||||
tools_used = []
|
tools_used = []
|
||||||
total_tokens = 0
|
total_tokens = 0
|
||||||
|
llm_calls = 0
|
||||||
was_truncated = False
|
was_truncated = False
|
||||||
max_rounds = 8
|
max_rounds = 8
|
||||||
|
|
||||||
for i in range(max_rounds):
|
for i in range(max_rounds):
|
||||||
response = await call_llm(messages, tools=TOOLS)
|
response = await call_llm(messages, tools=TOOLS)
|
||||||
|
llm_calls += 1
|
||||||
total_tokens += response.get("usage", {}).get("total_tokens", 0)
|
total_tokens += response.get("usage", {}).get("total_tokens", 0)
|
||||||
|
|
||||||
choice = response["choices"][0]
|
choice = response["choices"][0]
|
||||||
@@ -277,6 +279,7 @@ async def collect_ai_analysis(trade_date: str) -> dict:
|
|||||||
"content": part_prompt.format(title=trade_date) if part_idx == 0 else part_prompt,
|
"content": part_prompt.format(title=trade_date) if part_idx == 0 else part_prompt,
|
||||||
}]
|
}]
|
||||||
response = await call_llm(part_messages)
|
response = await call_llm(part_messages)
|
||||||
|
llm_calls += 1
|
||||||
total_tokens += response.get("usage", {}).get("total_tokens", 0)
|
total_tokens += response.get("usage", {}).get("total_tokens", 0)
|
||||||
choice = response["choices"][0]
|
choice = response["choices"][0]
|
||||||
part_content = choice["message"].get("content") or ""
|
part_content = choice["message"].get("content") or ""
|
||||||
@@ -292,9 +295,10 @@ async def collect_ai_analysis(trade_date: str) -> dict:
|
|||||||
final_content += (final_content and "\n\n" or "") + part_content
|
final_content += (final_content and "\n\n" or "") + part_content
|
||||||
|
|
||||||
summary = _extract_summary(final_content)
|
summary = _extract_summary(final_content)
|
||||||
report_id = _save_report(trade_date, final_content, summary, tools_used, total_tokens)
|
report_id = _save_report(trade_date, final_content, summary, tools_used, total_tokens, llm_calls)
|
||||||
|
print(f"[ai-service] 生成完成:LLM调用 {llm_calls} 次,tokens {total_tokens}")
|
||||||
|
|
||||||
return {"id": report_id, "tokens_used": total_tokens, "tools_used": tools_used, "truncated": was_truncated}
|
return {"id": report_id, "tokens_used": total_tokens, "llm_calls": llm_calls, "tools_used": tools_used, "truncated": was_truncated}
|
||||||
|
|
||||||
|
|
||||||
def _extract_summary(content: str) -> str:
|
def _extract_summary(content: str) -> str:
|
||||||
@@ -416,14 +420,14 @@ def _get_prev_snapshot_section(trade_date: str) -> str:
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
def _save_report(trade_date: str, content: str, summary: str, tools_used: list, tokens_used: int) -> int:
|
def _save_report(trade_date: str, content: str, summary: str, tools_used: list, tokens_used: int, llm_calls: int = 0) -> int:
|
||||||
"""保存报告到数据库(同日重生成:覆盖内容、generation_count+1、tokens 记当次消耗)"""
|
"""保存报告到数据库(同日重生成:覆盖内容、generation_count+1、tokens 记当次消耗)"""
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
try:
|
try:
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"""INSERT INTO ai_reports
|
"""INSERT INTO ai_reports
|
||||||
(trade_date, report_type, title, content, summary, tools_used, model, tokens_used, updated_at)
|
(trade_date, report_type, title, content, summary, tools_used, model, tokens_used, llm_calls, updated_at)
|
||||||
VALUES (?, 'daily', ?, ?, ?, ?, ?, ?, datetime('now','localtime'))
|
VALUES (?, 'daily', ?, ?, ?, ?, ?, ?, ?, datetime('now','localtime'))
|
||||||
ON CONFLICT(trade_date, report_type) DO UPDATE SET
|
ON CONFLICT(trade_date, report_type) DO UPDATE SET
|
||||||
title = excluded.title,
|
title = excluded.title,
|
||||||
content = excluded.content,
|
content = excluded.content,
|
||||||
@@ -431,6 +435,7 @@ def _save_report(trade_date: str, content: str, summary: str, tools_used: list,
|
|||||||
tools_used = excluded.tools_used,
|
tools_used = excluded.tools_used,
|
||||||
model = excluded.model,
|
model = excluded.model,
|
||||||
tokens_used = excluded.tokens_used,
|
tokens_used = excluded.tokens_used,
|
||||||
|
llm_calls = excluded.llm_calls,
|
||||||
updated_at = excluded.updated_at,
|
updated_at = excluded.updated_at,
|
||||||
generation_count = ai_reports.generation_count + 1""",
|
generation_count = ai_reports.generation_count + 1""",
|
||||||
(
|
(
|
||||||
@@ -441,6 +446,7 @@ def _save_report(trade_date: str, content: str, summary: str, tools_used: list,
|
|||||||
json.dumps(tools_used),
|
json.dumps(tools_used),
|
||||||
AI_MODEL,
|
AI_MODEL,
|
||||||
tokens_used,
|
tokens_used,
|
||||||
|
llm_calls,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export interface AiReport {
|
|||||||
model: string;
|
model: string;
|
||||||
tokens_used: number;
|
tokens_used: number;
|
||||||
generation_count?: number;
|
generation_count?: number;
|
||||||
|
llm_calls?: number | null;
|
||||||
issue_number?: number;
|
issue_number?: number;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at?: string | null;
|
updated_at?: string | null;
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ function AiAnalysisPage() {
|
|||||||
<span className="truncate">{report.updated_at || report.created_at}</span>
|
<span className="truncate">{report.updated_at || report.created_at}</span>
|
||||||
</span>
|
</span>
|
||||||
<span>{report.tokens_used?.toLocaleString()} tokens</span>
|
<span>{report.tokens_used?.toLocaleString()} tokens</span>
|
||||||
|
{report.llm_calls ? <span>{report.llm_calls} 次 LLM 调用</span> : null}
|
||||||
{(report.generation_count ?? 1) > 1 && (
|
{(report.generation_count ?? 1) > 1 && (
|
||||||
<span>第 {report.generation_count} 次生成</span>
|
<span>第 {report.generation_count} 次生成</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user