使用说明
LLM编排师
本地LLM编排器使用FastAPI构建,由Gemma 4 26B(MoE,4B活动参数,Q4_K_M)通过Ollama提供支持。具有本地工具调用、内置推理和MCP工具服务器,用于通过UDP进行NASA SUITS TSS2026遥测、参考文档搜索(文本+PDF)、通过base64图像注入进行直接视觉分析,以及EVA助手的Magic Leap 2耳机语音/运行时上下文。
快速开始
# Install dependencies (requires Python 3.12+)
uv sync
# Pull required Ollama models
ollama pull gemma4:26b # Chat model (~17GB)
ollama pull qwen3-embedding:0.6b # Embedding model (needed if RAG is re-enabled)
# Configure TSS server connection (set to the IP/port of the running TSS instance)
# Edit .env:
# TSS_UDP_HOST=<TSS server IP>
# TSS_UDP_PORT=14141
# Run the orchestrator
uv run python main.py
编排器开始 http://0.0.0.0:13853.
端点
| 方法 | 路径 | 描述 |
|---|---|---|
| 职位 | /chat |
与MCP工具调用和推理的代理聊天 |
| 得到 | /context |
上次令牌使用情况明细 /chat 呼叫 |
| 得到 | /health |
LLM后端连接、模型可用性、MCP工具数量 |
POST/聊天
// Request
{
"messages": [
{"role": "user", "content": "What is the EVA1 oxygen status?"}
],
"stream": false
}
// Response
{
"role": "assistant",
"content": "EVA1: O2 94%, CO2 0.5%, HR 72 bpm, suit pressure nominal."
}
该模型使用Gemma 4的原生思维来推理请求,然后自主调用MCP工具(例如,通过UDP获取实时TSS遥测数据,搜索参考文档)。思维跟踪记录在服务器端,但从未流式传输到客户端。
配置
所有设置都是从环境变量中读取的(默认情况下)。创建 .env 要覆盖的项目根目录中的文件:
# LLM provider: "ollama" (default), "afm", "llamacpp"
LLM_PROVIDER=ollama
LLM_MODEL=gemma4:26b
LLM_API_BASE= # auto-set per provider if empty
# Ollama host -- always localhost. If OLLAMA_HOST is set system-wide to 0.0.0.0
# (Ollama's server bind address), the orchestrator automatically remaps it to localhost.
OLLAMA_HOST=http://localhost:11434
OLLAMA_KEEP_ALIVE=30m
OLLAMA_PREWARM=true
# Tokenizer -- auto-resolved to local tokenizer_cache/ dir if pre-downloaded.
# Startup expects local tokenizer files for offline field use.
# TOKENIZER_NAME=google/gemma-4-26B-A4B-it
# Context management
MAX_CONTEXT_TOKENS=128000
SUMMARIZE_THRESHOLD=80000
# TSS2026 server (UDP telemetry -- used by the get_tss_state MCP tool)
# Set TSS_UDP_HOST to the IP of the running TSS instance.
TSS_UDP_HOST=10.206.64.189
TSS_UDP_PORT=14141
TSS_UDP_TIMEOUT=2.0
OLLAMA_PREWARM=true 在启动时发送一个空的Ollama生成请求,因此
聊天模型在第一个Unity/耳机请求之前加载。 OLLAMA_KEEP_ALIVE
在聊天请求中传递,以使模型在回合之间保持驻留。
提供程序默认值
| 提供程序 | 默认API基础 | 默认模型 | 默认标记器 | 注释 |
|---|---|---|---|---|
ollama |
http://localhost:11434 |
gemma4:26b |
google/gemma-4-26B-A4B-it |
完整的Ollama SDK,原生思维+工具 |
afm |
http://localhost:9999 |
mlx-community/Qwen3.5-35B-A3B-4bit |
Qwen/Qwen3.5-35B-A3B |
OpenAI兼容(AFM/MLX) |
llamacpp |
http://localhost:8080 |
gemma4 |
google/gemma-4-26B-A4B-it |
OpenAI兼容(火焰服务器) |
离线令牌化缓存
编排器使用HuggingFace标记器进行准确的标记计数和上下文预算。标记器文件是 预下载 进入 tokenizer_cache/ 因此,编排器在没有互联网接入的情况下启动,这是在本地网络上进行现场部署所必需的。
tokenizer_cache/
google--gemma-4-26B-A4B-it/ # Gemma 4 tokenizer (used by ollama + llamacpp providers)
tokenizer.json
tokenizer_config.json
chat_template.jinja
config.py 自动解析HF型号ID(例如。 google/gemma-4-26B-A4B-it)如果匹配的文件存在于中,则转到本地路径 tokenizer_cache/。目录名使用 -- 代替 / (例如。 google--gemma-4-26B-A4B-it). context_manager.py 使用以下命令加载标记器文件 local_files_only=True,因此field/online startup希望标记器已经在本地存在。
要为新的提供者/模型预下载标记器,请执行以下操作:
uv run python -c "
from transformers import AutoTokenizer
import os
name = 'google/gemma-4-26B-A4B-it' # change to the HF model ID
save_dir = os.path.join('tokenizer_cache', name.replace('/', '--'))
os.makedirs(save_dir, exist_ok=True)
tok = AutoTokenizer.from_pretrained(name)
tok.save_pretrained(save_dir)
print(f'Saved to {save_dir}')
"
建筑
orchestrator/
main.py FastAPI app, /chat agentic loop
config.py All configuration, env var loading, MCP server wiring
llm_provider.py Provider abstraction: Ollama, AFM/MLX, llama.cpp
tss_udp_client.py Async UDP client for TSS2026 telemetry
mcp_client.py MCP server lifecycle, tool discovery, tool execution
context_manager.py Token counting, budget calculation, truncation
context_summarizer.py Auto-summarizes when conversation exceeds 80k tokens
tokenizer_cache/ Pre-downloaded HF tokenizer files (offline operation)
docs/ Mission/headset references searchable by the LLM (text, PDF, images)
mcp_servers/
tss_tools_server.py MCP server: get_tss_state, search_docs, read_doc, inspect_image
rag_service.py Conversation history via LanceDB (disabled, preserved for later)
test_tss_udp.py Smoke test for UDP connectivity to TSS2026
主动MCP工具
| 工具 | 描述 | 最大输出 |
|---|---|---|
get_tss_state |
通过UDP获取实时TSS2026遥测数据。范围: all, eva, rover, ltv, ltv_errors, vitals. |
4K字符 |
search_docs |
为文本模式选择docs/。搜索文本文件和PDF。返回file:line:match,没有周围的上下文。 | 50场比赛 |
read_doc |
阅读文档或章节。使用after search_docs展开特定行周围的上下文。 | 2K默认值,8K最大值 |
inspect_image |
通过base64注入将docs/中的图像直接加载到LLM的视觉上下文中。主LLM看到图像本身(不是二手描述)。地图、图表、设备照片。 | 每张图片约3-5K视觉标记 |
LLM根据用户的提示决定何时调用这些工具。没有自动注入,也没有后台轮询。
禁用工具(保留以备后用)
| 工具 | 说明 | 如何重新启用 |
|---|---|---|
search_knowledge |
通过LanceDB嵌入对过去对话进行语义搜索 | 取消注释 tss_tools_server.py 和 main.py |
TSS2026集成
这 get_tss_state MCP工具通过UDP(端口14141)直接与NASA SUITS TSS2026服务器通信。TSS服务器是由NASA管理的外部依赖项,编排器只需要其IP地址和端口即可连接。
TSS协议使用大端二进制数据包:客户端发送8字节的请求([uint32 timestamp][uint32 command])并接收JSON遥测作为响应。
| 命令 | 作用域 | 返回数据 |
|---|---|---|
| 0 | rover |
加压漫游车遥测(位置、转向、激光雷达、驾驶室、电池) |
| 1 | eva |
EVA1/EVA2套件遥测、DCU、UIA、IMU、错误 |
| 2 | ltv |
LTV最后已知位置、信号强度 |
| 3 | ltv_errors |
LTV错误程序 |
| -- | vitals |
过滤后的EVA数据:心率、氧气、二氧化碳、温度、仅电池 |
| -- | all |
命令0-3组合 |
集 TSS_UDP_HOST 和 TSS_UDP_PORT 在 .env 以匹配正在运行的TSS实例。在本地开发过程中,您可以运行TSS2026的本地副本进行测试;在JSC测试周上,指向美国宇航局官方托管的实例。
# Verify connectivity
uv run python test_tss_udp.py
文档搜索
将参考文件放在 docs/ (支持子目录)。LLM通过两步工作流程搜索它们:
search_docs("egress procedure")--查找与file:行引用匹配的项read_doc("procedures/ev-team-procedure-timeline.pdf", around_line=42)--读取该匹配的上下文
支持的格式:Markdown、纯文本、PDF(通过PyMuPDF)。图像可以通过以下方式进行分析 inspect_image.
目前的任务/耳机参考包括:
| 路径 | 目的 |
|---|---|
headset_voice_commands.md |
精确的Magic Leap MLVoice/Vosk命令别名、事件ID、场景限制和 send recording 行为学 |
headset_unity_runtime.md |
Unity场景门控、记录按钮行为、HUD/参考地图行为、出口/入口自动化、LTV验证、迷你地图警告和TSS运行时注释 |
mission_description/ |
美国国家航空航天局SUITS任务概述和缩写列表 |
procedures/ |
EVA时间表、坐标和LTV维修程序 |
telemetry_ranges/ |
EVA遥测标称/非标称范围 |
maps/ |
带注释/原始岩石堆场地图、禁区和灰尘地图 |
peripherals/ |
UIA/DCU照片和LTV任务委员会概述/特写照片 |
注: docs/ 在...里 .gitignore --用您的任务文档在本地填充它。它没有提交到存储库。
数据流
Magic Leap 2 headset / client -> POST /chat
|
1. Build message list: [system_prompt] + [user messages]
|-- System prompt tells Luna about Vosk/MLVoice artifacts, "send recording",
| Magic Leap Unity scenes, and the headset docs to consult
2. If total tokens > 80k -> summarize older conversation history
3. Send to Gemma 4 (with think=True)
4. Model reasons internally (thinking trace logged, not streamed)
5. Model decides: answer directly OR call tools
|-- get_tss_state(scope=eva) -> UDP to TSS2026 -> live JSON
|-- search_docs("oxygen") -> grep over docs/ -> file:line matches
|-- search_docs("show reference map") -> exact headset voice/runtime docs
|-- read_doc("procedures.pdf", around_line=42) -> context excerpt
|-- inspect_image("maps/dust-map.png") -> base64 injected into LLM context
6. Tool results fed back -> model generates final answer
|-- If images were injected, base64 is stripped after the LLM responds
| (images exist in context for exactly one inference call)
7. Return response (content only, no thinking trace)
上下文摘要
当对话的总令牌计数超过 SUMMARIZE_THRESHOLD (默认80k),较旧的消息会通过LLM自动压缩为单个摘要消息。最近的4条消息始终保持不变。
摘要过程中的图像处理:
- 携带base64图像的消息会在摘要文本中注明(例如,“附加1张图像用于视觉分析”),以便摘要记录参考了视觉参考。
- Base64图像数据为 从不 包含在摘要输入中——仅对文本标签进行摘要。
- 摘要后,最近(未摘要)消息中幸存的任何图像都会被删除,因为LLM已经看到并回应了它们。
即时图像剥离(预摘要):
- LLM处理完图像后,图像会立即从对话历史记录中删除
main.py这意味着base64数据存在于上下文中 一 然后用轻量级的文本注释替换推理调用。一张高分辨率地图(约3900×2900像素)在一个回合内花费约5000个视觉符号,而不是整个对话。
Gemma 4推理
Gemma 4通过Ollama支持本土思维 think=True 参数。SDK会自动分离 message.thinking (内部推理)从 message.content (最终答案)。编排者:
- 通行证
think=True在每次聊天通话中 - 在服务器端记录思维轨迹长度
- 从不将思维轨迹流式传输或返回给客户端
测试
# Test TSS2026 UDP connectivity (requires running TSS server)
uv run python test_tss_udp.py
# Quick model test
uv run python -c "
import asyncio
from llm_provider import OllamaProvider
async def test():
p = OllamaProvider(host='http://localhost:11434')
r = await p.chat(model='gemma4:26b', messages=[{'role':'user','content':'Hello'}])
print(f'Content: {r.content}')
print(f'Thinking: {len(r.thinking)} chars' if r.thinking else 'No thinking')
asyncio.run(test())
"
安全
- MCP服务器可以公开文件系统访问权限或执行命令。不要将端口13853暴露给不受信任的网络。
- 这
.env文件在.gitignore.
