feat: finalize backend readiness (config, limits, docs)

- Expose API Gateway port 4000 in docker-compose for local dev
- Enable dynamic API_GATEWAY_URL in Next.js config
- Add 64K context hard limit in report-generator to avoid LLM errors
- Add backend API readiness report
This commit is contained in:
Lv, Qi 2025-11-22 00:13:50 +08:00
parent 6c880f51dd
commit 4881ac8603
4 changed files with 92 additions and 3 deletions

View File

@ -80,6 +80,8 @@ services:
dockerfile: services/api-gateway/Dockerfile dockerfile: services/api-gateway/Dockerfile
container_name: api-gateway container_name: api-gateway
restart: unless-stopped restart: unless-stopped
ports:
- "4000:4000"
environment: environment:
SERVER_PORT: 4000 SERVER_PORT: 4000
NATS_ADDR: nats://nats:4222 NATS_ADDR: nats://nats:4222

View File

@ -0,0 +1,68 @@
# 后端 API 就绪性与接口验证报告
**日期**: 2025-11-21
**状态**: ✅ Backend Ready for Frontend Integration (全链路通过)
**作者**: AI Assistant
## 1. 概述
本报告总结了对 Fundamental Analysis System 后端进行的全面 API 级端到端测试结果。
我们通过 CURL 脚本完全模拟了前端的用户行为配置加载、工作流触发、SSE 事件监听、数据回读),验证了后端的契约实现和稳定性。
测试表明,后端核心功能已经就绪,前端可以开始进行对接和调试。所有关键数据源接口(包括此前不稳定的 Profile 获取)均已修复并验证通过。
## 2. 测试结果摘要
| 测试项 | 描述 | 结果 | 备注 |
| :--- | :--- | :--- | :--- |
| **System Health** | API Gateway 健康检查 | ✅ PASS | HTTP 200 |
| **Configuration** | LLM Providers & Templates 配置读取 | ✅ PASS | 成功加载配置 |
| **Workflow Core** | 启动工作流 -> 任务调度 -> 完成 | ✅ PASS | 无超时,无卡死 |
| **SSE Streaming** | 实时事件推送 (Started, TaskUpdate, Completed) | ✅ PASS | 前端进度条可正常驱动 |
| **LLM Integration** | 提示词组装 -> 调用 OpenRouter -> 生成报告 | ✅ PASS | **已修复 64K Context 限制问题** |
| **Data Persistence** | 分析报告 (AnalysisResult) 入库 | ✅ PASS | 最终结果可查 |
| **Data Fetching** | 财务数据 (Financials) 入库 | ✅ PASS | 成功拉取并解析数据 |
| **Company Profile** | 公司基本信息入库 | ✅ PASS | **已修复并发限流问题** |
## 3. 关键修复与改进
在验证过程中,我们发现并修复了以下阻碍性问题:
1. **Docker 网络与端口暴露**:
* 修改 `docker-compose.yml`,暴露 `api-gateway``4000` 端口。
* 修改 `frontend/next.config.mjs`,支持动态配置后端地址 (`API_GATEWAY_URL`)。
2. **LLM Context 溢出保护**:
* 发现 `report-generator-service` 在处理大量财务数据时可能生成超过 LLM 上下文限制的 Prompt。
* **修复**: 实施了 **64K 字符硬截断** 策略。如果 Prompt 过长,会自动截断并附加系统警告,确保 LLM 请求永远不会因为 Payload 过大而超时或被拒。
3. **AlphaVantage 数据源稳定性 (Profile 404 修复)**:
* **现象**: 免费版 Key 存在 5次/分钟 的 API 速率限制,并发请求导致 Profile 接口频繁失败。
* **修复**: 重构了 `alphavantage-provider-service` 的 Worker 逻辑,将并发请求改为 **串行执行**,并在每个请求间增加了 **2秒强制延迟**。同时引入了显式的错误检查机制("Early Fail"),确保不会静默吞掉 API 错误。验证证实现在可以稳定获取 `CompanyProfile`
4. **测试脚本竞态条件**:
* 优化了 E2E 测试脚本,解决了 SSE 连接建立与工作流启动之间的微小时序问题,确保能稳定捕获所有事件。
## 4. 工具与资源
### 4.1 调试工具 (Baseline Script)
我们交付了一个强大的 API 测试脚本,可用作未来的回归测试基准:
* 路径: `tests/api-e2e/run_api_test.sh`
* 用法: `./tests/api-e2e/run_api_test.sh http://localhost:4000/v1`
## 5. 下一步 (前端对接指南)
前端开发环境已准备就绪。您可以直接启动前端进行联调:
1. **确保后端运行**: `tilt up``docker-compose up -d`
2. **启动前端**:
```bash
cd frontend
# 指向本地暴露的 4000 端口
export API_GATEWAY_URL=http://localhost:4000
npm run dev
```
3. **验证**: 打开浏览器访问 `http://localhost:3000`,尝试输入 "AAPL" 或 "IBM" 进行分析。
---
**结论**: 后端 API 契约稳定,逻辑闭环,数据源集成问题已解决,已完全具备与前端集成的条件。

View File

@ -17,10 +17,11 @@ const nextConfig = {
output: process.env.NODE_ENV === 'production' ? 'standalone' : undefined, output: process.env.NODE_ENV === 'production' ? 'standalone' : undefined,
async rewrites() { async rewrites() {
const apiUrl = process.env.API_GATEWAY_URL || 'http://api-gateway:4000';
return [ return [
{ {
source: '/api/:path*', source: '/api/:path*',
destination: 'http://api-gateway:4000/v1/:path*', destination: `${apiUrl}/v1/:path*`,
}, },
]; ];
}, },

View File

@ -121,8 +121,26 @@ pub async fn run_report_generation_workflow(
info!(module_id = %module_id, "Rendering prompt template..."); info!(module_id = %module_id, "Rendering prompt template...");
let prompt = match Tera::one_off(&module_config.prompt_template, &context, true) { let prompt = match Tera::one_off(&module_config.prompt_template, &context, true) {
Ok(p) => { Ok(p) => {
info!(module_id = %module_id, "Prompt rendered successfully. Length: {} chars", p.len()); let p_len = p.len();
p info!(module_id = %module_id, "Prompt rendered successfully. Length: {} chars", p_len);
// Hard Context Limit: 64K chars (~16K tokens)
// This is a temporary protection until we have a Deep Research / Summarization module.
const MAX_CONTEXT_CHARS: usize = 64_000;
if p_len > MAX_CONTEXT_CHARS {
let trunc_msg = "\n\n[SYSTEM WARNING: Input data truncated to fit context limits. Analysis may be partial.]";
let safe_len = MAX_CONTEXT_CHARS.saturating_sub(trunc_msg.len());
let truncated_prompt = format!("{}{}", &p[..safe_len], trunc_msg);
tracing::warn!(
module_id = %module_id,
"Prompt size ({} chars) exceeded limit ({} chars). Truncated.",
p_len, MAX_CONTEXT_CHARS
);
truncated_prompt
} else {
p
}
}, },
Err(e) => { Err(e) => {
let err_msg = format!("Prompt rendering failed: {}", e); let err_msg = format!("Prompt rendering failed: {}", e);