feat: Agnes上下文512K→1M + Ollama支持配置num_ctx

Agnes AI:
- adapter注释和API文档更新: 上下文512K→1M (官方已升级)

Ollama num_ctx 支持:
- MetonaGenerationParams 新增 contextLength 字段
- OllamaAdapter.toNativeRequest 发送 options.num_ctx
- AgentLoopEngine 从 config 读取并传递到请求参数
- database.service 新增 ollama.numCtx 默认配置(null=由模型决定)
- SettingsModal LLM设置: Ollama 选中时显示上下文长度输入框
- main.ts 启动时从 configService 读取 ollama.numCtx

数据流:
Settings → configService → main.ts → AgentLoopEngine.config
→ MetonaRequest.params → OllamaAdapter → options.num_ctx
This commit is contained in:
thzxx
2026-06-27 21:57:05 +08:00
parent 8b3dc4298d
commit e4c0259afc
9 changed files with 32 additions and 4 deletions
+2 -2
View File
@@ -495,7 +495,7 @@
<p style="margin-top: 12px;">在 Claw-Eval 基准测试中排名 General Leaderboard <strong style="color:var(--accent)">第 9</strong>Pass³ 分数 <strong style="color:var(--green)">60.9%</strong></p> <p style="margin-top: 12px;">在 Claw-Eval 基准测试中排名 General Leaderboard <strong style="color:var(--accent)">第 9</strong>Pass³ 分数 <strong style="color:var(--green)">60.9%</strong></p>
<div class="hero-meta"> <div class="hero-meta">
<span><span class="dot dot-blue"></span> 模型名称: <strong>agnes-2.0-flash</strong></span> <span><span class="dot dot-blue"></span> 模型名称: <strong>agnes-2.0-flash</strong></span>
<span><span class="dot dot-green"></span> 上下文: <strong>512K</strong> | 最大输出: <strong>65.5K</strong></span> <span><span class="dot dot-green"></span> 上下文: <strong>1M</strong> | 最大输出: <strong>65.5K</strong></span>
<span><span class="dot dot-orange"></span> Base URL: <strong>https://apihub.agnes-ai.com/v1</strong></span> <span><span class="dot dot-orange"></span> Base URL: <strong>https://apihub.agnes-ai.com/v1</strong></span>
</div> </div>
</div> </div>
@@ -1004,7 +1004,7 @@ response = client.chat.completions.<span class="hl-fn">create</span>(
<table class="params"> <table class="params">
<thead><tr><th>项目</th><th>数值</th></tr></thead> <thead><tr><th>项目</th><th>数值</th></tr></thead>
<tbody> <tbody>
<tr><td class="param-name">Context</td><td class="param-type">512K</td></tr> <tr><td class="param-name">Context</td><td class="param-type">1M</td></tr>
<tr><td class="param-name">Max Output</td><td class="param-type">65.5K</td></tr> <tr><td class="param-name">Max Output</td><td class="param-type">65.5K</td></tr>
</tbody> </tbody>
</table> </table>
@@ -100,7 +100,7 @@ export class AgnesAdapter extends BaseAdapter {
* Agnes AI 特有参数: * Agnes AI 特有参数:
* - 多模态图片:user 消息的 images[] → OpenAI content 数组 [{type:"text"}, {type:"image_url"}] * - 多模态图片:user 消息的 images[] → OpenAI content 数组 [{type:"text"}, {type:"image_url"}]
* - chat_template_kwargs: { enable_thinking: true } — 启用思考模式(非 thinking 字段) * - chat_template_kwargs: { enable_thinking: true } — 启用思考模式(非 thinking 字段)
* - 默认 max_tokens: 65536512K 上下文 * - 默认 max_tokens: 655361M 上下文,65.5K 最大输出
*/ */
private toNativeRequest(request: MetonaRequest, stream: boolean): Record<string, unknown> { private toNativeRequest(request: MetonaRequest, stream: boolean): Record<string, unknown> {
const messages = buildOpenAICompatibleMessages(request); const messages = buildOpenAICompatibleMessages(request);
@@ -388,6 +388,7 @@ export class OllamaAdapter extends BaseAdapter {
num_predict: request.params.maxTokens, num_predict: request.params.maxTokens,
top_p: request.params.topP, top_p: request.params.topP,
stop: request.params.stopSequences, stop: request.params.stopSequences,
num_ctx: request.params.contextLength,
}, },
}; };
+1
View File
@@ -147,6 +147,7 @@ export class AgentLoopEngine extends EventEmitter {
stream: true, stream: true,
thinkingEnabled: true, thinkingEnabled: true,
thinkingEffort: 'high', thinkingEffort: 'high',
contextLength: this.config.contextLength,
}, },
}; };
+2
View File
@@ -63,6 +63,8 @@ export interface AgentLoopConfig {
contextWindow: number; contextWindow: number;
retryCount: number; retryCount: number;
temperature: number; temperature: number;
/** Ollama 上下文窗口大小(num_ctx),其他 Provider 忽略 */
contextLength?: number;
} }
// ===== Agent Loop 输出 ===== // ===== Agent Loop 输出 =====
+2
View File
@@ -53,6 +53,8 @@ export interface MetonaGenerationParams {
thinkingEnabled?: boolean; thinkingEnabled?: boolean;
/** 思考强度(替代 thinkingBudget,各 Provider 映射见 Adapter 规范) */ /** 思考强度(替代 thinkingBudget,各 Provider 映射见 Adapter 规范) */
thinkingEffort?: 'low' | 'medium' | 'high' | 'max'; thinkingEffort?: 'low' | 'medium' | 'high' | 'max';
/** 上下文窗口大小(仅 Ollama 支持 num_ctx */
contextLength?: number;
} }
// ===== 安全约束 ===== // ===== 安全约束 =====
+5 -1
View File
@@ -147,7 +147,11 @@ async function initialize(): Promise<void> {
]; ];
// ===== Agent Loop ===== // ===== Agent Loop =====
const agentLoop = new AgentLoopEngine({}, adapter, toolRegistry, preToolHooks, postToolHooks); const ollamaNumCtx = configService.get<number>('ollama.numCtx');
const agentLoop = new AgentLoopEngine(
{ contextLength: ollamaNumCtx ?? undefined },
adapter, toolRegistry, preToolHooks, postToolHooks,
);
agentLoop.setTools(toolRegistry.listTools()); agentLoop.setTools(toolRegistry.listTools());
agentLoop.setWorkspacePath(workspaceInfo.path); agentLoop.setWorkspacePath(workspaceInfo.path);
+3
View File
@@ -270,6 +270,9 @@ export class DatabaseService {
{ key: 'logging.auditEnabled', value: 'true', category: 'logging' }, { key: 'logging.auditEnabled', value: 'true', category: 'logging' },
{ key: 'logging.traceEnabled', value: 'true', category: 'logging' }, { key: 'logging.traceEnabled', value: 'true', category: 'logging' },
// Ollama 配置
{ key: 'ollama.numCtx', value: 'null', category: 'ollama' },
// Onboarding // Onboarding
{ key: 'onboarding.completed', value: 'false', category: 'general' }, { key: 'onboarding.completed', value: 'false', category: 'general' },
]; ];
+15
View File
@@ -101,6 +101,7 @@ function LLMSettings() {
const [model, setModel] = useConfig('llm.model', ''); const [model, setModel] = useConfig('llm.model', '');
const [apiKey, setApiKey] = useConfig('llm.apiKey', ''); const [apiKey, setApiKey] = useConfig('llm.apiKey', '');
const [baseURL, setBaseURL] = useConfig('llm.baseURL', ''); const [baseURL, setBaseURL] = useConfig('llm.baseURL', '');
const [numCtx, setNumCtx] = useConfig('ollama.numCtx', null as number | null);
const [showKey, setShowKey] = useState(false); const [showKey, setShowKey] = useState(false);
return ( return (
@@ -118,6 +119,20 @@ function LLMSettings() {
<TextField size="small" label="API Key" type={showKey ? 'text' : 'password'} value={apiKey} onChange={(e) => setApiKey(e.target.value)} placeholder="sk-...(本地模型可留空)" <TextField size="small" label="API Key" type={showKey ? 'text' : 'password'} value={apiKey} onChange={(e) => setApiKey(e.target.value)} placeholder="sk-...(本地模型可留空)"
slotProps={{ input: { endAdornment: <IconButton size="small" onClick={() => setShowKey(!showKey)}>{showKey ? <EyeOff size={14} /> : <Eye size={14} />}</IconButton> } }} slotProps={{ input: { endAdornment: <IconButton size="small" onClick={() => setShowKey(!showKey)}>{showKey ? <EyeOff size={14} /> : <Eye size={14} />}</IconButton> } }}
/> />
{provider === 'ollama' && (
<TextField
size="small"
label="上下文长度 (num_ctx)"
type="number"
value={numCtx ?? ''}
onChange={(e) => {
const v = e.target.value;
setNumCtx(v === '' ? null : Number(v));
}}
placeholder="默认由模型决定(如 2048、4096、128000"
slotProps={{ htmlInput: { min: 512, step: 512 } }}
/>
)}
</Stack> </Stack>
); );
} }