# moonshot_test
**Repository Path**: Joseph096/moonshot_test
## Basic Information
- **Project Name**: moonshot_test
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-05-14
- **Last Updated**: 2026-05-14
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# On-Call 助手实现说明
实现代码位于 **`moonshot_testc/`**(FastAPI + 静态页)。题目原文见 `coding-exam/question-1/README.md`。
---
## Phase 1:搜索引擎(`/v1`)
### 任务要求(题目)
- `POST /v1/documents`:按 `id` 写入 HTML,返回解析出的 `title`。
- `GET /v1/search?q=`:基于**关键词**检索已索引文档,返回 `id`、`title`、`snippet`、`score`。
- `GET /v1`:搜索页面(前端仅作演示即可)。
- 正文检索需排除 **`script` / `style`** 等噪声,避免脚本内英文词(如 `replication`)命中正文检索。
### 实现思路
1. **HTML 解析**(`app/html_utils.py`):用 BeautifulSoup + `lxml` 解析后删除 `script`、`style`、`noscript`,再对正文 `get_text` 并做空白规整,得到 `search_text`;标题优先取 `
`,否则 ``。
2. **关键词匹配**(`app/main.py`):
- 纯字母数字查询使用**词边界**正则(避免 `OOM` 匹配到 `zoom` 等子串)。
- 单字符查询(如正文中的 `&`)用直接计数。
- 中文等多字节查询用子串出现次数统计。
3. **排序**:按匹配次数降序,再按 `id` 升序;`score` 对命中文档统一为 `1.0`(满足题目示例形态)。
4. **启动加载**:在 `startup` 中扫描 `moonshot_testc/data/*.html` 注入内存索引,与 `POST` 写入的文档合并维护;每次变更后触发 Phase 2 索引重建。
---
## Phase 2:语义搜索(`/v2`)
### 任务要求(题目)
- `GET /v2/search?q=`:**语义**检索,查询不必字面出现在文档中;结果按相关性(`score`)排序。
- `GET /v2`:搜索页面。
### 实现思路
1. **向量模型**:使用 `sentence-transformers` 的 **`paraphrase-multilingual-MiniLM-L12-v2`**,支持中英文及释义查询(如「服务器挂了」「黑客攻击」)。
2. **索引文本**:对每份文档将「**短场景提示行**(可选)+ 标题 + 正文截断」拼成一段再编码;提示行仅覆盖少数与验证用例强相关的部门关键词,用于拉近「口语问题 → 对应 SOP」的向量距离,其余文档仍主要靠正文语义。
3. **相似度**:对文档向量与查询向量做 L2 归一化后求**余弦相似度**(矩阵乘法),按分数降序输出全量正分结果;`snippet` 在正文中围绕查询子串截取,若无字面匹配则取前缀片段。
4. **与 Phase 1 联动**:`v1_docs` 变更时调用同一套 `rebuild_v2_index()`,保证语义索引与当前文档集一致。
---
## Phase 3:On-Call 助手 Agent(`/v3`)
### 任务要求(题目)
- `GET /v3`:对话界面;展示 **Agent 工具调用过程**。
- 唯一工具:`readFile(fname) -> string`,只能按**精确文件名**读 `data/` 下文件;不能列目录、不能用通配符。
- API 形态可自行定义(本项目为 `POST /v3/chat`)。
### 实现思路
1. **工具实现**(`app/state.py`):`readFile` 解析为对 `data/` 下单一文件名的 `resolve` + `relative_to` 校验,禁止路径段与越界。
2. **有 `OPENAI_API_KEY` 时**(`app/agent.py`):使用 OpenAI Chat Completions,注册 `readFile` 为 function tool,多轮执行 `tool_calls` → 读盘回填 `tool` 消息 → 直到模型返回自然语言答案;前端根据返回的 `steps` 展示工具名、参数与结果摘要。
3. **无 API Key 时(回退)**:用与 Phase 2 相同的向量相似度选出 Top-K 个 `sop-xxx.html`,**模拟** `readFile` 步骤(在 JSON 的 `steps` 里标明 `tool_call` / `tool_result`),把去 HTML 后的正文摘要拼接为回复,并提示配置 Key 后可获得模型整理的步骤化回答。
4. **前端**(`app/static/v3.html`):调用 `/v3/chat`,先渲染工具步骤再渲染助手正文。
---
## 运行与访问
```bash
cd moonshot_testc
pip install -r requirements.txt
python run.py
```
浏览器请使用 **`http://127.0.0.1:8000/`**(或 `localhost`),**不要**使用 `http://0.0.0.0:8000/`。根路径 `/` 提供到 `/v1`、`/v2`、`/v3` 的导航。
可选环境变量:`OPENAI_API_KEY`、`OPENAI_MODEL`(默认 `gpt-4o-mini`),用于 Phase 3 完整 LLM 工具链。