建立您的博客“-”部分的 “7:”内容生成“&”快速工程
一. 导言导言 导言 导言 导言
欢迎使用 {7!} 我们'} 已建立工作助理写作所需的一切部分#4),UI(部分#5),和当地LLM融合部分#6@)._Now it@'}现在是时候把大家聚集在一起, 集中关注最重要的是什么?
这是我自己编辑的A )Q MS K3Q 同一声音 同样的实用性 ,Q 一样
这是橡胶与路交汇的地方 我们探索先进 快速工程 如何让 LLM 产生与您的写作风格相匹配的建议 @.}
RAG 完整管道
执行“:”的完整流程
sequenceDiagram
participant User
participant Editor
participant Embedder
participant VectorDB
participant ContextBuilder
participant PromptEngine
participant LLM
participant UI
User->>Editor: Types text
Editor->>Embedder: Generate embedding for context
Embedder->>VectorDB: Search similar content
VectorDB-->>ContextBuilder: Return top K chunks
ContextBuilder->>ContextBuilder: Rank & filter
ContextBuilder->>PromptEngine: Provide context
PromptEngine->>PromptEngine: Build structured prompt
PromptEngine->>LLM: Generate suggestion
LLM-->>UI: Stream response
UI->>User: Display suggestion
上下文构建器处
建立精密的上下文构建器, 选择回收块并排序@. @%
namespace Mostlylucid.BlogLLM.Core.Services
{
public interface IContextBuilder
{
Task<ContextResult> BuildContextAsync(string currentText, int maxTokens = 2000);
}
public class ContextResult
{
public List<ContextChunk> Chunks { get; set; } = new();
public int TotalTokens { get; set; }
public string CombinedContext { get; set; } = string.Empty;
}
public class ContextChunk
{
public string Text { get; set; } = string.Empty;
public string Source { get; set; } = string.Empty;
public float RelevanceScore { get; set; }
public int TokenCount { get; set; }
}
public class ContextBuilder : IContextBuilder
{
private readonly BatchEmbeddingService _embedder;
private readonly QdrantVectorStore _vectorStore;
private readonly ILogger<ContextBuilder> _logger;
public ContextBuilder(
BatchEmbeddingService embedder,
QdrantVectorStore vectorStore,
ILogger<ContextBuilder> logger)
{
_embedder = embedder;
_vectorStore = vectorStore;
_logger = logger;
}
public async Task<ContextResult> BuildContextAsync(string currentText, int maxTokens = 2000)
{
// Extract meaningful context from current text
var searchText = ExtractSearchContext(currentText);
// Generate embedding
var embedding = _embedder.GenerateEmbedding(searchText);
// Retrieve similar chunks
var searchResults = await _vectorStore.SearchAsync(
queryEmbedding: embedding,
limit: 20, // Get more than we need
languageFilter: "en"
);
// Re-rank results
var rankedChunks = ReRankResults(searchResults, currentText);
// Select chunks that fit in token budget
var selectedChunks = SelectChunks(rankedChunks, maxTokens);
// Build combined context
var combinedContext = BuildCombinedContext(selectedChunks);
return new ContextResult
{
Chunks = selectedChunks,
TotalTokens = selectedChunks.Sum(c => c.TokenCount),
CombinedContext = combinedContext
};
}
private string ExtractSearchContext(string currentText)
{
// Get last 3 paragraphs or 500 characters
var paragraphs = currentText.Split("\n\n");
var lastParagraphs = paragraphs.TakeLast(3);
var searchText = string.Join("\n\n", lastParagraphs);
if (searchText.Length > 500)
{
searchText = searchText.Substring(searchText.Length - 500);
}
return searchText;
}
private List<ContextChunk> ReRankResults(List<SearchResult> results, string currentText)
{
var chunks = new List<ContextChunk>();
foreach (var result in results)
{
var chunk = new ContextChunk
{
Text = result.Text,
Source = $"{result.BlogPostTitle} - {result.SectionHeading}",
RelevanceScore = result.Score,
TokenCount = EstimateTokens(result.Text)
};
// Boost score if from same category
if (SharesCategories(result, currentText))
{
chunk.RelevanceScore *= 1.2f;
}
// Reduce score if very recent (might be too similar)
if (IsVeryRecent(result))
{
chunk.RelevanceScore *= 0.9f;
}
chunks.Add(chunk);
}
return chunks.OrderByDescending(c => c.RelevanceScore).ToList();
}
private List<ContextChunk> SelectChunks(List<ContextChunk> rankedChunks, int maxTokens)
{
var selected = new List<ContextChunk>();
var totalTokens = 0;
foreach (var chunk in rankedChunks)
{
if (totalTokens + chunk.TokenCount > maxTokens)
{
break;
}
selected.Add(chunk);
totalTokens += chunk.TokenCount;
_logger.LogDebug("Selected chunk from {Source} ({Tokens} tokens, score: {Score:F3})",
chunk.Source, chunk.TokenCount, chunk.RelevanceScore);
}
_logger.LogInformation("Selected {Count} chunks totaling {Tokens} tokens",
selected.Count, totalTokens);
return selected;
}
private string BuildCombinedContext(List<ContextChunk> chunks)
{
var sb = new StringBuilder();
for (int i = 0; i < chunks.Count; i++)
{
sb.AppendLine($"=== Reference {i + 1}: {chunks[i].Source} ===");
sb.AppendLine(chunks[i].Text);
sb.AppendLine();
}
return sb.ToString();
}
private int EstimateTokens(string text)
{
// Rough estimate: 1 token ≈ 4 characters
return text.Length / 4;
}
private bool SharesCategories(SearchResult result, string currentText)
{
// Simple heuristic - check if category names appear in current text
// In production, extract actual categories from metadata
return false;
}
private bool IsVeryRecent(SearchResult result)
{
// Reduce score for posts from last 7 days to encourage diversity
// In production, parse actual publish dates
return false;
}
}
}
高级快速工程
成功输出的关键是良好的提示@. @ Let'_%s 建立一个先进的快速工程系统 @.}#
即时模板
namespace Mostlylucid.BlogLLM.Core.Services
{
public enum PromptType
{
ContinueWriting,
SuggestStructure,
GenerateCode,
ImproveSection,
AddExample
}
public class PromptTemplate
{
public PromptType Type { get; set; }
public string SystemPrompt { get; set; } = string.Empty;
public string UserPromptTemplate { get; set; } = string.Empty;
public float Temperature { get; set; } = 0.7f;
public int MaxTokens { get; set; } = 300;
}
public static class PromptTemplates
{
public static readonly Dictionary<PromptType, PromptTemplate> Templates = new()
{
[PromptType.ContinueWriting] = new PromptTemplate
{
Type = PromptType.ContinueWriting,
SystemPrompt = @"You are a helpful writing assistant for a technical blog about .NET, ASP.NET Core, and related technologies.
Your writing style is:
- Conversational but professional
- Technical and detailed
- Includes code examples when relevant
- Uses practical, real-world examples
- Occasionally uses dry humor
- Avoids marketing fluff
When continuing text, maintain consistency with the existing tone and technical depth.",
UserPromptTemplate = @"Based on these excerpts from similar blog posts:
{context}
---
Current draft:
{current_text}
Task: Suggest 2-3 sentences to naturally continue the current thought. Match the technical depth and conversational tone.
Continuation:",
Temperature = 0.75f,
MaxTokens = 200
},
[PromptType.SuggestStructure] = new PromptTemplate
{
Type = PromptType.SuggestStructure,
SystemPrompt = @"You are a technical blog editor helping to organize content effectively.
Your suggestions should:
- Create logical flow from simple to complex
- Group related concepts together
- Include practical examples sections
- Ensure each section has clear purpose",
UserPromptTemplate = @"Based on how similar topics were structured:
{context}
---
Proposed section: {section_title}
Task: Suggest 5-7 subsections or bullet points for what this section should cover.
Structure (as markdown list):",
Temperature = 0.6f, // Lower for more structured output
MaxTokens = 300
},
[PromptType.GenerateCode] = new PromptTemplate
{
Type = PromptType.GenerateCode,
SystemPrompt = @"You are a C# coding expert specializing in ASP.NET Core, Entity Framework, and modern .NET development.
Your code should:
- Follow C# conventions and best practices
- Include XML doc comments for public members
- Handle edge cases and errors
- Be production-ready, not just proof-of-concept
- Include explanatory comments for complex logic",
UserPromptTemplate = @"Relevant code patterns from past posts:
{context}
---
Task: {code_request}
Generate a complete, working C# code example with comments.
Code:",
Temperature = 0.4f, // Lower for more consistent code
MaxTokens = 500
},
[PromptType.ImproveSection] = new PromptTemplate
{
Type = PromptType.ImproveSection,
SystemPrompt = @"You are an editor helping to improve technical blog content.
Focus on:
- Clarity and readability
- Technical accuracy
- Adding concrete examples
- Removing redundancy",
UserPromptTemplate = @"Examples of well-written content:
{context}
---
Current version:
{current_section}
Task: Suggest improvements to make this clearer and more engaging while maintaining technical depth.
Improved version:",
Temperature = 0.7f,
MaxTokens = 400
},
[PromptType.AddExample] = new PromptTemplate
{
Type = PromptType.AddExample,
SystemPrompt = @"You are a technical writer who excels at creating practical, real-world examples.
Your examples should:
- Be realistic and relatable
- Demonstrate the concept clearly
- Include enough detail to be useful
- Reference common scenarios developers face",
UserPromptTemplate = @"Similar examples from past posts:
{context}
---
Concept to illustrate: {concept}
Task: Create a practical example that demonstrates this concept in a real-world scenario.
Example:",
Temperature = 0.8f, // Higher for more creative examples
MaxTokens = 350
}
};
}
}
即时构建器服务
namespace Mostlylucid.BlogLLM.Core.Services
{
public interface IPromptBuilder
{
string BuildPrompt(PromptType type, Dictionary<string, string> variables, string context);
PromptTemplate GetTemplate(PromptType type);
}
public class PromptBuilder : IPromptBuilder
{
private readonly ILogger<PromptBuilder> _logger;
public PromptBuilder(ILogger<PromptBuilder> logger)
{
_logger = logger;
}
public string BuildPrompt(
PromptType type,
Dictionary<string, string> variables,
string context)
{
var template = PromptTemplates.Templates[type];
// Build full prompt with system message
var sb = new StringBuilder();
// System prompt
sb.AppendLine("<|system|>");
sb.AppendLine(template.SystemPrompt);
sb.AppendLine("</|system|>");
sb.AppendLine();
// User prompt with variables substituted
sb.AppendLine("<|user|>");
var userPrompt = template.UserPromptTemplate;
userPrompt = userPrompt.Replace("{context}", context);
foreach (var kvp in variables)
{
userPrompt = userPrompt.Replace($"{{{kvp.Key}}}", kvp.Value);
}
sb.AppendLine(userPrompt);
sb.AppendLine("</|user|>");
sb.AppendLine();
// Assistant prompt (model fills this in)
sb.AppendLine("<|assistant|>");
var fullPrompt = sb.ToString();
_logger.LogDebug("Built {Type} prompt ({Length} chars)",
type, fullPrompt.Length);
return fullPrompt;
}
public PromptTemplate GetTemplate(PromptType type)
{
return PromptTemplates.Templates[type];
}
}
}
内容生成处
现在让我们把“'”和一个高端的 “-” 内容生成服务连接起来,
namespace Mostlylucid.BlogLLM.Core.Services
{
public interface IContentGenerationService
{
Task<GenerationResult> GenerateAsync(
GenerationRequest request,
CancellationToken cancellationToken = default);
IAsyncEnumerable<string> GenerateStreamAsync(
GenerationRequest request,
CancellationToken cancellationToken = default);
}
public class GenerationRequest
{
public string CurrentText { get; set; } = string.Empty;
public PromptType Type { get; set; } = PromptType.ContinueWriting;
public Dictionary<string, string> Variables { get; set; } = new();
public int MaxContextTokens { get; set; } = 2000;
}
public class GenerationResult
{
public string GeneratedText { get; set; } = string.Empty;
public List<string> SourcePosts { get; set; } = new();
public int ContextTokensUsed { get; set; }
public int GeneratedTokens { get; set; }
public TimeSpan GenerationTime { get; set; }
}
public class ContentGenerationService : IContentGenerationService
{
private readonly IContextBuilder _contextBuilder;
private readonly IPromptBuilder _promptBuilder;
private readonly ILlmService _llmService;
private readonly ILogger<ContentGenerationService> _logger;
public ContentGenerationService(
IContextBuilder contextBuilder,
IPromptBuilder promptBuilder,
ILlmService llmService,
ILogger<ContentGenerationService> logger)
{
_contextBuilder = contextBuilder;
_promptBuilder = promptBuilder;
_llmService = llmService;
_logger = logger;
}
public async Task<GenerationResult> GenerateAsync(
GenerationRequest request,
CancellationToken cancellationToken = default)
{
var startTime = DateTime.UtcNow;
_logger.LogInformation("Starting content generation for {Type}", request.Type);
// Build context from similar posts
var contextResult = await _contextBuilder.BuildContextAsync(
request.CurrentText,
request.MaxContextTokens
);
_logger.LogInformation("Built context from {Count} chunks ({Tokens} tokens)",
contextResult.Chunks.Count, contextResult.TotalTokens);
// Build prompt
var prompt = _promptBuilder.BuildPrompt(
request.Type,
request.Variables,
contextResult.CombinedContext
);
// Get template for parameters
var template = _promptBuilder.GetTemplate(request.Type);
// Generate with LLM
var generated = await _llmService.GenerateAsync(prompt, cancellationToken);
var result = new GenerationResult
{
GeneratedText = CleanGeneratedText(generated),
SourcePosts = contextResult.Chunks
.Select(c => c.Source)
.Distinct()
.ToList(),
ContextTokensUsed = contextResult.TotalTokens,
GeneratedTokens = EstimateTokens(generated),
GenerationTime = DateTime.UtcNow - startTime
};
_logger.LogInformation("Generated {Tokens} tokens in {Ms}ms",
result.GeneratedTokens, result.GenerationTime.TotalMilliseconds);
return result;
}
public async IAsyncEnumerable<string> GenerateStreamAsync(
GenerationRequest request,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
// Build context
var contextResult = await _contextBuilder.BuildContextAsync(
request.CurrentText,
request.MaxContextTokens
);
// Build prompt
var prompt = _promptBuilder.BuildPrompt(
request.Type,
request.Variables,
contextResult.CombinedContext
);
// Stream from LLM
await foreach (var token in _llmService.GenerateStreamAsync(prompt, cancellationToken))
{
yield return token;
}
}
private string CleanGeneratedText(string text)
{
// Remove common artifacts
text = text.Trim();
// Remove model's own tags if it added them
text = text.Replace("<|assistant|>", "");
text = text.Replace("</|assistant|>", "");
// Remove incomplete sentences at end
if (!text.EndsWith(".") && !text.EndsWith("!") && !text.EndsWith("?"))
{
var lastPeriod = text.LastIndexOf('.');
if (lastPeriod > text.Length / 2) // Only if more than halfway through
{
text = text.Substring(0, lastPeriod + 1);
}
}
return text.Trim();
}
private int EstimateTokens(string text)
{
return text.Length / 4;
}
}
}
与UI整合
更新“建议查看模式”以使用新服务@:
public partial class SuggestionsViewModel : ViewModelBase
{
private readonly IContentGenerationService _generationService;
[RelayCommand]
private async Task GenerateContinuation()
{
IsGenerating = true;
try
{
var request = new GenerationRequest
{
CurrentText = GetCurrentText(),
Type = PromptType.ContinueWriting,
MaxContextTokens = 2000
};
var result = await _generationService.GenerateAsync(request);
AiSuggestion = result.GeneratedText;
SourcePosts.Clear();
foreach (var source in result.SourcePosts)
{
SourcePosts.Add(source);
}
StatusMessage = $"Generated in {result.GenerationTime.TotalSeconds:F1}s using {result.ContextTokensUsed} context tokens";
}
catch (Exception ex)
{
AiSuggestion = $"Error: {ex.Message}";
}
finally
{
IsGenerating = false;
}
}
[RelayCommand]
private async Task GenerateStructure()
{
var sectionTitle = PromptForSectionTitle(); // Show dialog
var request = new GenerationRequest
{
CurrentText = GetCurrentText(),
Type = PromptType.SuggestStructure,
Variables = new Dictionary<string, string>
{
["section_title"] = sectionTitle
}
};
// ... similar to above
}
}
实时流
为了更好的 UX,流牌, 当它们 '\ \ MPK2\
[RelayCommand]
private async Task GenerateWithStreaming()
{
IsGenerating = true;
AiSuggestion = "";
try
{
var request = new GenerationRequest
{
CurrentText = GetCurrentText(),
Type = PromptType.ContinueWriting
};
await foreach (var token in _generationService.GenerateStreamAsync(request))
{
// Update UI in real-time
await Dispatcher.UIThread.InvokeAsync(() =>
{
AiSuggestion += token;
});
}
}
finally
{
IsGenerating = false;
}
}
质量过滤
并非所有世代都是好. Let'增加质量检查?":
public class QualityFilter
{
public bool IsAcceptableQuality(string generatedText, string currentText)
{
// Too short
if (generatedText.Length < 50)
{
return false;
}
// Just repeats the prompt
if (generatedText.StartsWith(currentText.Substring(Math.Max(0, currentText.Length - 100))))
{
return false;
}
// Contains common failure modes
var badPatterns = new[]
{
"I cannot",
"I don't have",
"As an AI",
"I apologize"
};
if (badPatterns.Any(p => generatedText.Contains(p, StringComparison.OrdinalIgnoreCase)))
{
return false;
}
// Contains markdown artifacts
if (generatedText.Contains("```") && !generatedText.Contains("```\n"))
{
return false; // Incomplete code block
}
return true;
}
public string SuggestImprovement(string generatedText)
{
if (generatedText.Length < 50)
return "Too short - try increasing max_tokens";
if (!generatedText.Contains("."))
return "No complete sentences - check for truncation";
return "Quality seems acceptable";
}
}
使用率示例
继续写入
var request = new GenerationRequest
{
CurrentText = @"# Using Docker Compose for Development
Docker Compose makes it incredibly easy to manage multiple containers. In this post, I'll show you how to set up a complete development environment with",
Type = PromptType.ContinueWriting
};
var result = await _generationService.GenerateAsync(request);
// Result: "databases, caches, and message queues all defined in a single YAML file. We'll start with PostgreSQL and Redis, then add RabbitMQ for asynchronous messaging."
建议结构
var request = new GenerationRequest
{
CurrentText = "# Understanding Entity Framework Migrations",
Type = PromptType.SuggestStructure,
Variables = new Dictionary<string, string>
{
["section_title"] = "Best Practices for Production"
}
};
var result = await _generationService.GenerateAsync(request);
// Result:
// - Always review generated migrations before applying
// - Test migrations on staging environment first
// - Keep migration history in version control
// - Plan for rollback scenarios
// - Monitor migration performance on large tables
生成代码
var request = new GenerationRequest
{
CurrentText = "We need to configure Entity Framework with PostgreSQL",
Type = PromptType.GenerateCode,
Variables = new Dictionary<string, string>
{
["code_request"] = "Create a DbContext setup with PostgreSQL connection"
}
};
var result = await _generationService.GenerateAsync(request);
// Result: Complete C# code example
业绩监测
音轨生成性能 @: @%
public class GenerationMetrics
{
private readonly List<GenerationResult> _recentResults = new();
private readonly object _lock = new();
public void RecordResult(GenerationResult result)
{
lock (_lock)
{
_recentResults.Add(result);
if (_recentResults.Count > 100)
{
_recentResults.RemoveAt(0);
}
}
}
public GenerationStats GetStats()
{
lock (_lock)
{
if (_recentResults.Count == 0)
return new GenerationStats();
return new GenerationStats
{
AverageGenerationTimeMs = _recentResults.Average(r => r.GenerationTime.TotalMilliseconds),
AverageContextTokens = _recentResults.Average(r => r.ContextTokensUsed),
AverageGeneratedTokens = _recentResults.Average(r => r.GeneratedTokens),
TotalGenerations = _recentResults.Count
};
}
}
}
public class GenerationStats
{
public double AverageGenerationTimeMs { get; set; }
public double AverageContextTokens { get; set; }
public double AverageGeneratedTokens { get; set; }
public int TotalGenerations { get; set; }
}
摘要摘要
我们建立了完整的内容生成系统 :
- 带有智能块选择的 & ✅\ 上下文构建器
- ✅ 不同情景的高级快速模板
- ✅ 快速建造器,可变替代
- ✅ 内容生成服务 协调一切
- ✅Real-时间流向更好的 UX
- ✅ 拒绝错误输出的质量过滤
- 绩效监测
- 与 Windows 客户端整合
什么是' 下一步 @?
内 生产部署 最后部分 !), 我们的'将覆盖:
- 自动- 连接相关职位
- 部署部署战略
- 配置管理
- 不断改进
- 真实-世界使用模式
- 排除麻烦指南
- 未来改进
@We'#Lall 结束系列与部署并制作这出作品_-}@ready!}
系列导航
- 部分: 1:_ 导言 _&建筑
- 部分@2:GPU 设置 @&CUDA 在 CQ#
- “3: 理解嵌入器”部分
- 建立接收管道
- 窗口客户端
- 6:地方LLM整合部分
- *内容生成 &}快速工程 校对:Soup
- 生产部署
资源资源资源 资源和资源资源资源
看到你在 最后一期!
Finding related posts...